| author | |
| committer | |
| log | 3eb729b442d6cc69bd9a9d37816e9458190bc30b |
| tree | a412281c18914503c1566e79ab6a6f4eea187bf3 |
| parent | 1f653b7f8e9e358a5bfe2695a11c01da56f3d5ee |
| parent | c4cd592f0e1eeff5a4056796610d97010ae4e38c |
200 files changed, 34663 insertions(+), 14445 deletions(-)
.builds/freebsd.yml+1-1| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | image: freebsd/latest |
| 2 | 2 | secrets: |
| 3 | 3 | - 51bfddf5-86a6-4e01-8576-358c72a4a0a4 |
| 4 | - 5cfede76-914e-4071-893e-e5e2e6ae3cea | |
| 4 | - 512ed797-0927-475a-83fd-bc997792860c | |
| 5 | 5 | sources: |
| 6 | 6 | - https://github.com/ziglang/zig |
| 7 | 7 | tasks: |
.builds/netbsd.yml+1-1| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | image: netbsd/latest |
| 2 | 2 | secrets: |
| 3 | 3 | - 51bfddf5-86a6-4e01-8576-358c72a4a0a4 |
| 4 | - 5cfede76-914e-4071-893e-e5e2e6ae3cea | |
| 4 | - 512ed797-0927-475a-83fd-bc997792860c | |
| 5 | 5 | sources: |
| 6 | 6 | - https://github.com/ziglang/zig |
| 7 | 7 | tasks: |
CMakeLists.txt+16-14| ... | ... | @@ -537,41 +537,43 @@ set(ZIG_STAGE2_SOURCES |
| 537 | 537 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" |
| 538 | 538 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig" |
| 539 | 539 | "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig" |
| 540 | "${CMAKE_SOURCE_DIR}/src/Air.zig" | |
| 541 | "${CMAKE_SOURCE_DIR}/src/AstGen.zig" | |
| 540 | 542 | "${CMAKE_SOURCE_DIR}/src/Cache.zig" |
| 541 | 543 | "${CMAKE_SOURCE_DIR}/src/Compilation.zig" |
| 542 | 544 | "${CMAKE_SOURCE_DIR}/src/DepTokenizer.zig" |
| 545 | "${CMAKE_SOURCE_DIR}/src/Liveness.zig" | |
| 543 | 546 | "${CMAKE_SOURCE_DIR}/src/Module.zig" |
| 544 | 547 | "${CMAKE_SOURCE_DIR}/src/Package.zig" |
| 545 | 548 | "${CMAKE_SOURCE_DIR}/src/RangeSet.zig" |
| 549 | "${CMAKE_SOURCE_DIR}/src/Sema.zig" | |
| 546 | 550 | "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig" |
| 547 | 551 | "${CMAKE_SOURCE_DIR}/src/TypedValue.zig" |
| 548 | 552 | "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig" |
| 549 | "${CMAKE_SOURCE_DIR}/src/AstGen.zig" | |
| 553 | "${CMAKE_SOURCE_DIR}/src/Zir.zig" | |
| 554 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/bits.zig" | |
| 555 | "${CMAKE_SOURCE_DIR}/src/arch/arm/bits.zig" | |
| 556 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/bits.zig" | |
| 557 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig" | |
| 550 | 558 | "${CMAKE_SOURCE_DIR}/src/clang.zig" |
| 551 | 559 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" |
| 552 | 560 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" |
| 553 | 561 | "${CMAKE_SOURCE_DIR}/src/codegen.zig" |
| 554 | "${CMAKE_SOURCE_DIR}/src/codegen/aarch64.zig" | |
| 555 | "${CMAKE_SOURCE_DIR}/src/codegen/arm.zig" | |
| 556 | 562 | "${CMAKE_SOURCE_DIR}/src/codegen/c.zig" |
| 557 | 563 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig" |
| 558 | 564 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" |
| 559 | "${CMAKE_SOURCE_DIR}/src/codegen/riscv64.zig" | |
| 560 | 565 | "${CMAKE_SOURCE_DIR}/src/codegen/wasm.zig" |
| 561 | "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig" | |
| 562 | 566 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" |
| 563 | 567 | "${CMAKE_SOURCE_DIR}/src/introspect.zig" |
| 564 | "${CMAKE_SOURCE_DIR}/src/Air.zig" | |
| 565 | 568 | "${CMAKE_SOURCE_DIR}/src/libc_installation.zig" |
| 566 | 569 | "${CMAKE_SOURCE_DIR}/src/libcxx.zig" |
| 567 | 570 | "${CMAKE_SOURCE_DIR}/src/libtsan.zig" |
| 568 | 571 | "${CMAKE_SOURCE_DIR}/src/libunwind.zig" |
| 569 | 572 | "${CMAKE_SOURCE_DIR}/src/link.zig" |
| 570 | 573 | "${CMAKE_SOURCE_DIR}/src/link/C.zig" |
| 574 | "${CMAKE_SOURCE_DIR}/src/link/C/zig.h" | |
| 571 | 575 | "${CMAKE_SOURCE_DIR}/src/link/Coff.zig" |
| 572 | 576 | "${CMAKE_SOURCE_DIR}/src/link/Elf.zig" |
| 573 | "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig" | |
| 574 | "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig" | |
| 575 | 577 | "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" |
| 576 | 578 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" |
| 577 | 579 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig" |
| ... | ... | @@ -582,20 +584,22 @@ set(ZIG_STAGE2_SOURCES |
| 582 | 584 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig" |
| 583 | 585 | "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig" |
| 584 | 586 | "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig" |
| 587 | "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig" | |
| 588 | "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig" | |
| 585 | 589 | "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig" |
| 590 | "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin" | |
| 586 | 591 | "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" |
| 592 | "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig" | |
| 587 | 593 | "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig" |
| 588 | 594 | "${CMAKE_SOURCE_DIR}/src/link/tapi/parse/test.zig" |
| 589 | "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig" | |
| 590 | 595 | "${CMAKE_SOURCE_DIR}/src/link/tapi/yaml.zig" |
| 591 | "${CMAKE_SOURCE_DIR}/src/link/C/zig.h" | |
| 592 | "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin" | |
| 593 | "${CMAKE_SOURCE_DIR}/src/Liveness.zig" | |
| 594 | 596 | "${CMAKE_SOURCE_DIR}/src/main.zig" |
| 595 | 597 | "${CMAKE_SOURCE_DIR}/src/mingw.zig" |
| 596 | 598 | "${CMAKE_SOURCE_DIR}/src/musl.zig" |
| 599 | "${CMAKE_SOURCE_DIR}/src/print_air.zig" | |
| 597 | 600 | "${CMAKE_SOURCE_DIR}/src/print_env.zig" |
| 598 | 601 | "${CMAKE_SOURCE_DIR}/src/print_targets.zig" |
| 602 | "${CMAKE_SOURCE_DIR}/src/print_zir.zig" | |
| 599 | 603 | "${CMAKE_SOURCE_DIR}/src/stage1.zig" |
| 600 | 604 | "${CMAKE_SOURCE_DIR}/src/target.zig" |
| 601 | 605 | "${CMAKE_SOURCE_DIR}/src/tracy.zig" |
| ... | ... | @@ -605,8 +609,6 @@ set(ZIG_STAGE2_SOURCES |
| 605 | 609 | "${CMAKE_SOURCE_DIR}/src/value.zig" |
| 606 | 610 | "${CMAKE_SOURCE_DIR}/src/wasi_libc.zig" |
| 607 | 611 | "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig" |
| 608 | "${CMAKE_SOURCE_DIR}/src/Zir.zig" | |
| 609 | "${CMAKE_SOURCE_DIR}/src/Sema.zig" | |
| 610 | 612 | ) |
| 611 | 613 | |
| 612 | 614 | if(MSVC) |
build.zig+53-26| ... | ... | @@ -18,6 +18,7 @@ pub fn build(b: *Builder) !void { |
| 18 | 18 | const mode = b.standardReleaseOptions(); |
| 19 | 19 | const target = b.standardTargetOptions(.{}); |
| 20 | 20 | const single_threaded = b.option(bool, "single-threaded", "Build artifacts that run in single threaded mode") orelse false; |
| 21 | 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; | |
| 21 | 22 | |
| 22 | 23 | var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); |
| 23 | 24 | docgen_exe.single_threaded = single_threaded; |
| ... | ... | @@ -125,10 +126,18 @@ pub fn build(b: *Builder) !void { |
| 125 | 126 | exe.install(); |
| 126 | 127 | exe.setBuildMode(mode); |
| 127 | 128 | exe.setTarget(target); |
| 128 | toolchain_step.dependOn(&exe.step); | |
| 129 | if (!skip_stage2_tests) { | |
| 130 | toolchain_step.dependOn(&exe.step); | |
| 131 | } | |
| 129 | 132 | b.default_step.dependOn(&exe.step); |
| 130 | 133 | exe.single_threaded = single_threaded; |
| 131 | 134 | |
| 135 | if (target.isWindows() and target.getAbi() == .gnu) { | |
| 136 | // LTO is currently broken on mingw, this can be removed when it's fixed. | |
| 137 | exe.want_lto = false; | |
| 138 | test_stage2.want_lto = false; | |
| 139 | } | |
| 140 | ||
| 132 | 141 | const exe_options = b.addOptions(); |
| 133 | 142 | exe.addOptions("build_options", exe_options); |
| 134 | 143 | |
| ... | ... | @@ -182,8 +191,8 @@ pub fn build(b: *Builder) !void { |
| 182 | 191 | b.addSearchPrefix(cfg.cmake_prefix_path); |
| 183 | 192 | } |
| 184 | 193 | |
| 185 | try addCmakeCfgOptionsToExe(b, cfg, exe); | |
| 186 | try addCmakeCfgOptionsToExe(b, cfg, test_stage2); | |
| 194 | try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx); | |
| 195 | try addCmakeCfgOptionsToExe(b, cfg, test_stage2, use_zig_libcxx); | |
| 187 | 196 | } else { |
| 188 | 197 | // Here we are -Denable-llvm but no cmake integration. |
| 189 | 198 | try addStaticLlvmOptionsToExe(exe); |
| ... | ... | @@ -260,12 +269,24 @@ pub fn build(b: *Builder) !void { |
| 260 | 269 | b.allocator, |
| 261 | 270 | &[_][]const u8{ tracy_path, "TracyClient.cpp" }, |
| 262 | 271 | ) catch unreachable; |
| 272 | ||
| 273 | // On mingw, we need to opt into windows 7+ to get some features required by tracy. | |
| 274 | const tracy_c_flags: []const []const u8 = if (target.isWindows() and target.getAbi() == .gnu) | |
| 275 | &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined", "-D_WIN32_WINNT=0x601" } | |
| 276 | else | |
| 277 | &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }; | |
| 278 | ||
| 263 | 279 | exe.addIncludeDir(tracy_path); |
| 264 | exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }); | |
| 280 | exe.addCSourceFile(client_cpp, tracy_c_flags); | |
| 265 | 281 | if (!enable_llvm) { |
| 266 | 282 | exe.linkSystemLibraryName("c++"); |
| 267 | 283 | } |
| 268 | 284 | exe.linkLibC(); |
| 285 | ||
| 286 | if (target.isWindows()) { | |
| 287 | exe.linkSystemLibrary("dbghelp"); | |
| 288 | exe.linkSystemLibrary("ws2_32"); | |
| 289 | } | |
| 269 | 290 | } |
| 270 | 291 | |
| 271 | 292 | const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); |
| ... | ... | @@ -434,6 +455,7 @@ fn addCmakeCfgOptionsToExe( |
| 434 | 455 | b: *Builder, |
| 435 | 456 | cfg: CMakeConfig, |
| 436 | 457 | exe: *std.build.LibExeObjStep, |
| 458 | use_zig_libcxx: bool, | |
| 437 | 459 | ) !void { |
| 438 | 460 | exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{ |
| 439 | 461 | cfg.cmake_binary_dir, |
| ... | ... | @@ -446,28 +468,32 @@ fn addCmakeCfgOptionsToExe( |
| 446 | 468 | addCMakeLibraryList(exe, cfg.lld_libraries); |
| 447 | 469 | addCMakeLibraryList(exe, cfg.llvm_libraries); |
| 448 | 470 | |
| 449 | const need_cpp_includes = true; | |
| 450 | ||
| 451 | // System -lc++ must be used because in this code path we are attempting to link | |
| 452 | // against system-provided LLVM, Clang, LLD. | |
| 453 | if (exe.target.getOsTag() == .linux) { | |
| 454 | // First we try to static link against gcc libstdc++. If that doesn't work, | |
| 455 | // we fall back to -lc++ and cross our fingers. | |
| 456 | addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) { | |
| 457 | error.RequiredLibraryNotFound => { | |
| 458 | exe.linkSystemLibrary("c++"); | |
| 459 | }, | |
| 460 | else => |e| return e, | |
| 461 | }; | |
| 462 | exe.linkSystemLibrary("unwind"); | |
| 463 | } else if (exe.target.isFreeBSD()) { | |
| 464 | try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); | |
| 465 | exe.linkSystemLibrary("pthread"); | |
| 466 | } else if (exe.target.getOsTag() == .openbsd) { | |
| 467 | try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); | |
| 468 | try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes); | |
| 469 | } else if (exe.target.isDarwin()) { | |
| 470 | exe.linkSystemLibrary("c++"); | |
| 471 | if (use_zig_libcxx) { | |
| 472 | exe.linkLibCpp(); | |
| 473 | } else { | |
| 474 | const need_cpp_includes = true; | |
| 475 | ||
| 476 | // System -lc++ must be used because in this code path we are attempting to link | |
| 477 | // against system-provided LLVM, Clang, LLD. | |
| 478 | if (exe.target.getOsTag() == .linux) { | |
| 479 | // First we try to static link against gcc libstdc++. If that doesn't work, | |
| 480 | // we fall back to -lc++ and cross our fingers. | |
| 481 | addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) { | |
| 482 | error.RequiredLibraryNotFound => { | |
| 483 | exe.linkSystemLibrary("c++"); | |
| 484 | }, | |
| 485 | else => |e| return e, | |
| 486 | }; | |
| 487 | exe.linkSystemLibrary("unwind"); | |
| 488 | } else if (exe.target.isFreeBSD()) { | |
| 489 | try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); | |
| 490 | exe.linkSystemLibrary("pthread"); | |
| 491 | } else if (exe.target.getOsTag() == .openbsd) { | |
| 492 | try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); | |
| 493 | try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes); | |
| 494 | } else if (exe.target.isDarwin()) { | |
| 495 | exe.linkSystemLibrary("c++"); | |
| 496 | } | |
| 471 | 497 | } |
| 472 | 498 | |
| 473 | 499 | if (cfg.dia_guids_lib.len != 0) { |
| ... | ... | @@ -504,6 +530,7 @@ fn addStaticLlvmOptionsToExe( |
| 504 | 530 | if (exe.target.getOs().tag == .windows) { |
| 505 | 531 | exe.linkSystemLibrary("version"); |
| 506 | 532 | exe.linkSystemLibrary("uuid"); |
| 533 | exe.linkSystemLibrary("ole32"); | |
| 507 | 534 | } |
| 508 | 535 | } |
| 509 | 536 |
ci/azure/linux_script+17-4| ... | ... | @@ -20,7 +20,7 @@ cd $HOME |
| 20 | 20 | wget -nv "https://ziglang.org/deps/$CACHE_BASENAME.tar.xz" |
| 21 | 21 | tar xf "$CACHE_BASENAME.tar.xz" |
| 22 | 22 | |
| 23 | QEMUBASE="qemu-linux-x86_64-5.2.0.1" | |
| 23 | QEMUBASE="qemu-linux-x86_64-6.1.0.1" | |
| 24 | 24 | wget -nv "https://ziglang.org/deps/$QEMUBASE.tar.xz" |
| 25 | 25 | tar xf "$QEMUBASE.tar.xz" |
| 26 | 26 | export PATH="$(pwd)/$QEMUBASE/bin:$PATH" |
| ... | ... | @@ -71,9 +71,22 @@ make $JOBS install |
| 71 | 71 | |
| 72 | 72 | release/bin/zig test ../test/behavior.zig -fno-stage1 -fLLVM -I ../test |
| 73 | 73 | |
| 74 | release/bin/zig build test-toolchain -Denable-qemu -Denable-wasmtime | |
| 75 | release/bin/zig build test-std -Denable-qemu -Denable-wasmtime | |
| 76 | release/bin/zig build docs -Denable-qemu -Denable-wasmtime | |
| 74 | release/bin/zig build test-behavior -Denable-qemu -Denable-wasmtime | |
| 75 | release/bin/zig build test-compiler-rt -Denable-qemu -Denable-wasmtime | |
| 76 | release/bin/zig build test-std -Denable-qemu -Denable-wasmtime | |
| 77 | release/bin/zig build test-minilibc -Denable-qemu -Denable-wasmtime | |
| 78 | release/bin/zig build test-compare-output -Denable-qemu -Denable-wasmtime | |
| 79 | release/bin/zig build test-standalone -Denable-qemu -Denable-wasmtime | |
| 80 | release/bin/zig build test-stack-traces -Denable-qemu -Denable-wasmtime | |
| 81 | release/bin/zig build test-cli -Denable-qemu -Denable-wasmtime | |
| 82 | release/bin/zig build test-asm-link -Denable-qemu -Denable-wasmtime | |
| 83 | release/bin/zig build test-runtime-safety -Denable-qemu -Denable-wasmtime | |
| 84 | release/bin/zig build test-translate-c -Denable-qemu -Denable-wasmtime | |
| 85 | release/bin/zig build test-run-translated-c -Denable-qemu -Denable-wasmtime | |
| 86 | release/bin/zig build docs -Denable-qemu -Denable-wasmtime | |
| 87 | release/bin/zig build # test building self-hosted without LLVM | |
| 88 | release/bin/zig build test-fmt -Denable-qemu -Denable-wasmtime | |
| 89 | release/bin/zig build test-stage2 -Denable-qemu -Denable-wasmtime | |
| 77 | 90 | |
| 78 | 91 | # Look for HTML errors. |
| 79 | 92 | tidy -qe ../zig-cache/langref.html |
ci/azure/pipelines.yml+1-2| ... | ... | @@ -38,9 +38,8 @@ jobs: |
| 38 | 38 | timeoutInMinutes: 360 |
| 39 | 39 | steps: |
| 40 | 40 | - powershell: | |
| 41 | (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2021-06-04/msys2-base-x86_64-20210604.sfx.exe", "sfx.exe") | |
| 41 | (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2021-07-25/msys2-base-x86_64-20210725.sfx.exe", "sfx.exe") | |
| 42 | 42 | .\sfx.exe -y -o\ |
| 43 | del sfx.exe | |
| 44 | 43 | displayName: Download/Extract/Install MSYS2 |
| 45 | 44 | - script: | |
| 46 | 45 | @REM install updated filesystem package first without dependency checking |
ci/azure/windows_msvc_script.bat+1-1| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | @echo on |
| 2 | 2 | SET "SRCROOT=%cd%" |
| 3 | 3 | SET "PREVPATH=%PATH%" |
| 4 | SET "PREVMSYSEM=%MSYSTEM%" | |
| 4 | SET "PREVMSYSTEM=%MSYSTEM%" | |
| 5 | 5 | |
| 6 | 6 | set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem" |
| 7 | 7 | SET "MSYSTEM=MINGW64" |
doc/docgen.zig+10-3| ... | ... | @@ -901,6 +901,7 @@ fn tokenizeAndPrintRaw( |
| 901 | 901 | switch (token.tag) { |
| 902 | 902 | .eof => break, |
| 903 | 903 | |
| 904 | .keyword_addrspace, | |
| 904 | 905 | .keyword_align, |
| 905 | 906 | .keyword_and, |
| 906 | 907 | .keyword_asm, |
| ... | ... | @@ -1057,15 +1058,21 @@ fn tokenizeAndPrintRaw( |
| 1057 | 1058 | .plus_equal, |
| 1058 | 1059 | .plus_percent, |
| 1059 | 1060 | .plus_percent_equal, |
| 1061 | .plus_pipe, | |
| 1062 | .plus_pipe_equal, | |
| 1060 | 1063 | .minus, |
| 1061 | 1064 | .minus_equal, |
| 1062 | 1065 | .minus_percent, |
| 1063 | 1066 | .minus_percent_equal, |
| 1067 | .minus_pipe, | |
| 1068 | .minus_pipe_equal, | |
| 1064 | 1069 | .asterisk, |
| 1065 | 1070 | .asterisk_equal, |
| 1066 | 1071 | .asterisk_asterisk, |
| 1067 | 1072 | .asterisk_percent, |
| 1068 | 1073 | .asterisk_percent_equal, |
| 1074 | .asterisk_pipe, | |
| 1075 | .asterisk_pipe_equal, | |
| 1069 | 1076 | .arrow, |
| 1070 | 1077 | .colon, |
| 1071 | 1078 | .slash, |
| ... | ... | @@ -1078,6 +1085,8 @@ fn tokenizeAndPrintRaw( |
| 1078 | 1085 | .angle_bracket_left_equal, |
| 1079 | 1086 | .angle_bracket_angle_bracket_left, |
| 1080 | 1087 | .angle_bracket_angle_bracket_left_equal, |
| 1088 | .angle_bracket_angle_bracket_left_pipe, | |
| 1089 | .angle_bracket_angle_bracket_left_pipe_equal, | |
| 1081 | 1090 | .angle_bracket_right, |
| 1082 | 1091 | .angle_bracket_right_equal, |
| 1083 | 1092 | .angle_bracket_angle_bracket_right, |
| ... | ... | @@ -1222,9 +1231,7 @@ fn genHtml( |
| 1222 | 1231 | |
| 1223 | 1232 | try printSourceBlock(allocator, tokenizer, out, syntax_block); |
| 1224 | 1233 | |
| 1225 | // TODO: remove code.just_check_syntax after updating code samples | |
| 1226 | // that have stopped working due to a change in the compiler. | |
| 1227 | if (!do_code_tests or code.just_check_syntax) { | |
| 1234 | if (!do_code_tests) { | |
| 1228 | 1235 | continue; |
| 1229 | 1236 | } |
| 1230 | 1237 |
doc/langref.html.in+268-184| ... | ... | @@ -1244,8 +1244,9 @@ fn divide(a: i32, b: i32) i32 { |
| 1244 | 1244 | </p> |
| 1245 | 1245 | <p> |
| 1246 | 1246 | Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on |
| 1247 | integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and | |
| 1248 | {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets. | |
| 1247 | integer overflow. Alternative operators are provided for wrapping and saturating arithmetic on all targets. | |
| 1248 | {#syntax#}+%{#endsyntax#} and {#syntax#}-%{#endsyntax#} perform wrapping arithmetic | |
| 1249 | while {#syntax#}+|{#endsyntax#} and {#syntax#}-|{#endsyntax#} perform saturating arithmetic. | |
| 1249 | 1250 | </p> |
| 1250 | 1251 | <p> |
| 1251 | 1252 | Zig supports arbitrary bit-width integers, referenced by using |
| ... | ... | @@ -1395,6 +1396,23 @@ a +%= b{#endsyntax#}</pre></th> |
| 1395 | 1396 | <pre>{#syntax#}@as(u32, std.math.maxInt(u32)) +% 1 == 0{#endsyntax#}</pre> |
| 1396 | 1397 | </td> |
| 1397 | 1398 | </tr> |
| 1399 | <tr> | |
| 1400 | <th scope="row"><pre>{#syntax#}a +| b | |
| 1401 | a +|= b{#endsyntax#}</pre></th> | |
| 1402 | <td> | |
| 1403 | <ul> | |
| 1404 | <li>{#link|Integers#}</li> | |
| 1405 | </ul> | |
| 1406 | </td> | |
| 1407 | <td>Saturating Addition. | |
| 1408 | <ul> | |
| 1409 | <li>Invokes {#link|Peer Type Resolution#} for the operands.</li> | |
| 1410 | </ul> | |
| 1411 | </td> | |
| 1412 | <td> | |
| 1413 | <pre>{#syntax#}@as(u32, std.math.maxInt(u32)) +| 1 == @as(u32, std.math.maxInt(u32)){#endsyntax#}</pre> | |
| 1414 | </td> | |
| 1415 | </tr> | |
| 1398 | 1416 | <tr> |
| 1399 | 1417 | <th scope="row"><pre>{#syntax#}a - b |
| 1400 | 1418 | a -= b{#endsyntax#}</pre></th> |
| ... | ... | @@ -1434,6 +1452,23 @@ a -%= b{#endsyntax#}</pre></th> |
| 1434 | 1452 | <pre>{#syntax#}@as(u32, 0) -% 1 == std.math.maxInt(u32){#endsyntax#}</pre> |
| 1435 | 1453 | </td> |
| 1436 | 1454 | </tr> |
| 1455 | <tr> | |
| 1456 | <th scope="row"><pre>{#syntax#}a -| b | |
| 1457 | a -|= b{#endsyntax#}</pre></th> | |
| 1458 | <td> | |
| 1459 | <ul> | |
| 1460 | <li>{#link|Integers#}</li> | |
| 1461 | </ul> | |
| 1462 | </td> | |
| 1463 | <td>Saturating Subtraction. | |
| 1464 | <ul> | |
| 1465 | <li>Invokes {#link|Peer Type Resolution#} for the operands.</li> | |
| 1466 | </ul> | |
| 1467 | </td> | |
| 1468 | <td> | |
| 1469 | <pre>{#syntax#}@as(u32, 0) -| 1 == 0{#endsyntax#}</pre> | |
| 1470 | </td> | |
| 1471 | </tr> | |
| 1437 | 1472 | <tr> |
| 1438 | 1473 | <th scope="row"><pre>{#syntax#}-a{#endsyntax#}</pre></th> |
| 1439 | 1474 | <td> |
| ... | ... | @@ -1508,6 +1543,23 @@ a *%= b{#endsyntax#}</pre></th> |
| 1508 | 1543 | <pre>{#syntax#}@as(u8, 200) *% 2 == 144{#endsyntax#}</pre> |
| 1509 | 1544 | </td> |
| 1510 | 1545 | </tr> |
| 1546 | <tr> | |
| 1547 | <th scope="row"><pre>{#syntax#}a *| b | |
| 1548 | a *|= b{#endsyntax#}</pre></th> | |
| 1549 | <td> | |
| 1550 | <ul> | |
| 1551 | <li>{#link|Integers#}</li> | |
| 1552 | </ul> | |
| 1553 | </td> | |
| 1554 | <td>Saturating Multiplication. | |
| 1555 | <ul> | |
| 1556 | <li>Invokes {#link|Peer Type Resolution#} for the operands.</li> | |
| 1557 | </ul> | |
| 1558 | </td> | |
| 1559 | <td> | |
| 1560 | <pre>{#syntax#}@as(u8, 200) *| 2 == 255{#endsyntax#}</pre> | |
| 1561 | </td> | |
| 1562 | </tr> | |
| 1511 | 1563 | <tr> |
| 1512 | 1564 | <th scope="row"><pre>{#syntax#}a / b |
| 1513 | 1565 | a /= b{#endsyntax#}</pre></th> |
| ... | ... | @@ -1577,6 +1629,24 @@ a <<= b{#endsyntax#}</pre></th> |
| 1577 | 1629 | <pre>{#syntax#}1 << 8 == 256{#endsyntax#}</pre> |
| 1578 | 1630 | </td> |
| 1579 | 1631 | </tr> |
| 1632 | <tr> | |
| 1633 | <th scope="row"><pre>{#syntax#}a <<| b | |
| 1634 | a <<|= b{#endsyntax#}</pre></th> | |
| 1635 | <td> | |
| 1636 | <ul> | |
| 1637 | <li>{#link|Integers#}</li> | |
| 1638 | </ul> | |
| 1639 | </td> | |
| 1640 | <td>Saturating Bit Shift Left. | |
| 1641 | <ul> | |
| 1642 | <li>See also {#link|@shlExact#}.</li> | |
| 1643 | <li>See also {#link|@shlWithOverflow#}.</li> | |
| 1644 | </ul> | |
| 1645 | </td> | |
| 1646 | <td> | |
| 1647 | <pre>{#syntax#}@as(u8, 1) <<| 8 == 255{#endsyntax#}</pre> | |
| 1648 | </td> | |
| 1649 | </tr> | |
| 1580 | 1650 | <tr> |
| 1581 | 1651 | <th scope="row"><pre>{#syntax#}a >> b |
| 1582 | 1652 | a >>= b{#endsyntax#}</pre></th> |
| ... | ... | @@ -1968,14 +2038,14 @@ const B = error{Two}; |
| 1968 | 2038 | a!b |
| 1969 | 2039 | x{} |
| 1970 | 2040 | !x -x -%x ~x &x ?x |
| 1971 | * / % ** *% || | |
| 1972 | + - ++ +% -% | |
| 1973 | << >> | |
| 2041 | * / % ** *% *| || | |
| 2042 | + - ++ +% -% +| -| | |
| 2043 | << >> <<| | |
| 1974 | 2044 | & ^ | orelse catch |
| 1975 | 2045 | == != < > <= >= |
| 1976 | 2046 | and |
| 1977 | 2047 | or |
| 1978 | = *= /= %= += -= <<= >>= &= ^= |={#endsyntax#}</pre> | |
| 2048 | = *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |={#endsyntax#}</pre> | |
| 1979 | 2049 | {#header_close#} |
| 1980 | 2050 | {#header_close#} |
| 1981 | 2051 | {#header_open|Arrays#} |
| ... | ... | @@ -2125,7 +2195,7 @@ fn dump(args: anytype) !void { |
| 2125 | 2195 | |
| 2126 | 2196 | {#header_open|Multidimensional Arrays#} |
| 2127 | 2197 | <p> |
| 2128 | Mutlidimensional arrays can be created by nesting arrays: | |
| 2198 | Multidimensional arrays can be created by nesting arrays: | |
| 2129 | 2199 | </p> |
| 2130 | 2200 | {#code_begin|test|multidimensional#} |
| 2131 | 2201 | const std = @import("std"); |
| ... | ... | @@ -2898,7 +2968,7 @@ fn bar(x: *const u3) u3 { |
| 2898 | 2968 | } |
| 2899 | 2969 | {#code_end#} |
| 2900 | 2970 | <p> |
| 2901 | In this case, the function {#syntax#}bar{#endsyntax#} cannot be called becuse the pointer | |
| 2971 | In this case, the function {#syntax#}bar{#endsyntax#} cannot be called because the pointer | |
| 2902 | 2972 | to the non-ABI-aligned field mentions the bit offset, but the function expects an ABI-aligned pointer. |
| 2903 | 2973 | </p> |
| 2904 | 2974 | <p> |
| ... | ... | @@ -4771,6 +4841,8 @@ test "parse u64" { |
| 4771 | 4841 | {#header_open|catch#} |
| 4772 | 4842 | <p>If you want to provide a default value, you can use the {#syntax#}catch{#endsyntax#} binary operator:</p> |
| 4773 | 4843 | {#code_begin|syntax#} |
| 4844 | const parseU64 = @import("error_union_parsing_u64.zig").parseU64; | |
| 4845 | ||
| 4774 | 4846 | fn doAThing(str: []u8) void { |
| 4775 | 4847 | const number = parseU64(str, 10) catch 13; |
| 4776 | 4848 | _ = number; // ... |
| ... | ... | @@ -4786,6 +4858,8 @@ fn doAThing(str: []u8) void { |
| 4786 | 4858 | <p>Let's say you wanted to return the error if you got one, otherwise continue with the |
| 4787 | 4859 | function logic:</p> |
| 4788 | 4860 | {#code_begin|syntax#} |
| 4861 | const parseU64 = @import("error_union_parsing_u64.zig").parseU64; | |
| 4862 | ||
| 4789 | 4863 | fn doAThing(str: []u8) !void { |
| 4790 | 4864 | const number = parseU64(str, 10) catch |err| return err; |
| 4791 | 4865 | _ = number; // ... |
| ... | ... | @@ -4795,6 +4869,8 @@ fn doAThing(str: []u8) !void { |
| 4795 | 4869 | There is a shortcut for this. The {#syntax#}try{#endsyntax#} expression: |
| 4796 | 4870 | </p> |
| 4797 | 4871 | {#code_begin|syntax#} |
| 4872 | const parseU64 = @import("error_union_parsing_u64.zig").parseU64; | |
| 4873 | ||
| 4798 | 4874 | fn doAThing(str: []u8) !void { |
| 4799 | 4875 | const number = try parseU64(str, 10); |
| 4800 | 4876 | _ = number; // ... |
| ... | ... | @@ -4810,7 +4886,7 @@ fn doAThing(str: []u8) !void { |
| 4810 | 4886 | Maybe you know with complete certainty that an expression will never be an error. |
| 4811 | 4887 | In this case you can do this: |
| 4812 | 4888 | </p> |
| 4813 | {#code_begin|syntax#}const number = parseU64("1234", 10) catch unreachable;{#code_end#} | |
| 4889 | {#syntax#}const number = parseU64("1234", 10) catch unreachable;{#endsyntax#} | |
| 4814 | 4890 | <p> |
| 4815 | 4891 | Here we know for sure that "1234" will parse successfully. So we put the |
| 4816 | 4892 | {#syntax#}unreachable{#endsyntax#} value on the right hand side. {#syntax#}unreachable{#endsyntax#} generates |
| ... | ... | @@ -4822,7 +4898,7 @@ fn doAThing(str: []u8) !void { |
| 4822 | 4898 | Finally, you may want to take a different action for every situation. For that, we combine |
| 4823 | 4899 | the {#link|if#} and {#link|switch#} expression: |
| 4824 | 4900 | </p> |
| 4825 | {#code_begin|syntax#} | |
| 4901 | {#syntax_block|zig|handle_all_error_scenarios.zig#} | |
| 4826 | 4902 | fn doAThing(str: []u8) void { |
| 4827 | 4903 | if (parseU64(str, 10)) |number| { |
| 4828 | 4904 | doSomethingWithNumber(number); |
| ... | ... | @@ -4834,7 +4910,7 @@ fn doAThing(str: []u8) void { |
| 4834 | 4910 | error.InvalidChar => unreachable, |
| 4835 | 4911 | } |
| 4836 | 4912 | } |
| 4837 | {#code_end#} | |
| 4913 | {#end_syntax_block#} | |
| 4838 | 4914 | {#header_open|errdefer#} |
| 4839 | 4915 | <p> |
| 4840 | 4916 | The other component to error handling is defer statements. |
| ... | ... | @@ -4845,7 +4921,7 @@ fn doAThing(str: []u8) void { |
| 4845 | 4921 | <p> |
| 4846 | 4922 | Example: |
| 4847 | 4923 | </p> |
| 4848 | {#code_begin|syntax#} | |
| 4924 | {#syntax_block|zig|errdefer_example.zig#} | |
| 4849 | 4925 | fn createFoo(param: i32) !Foo { |
| 4850 | 4926 | const foo = try tryToAllocateFoo(); |
| 4851 | 4927 | // now we have allocated foo. we need to free it if the function fails. |
| ... | ... | @@ -4863,7 +4939,7 @@ fn createFoo(param: i32) !Foo { |
| 4863 | 4939 | // but the defer will run! |
| 4864 | 4940 | return foo; |
| 4865 | 4941 | } |
| 4866 | {#code_end#} | |
| 4942 | {#end_syntax_block#} | |
| 4867 | 4943 | <p> |
| 4868 | 4944 | The neat thing about this is that you get robust error handling without |
| 4869 | 4945 | the verbosity and cognitive overhead of trying to make sure every exit path |
| ... | ... | @@ -4955,7 +5031,7 @@ test "merge error sets" { |
| 4955 | 5031 | {#header_open|Inferred Error Sets#} |
| 4956 | 5032 | <p> |
| 4957 | 5033 | Because many functions in Zig return a possible error, Zig supports inferring the error set. |
| 4958 | To infer the error set for a function, use this syntax: | |
| 5034 | To infer the error set for a function, prepend the {#syntax#}!{#endsyntax#} operator to the function’s return type, like {#syntax#}!T{#endsyntax#}: | |
| 4959 | 5035 | </p> |
| 4960 | 5036 | {#code_begin|test|inferred_error_sets#} |
| 4961 | 5037 | // With an inferred error set |
| ... | ... | @@ -5132,12 +5208,12 @@ fn bang2() void { |
| 5132 | 5208 | For the case when no errors are returned, the cost is a single memory write operation, only in the first non-failable function in the call graph that calls a failable function, i.e. when a function returning {#syntax#}void{#endsyntax#} calls a function returning {#syntax#}error{#endsyntax#}. |
| 5133 | 5209 | This is to initialize this struct in the stack memory: |
| 5134 | 5210 | </p> |
| 5135 | {#code_begin|syntax#} | |
| 5211 | {#syntax_block|zig|stack_trace_struct.zig#} | |
| 5136 | 5212 | pub const StackTrace = struct { |
| 5137 | 5213 | index: usize, |
| 5138 | 5214 | instruction_addresses: [N]usize, |
| 5139 | 5215 | }; |
| 5140 | {#code_end#} | |
| 5216 | {#end_syntax_block#} | |
| 5141 | 5217 | <p> |
| 5142 | 5218 | Here, N is the maximum function call depth as determined by call graph analysis. Recursion is ignored and counts for 2. |
| 5143 | 5219 | </p> |
| ... | ... | @@ -5150,13 +5226,13 @@ pub const StackTrace = struct { |
| 5150 | 5226 | <p> |
| 5151 | 5227 | When generating the code for a function that returns an error, just before the {#syntax#}return{#endsyntax#} statement (only for the {#syntax#}return{#endsyntax#} statements that return errors), Zig generates a call to this function: |
| 5152 | 5228 | </p> |
| 5153 | {#code_begin|syntax#} | |
| 5229 | {#syntax_block|zig|zig_return_error_fn.zig#} | |
| 5154 | 5230 | // marked as "no-inline" in LLVM IR |
| 5155 | 5231 | fn __zig_return_error(stack_trace: *StackTrace) void { |
| 5156 | 5232 | stack_trace.instruction_addresses[stack_trace.index] = @returnAddress(); |
| 5157 | 5233 | stack_trace.index = (stack_trace.index + 1) % N; |
| 5158 | 5234 | } |
| 5159 | {#code_end#} | |
| 5235 | {#end_syntax_block#} | |
| 5160 | 5236 | <p> |
| 5161 | 5237 | The cost is 2 math operations plus some memory reads and writes. The memory accessed is constrained and should remain cached for the duration of the error return bubbling. |
| 5162 | 5238 | </p> |
| ... | ... | @@ -5206,16 +5282,16 @@ const optional_int: ?i32 = 5678; |
| 5206 | 5282 | Task: call malloc, if the result is null, return null. |
| 5207 | 5283 | </p> |
| 5208 | 5284 | <p>C code</p> |
| 5209 | <pre><code class="cpp">// malloc prototype included for reference | |
| 5285 | {#syntax_block|c|call_malloc_in_c.c#}// malloc prototype included for reference | |
| 5210 | 5286 | void *malloc(size_t size); |
| 5211 | 5287 | |
| 5212 | 5288 | struct Foo *do_a_thing(void) { |
| 5213 | 5289 | char *ptr = malloc(1234); |
| 5214 | 5290 | if (!ptr) return NULL; |
| 5215 | 5291 | // ... |
| 5216 | }</code></pre> | |
| 5292 | }{#end_syntax_block#} | |
| 5217 | 5293 | <p>Zig code</p> |
| 5218 | {#code_begin|syntax#} | |
| 5294 | {#syntax_block|zig|call_malloc_from_zig.zig#} | |
| 5219 | 5295 | // malloc prototype included for reference |
| 5220 | 5296 | extern fn malloc(size: size_t) ?*u8; |
| 5221 | 5297 | |
| ... | ... | @@ -5223,7 +5299,7 @@ fn doAThing() ?*Foo { |
| 5223 | 5299 | const ptr = malloc(1234) orelse return null; |
| 5224 | 5300 | _ = ptr; // ... |
| 5225 | 5301 | } |
| 5226 | {#code_end#} | |
| 5302 | {#end_syntax_block#} | |
| 5227 | 5303 | <p> |
| 5228 | 5304 | Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr" |
| 5229 | 5305 | is {#syntax#}*u8{#endsyntax#} <em>not</em> {#syntax#}?*u8{#endsyntax#}. The {#syntax#}orelse{#endsyntax#} keyword |
| ... | ... | @@ -5233,7 +5309,7 @@ fn doAThing() ?*Foo { |
| 5233 | 5309 | <p> |
| 5234 | 5310 | The other form of checking against NULL you might see looks like this: |
| 5235 | 5311 | </p> |
| 5236 | <pre><code class="cpp">void do_a_thing(struct Foo *foo) { | |
| 5312 | {#syntax_block|c|checking_null_in_c.c#}void do_a_thing(struct Foo *foo) { | |
| 5237 | 5313 | // do some stuff |
| 5238 | 5314 | |
| 5239 | 5315 | if (foo) { |
| ... | ... | @@ -5241,11 +5317,14 @@ fn doAThing() ?*Foo { |
| 5241 | 5317 | } |
| 5242 | 5318 | |
| 5243 | 5319 | // do some stuff |
| 5244 | }</code></pre> | |
| 5320 | }{#end_syntax_block#} | |
| 5245 | 5321 | <p> |
| 5246 | 5322 | In Zig you can accomplish the same thing: |
| 5247 | 5323 | </p> |
| 5248 | {#code_begin|syntax#} | |
| 5324 | {#code_begin|syntax|checking_null_in_zig#} | |
| 5325 | const Foo = struct{}; | |
| 5326 | fn doSomethingWithFoo(foo: *Foo) void { _ = foo; } | |
| 5327 | ||
| 5249 | 5328 | fn doAThing(optional_foo: ?*Foo) void { |
| 5250 | 5329 | // do some stuff |
| 5251 | 5330 | |
| ... | ... | @@ -5540,7 +5619,7 @@ test "coerce to optionals" { |
| 5540 | 5619 | } |
| 5541 | 5620 | {#code_end#} |
| 5542 | 5621 | <p>It works nested inside the {#link|Error Union Type#}, too:</p> |
| 5543 | {#code_begin|test|test_corerce_optional_wrapped_error_union#} | |
| 5622 | {#code_begin|test|test_coerce_optional_wrapped_error_union#} | |
| 5544 | 5623 | const std = @import("std"); |
| 5545 | 5624 | const expect = std.testing.expect; |
| 5546 | 5625 | |
| ... | ... | @@ -6111,7 +6190,7 @@ test "perform fn" { |
| 6111 | 6190 | different code. In this example, the function {#syntax#}performFn{#endsyntax#} is generated three different times, |
| 6112 | 6191 | for the different values of {#syntax#}prefix_char{#endsyntax#} provided: |
| 6113 | 6192 | </p> |
| 6114 | {#code_begin|syntax#} | |
| 6193 | {#syntax_block|zig|performFn_1#} | |
| 6115 | 6194 | // From the line: |
| 6116 | 6195 | // expect(performFn('t', 1) == 6); |
| 6117 | 6196 | fn performFn(start_value: i32) i32 { |
| ... | ... | @@ -6120,8 +6199,8 @@ fn performFn(start_value: i32) i32 { |
| 6120 | 6199 | result = three(result); |
| 6121 | 6200 | return result; |
| 6122 | 6201 | } |
| 6123 | {#code_end#} | |
| 6124 | {#code_begin|syntax#} | |
| 6202 | {#end_syntax_block#} | |
| 6203 | {#syntax_block|zig|performFn_2#} | |
| 6125 | 6204 | // From the line: |
| 6126 | 6205 | // expect(performFn('o', 0) == 1); |
| 6127 | 6206 | fn performFn(start_value: i32) i32 { |
| ... | ... | @@ -6129,15 +6208,15 @@ fn performFn(start_value: i32) i32 { |
| 6129 | 6208 | result = one(result); |
| 6130 | 6209 | return result; |
| 6131 | 6210 | } |
| 6132 | {#code_end#} | |
| 6133 | {#code_begin|syntax#} | |
| 6211 | {#end_syntax_block#} | |
| 6212 | {#syntax_block|zig|performFn_3#} | |
| 6134 | 6213 | // From the line: |
| 6135 | 6214 | // expect(performFn('w', 99) == 99); |
| 6136 | 6215 | fn performFn(start_value: i32) i32 { |
| 6137 | 6216 | var result: i32 = start_value; |
| 6138 | 6217 | return result; |
| 6139 | 6218 | } |
| 6140 | {#code_end#} | |
| 6219 | {#end_syntax_block#} | |
| 6141 | 6220 | <p> |
| 6142 | 6221 | Note that this happens even in a debug build; in a release build these generated functions still |
| 6143 | 6222 | pass through rigorous LLVM optimizations. The important thing to note, however, is not that this |
| ... | ... | @@ -6367,11 +6446,11 @@ const Node = struct { |
| 6367 | 6446 | it works fine. |
| 6368 | 6447 | </p> |
| 6369 | 6448 | {#header_close#} |
| 6370 | {#header_open|Case Study: printf in Zig#} | |
| 6449 | {#header_open|Case Study: print in Zig#} | |
| 6371 | 6450 | <p> |
| 6372 | Putting all of this together, let's see how {#syntax#}printf{#endsyntax#} works in Zig. | |
| 6451 | Putting all of this together, let's see how {#syntax#}print{#endsyntax#} works in Zig. | |
| 6373 | 6452 | </p> |
| 6374 | {#code_begin|exe|printf#} | |
| 6453 | {#code_begin|exe|print#} | |
| 6375 | 6454 | const print = @import("std").debug.print; |
| 6376 | 6455 | |
| 6377 | 6456 | const a_number: i32 = 1234; |
| ... | ... | @@ -6386,67 +6465,84 @@ pub fn main() void { |
| 6386 | 6465 | Let's crack open the implementation of this and see how it works: |
| 6387 | 6466 | </p> |
| 6388 | 6467 | |
| 6389 | {#code_begin|syntax#} | |
| 6390 | /// Calls print and then flushes the buffer. | |
| 6391 | pub fn printf(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void { | |
| 6392 | const State = enum { | |
| 6393 | start, | |
| 6394 | open_brace, | |
| 6395 | close_brace, | |
| 6396 | }; | |
| 6397 | ||
| 6398 | comptime var start_index: usize = 0; | |
| 6399 | comptime var state = State.start; | |
| 6400 | comptime var next_arg: usize = 0; | |
| 6468 | {#code_begin|syntax|poc_print_fn#} | |
| 6469 | const Writer = struct { | |
| 6470 | /// Calls print and then flushes the buffer. | |
| 6471 | pub fn print(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void { | |
| 6472 | const State = enum { | |
| 6473 | start, | |
| 6474 | open_brace, | |
| 6475 | close_brace, | |
| 6476 | }; | |
| 6401 | 6477 | |
| 6402 | inline for (format) |c, i| { | |
| 6403 | switch (state) { | |
| 6404 | State.start => switch (c) { | |
| 6405 | '{' => { | |
| 6406 | if (start_index < i) try self.write(format[start_index..i]); | |
| 6407 | state = State.open_brace; | |
| 6478 | comptime var start_index: usize = 0; | |
| 6479 | comptime var state = State.start; | |
| 6480 | comptime var next_arg: usize = 0; | |
| 6481 | ||
| 6482 | inline for (format) |c, i| { | |
| 6483 | switch (state) { | |
| 6484 | State.start => switch (c) { | |
| 6485 | '{' => { | |
| 6486 | if (start_index < i) try self.write(format[start_index..i]); | |
| 6487 | state = State.open_brace; | |
| 6488 | }, | |
| 6489 | '}' => { | |
| 6490 | if (start_index < i) try self.write(format[start_index..i]); | |
| 6491 | state = State.close_brace; | |
| 6492 | }, | |
| 6493 | else => {}, | |
| 6408 | 6494 | }, |
| 6409 | '}' => { | |
| 6410 | if (start_index < i) try self.write(format[start_index..i]); | |
| 6411 | state = State.close_brace; | |
| 6495 | State.open_brace => switch (c) { | |
| 6496 | '{' => { | |
| 6497 | state = State.start; | |
| 6498 | start_index = i; | |
| 6499 | }, | |
| 6500 | '}' => { | |
| 6501 | try self.printValue(args[next_arg]); | |
| 6502 | next_arg += 1; | |
| 6503 | state = State.start; | |
| 6504 | start_index = i + 1; | |
| 6505 | }, | |
| 6506 | 's' => { | |
| 6507 | continue; | |
| 6508 | }, | |
| 6509 | else => @compileError("Unknown format character: " ++ [1]u8{c}), | |
| 6412 | 6510 | }, |
| 6413 | else => {}, | |
| 6414 | }, | |
| 6415 | State.open_brace => switch (c) { | |
| 6416 | '{' => { | |
| 6417 | state = State.start; | |
| 6418 | start_index = i; | |
| 6419 | }, | |
| 6420 | '}' => { | |
| 6421 | try self.printValue(args[next_arg]); | |
| 6422 | next_arg += 1; | |
| 6423 | state = State.start; | |
| 6424 | start_index = i + 1; | |
| 6511 | State.close_brace => switch (c) { | |
| 6512 | '}' => { | |
| 6513 | state = State.start; | |
| 6514 | start_index = i; | |
| 6515 | }, | |
| 6516 | else => @compileError("Single '}' encountered in format string"), | |
| 6425 | 6517 | }, |
| 6426 | else => @compileError("Unknown format character: " ++ c), | |
| 6427 | }, | |
| 6428 | State.close_brace => switch (c) { | |
| 6429 | '}' => { | |
| 6430 | state = State.start; | |
| 6431 | start_index = i; | |
| 6432 | }, | |
| 6433 | else => @compileError("Single '}' encountered in format string"), | |
| 6434 | }, | |
| 6518 | } | |
| 6435 | 6519 | } |
| 6436 | } | |
| 6437 | comptime { | |
| 6438 | if (args.len != next_arg) { | |
| 6439 | @compileError("Unused arguments"); | |
| 6520 | comptime { | |
| 6521 | if (args.len != next_arg) { | |
| 6522 | @compileError("Unused arguments"); | |
| 6523 | } | |
| 6524 | if (state != State.start) { | |
| 6525 | @compileError("Incomplete format string: " ++ format); | |
| 6526 | } | |
| 6440 | 6527 | } |
| 6441 | if (state != State.Start) { | |
| 6442 | @compileError("Incomplete format string: " ++ format); | |
| 6528 | if (start_index < format.len) { | |
| 6529 | try self.write(format[start_index..format.len]); | |
| 6443 | 6530 | } |
| 6531 | try self.flush(); | |
| 6444 | 6532 | } |
| 6445 | if (start_index < format.len) { | |
| 6446 | try self.write(format[start_index..format.len]); | |
| 6533 | ||
| 6534 | fn write(self: *Writer, value: []const u8) !void { | |
| 6535 | _ = self; | |
| 6536 | _ = value; | |
| 6447 | 6537 | } |
| 6448 | try self.flush(); | |
| 6449 | } | |
| 6538 | pub fn printValue(self: *Writer, value: anytype) !void { | |
| 6539 | _ = self; | |
| 6540 | _ = value; | |
| 6541 | } | |
| 6542 | fn flush(self: *Writer) !void { | |
| 6543 | _ = self; | |
| 6544 | } | |
| 6545 | }; | |
| 6450 | 6546 | {#code_end#} |
| 6451 | 6547 | <p> |
| 6452 | 6548 | This is a proof of concept implementation; the actual function in the standard library has more |
| ... | ... | @@ -6459,8 +6555,8 @@ pub fn printf(self: *Writer, comptime format: []const u8, args: anytype) anyerro |
| 6459 | 6555 | When this function is analyzed from our example code above, Zig partially evaluates the function |
| 6460 | 6556 | and emits a function that actually looks like this: |
| 6461 | 6557 | </p> |
| 6462 | {#code_begin|syntax#} | |
| 6463 | pub fn printf(self: *Writer, arg0: i32, arg1: []const u8) !void { | |
| 6558 | {#syntax_block|zig|Emitted print Function#} | |
| 6559 | pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void { | |
| 6464 | 6560 | try self.write("here is a string: '"); |
| 6465 | 6561 | try self.printValue(arg0); |
| 6466 | 6562 | try self.write("' here is a number: "); |
| ... | ... | @@ -6468,28 +6564,46 @@ pub fn printf(self: *Writer, arg0: i32, arg1: []const u8) !void { |
| 6468 | 6564 | try self.write("\n"); |
| 6469 | 6565 | try self.flush(); |
| 6470 | 6566 | } |
| 6471 | {#code_end#} | |
| 6567 | {#end_syntax_block#} | |
| 6472 | 6568 | <p> |
| 6473 | 6569 | {#syntax#}printValue{#endsyntax#} is a function that takes a parameter of any type, and does different things depending |
| 6474 | 6570 | on the type: |
| 6475 | 6571 | </p> |
| 6476 | {#code_begin|syntax#} | |
| 6477 | pub fn printValue(self: *Writer, value: anytype) !void { | |
| 6478 | switch (@typeInfo(@TypeOf(value))) { | |
| 6479 | .Int => { | |
| 6480 | return self.printInt(T, value); | |
| 6481 | }, | |
| 6482 | .Float => { | |
| 6483 | return self.printFloat(T, value); | |
| 6484 | }, | |
| 6485 | else => { | |
| 6486 | @compileError("Unable to print type '" ++ @typeName(T) ++ "'"); | |
| 6487 | }, | |
| 6572 | {#code_begin|syntax|poc_printValue_fn#} | |
| 6573 | const Writer = struct { | |
| 6574 | pub fn printValue(self: *Writer, value: anytype) !void { | |
| 6575 | switch (@typeInfo(@TypeOf(value))) { | |
| 6576 | .Int => { | |
| 6577 | return self.writeInt(value); | |
| 6578 | }, | |
| 6579 | .Float => { | |
| 6580 | return self.writeFloat(value); | |
| 6581 | }, | |
| 6582 | .Pointer => { | |
| 6583 | return self.write(value); | |
| 6584 | }, | |
| 6585 | else => { | |
| 6586 | @compileError("Unable to print type '" ++ @typeName(@TypeOf(value)) ++ "'"); | |
| 6587 | }, | |
| 6588 | } | |
| 6488 | 6589 | } |
| 6489 | } | |
| 6590 | ||
| 6591 | fn write(self: *Writer, value: []const u8) !void { | |
| 6592 | _ = self; | |
| 6593 | _ = value; | |
| 6594 | } | |
| 6595 | fn writeInt(self: *Writer, value: anytype) !void { | |
| 6596 | _ = self; | |
| 6597 | _ = value; | |
| 6598 | } | |
| 6599 | fn writeFloat(self: *Writer, value: anytype) !void { | |
| 6600 | _ = self; | |
| 6601 | _ = value; | |
| 6602 | } | |
| 6603 | }; | |
| 6490 | 6604 | {#code_end#} |
| 6491 | 6605 | <p> |
| 6492 | And now, what happens if we give too many arguments to {#syntax#}printf{#endsyntax#}? | |
| 6606 | And now, what happens if we give too many arguments to {#syntax#}print{#endsyntax#}? | |
| 6493 | 6607 | </p> |
| 6494 | 6608 | {#code_begin|test_err|Unused argument in 'here is a string: '{s}' here is a number: {}#} |
| 6495 | 6609 | const print = @import("std").debug.print; |
| ... | ... | @@ -6497,7 +6611,7 @@ const print = @import("std").debug.print; |
| 6497 | 6611 | const a_number: i32 = 1234; |
| 6498 | 6612 | const a_string = "foobar"; |
| 6499 | 6613 | |
| 6500 | test "printf too many arguments" { | |
| 6614 | test "print too many arguments" { | |
| 6501 | 6615 | print("here is a string: '{s}' here is a number: {}\n", .{ |
| 6502 | 6616 | a_string, |
| 6503 | 6617 | a_number, |
| ... | ... | @@ -6512,7 +6626,7 @@ test "printf too many arguments" { |
| 6512 | 6626 | Zig doesn't care whether the format argument is a string literal, |
| 6513 | 6627 | only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}: |
| 6514 | 6628 | </p> |
| 6515 | {#code_begin|exe|printf#} | |
| 6629 | {#code_begin|exe|print#} | |
| 6516 | 6630 | const print = @import("std").debug.print; |
| 6517 | 6631 | |
| 6518 | 6632 | const a_number: i32 = 1234; |
| ... | ... | @@ -7118,16 +7232,6 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 { |
| 7118 | 7232 | If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}. |
| 7119 | 7233 | </p> |
| 7120 | 7234 | {#header_close#} |
| 7121 | {#header_open|@addWithSaturation#} | |
| 7122 | <pre>{#syntax#}@addWithSaturation(a: T, b: T) T{#endsyntax#}</pre> | |
| 7123 | <p> | |
| 7124 | Returns {#syntax#}a + b{#endsyntax#}. The result will be clamped between the type maximum and minimum. | |
| 7125 | </p> | |
| 7126 | <p> | |
| 7127 | Once <a href="https://github.com/ziglang/zig/issues/1284">Saturating arithmetic</a>. | |
| 7128 | is completed, the syntax {#syntax#}a +| b{#endsyntax#} will be equivalent to calling {#syntax#}@addWithSaturation(a, b){#endsyntax#}. | |
| 7129 | </p> | |
| 7130 | {#header_close#} | |
| 7131 | 7235 | {#header_open|@alignCast#} |
| 7132 | 7236 | <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre> |
| 7133 | 7237 | <p> |
| ... | ... | @@ -7216,7 +7320,9 @@ fn func(y: *i32) void { |
| 7216 | 7320 | {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float, |
| 7217 | 7321 | an integer or an enum. |
| 7218 | 7322 | </p> |
| 7323 | {#see_also|@atomicStore|@atomicRmw|@fence|@cmpxchgWeak|@cmpxchgStrong#} | |
| 7219 | 7324 | {#header_close#} |
| 7325 | ||
| 7220 | 7326 | {#header_open|@atomicRmw#} |
| 7221 | 7327 | <pre>{#syntax#}@atomicRmw(comptime T: type, ptr: *T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre> |
| 7222 | 7328 | <p> |
| ... | ... | @@ -7242,7 +7348,9 @@ fn func(y: *i32) void { |
| 7242 | 7348 | <li>{#syntax#}.Max{#endsyntax#} - stores the operand if it is larger. Supports integers and floats.</li> |
| 7243 | 7349 | <li>{#syntax#}.Min{#endsyntax#} - stores the operand if it is smaller. Supports integers and floats.</li> |
| 7244 | 7350 | </ul> |
| 7351 | {#see_also|@atomicStore|@atomicLoad|@fence|@cmpxchgWeak|@cmpxchgStrong#} | |
| 7245 | 7352 | {#header_close#} |
| 7353 | ||
| 7246 | 7354 | {#header_open|@atomicStore#} |
| 7247 | 7355 | <pre>{#syntax#}@atomicStore(comptime T: type, ptr: *T, value: T, comptime ordering: builtin.AtomicOrder) void{#endsyntax#}</pre> |
| 7248 | 7356 | <p> |
| ... | ... | @@ -7252,6 +7360,7 @@ fn func(y: *i32) void { |
| 7252 | 7360 | {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float, |
| 7253 | 7361 | an integer or an enum. |
| 7254 | 7362 | </p> |
| 7363 | {#see_also|@atomicLoad|@atomicRmw|@fence|@cmpxchgWeak|@cmpxchgStrong#} | |
| 7255 | 7364 | {#header_close#} |
| 7256 | 7365 | |
| 7257 | 7366 | {#header_open|@bitCast#} |
| ... | ... | @@ -7396,9 +7505,11 @@ fn add(a: i32, b: i32) i32 { |
| 7396 | 7505 | {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The |
| 7397 | 7506 | {#syntax#}CallOptions{#endsyntax#} struct is reproduced here: |
| 7398 | 7507 | </p> |
| 7399 | {#code_begin|syntax#} | |
| 7508 | {#syntax_block|zig|builtin.CallOptions struct#} | |
| 7400 | 7509 | pub const CallOptions = struct { |
| 7401 | 7510 | modifier: Modifier = .auto, |
| 7511 | ||
| 7512 | /// Only valid when `Modifier` is `Modifier.async_kw`. | |
| 7402 | 7513 | stack: ?[]align(std.Target.stack_align) u8 = null, |
| 7403 | 7514 | |
| 7404 | 7515 | pub const Modifier = enum { |
| ... | ... | @@ -7435,7 +7546,7 @@ pub const CallOptions = struct { |
| 7435 | 7546 | compile_time, |
| 7436 | 7547 | }; |
| 7437 | 7548 | }; |
| 7438 | {#code_end#} | |
| 7549 | {#end_syntax_block#} | |
| 7439 | 7550 | {#header_close#} |
| 7440 | 7551 | |
| 7441 | 7552 | {#header_open|@cDefine#} |
| ... | ... | @@ -7540,15 +7651,16 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v |
| 7540 | 7651 | an integer or an enum. |
| 7541 | 7652 | </p> |
| 7542 | 7653 | <p>{#syntax#}@typeInfo(@TypeOf(ptr)).Pointer.alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p> |
| 7543 | {#see_also|Compile Variables|cmpxchgWeak#} | |
| 7654 | {#see_also|@atomicStore|@atomicLoad|@atomicRmw|@fence|@cmpxchgWeak#} | |
| 7544 | 7655 | {#header_close#} |
| 7656 | ||
| 7545 | 7657 | {#header_open|@cmpxchgWeak#} |
| 7546 | 7658 | <pre>{#syntax#}@cmpxchgWeak(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T{#endsyntax#}</pre> |
| 7547 | 7659 | <p> |
| 7548 | 7660 | This function performs a weak atomic compare exchange operation. It's the equivalent of this code, |
| 7549 | 7661 | except atomic: |
| 7550 | 7662 | </p> |
| 7551 | {#code_begin|syntax#} | |
| 7663 | {#syntax_block|zig|cmpxchgWeakButNotAtomic#} | |
| 7552 | 7664 | fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T { |
| 7553 | 7665 | const old_value = ptr.*; |
| 7554 | 7666 | if (old_value == expected_value and usuallyTrueButSometimesFalse()) { |
| ... | ... | @@ -7558,7 +7670,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 7558 | 7670 | return old_value; |
| 7559 | 7671 | } |
| 7560 | 7672 | } |
| 7561 | {#code_end#} | |
| 7673 | {#end_syntax_block#} | |
| 7562 | 7674 | <p> |
| 7563 | 7675 | If you are using cmpxchg in a loop, the sporadic failure will be no problem, and {#syntax#}cmpxchgWeak{#endsyntax#} |
| 7564 | 7676 | is the better choice, because it can be implemented more efficiently in machine instructions. |
| ... | ... | @@ -7569,7 +7681,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 7569 | 7681 | an integer or an enum. |
| 7570 | 7682 | </p> |
| 7571 | 7683 | <p>{#syntax#}@typeInfo(@TypeOf(ptr)).Pointer.alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p> |
| 7572 | {#see_also|Compile Variables|cmpxchgStrong#} | |
| 7684 | {#see_also|@atomicStore|@atomicLoad|@atomicRmw|@fence|@cmpxchgStrong#} | |
| 7573 | 7685 | {#header_close#} |
| 7574 | 7686 | |
| 7575 | 7687 | {#header_open|@compileError#} |
| ... | ... | @@ -7617,7 +7729,7 @@ test "main" { |
| 7617 | 7729 | } |
| 7618 | 7730 | {#code_end#} |
| 7619 | 7731 | <p> |
| 7620 | will ouput: | |
| 7732 | will output: | |
| 7621 | 7733 | </p> |
| 7622 | 7734 | <p> |
| 7623 | 7735 | If all {#syntax#}@compileLog{#endsyntax#} calls are removed or |
| ... | ... | @@ -7734,7 +7846,7 @@ test "main" { |
| 7734 | 7846 | the tag value is used as the enumeration value. |
| 7735 | 7847 | </p> |
| 7736 | 7848 | <p> |
| 7737 | If there is only one possible enum value, the resut is a {#syntax#}comptime_int{#endsyntax#} | |
| 7849 | If there is only one possible enum value, the result is a {#syntax#}comptime_int{#endsyntax#} | |
| 7738 | 7850 | known at {#link|comptime#}. |
| 7739 | 7851 | </p> |
| 7740 | 7852 | {#see_also|@intToEnum#} |
| ... | ... | @@ -7849,7 +7961,7 @@ export fn @"A function name that is a complete sentence."() void {} |
| 7849 | 7961 | <p> |
| 7850 | 7962 | {#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}. |
| 7851 | 7963 | </p> |
| 7852 | {#see_also|Compile Variables#} | |
| 7964 | {#see_also|@atomicStore|@atomicLoad|@atomicRmw|@cmpxchgWeak|@cmpxchgStrong#} | |
| 7853 | 7965 | {#header_close#} |
| 7854 | 7966 | |
| 7855 | 7967 | {#header_open|@field#} |
| ... | ... | @@ -8241,22 +8353,6 @@ test "@wasmMemoryGrow" { |
| 8241 | 8353 | </p> |
| 8242 | 8354 | {#header_close#} |
| 8243 | 8355 | |
| 8244 | {#header_open|@mulWithSaturation#} | |
| 8245 | <pre>{#syntax#}@mulWithSaturation(a: T, b: T) T{#endsyntax#}</pre> | |
| 8246 | <p> | |
| 8247 | Returns {#syntax#}a * b{#endsyntax#}. The result will be clamped between the type maximum and minimum. | |
| 8248 | </p> | |
| 8249 | <p> | |
| 8250 | Once <a href="https://github.com/ziglang/zig/issues/1284">Saturating arithmetic</a>. | |
| 8251 | is completed, the syntax {#syntax#}a *| b{#endsyntax#} will be equivalent to calling {#syntax#}@mulWithSaturation(a, b){#endsyntax#}. | |
| 8252 | </p> | |
| 8253 | <p> | |
| 8254 | NOTE: Currently there is a bug in the llvm.smul.fix.sat intrinsic which affects {#syntax#}@mulWithSaturation{#endsyntax#} of signed integers. | |
| 8255 | This may result in an incorrect sign bit when there is overflow. This will be fixed in zig's 0.9.0 release. | |
| 8256 | Check <a href="https://github.com/ziglang/zig/issues/9643">this issue</a> for more information. | |
| 8257 | </p> | |
| 8258 | {#header_close#} | |
| 8259 | ||
| 8260 | 8356 | {#header_open|@panic#} |
| 8261 | 8357 | <pre>{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}</pre> |
| 8262 | 8358 | <p> |
| ... | ... | @@ -8474,14 +8570,16 @@ test "@setRuntimeSafety" { |
| 8474 | 8570 | {#header_open|@shlExact#} |
| 8475 | 8571 | <pre>{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre> |
| 8476 | 8572 | <p> |
| 8477 | Performs the left shift operation ({#syntax#}<<{#endsyntax#}). Caller guarantees | |
| 8478 | that the shift will not shift any 1 bits out. | |
| 8573 | Performs the left shift operation ({#syntax#}<<{#endsyntax#}). | |
| 8574 | For unsigned integers, the result is {#link|undefined#} if any 1 bits | |
| 8575 | are shifted out. For signed integers, the result is {#link|undefined#} if | |
| 8576 | any bits that disagree with the resultant sign bit are shifted out. | |
| 8479 | 8577 | </p> |
| 8480 | 8578 | <p> |
| 8481 | 8579 | The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits. |
| 8482 | 8580 | This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior. |
| 8483 | 8581 | </p> |
| 8484 | {#see_also|@shrExact|@shlWithOverflow|@shlWithSaturation#} | |
| 8582 | {#see_also|@shrExact|@shlWithOverflow#} | |
| 8485 | 8583 | {#header_close#} |
| 8486 | 8584 | |
| 8487 | 8585 | {#header_open|@shlWithOverflow#} |
| ... | ... | @@ -8495,24 +8593,9 @@ test "@setRuntimeSafety" { |
| 8495 | 8593 | The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits. |
| 8496 | 8594 | This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior. |
| 8497 | 8595 | </p> |
| 8498 | {#see_also|@shlExact|@shrExact|@shlWithSaturation#} | |
| 8596 | {#see_also|@shlExact|@shrExact#} | |
| 8499 | 8597 | {#header_close#} |
| 8500 | 8598 | |
| 8501 | {#header_open|@shlWithSaturation#} | |
| 8502 | <pre>{#syntax#}@shlWithSaturation(a: T, shift_amt: T) T{#endsyntax#}</pre> | |
| 8503 | <p> | |
| 8504 | Returns {#syntax#}a << b{#endsyntax#}. The result will be clamped between type minimum and maximum. | |
| 8505 | </p> | |
| 8506 | <p> | |
| 8507 | Once <a href="https://github.com/ziglang/zig/issues/1284">Saturating arithmetic</a>. | |
| 8508 | is completed, the syntax {#syntax#}a <<| b{#endsyntax#} will be equivalent to calling {#syntax#}@shlWithSaturation(a, b){#endsyntax#}. | |
| 8509 | </p> | |
| 8510 | <p> | |
| 8511 | Unlike other @shl builtins, shift_amt doesn't need to be a Log2T as saturated overshifting is well defined. | |
| 8512 | </p> | |
| 8513 | {#see_also|@shlExact|@shrExact|@shlWithOverflow#} | |
| 8514 | {#header_close#} | |
| 8515 | ||
| 8516 | 8599 | {#header_open|@shrExact#} |
| 8517 | 8600 | <pre>{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre> |
| 8518 | 8601 | <p> |
| ... | ... | @@ -8523,7 +8606,7 @@ test "@setRuntimeSafety" { |
| 8523 | 8606 | The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits. |
| 8524 | 8607 | This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior. |
| 8525 | 8608 | </p> |
| 8526 | {#see_also|@shlExact|@shlWithOverflow|@shlWithSaturation#} | |
| 8609 | {#see_also|@shlExact|@shlWithOverflow#} | |
| 8527 | 8610 | {#header_close#} |
| 8528 | 8611 | |
| 8529 | 8612 | {#header_open|@shuffle#} |
| ... | ... | @@ -8684,7 +8767,7 @@ fn doTheTest() !void { |
| 8684 | 8767 | {#header_open|@sin#} |
| 8685 | 8768 | <pre>{#syntax#}@sin(value: anytype) @TypeOf(value){#endsyntax#}</pre> |
| 8686 | 8769 | <p> |
| 8687 | Sine trigometric function on a floating point number. Uses a dedicated hardware instruction | |
| 8770 | Sine trigonometric function on a floating point number. Uses a dedicated hardware instruction | |
| 8688 | 8771 | when available. |
| 8689 | 8772 | </p> |
| 8690 | 8773 | <p> |
| ... | ... | @@ -8695,7 +8778,7 @@ fn doTheTest() !void { |
| 8695 | 8778 | {#header_open|@cos#} |
| 8696 | 8779 | <pre>{#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}</pre> |
| 8697 | 8780 | <p> |
| 8698 | Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction | |
| 8781 | Cosine trigonometric function on a floating point number. Uses a dedicated hardware instruction | |
| 8699 | 8782 | when available. |
| 8700 | 8783 | </p> |
| 8701 | 8784 | <p> |
| ... | ... | @@ -8783,7 +8866,7 @@ fn doTheTest() !void { |
| 8783 | 8866 | {#header_open|@ceil#} |
| 8784 | 8867 | <pre>{#syntax#}@ceil(value: anytype) @TypeOf(value){#endsyntax#}</pre> |
| 8785 | 8868 | <p> |
| 8786 | Returns the largest integral value not less than the given floating point number. | |
| 8869 | Returns the smallest integral value not less than the given floating point number. | |
| 8787 | 8870 | Uses a dedicated hardware instruction when available. |
| 8788 | 8871 | </p> |
| 8789 | 8872 | <p> |
| ... | ... | @@ -8823,17 +8906,6 @@ fn doTheTest() !void { |
| 8823 | 8906 | </p> |
| 8824 | 8907 | {#header_close#} |
| 8825 | 8908 | |
| 8826 | {#header_open|@subWithSaturation#} | |
| 8827 | <pre>{#syntax#}@subWithSaturation(a: T, b: T) T{#endsyntax#}</pre> | |
| 8828 | <p> | |
| 8829 | Returns {#syntax#}a - b{#endsyntax#}. The result will be clamped between the type maximum and minimum. | |
| 8830 | </p> | |
| 8831 | <p> | |
| 8832 | Once <a href="https://github.com/ziglang/zig/issues/1284">Saturating arithmetic</a>. | |
| 8833 | is completed, the syntax {#syntax#}a -| b{#endsyntax#} will be equivalent to calling {#syntax#}@subWithSaturation(a, b){#endsyntax#}. | |
| 8834 | </p> | |
| 8835 | {#header_close#} | |
| 8836 | ||
| 8837 | 8909 | {#header_open|@tagName#} |
| 8838 | 8910 | <pre>{#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}</pre> |
| 8839 | 8911 | <p> |
| ... | ... | @@ -10153,7 +10225,7 @@ pub fn main() void { |
| 10153 | 10225 | This expression is evaluated at compile-time and is used to control |
| 10154 | 10226 | preprocessor directives and include multiple <code class="file">.h</code> files: |
| 10155 | 10227 | </p> |
| 10156 | {#code_begin|syntax#} | |
| 10228 | {#syntax_block|zig|@cImport Expression#} | |
| 10157 | 10229 | const builtin = @import("builtin"); |
| 10158 | 10230 | |
| 10159 | 10231 | const c = @cImport({ |
| ... | ... | @@ -10167,7 +10239,7 @@ const c = @cImport({ |
| 10167 | 10239 | } |
| 10168 | 10240 | @cInclude("soundio.h"); |
| 10169 | 10241 | }); |
| 10170 | {#code_end#} | |
| 10242 | {#end_syntax_block#} | |
| 10171 | 10243 | {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#} |
| 10172 | 10244 | {#header_close#} |
| 10173 | 10245 | |
| ... | ... | @@ -10273,7 +10345,7 @@ pub fn main() void { |
| 10273 | 10345 | <p> |
| 10274 | 10346 | Some C constructs cannot be translated to Zig - for example, <em>goto</em>, |
| 10275 | 10347 | structs with bitfields, and token-pasting macros. Zig employs <em>demotion</em> to allow translation |
| 10276 | to continue in the face of non-translateable entities. | |
| 10348 | to continue in the face of non-translatable entities. | |
| 10277 | 10349 | </p> |
| 10278 | 10350 | <p> |
| 10279 | 10351 | Demotion comes in three varieties - {#link|opaque#}, <em>extern</em>, and |
| ... | ... | @@ -10283,13 +10355,13 @@ pub fn main() void { |
| 10283 | 10355 | Functions that contain opaque types or code constructs that cannot be translated will be demoted |
| 10284 | 10356 | to {#syntax#}extern{#endsyntax#} declarations. |
| 10285 | 10357 | |
| 10286 | Thus, non-translateable types can still be used as pointers, and non-translateable functions | |
| 10358 | Thus, non-translatable types can still be used as pointers, and non-translatable functions | |
| 10287 | 10359 | can be called so long as the linker is aware of the compiled function. |
| 10288 | 10360 | </p> |
| 10289 | 10361 | <p> |
| 10290 | 10362 | {#syntax#}@compileError{#endsyntax#} is used when top-level definitions (global variables, |
| 10291 | 10363 | function prototypes, macros) cannot be translated or demoted. Since Zig uses lazy analysis for |
| 10292 | top-level declarations, untranslateable entities will not cause a compile error in your code unless | |
| 10364 | top-level declarations, untranslatable entities will not cause a compile error in your code unless | |
| 10293 | 10365 | you actually use them. |
| 10294 | 10366 | </p> |
| 10295 | 10367 | {#see_also|opaque|extern|@compileError#} |
| ... | ... | @@ -10301,7 +10373,7 @@ pub fn main() void { |
| 10301 | 10373 | can be translated to Zig. Macros that cannot be translated will be be demoted to |
| 10302 | 10374 | {#syntax#}@compileError{#endsyntax#}. Note that C code which <em>uses</em> macros will be |
| 10303 | 10375 | translated without any additional issues (since Zig operates on the pre-processed source |
| 10304 | with macros expanded). It is merely the macros themselves which may not be translateable to | |
| 10376 | with macros expanded). It is merely the macros themselves which may not be translatable to | |
| 10305 | 10377 | Zig. |
| 10306 | 10378 | </p> |
| 10307 | 10379 | <p>Consider the following example:</p> |
| ... | ... | @@ -10321,7 +10393,7 @@ pub export fn foo() c_int { |
| 10321 | 10393 | } |
| 10322 | 10394 | pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected token .Equal"); // macro.c:1:9 |
| 10323 | 10395 | {#code_end#} |
| 10324 | <p>Note that {#syntax#}foo{#endsyntax#} was translated correctly despite using a non-translateable | |
| 10396 | <p>Note that {#syntax#}foo{#endsyntax#} was translated correctly despite using a non-translatable | |
| 10325 | 10397 | macro. {#syntax#}MAKELOCAL{#endsyntax#} was demoted to {#syntax#}@compileError{#endsyntax#} since |
| 10326 | 10398 | it cannot be expressed as a Zig function; this simply means that you cannot directly use |
| 10327 | 10399 | {#syntax#}MAKELOCAL{#endsyntax#} from Zig. |
| ... | ... | @@ -10491,8 +10563,8 @@ const typedArray = new Uint8Array(source); |
| 10491 | 10563 | |
| 10492 | 10564 | WebAssembly.instantiate(typedArray, { |
| 10493 | 10565 | env: { |
| 10494 | print: (result) =&gt; { console.log(`The result is ${result}`); } | |
| 10495 | }}).then(result =&gt; { | |
| 10566 | print: (result) => { console.log(`The result is ${result}`); } | |
| 10567 | }}).then(result => { | |
| 10496 | 10568 | const add = result.instance.exports.add; |
| 10497 | 10569 | add(1, 2); |
| 10498 | 10570 | });{#end_syntax_block#} |
| ... | ... | @@ -11787,6 +11859,7 @@ AssignOp |
| 11787 | 11859 | / PLUSEQUAL |
| 11788 | 11860 | / MINUSEQUAL |
| 11789 | 11861 | / LARROW2EQUAL |
| 11862 | / LARROW2PIPEEQUAL | |
| 11790 | 11863 | / RARROW2EQUAL |
| 11791 | 11864 | / AMPERSANDEQUAL |
| 11792 | 11865 | / CARETEQUAL |
| ... | ... | @@ -11821,6 +11894,8 @@ AdditionOp |
| 11821 | 11894 | / PLUS2 |
| 11822 | 11895 | / PLUSPERCENT |
| 11823 | 11896 | / MINUSPERCENT |
| 11897 | / PLUSPIPE | |
| 11898 | / MINUSPIPE | |
| 11824 | 11899 | |
| 11825 | 11900 | MultiplyOp |
| 11826 | 11901 | &lt;- PIPE2 |
| ... | ... | @@ -11829,6 +11904,7 @@ MultiplyOp |
| 11829 | 11904 | / PERCENT |
| 11830 | 11905 | / ASTERISK2 |
| 11831 | 11906 | / ASTERISKPERCENT |
| 11907 | / ASTERISKPIPE | |
| 11832 | 11908 | |
| 11833 | 11909 | PrefixOp |
| 11834 | 11910 | &lt;- EXCLAMATIONMARK |
| ... | ... | @@ -11992,6 +12068,8 @@ ASTERISK2 &lt;- '**' skip |
| 11992 | 12068 | ASTERISKEQUAL &lt;- '*=' skip |
| 11993 | 12069 | ASTERISKPERCENT &lt;- '*%' ![=] skip |
| 11994 | 12070 | ASTERISKPERCENTEQUAL &lt;- '*%=' skip |
| 12071 | ASTERISKPIPE &lt;- '*|' ![=] skip | |
| 12072 | ASTERISKPIPEEQUAL &lt;- '*|=' skip | |
| 11995 | 12073 | CARET &lt;- '^' ![=] skip |
| 11996 | 12074 | CARETEQUAL &lt;- '^=' skip |
| 11997 | 12075 | COLON &lt;- ':' skip |
| ... | ... | @@ -12008,6 +12086,8 @@ EXCLAMATIONMARK &lt;- '!' ![=] skip |
| 12008 | 12086 | EXCLAMATIONMARKEQUAL &lt;- '!=' skip |
| 12009 | 12087 | LARROW &lt;- '&lt;' ![&lt;=] skip |
| 12010 | 12088 | LARROW2 &lt;- '&lt;&lt;' ![=] skip |
| 12089 | LARROW2PIPE &lt;- '&lt;&lt;|' ![=] skip | |
| 12090 | LARROW2PIPEEQUAL &lt;- '&lt;&lt;|=' ![=] skip | |
| 12011 | 12091 | LARROW2EQUAL &lt;- '&lt;&lt;=' skip |
| 12012 | 12092 | LARROWEQUAL &lt;- '&lt;=' skip |
| 12013 | 12093 | LBRACE &lt;- '{' skip |
| ... | ... | @@ -12017,6 +12097,8 @@ MINUS &lt;- '-' ![%=&gt;] skip |
| 12017 | 12097 | MINUSEQUAL &lt;- '-=' skip |
| 12018 | 12098 | MINUSPERCENT &lt;- '-%' ![=] skip |
| 12019 | 12099 | MINUSPERCENTEQUAL &lt;- '-%=' skip |
| 12100 | MINUSPIPE &lt;- '-|' ![=] skip | |
| 12101 | MINUSPIPEEQUAL &lt;- '-|=' skip | |
| 12020 | 12102 | MINUSRARROW &lt;- '-&gt;' skip |
| 12021 | 12103 | PERCENT &lt;- '%' ![=] skip |
| 12022 | 12104 | PERCENTEQUAL &lt;- '%=' skip |
| ... | ... | @@ -12028,6 +12110,8 @@ PLUS2 &lt;- '++' skip |
| 12028 | 12110 | PLUSEQUAL &lt;- '+=' skip |
| 12029 | 12111 | PLUSPERCENT &lt;- '+%' ![=] skip |
| 12030 | 12112 | PLUSPERCENTEQUAL &lt;- '+%=' skip |
| 12113 | PLUSPIPE &lt;- '+|' ![=] skip | |
| 12114 | PLUSPIPEEQUAL &lt;- '+|=' skip | |
| 12031 | 12115 | LETTERC &lt;- 'c' skip |
| 12032 | 12116 | QUESTIONMARK &lt;- '?' skip |
| 12033 | 12117 | RARROW &lt;- '&gt;' ![&gt;=] skip |
lib/libc/mingw/stdio/fseeki64.c created+50| ... | ... | @@ -0,0 +1,50 @@ |
| 1 | /** | |
| 2 | * This file has no copyright assigned and is placed in the Public Domain. | |
| 3 | * This file is part of the mingw-w64 runtime package. | |
| 4 | * No warranty is given; refer to the file DISCLAIMER.PD within this package. | |
| 5 | */ | |
| 6 | #include <stdio.h> | |
| 7 | #include <io.h> | |
| 8 | #include <errno.h> | |
| 9 | ||
| 10 | #if !defined(__arm__) && !defined(__aarch64__) /* we have F_ARM_ANY(_fseeki64) in msvcrt.def.in */ | |
| 11 | int __cdecl _fseeki64(FILE* stream, __int64 offset, int whence) | |
| 12 | { | |
| 13 | fpos_t pos; | |
| 14 | if (whence == SEEK_CUR) | |
| 15 | { | |
| 16 | /* If stream is invalid, fgetpos sets errno. */ | |
| 17 | if (fgetpos (stream, &pos)) | |
| 18 | return (-1); | |
| 19 | pos += (fpos_t) offset; | |
| 20 | } | |
| 21 | else if (whence == SEEK_END) | |
| 22 | { | |
| 23 | /* If writing, we need to flush before getting file length. */ | |
| 24 | fflush (stream); | |
| 25 | pos = (fpos_t) (_filelengthi64 (_fileno (stream)) + offset); | |
| 26 | } | |
| 27 | else if (whence == SEEK_SET) | |
| 28 | pos = (fpos_t) offset; | |
| 29 | else | |
| 30 | { | |
| 31 | errno = EINVAL; | |
| 32 | return (-1); | |
| 33 | } | |
| 34 | return fsetpos (stream, &pos); | |
| 35 | } | |
| 36 | ||
| 37 | int __cdecl (*__MINGW_IMP_SYMBOL(_fseeki64))(FILE*, __int64, int) = _fseeki64; | |
| 38 | #endif /* !defined(__arm__) && !defined(__aarch64__) */ | |
| 39 | ||
| 40 | __int64 __cdecl _ftelli64(FILE* stream) | |
| 41 | { | |
| 42 | fpos_t pos; | |
| 43 | if (fgetpos (stream, &pos)) | |
| 44 | return -1LL; | |
| 45 | else | |
| 46 | return (__int64) pos; | |
| 47 | } | |
| 48 | ||
| 49 | __int64 __cdecl (*__MINGW_IMP_SYMBOL(_ftelli64))(FILE*) = _ftelli64; | |
| 50 |
lib/std/Thread.zig+13-2| ... | ... | @@ -41,6 +41,7 @@ pub const max_name_len = switch (target.os.tag) { |
| 41 | 41 | .netbsd => 31, |
| 42 | 42 | .freebsd => 15, |
| 43 | 43 | .openbsd => 31, |
| 44 | .solaris => 31, | |
| 44 | 45 | else => 0, |
| 45 | 46 | }; |
| 46 | 47 | |
| ... | ... | @@ -112,7 +113,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { |
| 112 | 113 | else => |e| return os.unexpectedErrno(e), |
| 113 | 114 | } |
| 114 | 115 | }, |
| 115 | .netbsd => if (use_pthreads) { | |
| 116 | .netbsd, .solaris => if (use_pthreads) { | |
| 116 | 117 | const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr, null); |
| 117 | 118 | switch (err) { |
| 118 | 119 | .SUCCESS => return, |
| ... | ... | @@ -202,7 +203,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co |
| 202 | 203 | else => |e| return os.unexpectedErrno(e), |
| 203 | 204 | } |
| 204 | 205 | }, |
| 205 | .netbsd => if (use_pthreads) { | |
| 206 | .netbsd, .solaris => if (use_pthreads) { | |
| 206 | 207 | const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1); |
| 207 | 208 | switch (err) { |
| 208 | 209 | .SUCCESS => return std.mem.sliceTo(buffer, 0), |
| ... | ... | @@ -565,6 +566,16 @@ const PosixThreadImpl = struct { |
| 565 | 566 | }; |
| 566 | 567 | return @intCast(usize, count); |
| 567 | 568 | }, |
| 569 | .solaris => { | |
| 570 | // The "proper" way to get the cpu count would be to query | |
| 571 | // /dev/kstat via ioctls, and traverse a linked list for each | |
| 572 | // cpu. | |
| 573 | const rc = c.sysconf(os._SC.NPROCESSORS_ONLN); | |
| 574 | return switch (os.errno(rc)) { | |
| 575 | .SUCCESS => @intCast(usize, rc), | |
| 576 | else => |err| os.unexpectedErrno(err), | |
| 577 | }; | |
| 578 | }, | |
| 568 | 579 | .haiku => { |
| 569 | 580 | var count: u32 = undefined; |
| 570 | 581 | var system_info: os.system_info = undefined; |
lib/std/Thread/Condition.zig+1-1| ... | ... | @@ -54,7 +54,7 @@ pub const WindowsCondition = struct { |
| 54 | 54 | pub fn wait(cond: *WindowsCondition, mutex: *Mutex) void { |
| 55 | 55 | const rc = windows.kernel32.SleepConditionVariableSRW( |
| 56 | 56 | &cond.cond, |
| 57 | &mutex.srwlock, | |
| 57 | &mutex.impl.srwlock, | |
| 58 | 58 | windows.INFINITE, |
| 59 | 59 | @as(windows.ULONG, 0), |
| 60 | 60 | ); |
lib/std/Thread/Mutex.zig+97-80| ... | ... | @@ -33,17 +33,29 @@ const testing = std.testing; |
| 33 | 33 | const StaticResetEvent = std.thread.StaticResetEvent; |
| 34 | 34 | |
| 35 | 35 | /// Try to acquire the mutex without blocking. Returns `null` if the mutex is |
| 36 | /// unavailable. Otherwise returns `Held`. Call `release` on `Held`. | |
| 37 | pub fn tryAcquire(m: *Mutex) ?Impl.Held { | |
| 36 | /// unavailable. Otherwise returns `Held`. Call `release` on `Held`, or use | |
| 37 | /// releaseDirect(). | |
| 38 | pub fn tryAcquire(m: *Mutex) ?Held { | |
| 38 | 39 | return m.impl.tryAcquire(); |
| 39 | 40 | } |
| 40 | 41 | |
| 41 | 42 | /// Acquire the mutex. Deadlocks if the mutex is already |
| 42 | 43 | /// held by the calling thread. |
| 43 | pub fn acquire(m: *Mutex) Impl.Held { | |
| 44 | pub fn acquire(m: *Mutex) Held { | |
| 44 | 45 | return m.impl.acquire(); |
| 45 | 46 | } |
| 46 | 47 | |
| 48 | /// Release the mutex. Prefer Held.release() if available. | |
| 49 | pub fn releaseDirect(m: *Mutex) void { | |
| 50 | return m.impl.releaseDirect(); | |
| 51 | } | |
| 52 | ||
| 53 | /// A held mutex handle. Call release to allow other threads to | |
| 54 | /// take the mutex. Do not call release() more than once. | |
| 55 | /// For more complex scenarios, this handle can be discarded | |
| 56 | /// and Mutex.releaseDirect can be called instead. | |
| 57 | pub const Held = Impl.Held; | |
| 58 | ||
| 47 | 59 | const Impl = if (builtin.single_threaded) |
| 48 | 60 | Dummy |
| 49 | 61 | else if (builtin.os.tag == .windows) |
| ... | ... | @@ -53,6 +65,32 @@ else if (std.Thread.use_pthreads) |
| 53 | 65 | else |
| 54 | 66 | AtomicMutex; |
| 55 | 67 | |
| 68 | fn HeldInterface(comptime MutexType: type) type { | |
| 69 | return struct { | |
| 70 | const Mixin = @This(); | |
| 71 | pub const Held = struct { | |
| 72 | mutex: *MutexType, | |
| 73 | ||
| 74 | pub fn release(held: Mixin.Held) void { | |
| 75 | held.mutex.releaseDirect(); | |
| 76 | } | |
| 77 | }; | |
| 78 | ||
| 79 | pub fn tryAcquire(m: *MutexType) ?Mixin.Held { | |
| 80 | if (m.tryAcquireDirect()) { | |
| 81 | return Mixin.Held{ .mutex = m }; | |
| 82 | } else { | |
| 83 | return null; | |
| 84 | } | |
| 85 | } | |
| 86 | ||
| 87 | pub fn acquire(m: *MutexType) Mixin.Held { | |
| 88 | m.acquireDirect(); | |
| 89 | return Mixin.Held{ .mutex = m }; | |
| 90 | } | |
| 91 | }; | |
| 92 | } | |
| 93 | ||
| 56 | 94 | pub const AtomicMutex = struct { |
| 57 | 95 | state: State = .unlocked, |
| 58 | 96 | |
| ... | ... | @@ -62,39 +100,32 @@ pub const AtomicMutex = struct { |
| 62 | 100 | waiting, |
| 63 | 101 | }; |
| 64 | 102 | |
| 65 | pub const Held = struct { | |
| 66 | mutex: *AtomicMutex, | |
| 103 | pub usingnamespace HeldInterface(@This()); | |
| 67 | 104 | |
| 68 | pub fn release(held: Held) void { | |
| 69 | switch (@atomicRmw(State, &held.mutex.state, .Xchg, .unlocked, .Release)) { | |
| 70 | .unlocked => unreachable, | |
| 71 | .locked => {}, | |
| 72 | .waiting => held.mutex.unlockSlow(), | |
| 73 | } | |
| 74 | } | |
| 75 | }; | |
| 76 | ||
| 77 | pub fn tryAcquire(m: *AtomicMutex) ?Held { | |
| 78 | if (@cmpxchgStrong( | |
| 105 | fn tryAcquireDirect(m: *AtomicMutex) bool { | |
| 106 | return @cmpxchgStrong( | |
| 79 | 107 | State, |
| 80 | 108 | &m.state, |
| 81 | 109 | .unlocked, |
| 82 | 110 | .locked, |
| 83 | 111 | .Acquire, |
| 84 | 112 | .Monotonic, |
| 85 | ) == null) { | |
| 86 | return Held{ .mutex = m }; | |
| 87 | } else { | |
| 88 | return null; | |
| 89 | } | |
| 113 | ) == null; | |
| 90 | 114 | } |
| 91 | 115 | |
| 92 | pub fn acquire(m: *AtomicMutex) Held { | |
| 116 | fn acquireDirect(m: *AtomicMutex) void { | |
| 93 | 117 | switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) { |
| 94 | 118 | .unlocked => {}, |
| 95 | 119 | else => |s| m.lockSlow(s), |
| 96 | 120 | } |
| 97 | return Held{ .mutex = m }; | |
| 121 | } | |
| 122 | ||
| 123 | fn releaseDirect(m: *AtomicMutex) void { | |
| 124 | switch (@atomicRmw(State, &m.state, .Xchg, .unlocked, .Release)) { | |
| 125 | .unlocked => unreachable, | |
| 126 | .locked => {}, | |
| 127 | .waiting => m.unlockSlow(), | |
| 128 | } | |
| 98 | 129 | } |
| 99 | 130 | |
| 100 | 131 | fn lockSlow(m: *AtomicMutex, current_state: State) void { |
| ... | ... | @@ -171,36 +202,20 @@ pub const AtomicMutex = struct { |
| 171 | 202 | pub const PthreadMutex = struct { |
| 172 | 203 | pthread_mutex: std.c.pthread_mutex_t = .{}, |
| 173 | 204 | |
| 174 | pub const Held = struct { | |
| 175 | mutex: *PthreadMutex, | |
| 176 | ||
| 177 | pub fn release(held: Held) void { | |
| 178 | switch (std.c.pthread_mutex_unlock(&held.mutex.pthread_mutex)) { | |
| 179 | .SUCCESS => return, | |
| 180 | .INVAL => unreachable, | |
| 181 | .AGAIN => unreachable, | |
| 182 | .PERM => unreachable, | |
| 183 | else => unreachable, | |
| 184 | } | |
| 185 | } | |
| 186 | }; | |
| 205 | pub usingnamespace HeldInterface(@This()); | |
| 187 | 206 | |
| 188 | /// Try to acquire the mutex without blocking. Returns null if | |
| 189 | /// the mutex is unavailable. Otherwise returns Held. Call | |
| 190 | /// release on Held. | |
| 191 | pub fn tryAcquire(m: *PthreadMutex) ?Held { | |
| 192 | if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS) { | |
| 193 | return Held{ .mutex = m }; | |
| 194 | } else { | |
| 195 | return null; | |
| 196 | } | |
| 207 | /// Try to acquire the mutex without blocking. Returns true if | |
| 208 | /// the mutex is unavailable. Otherwise returns false. Call | |
| 209 | /// release when done. | |
| 210 | fn tryAcquireDirect(m: *PthreadMutex) bool { | |
| 211 | return std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS; | |
| 197 | 212 | } |
| 198 | 213 | |
| 199 | 214 | /// Acquire the mutex. Will deadlock if the mutex is already |
| 200 | 215 | /// held by the calling thread. |
| 201 | pub fn acquire(m: *PthreadMutex) Held { | |
| 216 | fn acquireDirect(m: *PthreadMutex) void { | |
| 202 | 217 | switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) { |
| 203 | .SUCCESS => return Held{ .mutex = m }, | |
| 218 | .SUCCESS => {}, | |
| 204 | 219 | .INVAL => unreachable, |
| 205 | 220 | .BUSY => unreachable, |
| 206 | 221 | .AGAIN => unreachable, |
| ... | ... | @@ -209,6 +224,16 @@ pub const PthreadMutex = struct { |
| 209 | 224 | else => unreachable, |
| 210 | 225 | } |
| 211 | 226 | } |
| 227 | ||
| 228 | fn releaseDirect(m: *PthreadMutex) void { | |
| 229 | switch (std.c.pthread_mutex_unlock(&m.pthread_mutex)) { | |
| 230 | .SUCCESS => return, | |
| 231 | .INVAL => unreachable, | |
| 232 | .AGAIN => unreachable, | |
| 233 | .PERM => unreachable, | |
| 234 | else => unreachable, | |
| 235 | } | |
| 236 | } | |
| 212 | 237 | }; |
| 213 | 238 | |
| 214 | 239 | /// This has the sematics as `Mutex`, however it does not actually do any |
| ... | ... | @@ -216,58 +241,50 @@ pub const PthreadMutex = struct { |
| 216 | 241 | pub const Dummy = struct { |
| 217 | 242 | lock: @TypeOf(lock_init) = lock_init, |
| 218 | 243 | |
| 219 | const lock_init = if (std.debug.runtime_safety) false else {}; | |
| 220 | ||
| 221 | pub const Held = struct { | |
| 222 | mutex: *Dummy, | |
| 244 | pub usingnamespace HeldInterface(@This()); | |
| 223 | 245 | |
| 224 | pub fn release(held: Held) void { | |
| 225 | if (std.debug.runtime_safety) { | |
| 226 | held.mutex.lock = false; | |
| 227 | } | |
| 228 | } | |
| 229 | }; | |
| 246 | const lock_init = if (std.debug.runtime_safety) false else {}; | |
| 230 | 247 | |
| 231 | /// Try to acquire the mutex without blocking. Returns null if | |
| 232 | /// the mutex is unavailable. Otherwise returns Held. Call | |
| 233 | /// release on Held. | |
| 234 | pub fn tryAcquire(m: *Dummy) ?Held { | |
| 248 | /// Try to acquire the mutex without blocking. Returns false if | |
| 249 | /// the mutex is unavailable. Otherwise returns true. | |
| 250 | fn tryAcquireDirect(m: *Dummy) bool { | |
| 235 | 251 | if (std.debug.runtime_safety) { |
| 236 | if (m.lock) return null; | |
| 252 | if (m.lock) return false; | |
| 237 | 253 | m.lock = true; |
| 238 | 254 | } |
| 239 | return Held{ .mutex = m }; | |
| 255 | return true; | |
| 240 | 256 | } |
| 241 | 257 | |
| 242 | 258 | /// Acquire the mutex. Will deadlock if the mutex is already |
| 243 | 259 | /// held by the calling thread. |
| 244 | pub fn acquire(m: *Dummy) Held { | |
| 245 | return m.tryAcquire() orelse @panic("deadlock detected"); | |
| 260 | fn acquireDirect(m: *Dummy) void { | |
| 261 | if (!m.tryAcquireDirect()) { | |
| 262 | @panic("deadlock detected"); | |
| 263 | } | |
| 264 | } | |
| 265 | ||
| 266 | fn releaseDirect(m: *Dummy) void { | |
| 267 | if (std.debug.runtime_safety) { | |
| 268 | m.lock = false; | |
| 269 | } | |
| 246 | 270 | } |
| 247 | 271 | }; |
| 248 | 272 | |
| 249 | 273 | const WindowsMutex = struct { |
| 250 | 274 | srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT, |
| 251 | 275 | |
| 252 | pub const Held = struct { | |
| 253 | mutex: *WindowsMutex, | |
| 276 | pub usingnamespace HeldInterface(@This()); | |
| 254 | 277 | |
| 255 | pub fn release(held: Held) void { | |
| 256 | windows.kernel32.ReleaseSRWLockExclusive(&held.mutex.srwlock); | |
| 257 | } | |
| 258 | }; | |
| 259 | ||
| 260 | pub fn tryAcquire(m: *WindowsMutex) ?Held { | |
| 261 | if (windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE) { | |
| 262 | return Held{ .mutex = m }; | |
| 263 | } else { | |
| 264 | return null; | |
| 265 | } | |
| 278 | fn tryAcquireDirect(m: *WindowsMutex) bool { | |
| 279 | return windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE; | |
| 266 | 280 | } |
| 267 | 281 | |
| 268 | pub fn acquire(m: *WindowsMutex) Held { | |
| 282 | fn acquireDirect(m: *WindowsMutex) void { | |
| 269 | 283 | windows.kernel32.AcquireSRWLockExclusive(&m.srwlock); |
| 270 | return Held{ .mutex = m }; | |
| 284 | } | |
| 285 | ||
| 286 | fn releaseDirect(m: *WindowsMutex) void { | |
| 287 | windows.kernel32.ReleaseSRWLockExclusive(&m.srwlock); | |
| 271 | 288 | } |
| 272 | 289 | }; |
| 273 | 290 |
lib/std/array_hash_map.zig+10-10| ... | ... | @@ -90,7 +90,7 @@ pub fn ArrayHashMap( |
| 90 | 90 | /// Modifying the key is allowed only if it does not change the hash. |
| 91 | 91 | /// Modifying the value is allowed. |
| 92 | 92 | /// Entry pointers become invalid whenever this ArrayHashMap is modified, |
| 93 | /// unless `ensureCapacity` was previously used. | |
| 93 | /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used. | |
| 94 | 94 | pub const Entry = Unmanaged.Entry; |
| 95 | 95 | |
| 96 | 96 | /// A KV pair which has been copied out of the backing store |
| ... | ... | @@ -110,7 +110,7 @@ pub fn ArrayHashMap( |
| 110 | 110 | /// Modifying the key is allowed only if it does not change the hash. |
| 111 | 111 | /// Modifying the value is allowed. |
| 112 | 112 | /// Entry pointers become invalid whenever this ArrayHashMap is modified, |
| 113 | /// unless `ensureCapacity` was previously used. | |
| 113 | /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used. | |
| 114 | 114 | pub const GetOrPutResult = Unmanaged.GetOrPutResult; |
| 115 | 115 | |
| 116 | 116 | /// An Iterator over Entry pointers. |
| ... | ... | @@ -478,7 +478,7 @@ pub fn ArrayHashMapUnmanaged( |
| 478 | 478 | /// Modifying the key is allowed only if it does not change the hash. |
| 479 | 479 | /// Modifying the value is allowed. |
| 480 | 480 | /// Entry pointers become invalid whenever this ArrayHashMap is modified, |
| 481 | /// unless `ensureCapacity` was previously used. | |
| 481 | /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used. | |
| 482 | 482 | pub const Entry = struct { |
| 483 | 483 | key_ptr: *K, |
| 484 | 484 | value_ptr: *V, |
| ... | ... | @@ -509,7 +509,7 @@ pub fn ArrayHashMapUnmanaged( |
| 509 | 509 | /// Modifying the key is allowed only if it does not change the hash. |
| 510 | 510 | /// Modifying the value is allowed. |
| 511 | 511 | /// Entry pointers become invalid whenever this ArrayHashMap is modified, |
| 512 | /// unless `ensureCapacity` was previously used. | |
| 512 | /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used. | |
| 513 | 513 | pub const GetOrPutResult = struct { |
| 514 | 514 | key_ptr: *K, |
| 515 | 515 | value_ptr: *V, |
| ... | ... | @@ -759,20 +759,20 @@ pub fn ArrayHashMapUnmanaged( |
| 759 | 759 | } |
| 760 | 760 | pub fn ensureTotalCapacityContext(self: *Self, allocator: *Allocator, new_capacity: usize, ctx: Context) !void { |
| 761 | 761 | if (new_capacity <= linear_scan_max) { |
| 762 | try self.entries.ensureCapacity(allocator, new_capacity); | |
| 762 | try self.entries.ensureTotalCapacity(allocator, new_capacity); | |
| 763 | 763 | return; |
| 764 | 764 | } |
| 765 | 765 | |
| 766 | 766 | if (self.index_header) |header| { |
| 767 | 767 | if (new_capacity <= header.capacity()) { |
| 768 | try self.entries.ensureCapacity(allocator, new_capacity); | |
| 768 | try self.entries.ensureTotalCapacity(allocator, new_capacity); | |
| 769 | 769 | return; |
| 770 | 770 | } |
| 771 | 771 | } |
| 772 | 772 | |
| 773 | 773 | const new_bit_index = try IndexHeader.findBitIndex(new_capacity); |
| 774 | 774 | const new_header = try IndexHeader.alloc(allocator, new_bit_index); |
| 775 | try self.entries.ensureCapacity(allocator, new_capacity); | |
| 775 | try self.entries.ensureTotalCapacity(allocator, new_capacity); | |
| 776 | 776 | |
| 777 | 777 | if (self.index_header) |old_header| old_header.free(allocator); |
| 778 | 778 | self.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, new_header); |
| ... | ... | @@ -1441,7 +1441,7 @@ pub fn ArrayHashMapUnmanaged( |
| 1441 | 1441 | unreachable; |
| 1442 | 1442 | } |
| 1443 | 1443 | |
| 1444 | /// Must ensureCapacity before calling this. | |
| 1444 | /// Must `ensureTotalCapacity`/`ensureUnusedCapacity` before calling this. | |
| 1445 | 1445 | fn getOrPutInternal(self: *Self, key: anytype, ctx: anytype, header: *IndexHeader, comptime I: type) GetOrPutResult { |
| 1446 | 1446 | const slice = self.entries.slice(); |
| 1447 | 1447 | const hashes_array = if (store_hash) slice.items(.hash) else {}; |
| ... | ... | @@ -1485,7 +1485,7 @@ pub fn ArrayHashMapUnmanaged( |
| 1485 | 1485 | } |
| 1486 | 1486 | |
| 1487 | 1487 | // This pointer survives the following append because we call |
| 1488 | // entries.ensureCapacity before getOrPutInternal. | |
| 1488 | // entries.ensureTotalCapacity before getOrPutInternal. | |
| 1489 | 1489 | const hash_match = if (store_hash) h == hashes_array[slot_data.entry_index] else true; |
| 1490 | 1490 | if (hash_match and checkedEql(ctx, key, keys_array[slot_data.entry_index])) { |
| 1491 | 1491 | return .{ |
| ... | ... | @@ -1946,7 +1946,7 @@ test "iterator hash map" { |
| 1946 | 1946 | var reset_map = AutoArrayHashMap(i32, i32).init(std.testing.allocator); |
| 1947 | 1947 | defer reset_map.deinit(); |
| 1948 | 1948 | |
| 1949 | // test ensureCapacity with a 0 parameter | |
| 1949 | // test ensureTotalCapacity with a 0 parameter | |
| 1950 | 1950 | try reset_map.ensureTotalCapacity(0); |
| 1951 | 1951 | |
| 1952 | 1952 | try reset_map.putNoClobber(0, 11); |
lib/std/build.zig+7-6| ... | ... | @@ -684,7 +684,11 @@ pub const Builder = struct { |
| 684 | 684 | ); |
| 685 | 685 | const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract"); |
| 686 | 686 | |
| 687 | const triple = maybe_triple orelse return args.default_target; | |
| 687 | if (maybe_triple == null and mcpu == null) { | |
| 688 | return args.default_target; | |
| 689 | } | |
| 690 | ||
| 691 | const triple = maybe_triple orelse "native"; | |
| 688 | 692 | |
| 689 | 693 | var diags: CrossTarget.ParseOptions.Diagnostics = .{}; |
| 690 | 694 | const selected_target = CrossTarget.parse(.{ |
| ... | ... | @@ -2432,11 +2436,8 @@ pub const LibExeObjStep = struct { |
| 2432 | 2436 | |
| 2433 | 2437 | if (populated_cpu_features.eql(cross.cpu.features)) { |
| 2434 | 2438 | // The CPU name alone is sufficient. |
| 2435 | // If it is the baseline CPU, no command line args are required. | |
| 2436 | if (cross.cpu.model != std.Target.Cpu.baseline(cross.cpu.arch).model) { | |
| 2437 | try zig_args.append("-mcpu"); | |
| 2438 | try zig_args.append(cross.cpu.model.name); | |
| 2439 | } | |
| 2439 | try zig_args.append("-mcpu"); | |
| 2440 | try zig_args.append(cross.cpu.model.name); | |
| 2440 | 2441 | } else { |
| 2441 | 2442 | var mcpu_buffer = std.ArrayList(u8).init(builder.allocator); |
| 2442 | 2443 |
lib/std/builtin.zig+11-1| ... | ... | @@ -166,6 +166,15 @@ pub const CallingConvention = enum { |
| 166 | 166 | SysV, |
| 167 | 167 | }; |
| 168 | 168 | |
| 169 | /// This data structure is used by the Zig language code generation and | |
| 170 | /// therefore must be kept in sync with the compiler implementation. | |
| 171 | pub const AddressSpace = enum { | |
| 172 | generic, | |
| 173 | gs, | |
| 174 | fs, | |
| 175 | ss, | |
| 176 | }; | |
| 177 | ||
| 169 | 178 | /// This data structure is used by the Zig language code generation and |
| 170 | 179 | /// therefore must be kept in sync with the compiler implementation. |
| 171 | 180 | pub const SourceLocation = struct { |
| ... | ... | @@ -226,6 +235,7 @@ pub const TypeInfo = union(enum) { |
| 226 | 235 | is_const: bool, |
| 227 | 236 | is_volatile: bool, |
| 228 | 237 | alignment: comptime_int, |
| 238 | address_space: AddressSpace, | |
| 229 | 239 | child: type, |
| 230 | 240 | is_allowzero: bool, |
| 231 | 241 | |
| ... | ... | @@ -700,7 +710,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn |
| 700 | 710 | }, |
| 701 | 711 | else => { |
| 702 | 712 | const first_trace_addr = @returnAddress(); |
| 703 | std.debug.panicExtra(error_return_trace, first_trace_addr, "{s}", .{msg}); | |
| 713 | std.debug.panicImpl(error_return_trace, first_trace_addr, msg); | |
| 704 | 714 | }, |
| 705 | 715 | } |
| 706 | 716 | } |
lib/std/c.zig+27| ... | ... | @@ -252,6 +252,33 @@ pub extern "c" fn kevent( |
| 252 | 252 | timeout: ?*const c.timespec, |
| 253 | 253 | ) c_int; |
| 254 | 254 | |
| 255 | pub extern "c" fn port_create() c.port_t; | |
| 256 | pub extern "c" fn port_associate( | |
| 257 | port: c.port_t, | |
| 258 | source: u32, | |
| 259 | object: usize, | |
| 260 | events: u32, | |
| 261 | user_var: ?*c_void, | |
| 262 | ) c_int; | |
| 263 | pub extern "c" fn port_dissociate(port: c.port_t, source: u32, object: usize) c_int; | |
| 264 | pub extern "c" fn port_send(port: c.port_t, events: u32, user_var: ?*c_void) c_int; | |
| 265 | pub extern "c" fn port_sendn( | |
| 266 | ports: [*]c.port_t, | |
| 267 | errors: []u32, | |
| 268 | num_ports: u32, | |
| 269 | events: u32, | |
| 270 | user_var: ?*c_void, | |
| 271 | ) c_int; | |
| 272 | pub extern "c" fn port_get(port: c.port_t, event: *c.port_event, timeout: ?*c.timespec) c_int; | |
| 273 | pub extern "c" fn port_getn( | |
| 274 | port: c.port_t, | |
| 275 | event_list: []c.port_event, | |
| 276 | max_events: u32, | |
| 277 | events_retrieved: *u32, | |
| 278 | timeout: ?*c.timespec, | |
| 279 | ) c_int; | |
| 280 | pub extern "c" fn port_alert(port: c.port_t, flags: u32, events: u32, user_var: ?*c_void) c_int; | |
| 281 | ||
| 255 | 282 | pub extern "c" fn getaddrinfo( |
| 256 | 283 | noalias node: ?[*:0]const u8, |
| 257 | 284 | noalias service: ?[*:0]const u8, |
lib/std/c/darwin.zig+1| ... | ... | @@ -291,6 +291,7 @@ pub const sockaddr = extern struct { |
| 291 | 291 | family: sa_family_t, |
| 292 | 292 | data: [14]u8, |
| 293 | 293 | |
| 294 | pub const SS_MAXSIZE = 128; | |
| 294 | 295 | pub const storage = std.x.os.Socket.Address.Native.Storage; |
| 295 | 296 | pub const in = extern struct { |
| 296 | 297 | len: u8 = @sizeOf(in), |
lib/std/c/dragonfly.zig+1| ... | ... | @@ -465,6 +465,7 @@ pub const sockaddr = extern struct { |
| 465 | 465 | family: u8, |
| 466 | 466 | data: [14]u8, |
| 467 | 467 | |
| 468 | pub const SS_MAXSIZE = 128; | |
| 468 | 469 | pub const storage = std.x.os.Socket.Address.Native.Storage; |
| 469 | 470 | |
| 470 | 471 | pub const in = extern struct { |
lib/std/c/freebsd.zig+1| ... | ... | @@ -323,6 +323,7 @@ pub const sockaddr = extern struct { |
| 323 | 323 | /// actually longer; address value |
| 324 | 324 | data: [14]u8, |
| 325 | 325 | |
| 326 | pub const SS_MAXSIZE = 128; | |
| 326 | 327 | pub const storage = std.x.os.Socket.Address.Native.Storage; |
| 327 | 328 | |
| 328 | 329 | pub const in = extern struct { |
lib/std/c/haiku.zig+1| ... | ... | @@ -339,6 +339,7 @@ pub const sockaddr = extern struct { |
| 339 | 339 | /// actually longer; address value |
| 340 | 340 | data: [14]u8, |
| 341 | 341 | |
| 342 | pub const SS_MAXSIZE = 128; | |
| 342 | 343 | pub const storage = std.x.os.Socket.Address.Native.Storage; |
| 343 | 344 | |
| 344 | 345 | pub const in = extern struct { |
lib/std/c/netbsd.zig+1| ... | ... | @@ -476,6 +476,7 @@ pub const sockaddr = extern struct { |
| 476 | 476 | /// actually longer; address value |
| 477 | 477 | data: [14]u8, |
| 478 | 478 | |
| 479 | pub const SS_MAXSIZE = 128; | |
| 479 | 480 | pub const storage = std.x.os.Socket.Address.Native.Storage; |
| 480 | 481 | |
| 481 | 482 | pub const in = extern struct { |
lib/std/c/openbsd.zig+1| ... | ... | @@ -279,6 +279,7 @@ pub const sockaddr = extern struct { |
| 279 | 279 | /// actually longer; address value |
| 280 | 280 | data: [14]u8, |
| 281 | 281 | |
| 282 | pub const SS_MAXSIZE = 256; | |
| 282 | 283 | pub const storage = std.x.os.Socket.Address.Native.Storage; |
| 283 | 284 | |
| 284 | 285 | pub const in = extern struct { |
lib/std/c/solaris.zig+1909-11| ... | ... | @@ -1,15 +1,1913 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const maxInt = std.math.maxInt; | |
| 4 | const iovec = std.os.iovec; | |
| 5 | const iovec_const = std.os.iovec_const; | |
| 6 | const timezone = std.c.timezone; | |
| 7 | ||
| 8 | extern "c" fn ___errno() *c_int; | |
| 9 | pub const _errno = ___errno; | |
| 10 | ||
| 11 | pub const dl_iterate_phdr_callback = fn (info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int; | |
| 12 | pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int; | |
| 13 | ||
| 14 | pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; | |
| 15 | pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; | |
| 16 | pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; | |
| 17 | pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void; | |
| 18 | pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; | |
| 19 | pub extern "c" fn sysconf(sc: c_int) i64; | |
| 20 | pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) c_int; | |
| 21 | pub extern "c" fn madvise(address: [*]u8, len: usize, advise: u32) c_int; | |
| 22 | ||
| 1 | 23 | pub const pthread_mutex_t = extern struct { |
| 2 | __pthread_mutex_flag1: u16 = 0, | |
| 3 | __pthread_mutex_flag2: u8 = 0, | |
| 4 | __pthread_mutex_ceiling: u8 = 0, | |
| 5 | __pthread_mutex_type: u16 = 0, | |
| 6 | __pthread_mutex_magic: u16 = 0x4d58, | |
| 7 | __pthread_mutex_lock: u64 = 0, | |
| 8 | __pthread_mutex_data: u64 = 0, | |
| 24 | flag1: u16 = 0, | |
| 25 | flag2: u8 = 0, | |
| 26 | ceiling: u8 = 0, | |
| 27 | @"type": u16 = 0, | |
| 28 | magic: u16 = 0x4d58, | |
| 29 | lock: u64 = 0, | |
| 30 | data: u64 = 0, | |
| 9 | 31 | }; |
| 10 | 32 | pub const pthread_cond_t = extern struct { |
| 11 | __pthread_cond_flag: u32 = 0, | |
| 12 | __pthread_cond_type: u16 = 0, | |
| 13 | __pthread_cond_magic: u16 = 0x4356, | |
| 14 | __pthread_cond_data: u64 = 0, | |
| 33 | flag: [4]u8 = [_]u8{0} ** 4, | |
| 34 | @"type": u16 = 0, | |
| 35 | magic: u16 = 0x4356, | |
| 36 | data: u64 = 0, | |
| 15 | 37 | }; |
| 38 | pub const pthread_rwlock_t = extern struct { | |
| 39 | readers: i32 = 0, | |
| 40 | @"type": u16 = 0, | |
| 41 | magic: u16 = 0x5257, | |
| 42 | mutex: pthread_mutex_t = .{}, | |
| 43 | readercv: pthread_cond_t = .{}, | |
| 44 | writercv: pthread_cond_t = .{}, | |
| 45 | }; | |
| 46 | pub const pthread_attr_t = extern struct { | |
| 47 | mutexattr: ?*c_void = null, | |
| 48 | }; | |
| 49 | pub const pthread_key_t = c_int; | |
| 50 | ||
| 51 | pub const sem_t = extern struct { | |
| 52 | count: u32 = 0, | |
| 53 | @"type": u16 = 0, | |
| 54 | magic: u16 = 0x534d, | |
| 55 | __pad1: [3]u64 = [_]u64{0} ** 3, | |
| 56 | __pad2: [2]u64 = [_]u64{0} ** 2, | |
| 57 | }; | |
| 58 | ||
| 59 | pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8, arg: ?*c_void) E; | |
| 60 | pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) E; | |
| 61 | ||
| 62 | pub const blkcnt_t = i64; | |
| 63 | pub const blksize_t = i32; | |
| 64 | pub const clock_t = i64; | |
| 65 | pub const dev_t = i32; | |
| 66 | pub const fd_t = c_int; | |
| 67 | pub const gid_t = u32; | |
| 68 | pub const ino_t = u64; | |
| 69 | pub const mode_t = u32; | |
| 70 | pub const nlink_t = u32; | |
| 71 | pub const off_t = i64; | |
| 72 | pub const pid_t = i32; | |
| 73 | pub const socklen_t = u32; | |
| 74 | pub const time_t = i64; | |
| 75 | pub const suseconds_t = i64; | |
| 76 | pub const uid_t = u32; | |
| 77 | pub const major_t = u32; | |
| 78 | pub const minor_t = u32; | |
| 79 | pub const port_t = c_int; | |
| 80 | pub const nfds_t = usize; | |
| 81 | pub const id_t = i32; | |
| 82 | pub const taskid_t = id_t; | |
| 83 | pub const projid_t = id_t; | |
| 84 | pub const poolid_t = id_t; | |
| 85 | pub const zoneid_t = id_t; | |
| 86 | pub const ctid_t = id_t; | |
| 87 | ||
| 88 | pub const dl_phdr_info = extern struct { | |
| 89 | dlpi_addr: std.elf.Addr, | |
| 90 | dlpi_name: ?[*:0]const u8, | |
| 91 | dlpi_phdr: [*]std.elf.Phdr, | |
| 92 | dlpi_phnum: std.elf.Half, | |
| 93 | /// Incremented when a new object is mapped into the process. | |
| 94 | dlpi_adds: u64, | |
| 95 | /// Incremented when an object is unmapped from the process. | |
| 96 | dlpi_subs: u64, | |
| 97 | }; | |
| 98 | ||
| 99 | pub const RTLD = struct { | |
| 100 | pub const LAZY = 0x00001; | |
| 101 | pub const NOW = 0x00002; | |
| 102 | pub const NOLOAD = 0x00004; | |
| 103 | pub const GLOBAL = 0x00100; | |
| 104 | pub const LOCAL = 0x00000; | |
| 105 | pub const PARENT = 0x00200; | |
| 106 | pub const GROUP = 0x00400; | |
| 107 | pub const WORLD = 0x00800; | |
| 108 | pub const NODELETE = 0x01000; | |
| 109 | pub const FIRST = 0x02000; | |
| 110 | pub const CONFGEN = 0x10000; | |
| 111 | ||
| 112 | pub const NEXT = @intToPtr(*c_void, @bitCast(usize, @as(isize, -1))); | |
| 113 | pub const DEFAULT = @intToPtr(*c_void, @bitCast(usize, @as(isize, -2))); | |
| 114 | pub const SELF = @intToPtr(*c_void, @bitCast(usize, @as(isize, -3))); | |
| 115 | pub const PROBE = @intToPtr(*c_void, @bitCast(usize, @as(isize, -4))); | |
| 116 | }; | |
| 117 | ||
| 118 | pub const Flock = extern struct { | |
| 119 | l_type: c_short, | |
| 120 | l_whence: c_short, | |
| 121 | l_start: off_t, | |
| 122 | // len == 0 means until end of file. | |
| 123 | l_len: off_t, | |
| 124 | l_sysid: c_int, | |
| 125 | l_pid: pid_t, | |
| 126 | __pad: [4]c_long, | |
| 127 | }; | |
| 128 | ||
| 129 | pub const utsname = extern struct { | |
| 130 | sysname: [256:0]u8, | |
| 131 | nodename: [256:0]u8, | |
| 132 | release: [256:0]u8, | |
| 133 | version: [256:0]u8, | |
| 134 | machine: [256:0]u8, | |
| 135 | domainname: [256:0]u8, | |
| 136 | }; | |
| 137 | ||
| 138 | pub const addrinfo = extern struct { | |
| 139 | flags: i32, | |
| 140 | family: i32, | |
| 141 | socktype: i32, | |
| 142 | protocol: i32, | |
| 143 | addrlen: socklen_t, | |
| 144 | canonname: ?[*:0]u8, | |
| 145 | addr: ?*sockaddr, | |
| 146 | next: ?*addrinfo, | |
| 147 | }; | |
| 148 | ||
| 149 | pub const EAI = enum(c_int) { | |
| 150 | /// address family for hostname not supported | |
| 151 | ADDRFAMILY = 1, | |
| 152 | /// name could not be resolved at this time | |
| 153 | AGAIN = 2, | |
| 154 | /// flags parameter had an invalid value | |
| 155 | BADFLAGS = 3, | |
| 156 | /// non-recoverable failure in name resolution | |
| 157 | FAIL = 4, | |
| 158 | /// address family not recognized | |
| 159 | FAMILY = 5, | |
| 160 | /// memory allocation failure | |
| 161 | MEMORY = 6, | |
| 162 | /// no address associated with hostname | |
| 163 | NODATA = 7, | |
| 164 | /// name does not resolve | |
| 165 | NONAME = 8, | |
| 166 | /// service not recognized for socket type | |
| 167 | SERVICE = 9, | |
| 168 | /// intended socket type was not recognized | |
| 169 | SOCKTYPE = 10, | |
| 170 | /// system error returned in errno | |
| 171 | SYSTEM = 11, | |
| 172 | /// argument buffer overflow | |
| 173 | OVERFLOW = 12, | |
| 174 | /// resolved protocol is unknown | |
| 175 | PROTOCOL = 13, | |
| 176 | ||
| 177 | _, | |
| 178 | }; | |
| 179 | ||
| 180 | pub const EAI_MAX = 14; | |
| 181 | ||
| 182 | pub const msghdr = extern struct { | |
| 183 | /// optional address | |
| 184 | msg_name: ?*sockaddr, | |
| 185 | /// size of address | |
| 186 | msg_namelen: socklen_t, | |
| 187 | /// scatter/gather array | |
| 188 | msg_iov: [*]iovec, | |
| 189 | /// # elements in msg_iov | |
| 190 | msg_iovlen: i32, | |
| 191 | /// ancillary data | |
| 192 | msg_control: ?*c_void, | |
| 193 | /// ancillary data buffer len | |
| 194 | msg_controllen: socklen_t, | |
| 195 | /// flags on received message | |
| 196 | msg_flags: i32, | |
| 197 | }; | |
| 198 | ||
| 199 | pub const msghdr_const = extern struct { | |
| 200 | /// optional address | |
| 201 | msg_name: ?*const sockaddr, | |
| 202 | /// size of address | |
| 203 | msg_namelen: socklen_t, | |
| 204 | /// scatter/gather array | |
| 205 | msg_iov: [*]iovec_const, | |
| 206 | /// # elements in msg_iov | |
| 207 | msg_iovlen: i32, | |
| 208 | /// ancillary data | |
| 209 | msg_control: ?*c_void, | |
| 210 | /// ancillary data buffer len | |
| 211 | msg_controllen: socklen_t, | |
| 212 | /// flags on received message | |
| 213 | msg_flags: i32, | |
| 214 | }; | |
| 215 | ||
| 216 | pub const cmsghdr = extern struct { | |
| 217 | cmsg_len: socklen_t, | |
| 218 | cmsg_level: i32, | |
| 219 | cmsg_type: i32, | |
| 220 | }; | |
| 221 | ||
| 222 | /// The stat structure used by libc. | |
| 223 | pub const Stat = extern struct { | |
| 224 | dev: dev_t, | |
| 225 | ino: ino_t, | |
| 226 | mode: mode_t, | |
| 227 | nlink: nlink_t, | |
| 228 | uid: uid_t, | |
| 229 | gid: gid_t, | |
| 230 | rdev: dev_t, | |
| 231 | size: off_t, | |
| 232 | atim: timespec, | |
| 233 | mtim: timespec, | |
| 234 | ctim: timespec, | |
| 235 | blksize: blksize_t, | |
| 236 | blocks: blkcnt_t, | |
| 237 | fstype: [16]u8, | |
| 238 | ||
| 239 | pub fn atime(self: @This()) timespec { | |
| 240 | return self.atim; | |
| 241 | } | |
| 242 | ||
| 243 | pub fn mtime(self: @This()) timespec { | |
| 244 | return self.mtim; | |
| 245 | } | |
| 246 | ||
| 247 | pub fn ctime(self: @This()) timespec { | |
| 248 | return self.ctim; | |
| 249 | } | |
| 250 | }; | |
| 251 | ||
| 252 | pub const timespec = extern struct { | |
| 253 | tv_sec: i64, | |
| 254 | tv_nsec: isize, | |
| 255 | }; | |
| 256 | ||
| 257 | pub const timeval = extern struct { | |
| 258 | /// seconds | |
| 259 | tv_sec: time_t, | |
| 260 | /// microseconds | |
| 261 | tv_usec: suseconds_t, | |
| 262 | }; | |
| 263 | ||
| 264 | pub const MAXNAMLEN = 511; | |
| 265 | ||
| 266 | pub const dirent = extern struct { | |
| 267 | /// Inode number of entry. | |
| 268 | d_ino: ino_t, | |
| 269 | /// Offset of this entry on disk. | |
| 270 | d_off: off_t, | |
| 271 | /// Length of this record. | |
| 272 | d_reclen: u16, | |
| 273 | /// File name. | |
| 274 | d_name: [MAXNAMLEN:0]u8, | |
| 275 | ||
| 276 | pub fn reclen(self: dirent) u16 { | |
| 277 | return self.d_reclen; | |
| 278 | } | |
| 279 | }; | |
| 280 | ||
| 281 | pub const SOCK = struct { | |
| 282 | /// Datagram. | |
| 283 | pub const DGRAM = 1; | |
| 284 | /// STREAM. | |
| 285 | pub const STREAM = 2; | |
| 286 | /// Raw-protocol interface. | |
| 287 | pub const RAW = 4; | |
| 288 | /// Reliably-delivered message. | |
| 289 | pub const RDM = 5; | |
| 290 | /// Sequenced packed stream. | |
| 291 | pub const SEQPACKET = 6; | |
| 292 | ||
| 293 | pub const NONBLOCK = 0x100000; | |
| 294 | pub const NDELAY = 0x200000; | |
| 295 | pub const CLOEXEC = 0x080000; | |
| 296 | }; | |
| 297 | ||
| 298 | pub const SO = struct { | |
| 299 | pub const DEBUG = 0x0001; | |
| 300 | pub const ACCEPTCONN = 0x0002; | |
| 301 | pub const REUSEADDR = 0x0004; | |
| 302 | pub const KEEPALIVE = 0x0008; | |
| 303 | pub const DONTROUTE = 0x0010; | |
| 304 | pub const BROADCAST = 0x0020; | |
| 305 | pub const USELOOPBACK = 0x0040; | |
| 306 | pub const LINGER = 0x0080; | |
| 307 | pub const OOBINLINE = 0x0100; | |
| 308 | pub const DGRAM_ERRIND = 0x0200; | |
| 309 | pub const RECVUCRED = 0x0400; | |
| 310 | ||
| 311 | pub const SNDBUF = 0x1001; | |
| 312 | pub const RCVBUF = 0x1002; | |
| 313 | pub const SNDLOWAT = 0x1003; | |
| 314 | pub const RCVLOWAT = 0x1004; | |
| 315 | pub const SNDTIMEO = 0x1005; | |
| 316 | pub const RCVTIMEO = 0x1006; | |
| 317 | pub const ERROR = 0x1007; | |
| 318 | pub const TYPE = 0x1008; | |
| 319 | pub const PROTOTYPE = 0x1009; | |
| 320 | pub const ANON_MLP = 0x100a; | |
| 321 | pub const MAC_EXEMPT = 0x100b; | |
| 322 | pub const DOMAIN = 0x100c; | |
| 323 | pub const RCVPSH = 0x100d; | |
| 324 | ||
| 325 | pub const SECATTR = 0x1011; | |
| 326 | pub const TIMESTAMP = 0x1013; | |
| 327 | pub const ALLZONES = 0x1014; | |
| 328 | pub const EXCLBIND = 0x1015; | |
| 329 | pub const MAC_IMPLICIT = 0x1016; | |
| 330 | pub const VRRP = 0x1017; | |
| 331 | }; | |
| 332 | ||
| 333 | pub const SOMAXCONN = 128; | |
| 334 | ||
| 335 | pub const SCM = struct { | |
| 336 | pub const UCRED = 0x1012; | |
| 337 | pub const RIGHTS = 0x1010; | |
| 338 | pub const TIMESTAMP = SO.TIMESTAMP; | |
| 339 | }; | |
| 340 | ||
| 341 | pub const AF = struct { | |
| 342 | pub const UNSPEC = 0; | |
| 343 | pub const UNIX = 1; | |
| 344 | pub const LOCAL = UNIX; | |
| 345 | pub const FILE = UNIX; | |
| 346 | pub const INET = 2; | |
| 347 | pub const IMPLINK = 3; | |
| 348 | pub const PUP = 4; | |
| 349 | pub const CHAOS = 5; | |
| 350 | pub const NS = 6; | |
| 351 | pub const NBS = 7; | |
| 352 | pub const ECMA = 8; | |
| 353 | pub const DATAKIT = 9; | |
| 354 | pub const CCITT = 10; | |
| 355 | pub const SNA = 11; | |
| 356 | pub const DECnet = 12; | |
| 357 | pub const DLI = 13; | |
| 358 | pub const LAT = 14; | |
| 359 | pub const HYLINK = 15; | |
| 360 | pub const APPLETALK = 16; | |
| 361 | pub const NIT = 17; | |
| 362 | pub const @"802" = 18; | |
| 363 | pub const OSI = 19; | |
| 364 | pub const X25 = 20; | |
| 365 | pub const OSINET = 21; | |
| 366 | pub const GOSIP = 22; | |
| 367 | pub const IPX = 23; | |
| 368 | pub const ROUTE = 24; | |
| 369 | pub const LINK = 25; | |
| 370 | pub const INET6 = 26; | |
| 371 | pub const KEY = 27; | |
| 372 | pub const NCA = 28; | |
| 373 | pub const POLICY = 29; | |
| 374 | pub const INET_OFFLOAD = 30; | |
| 375 | pub const TRILL = 31; | |
| 376 | pub const PACKET = 32; | |
| 377 | pub const LX_NETLINK = 33; | |
| 378 | pub const MAX = 33; | |
| 379 | }; | |
| 380 | ||
| 381 | pub const SOL = struct { | |
| 382 | pub const SOCKET = 0xffff; | |
| 383 | pub const ROUTE = 0xfffe; | |
| 384 | pub const PACKET = 0xfffd; | |
| 385 | pub const FILTER = 0xfffc; | |
| 386 | }; | |
| 387 | ||
| 388 | pub const PF = struct { | |
| 389 | pub const UNSPEC = AF.UNSPEC; | |
| 390 | pub const UNIX = AF.UNIX; | |
| 391 | pub const LOCAL = UNIX; | |
| 392 | pub const FILE = UNIX; | |
| 393 | pub const INET = AF.INET; | |
| 394 | pub const IMPLINK = AF.IMPLINK; | |
| 395 | pub const PUP = AF.PUP; | |
| 396 | pub const CHAOS = AF.CHAOS; | |
| 397 | pub const NS = AF.NS; | |
| 398 | pub const NBS = AF.NBS; | |
| 399 | pub const ECMA = AF.ECMA; | |
| 400 | pub const DATAKIT = AF.DATAKIT; | |
| 401 | pub const CCITT = AF.CCITT; | |
| 402 | pub const SNA = AF.SNA; | |
| 403 | pub const DECnet = AF.DECnet; | |
| 404 | pub const DLI = AF.DLI; | |
| 405 | pub const LAT = AF.LAT; | |
| 406 | pub const HYLINK = AF.HYLINK; | |
| 407 | pub const APPLETALK = AF.APPLETALK; | |
| 408 | pub const NIT = AF.NIT; | |
| 409 | pub const @"802" = AF.@"802"; | |
| 410 | pub const OSI = AF.OSI; | |
| 411 | pub const X25 = AF.X25; | |
| 412 | pub const OSINET = AF.OSINET; | |
| 413 | pub const GOSIP = AF.GOSIP; | |
| 414 | pub const IPX = AF.IPX; | |
| 415 | pub const ROUTE = AF.ROUTE; | |
| 416 | pub const LINK = AF.LINK; | |
| 417 | pub const INET6 = AF.INET6; | |
| 418 | pub const KEY = AF.KEY; | |
| 419 | pub const NCA = AF.NCA; | |
| 420 | pub const POLICY = AF.POLICY; | |
| 421 | pub const TRILL = AF.TRILL; | |
| 422 | pub const PACKET = AF.PACKET; | |
| 423 | pub const LX_NETLINK = AF.LX_NETLINK; | |
| 424 | pub const MAX = AF.MAX; | |
| 425 | }; | |
| 426 | ||
| 427 | pub const in_port_t = u16; | |
| 428 | pub const sa_family_t = u16; | |
| 429 | ||
| 430 | pub const sockaddr = extern struct { | |
| 431 | /// address family | |
| 432 | family: sa_family_t, | |
| 433 | ||
| 434 | /// actually longer; address value | |
| 435 | data: [14]u8, | |
| 436 | ||
| 437 | pub const SS_MAXSIZE = 256; | |
| 438 | pub const storage = std.x.os.Socket.Address.Native.Storage; | |
| 439 | ||
| 440 | pub const in = extern struct { | |
| 441 | family: sa_family_t = AF.INET, | |
| 442 | port: in_port_t, | |
| 443 | addr: u32, | |
| 444 | zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, | |
| 445 | }; | |
| 446 | ||
| 447 | pub const in6 = extern struct { | |
| 448 | family: sa_family_t = AF.INET6, | |
| 449 | port: in_port_t, | |
| 450 | flowinfo: u32, | |
| 451 | addr: [16]u8, | |
| 452 | scope_id: u32, | |
| 453 | __src_id: u32 = 0, | |
| 454 | }; | |
| 455 | ||
| 456 | /// Definitions for UNIX IPC domain. | |
| 457 | pub const un = extern struct { | |
| 458 | family: sa_family_t = AF.UNIX, | |
| 459 | path: [108]u8, | |
| 460 | }; | |
| 461 | }; | |
| 462 | ||
| 463 | pub const AI = struct { | |
| 464 | /// IPv4-mapped IPv6 address | |
| 465 | pub const V4MAPPED = 0x0001; | |
| 466 | pub const ALL = 0x0002; | |
| 467 | /// only if any address is assigned | |
| 468 | pub const ADDRCONFIG = 0x0004; | |
| 469 | /// get address to use bind() | |
| 470 | pub const PASSIVE = 0x0008; | |
| 471 | /// fill ai_canonname | |
| 472 | pub const CANONNAME = 0x0010; | |
| 473 | /// prevent host name resolution | |
| 474 | pub const NUMERICHOST = 0x0020; | |
| 475 | /// prevent service name resolution | |
| 476 | pub const NUMERICSERV = 0x0040; | |
| 477 | }; | |
| 478 | ||
| 479 | pub const NI = struct { | |
| 480 | pub const NOFQDN = 0x0001; | |
| 481 | pub const NUMERICHOST = 0x0002; | |
| 482 | pub const NAMEREQD = 0x0004; | |
| 483 | pub const NUMERICSERV = 0x0008; | |
| 484 | pub const DGRAM = 0x0010; | |
| 485 | pub const WITHSCOPEID = 0x0020; | |
| 486 | pub const NUMERICSCOPE = 0x0040; | |
| 487 | ||
| 488 | pub const MAXHOST = 1025; | |
| 489 | pub const MAXSERV = 32; | |
| 490 | }; | |
| 491 | ||
| 492 | pub const PATH_MAX = 1024; | |
| 493 | pub const IOV_MAX = 1024; | |
| 494 | ||
| 495 | pub const STDIN_FILENO = 0; | |
| 496 | pub const STDOUT_FILENO = 1; | |
| 497 | pub const STDERR_FILENO = 2; | |
| 498 | ||
| 499 | pub const PROT = struct { | |
| 500 | pub const NONE = 0; | |
| 501 | pub const READ = 1; | |
| 502 | pub const WRITE = 2; | |
| 503 | pub const EXEC = 4; | |
| 504 | }; | |
| 505 | ||
| 506 | pub const CLOCK = struct { | |
| 507 | pub const VIRTUAL = 1; | |
| 508 | pub const THREAD_CPUTIME_ID = 2; | |
| 509 | pub const REALTIME = 3; | |
| 510 | pub const MONOTONIC = 4; | |
| 511 | pub const PROCESS_CPUTIME_ID = 5; | |
| 512 | pub const HIGHRES = MONOTONIC; | |
| 513 | pub const PROF = THREAD_CPUTIME_ID; | |
| 514 | }; | |
| 515 | ||
| 516 | pub const MAP = struct { | |
| 517 | pub const FAILED = @intToPtr(*c_void, maxInt(usize)); | |
| 518 | pub const SHARED = 0x0001; | |
| 519 | pub const PRIVATE = 0x0002; | |
| 520 | pub const TYPE = 0x000f; | |
| 521 | ||
| 522 | pub const FILE = 0x0000; | |
| 523 | pub const FIXED = 0x0010; | |
| 524 | // Unimplemented | |
| 525 | pub const RENAME = 0x0020; | |
| 526 | pub const NORESERVE = 0x0040; | |
| 527 | /// Force mapping in lower 4G address space | |
| 528 | pub const @"32BIT" = 0x0080; | |
| 529 | ||
| 530 | pub const ANON = 0x0100; | |
| 531 | pub const ANONYMOUS = ANON; | |
| 532 | pub const ALIGN = 0x0200; | |
| 533 | pub const TEXT = 0x0400; | |
| 534 | pub const INITDATA = 0x0800; | |
| 535 | }; | |
| 536 | ||
| 537 | pub const MADV = struct { | |
| 538 | /// no further special treatment | |
| 539 | pub const NORMAL = 0; | |
| 540 | /// expect random page references | |
| 541 | pub const RANDOM = 1; | |
| 542 | /// expect sequential page references | |
| 543 | pub const SEQUENTIAL = 2; | |
| 544 | /// will need these pages | |
| 545 | pub const WILLNEED = 3; | |
| 546 | /// don't need these pages | |
| 547 | pub const DONTNEED = 4; | |
| 548 | /// contents can be freed | |
| 549 | pub const FREE = 5; | |
| 550 | /// default access | |
| 551 | pub const ACCESS_DEFAULT = 6; | |
| 552 | /// next LWP to access heavily | |
| 553 | pub const ACCESS_LWP = 7; | |
| 554 | /// many processes to access heavily | |
| 555 | pub const ACCESS_MANY = 8; | |
| 556 | /// contents will be purged | |
| 557 | pub const PURGE = 9; | |
| 558 | }; | |
| 559 | ||
| 560 | pub const W = struct { | |
| 561 | pub const EXITED = 0o001; | |
| 562 | pub const TRAPPED = 0o002; | |
| 563 | pub const UNTRACED = 0o004; | |
| 564 | pub const STOPPED = UNTRACED; | |
| 565 | pub const CONTINUED = 0o010; | |
| 566 | pub const NOHANG = 0o100; | |
| 567 | pub const NOWAIT = 0o200; | |
| 568 | ||
| 569 | pub fn EXITSTATUS(s: u32) u8 { | |
| 570 | return @intCast(u8, (s >> 8) & 0xff); | |
| 571 | } | |
| 572 | pub fn TERMSIG(s: u32) u32 { | |
| 573 | return s & 0x7f; | |
| 574 | } | |
| 575 | pub fn STOPSIG(s: u32) u32 { | |
| 576 | return EXITSTATUS(s); | |
| 577 | } | |
| 578 | pub fn IFEXITED(s: u32) bool { | |
| 579 | return TERMSIG(s) == 0; | |
| 580 | } | |
| 581 | ||
| 582 | pub fn IFCONTINUED(s: u32) bool { | |
| 583 | return ((s & 0o177777) == 0o177777); | |
| 584 | } | |
| 585 | ||
| 586 | pub fn IFSTOPPED(s: u32) bool { | |
| 587 | return (s & 0x00ff != 0o177) and !(s & 0xff00 != 0); | |
| 588 | } | |
| 589 | ||
| 590 | pub fn IFSIGNALED(s: u32) bool { | |
| 591 | return s & 0x00ff > 0 and s & 0xff00 == 0; | |
| 592 | } | |
| 593 | }; | |
| 594 | ||
| 595 | pub const SA = struct { | |
| 596 | pub const ONSTACK = 0x00000001; | |
| 597 | pub const RESETHAND = 0x00000002; | |
| 598 | pub const RESTART = 0x00000004; | |
| 599 | pub const SIGINFO = 0x00000008; | |
| 600 | pub const NODEFER = 0x00000010; | |
| 601 | pub const NOCLDWAIT = 0x00010000; | |
| 602 | }; | |
| 603 | ||
| 604 | // access function | |
| 605 | pub const F_OK = 0; // test for existence of file | |
| 606 | pub const X_OK = 1; // test for execute or search permission | |
| 607 | pub const W_OK = 2; // test for write permission | |
| 608 | pub const R_OK = 4; // test for read permission | |
| 609 | ||
| 610 | pub const F = struct { | |
| 611 | /// Unlock a previously locked region | |
| 612 | pub const ULOCK = 0; | |
| 613 | /// Lock a region for exclusive use | |
| 614 | pub const LOCK = 1; | |
| 615 | /// Test and lock a region for exclusive use | |
| 616 | pub const TLOCK = 2; | |
| 617 | /// Test a region for other processes locks | |
| 618 | pub const TEST = 3; | |
| 619 | ||
| 620 | /// Duplicate fildes | |
| 621 | pub const DUPFD = 0; | |
| 622 | /// Get fildes flags | |
| 623 | pub const GETFD = 1; | |
| 624 | /// Set fildes flags | |
| 625 | pub const SETFD = 2; | |
| 626 | /// Get file flags | |
| 627 | pub const GETFL = 3; | |
| 628 | /// Get file flags including open-only flags | |
| 629 | pub const GETXFL = 45; | |
| 630 | /// Set file flags | |
| 631 | pub const SETFL = 4; | |
| 632 | ||
| 633 | /// Unused | |
| 634 | pub const CHKFL = 8; | |
| 635 | /// Duplicate fildes at third arg | |
| 636 | pub const DUP2FD = 9; | |
| 637 | /// Like DUP2FD with O_CLOEXEC set EINVAL is fildes matches arg1 | |
| 638 | pub const DUP2FD_CLOEXEC = 36; | |
| 639 | /// Like DUPFD with O_CLOEXEC set | |
| 640 | pub const DUPFD_CLOEXEC = 37; | |
| 641 | ||
| 642 | /// Is the file desc. a stream ? | |
| 643 | pub const ISSTREAM = 13; | |
| 644 | /// Turn on private access to file | |
| 645 | pub const PRIV = 15; | |
| 646 | /// Turn off private access to file | |
| 647 | pub const NPRIV = 16; | |
| 648 | /// UFS quota call | |
| 649 | pub const QUOTACTL = 17; | |
| 650 | /// Get number of BLKSIZE blocks allocated | |
| 651 | pub const BLOCKS = 18; | |
| 652 | /// Get optimal I/O block size | |
| 653 | pub const BLKSIZE = 19; | |
| 654 | /// Get owner (socket emulation) | |
| 655 | pub const GETOWN = 23; | |
| 656 | /// Set owner (socket emulation) | |
| 657 | pub const SETOWN = 24; | |
| 658 | /// Object reuse revoke access to file desc. | |
| 659 | pub const REVOKE = 25; | |
| 660 | /// Does vp have NFS locks private to lock manager | |
| 661 | pub const HASREMOTELOCKS = 26; | |
| 662 | ||
| 663 | /// Set file lock | |
| 664 | pub const SETLK = 6; | |
| 665 | /// Set file lock and wait | |
| 666 | pub const SETLKW = 7; | |
| 667 | /// Allocate file space | |
| 668 | pub const ALLOCSP = 10; | |
| 669 | /// Free file space | |
| 670 | pub const FREESP = 11; | |
| 671 | /// Get file lock | |
| 672 | pub const GETLK = 14; | |
| 673 | /// Get file lock owned by file | |
| 674 | pub const OFD_GETLK = 47; | |
| 675 | /// Set file lock owned by file | |
| 676 | pub const OFD_SETLK = 48; | |
| 677 | /// Set file lock owned by file and wait | |
| 678 | pub const OFD_SETLKW = 49; | |
| 679 | /// Set a file share reservation | |
| 680 | pub const SHARE = 40; | |
| 681 | /// Remove a file share reservation | |
| 682 | pub const UNSHARE = 41; | |
| 683 | /// Create Poison FD | |
| 684 | pub const BADFD = 46; | |
| 685 | ||
| 686 | /// Read lock | |
| 687 | pub const RDLCK = 1; | |
| 688 | /// Write lock | |
| 689 | pub const WRLCK = 2; | |
| 690 | /// Remove lock(s) | |
| 691 | pub const UNLCK = 3; | |
| 692 | /// remove remote locks for a given system | |
| 693 | pub const UNLKSYS = 4; | |
| 694 | ||
| 695 | // f_access values | |
| 696 | /// Read-only share access | |
| 697 | pub const RDACC = 0x1; | |
| 698 | /// Write-only share access | |
| 699 | pub const WRACC = 0x2; | |
| 700 | /// Read-Write share access | |
| 701 | pub const RWACC = 0x3; | |
| 702 | ||
| 703 | // f_deny values | |
| 704 | /// Don't deny others access | |
| 705 | pub const NODNY = 0x0; | |
| 706 | /// Deny others read share access | |
| 707 | pub const RDDNY = 0x1; | |
| 708 | /// Deny others write share access | |
| 709 | pub const WRDNY = 0x2; | |
| 710 | /// Deny others read or write share access | |
| 711 | pub const RWDNY = 0x3; | |
| 712 | /// private flag: Deny delete share access | |
| 713 | pub const RMDNY = 0x4; | |
| 714 | }; | |
| 715 | ||
| 716 | pub const O = struct { | |
| 717 | pub const RDONLY = 0; | |
| 718 | pub const WRONLY = 1; | |
| 719 | pub const RDWR = 2; | |
| 720 | pub const SEARCH = 0x200000; | |
| 721 | pub const EXEC = 0x400000; | |
| 722 | pub const NDELAY = 0x04; | |
| 723 | pub const APPEND = 0x08; | |
| 724 | pub const SYNC = 0x10; | |
| 725 | pub const DSYNC = 0x40; | |
| 726 | pub const RSYNC = 0x8000; | |
| 727 | pub const NONBLOCK = 0x80; | |
| 728 | pub const LARGEFILE = 0x2000; | |
| 729 | ||
| 730 | pub const CREAT = 0x100; | |
| 731 | pub const TRUNC = 0x200; | |
| 732 | pub const EXCL = 0x400; | |
| 733 | pub const NOCTTY = 0x800; | |
| 734 | pub const XATTR = 0x4000; | |
| 735 | pub const NOFOLLOW = 0x20000; | |
| 736 | pub const NOLINKS = 0x40000; | |
| 737 | pub const CLOEXEC = 0x800000; | |
| 738 | pub const DIRECTORY = 0x1000000; | |
| 739 | pub const DIRECT = 0x2000000; | |
| 740 | }; | |
| 741 | ||
| 742 | pub const LOCK = struct { | |
| 743 | pub const SH = 1; | |
| 744 | pub const EX = 2; | |
| 745 | pub const NB = 4; | |
| 746 | pub const UN = 8; | |
| 747 | }; | |
| 748 | ||
| 749 | pub const FD_CLOEXEC = 1; | |
| 750 | ||
| 751 | pub const SEEK = struct { | |
| 752 | pub const SET = 0; | |
| 753 | pub const CUR = 1; | |
| 754 | pub const END = 2; | |
| 755 | pub const DATA = 3; | |
| 756 | pub const HOLE = 4; | |
| 757 | }; | |
| 758 | ||
| 759 | pub const tcflag_t = c_uint; | |
| 760 | pub const cc_t = u8; | |
| 761 | pub const speed_t = c_uint; | |
| 762 | ||
| 763 | pub const NCCS = 19; | |
| 764 | ||
| 765 | pub const termios = extern struct { | |
| 766 | c_iflag: tcflag_t, | |
| 767 | c_oflag: tcflag_t, | |
| 768 | c_cflag: tcflag_t, | |
| 769 | c_lflag: tcflag_t, | |
| 770 | c_cc: [NCCS]cc_t, | |
| 771 | }; | |
| 772 | ||
| 773 | fn tioc(t: u16, num: u8) u16 { | |
| 774 | return (t << 8) | num; | |
| 775 | } | |
| 776 | ||
| 777 | pub const T = struct { | |
| 778 | pub const CGETA = tioc('T', 1); | |
| 779 | pub const CSETA = tioc('T', 2); | |
| 780 | pub const CSETAW = tioc('T', 3); | |
| 781 | pub const CSETAF = tioc('T', 4); | |
| 782 | pub const CSBRK = tioc('T', 5); | |
| 783 | pub const CXONC = tioc('T', 6); | |
| 784 | pub const CFLSH = tioc('T', 7); | |
| 785 | pub const IOCGWINSZ = tioc('T', 104); | |
| 786 | pub const IOCSWINSZ = tioc('T', 103); | |
| 787 | // Softcarrier ioctls | |
| 788 | pub const IOCGSOFTCAR = tioc('T', 105); | |
| 789 | pub const IOCSSOFTCAR = tioc('T', 106); | |
| 790 | // termios ioctls | |
| 791 | pub const CGETS = tioc('T', 13); | |
| 792 | pub const CSETS = tioc('T', 14); | |
| 793 | pub const CSANOW = tioc('T', 14); | |
| 794 | pub const CSETSW = tioc('T', 15); | |
| 795 | pub const CSADRAIN = tioc('T', 15); | |
| 796 | pub const CSETSF = tioc('T', 16); | |
| 797 | pub const IOCSETLD = tioc('T', 123); | |
| 798 | pub const IOCGETLD = tioc('T', 124); | |
| 799 | // NTP PPS ioctls | |
| 800 | pub const IOCGPPS = tioc('T', 125); | |
| 801 | pub const IOCSPPS = tioc('T', 126); | |
| 802 | pub const IOCGPPSEV = tioc('T', 127); | |
| 803 | ||
| 804 | pub const IOCGETD = tioc('t', 0); | |
| 805 | pub const IOCSETD = tioc('t', 1); | |
| 806 | pub const IOCHPCL = tioc('t', 2); | |
| 807 | pub const IOCGETP = tioc('t', 8); | |
| 808 | pub const IOCSETP = tioc('t', 9); | |
| 809 | pub const IOCSETN = tioc('t', 10); | |
| 810 | pub const IOCEXCL = tioc('t', 13); | |
| 811 | pub const IOCNXCL = tioc('t', 14); | |
| 812 | pub const IOCFLUSH = tioc('t', 16); | |
| 813 | pub const IOCSETC = tioc('t', 17); | |
| 814 | pub const IOCGETC = tioc('t', 18); | |
| 815 | /// bis local mode bits | |
| 816 | pub const IOCLBIS = tioc('t', 127); | |
| 817 | /// bic local mode bits | |
| 818 | pub const IOCLBIC = tioc('t', 126); | |
| 819 | /// set entire local mode word | |
| 820 | pub const IOCLSET = tioc('t', 125); | |
| 821 | /// get local modes | |
| 822 | pub const IOCLGET = tioc('t', 124); | |
| 823 | /// set break bit | |
| 824 | pub const IOCSBRK = tioc('t', 123); | |
| 825 | /// clear break bit | |
| 826 | pub const IOCCBRK = tioc('t', 122); | |
| 827 | /// set data terminal ready | |
| 828 | pub const IOCSDTR = tioc('t', 121); | |
| 829 | /// clear data terminal ready | |
| 830 | pub const IOCCDTR = tioc('t', 120); | |
| 831 | /// set local special chars | |
| 832 | pub const IOCSLTC = tioc('t', 117); | |
| 833 | /// get local special chars | |
| 834 | pub const IOCGLTC = tioc('t', 116); | |
| 835 | /// driver output queue size | |
| 836 | pub const IOCOUTQ = tioc('t', 115); | |
| 837 | /// void tty association | |
| 838 | pub const IOCNOTTY = tioc('t', 113); | |
| 839 | /// get a ctty | |
| 840 | pub const IOCSCTTY = tioc('t', 132); | |
| 841 | /// stop output, like ^S | |
| 842 | pub const IOCSTOP = tioc('t', 111); | |
| 843 | /// start output, like ^Q | |
| 844 | pub const IOCSTART = tioc('t', 110); | |
| 845 | /// get pgrp of tty | |
| 846 | pub const IOCGPGRP = tioc('t', 20); | |
| 847 | /// set pgrp of tty | |
| 848 | pub const IOCSPGRP = tioc('t', 21); | |
| 849 | /// get session id on ctty | |
| 850 | pub const IOCGSID = tioc('t', 22); | |
| 851 | /// simulate terminal input | |
| 852 | pub const IOCSTI = tioc('t', 23); | |
| 853 | /// set all modem bits | |
| 854 | pub const IOCMSET = tioc('t', 26); | |
| 855 | /// bis modem bits | |
| 856 | pub const IOCMBIS = tioc('t', 27); | |
| 857 | /// bic modem bits | |
| 858 | pub const IOCMBIC = tioc('t', 28); | |
| 859 | /// get all modem bits | |
| 860 | pub const IOCMGET = tioc('t', 29); | |
| 861 | }; | |
| 862 | ||
| 863 | pub const winsize = extern struct { | |
| 864 | ws_row: u16, | |
| 865 | ws_col: u16, | |
| 866 | ws_xpixel: u16, | |
| 867 | ws_ypixel: u16, | |
| 868 | }; | |
| 869 | ||
| 870 | const NSIG = 75; | |
| 871 | ||
| 872 | pub const SIG = struct { | |
| 873 | pub const DFL = @intToPtr(?Sigaction.sigaction_fn, 0); | |
| 874 | pub const ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); | |
| 875 | pub const IGN = @intToPtr(?Sigaction.sigaction_fn, 1); | |
| 876 | pub const HOLD = @intToPtr(?Sigaction.sigaction_fn, 2); | |
| 877 | ||
| 878 | pub const WORDS = 4; | |
| 879 | pub const MAXSIG = 75; | |
| 880 | ||
| 881 | pub const SIG_BLOCK = 1; | |
| 882 | pub const SIG_UNBLOCK = 2; | |
| 883 | pub const SIG_SETMASK = 3; | |
| 884 | ||
| 885 | pub const HUP = 1; | |
| 886 | pub const INT = 2; | |
| 887 | pub const QUIT = 3; | |
| 888 | pub const ILL = 4; | |
| 889 | pub const TRAP = 5; | |
| 890 | pub const IOT = 6; | |
| 891 | pub const ABRT = 6; | |
| 892 | pub const EMT = 7; | |
| 893 | pub const FPE = 8; | |
| 894 | pub const KILL = 9; | |
| 895 | pub const BUS = 10; | |
| 896 | pub const SEGV = 11; | |
| 897 | pub const SYS = 12; | |
| 898 | pub const PIPE = 13; | |
| 899 | pub const ALRM = 14; | |
| 900 | pub const TERM = 15; | |
| 901 | pub const USR1 = 16; | |
| 902 | pub const USR2 = 17; | |
| 903 | pub const CLD = 18; | |
| 904 | pub const CHLD = 18; | |
| 905 | pub const PWR = 19; | |
| 906 | pub const WINCH = 20; | |
| 907 | pub const URG = 21; | |
| 908 | pub const POLL = 22; | |
| 909 | pub const IO = .POLL; | |
| 910 | pub const STOP = 23; | |
| 911 | pub const TSTP = 24; | |
| 912 | pub const CONT = 25; | |
| 913 | pub const TTIN = 26; | |
| 914 | pub const TTOU = 27; | |
| 915 | pub const VTALRM = 28; | |
| 916 | pub const PROF = 29; | |
| 917 | pub const XCPU = 30; | |
| 918 | pub const XFSZ = 31; | |
| 919 | pub const WAITING = 32; | |
| 920 | pub const LWP = 33; | |
| 921 | pub const FREEZE = 34; | |
| 922 | pub const THAW = 35; | |
| 923 | pub const CANCEL = 36; | |
| 924 | pub const LOST = 37; | |
| 925 | pub const XRES = 38; | |
| 926 | pub const JVM1 = 39; | |
| 927 | pub const JVM2 = 40; | |
| 928 | pub const INFO = 41; | |
| 929 | ||
| 930 | pub const RTMIN = 42; | |
| 931 | pub const RTMAX = 74; | |
| 932 | ||
| 933 | pub inline fn IDX(sig: usize) usize { | |
| 934 | return sig - 1; | |
| 935 | } | |
| 936 | pub inline fn WORD(sig: usize) usize { | |
| 937 | return IDX(sig) >> 5; | |
| 938 | } | |
| 939 | pub inline fn BIT(sig: usize) usize { | |
| 940 | return 1 << (IDX(sig) & 31); | |
| 941 | } | |
| 942 | pub inline fn VALID(sig: usize) usize { | |
| 943 | return sig <= MAXSIG and sig > 0; | |
| 944 | } | |
| 945 | }; | |
| 946 | ||
| 947 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. | |
| 948 | pub const Sigaction = extern struct { | |
| 949 | pub const handler_fn = fn (c_int) callconv(.C) void; | |
| 950 | pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void; | |
| 951 | ||
| 952 | /// signal options | |
| 953 | flags: c_uint, | |
| 954 | /// signal handler | |
| 955 | handler: extern union { | |
| 956 | handler: ?handler_fn, | |
| 957 | sigaction: ?sigaction_fn, | |
| 958 | }, | |
| 959 | /// signal mask to apply | |
| 960 | mask: sigset_t, | |
| 961 | }; | |
| 962 | ||
| 963 | pub const sigval_t = extern union { | |
| 964 | int: c_int, | |
| 965 | ptr: ?*c_void, | |
| 966 | }; | |
| 967 | ||
| 968 | pub const siginfo_t = extern struct { | |
| 969 | signo: c_int, | |
| 970 | code: c_int, | |
| 971 | errno: c_int, | |
| 972 | // 64bit architectures insert 4bytes of padding here, this is done by | |
| 973 | // correctly aligning the reason field | |
| 974 | reason: extern union { | |
| 975 | proc: extern struct { | |
| 976 | pid: pid_t, | |
| 977 | pdata: extern union { | |
| 978 | kill: extern struct { | |
| 979 | uid: uid_t, | |
| 980 | value: sigval_t, | |
| 981 | }, | |
| 982 | cld: extern struct { | |
| 983 | utime: clock_t, | |
| 984 | status: c_int, | |
| 985 | stime: clock_t, | |
| 986 | }, | |
| 987 | }, | |
| 988 | contract: ctid_t, | |
| 989 | zone: zoneid_t, | |
| 990 | }, | |
| 991 | fault: extern struct { | |
| 992 | addr: ?*c_void, | |
| 993 | trapno: c_int, | |
| 994 | pc: ?*c_void, | |
| 995 | }, | |
| 996 | file: extern struct { | |
| 997 | // fd not currently available for SIGPOLL. | |
| 998 | fd: c_int, | |
| 999 | band: c_long, | |
| 1000 | }, | |
| 1001 | prof: extern struct { | |
| 1002 | addr: ?*c_void, | |
| 1003 | timestamp: timespec, | |
| 1004 | syscall: c_short, | |
| 1005 | sysarg: u8, | |
| 1006 | fault: u8, | |
| 1007 | args: [8]c_long, | |
| 1008 | state: [10]c_int, | |
| 1009 | }, | |
| 1010 | rctl: extern struct { | |
| 1011 | entity: i32, | |
| 1012 | }, | |
| 1013 | __pad: [256 - 4 * @sizeOf(c_int)]u8, | |
| 1014 | } align(@sizeOf(usize)), | |
| 1015 | }; | |
| 1016 | ||
| 1017 | comptime { | |
| 1018 | std.debug.assert(@sizeOf(siginfo_t) == 256); | |
| 1019 | std.debug.assert(@alignOf(siginfo_t) == @sizeOf(usize)); | |
| 1020 | } | |
| 1021 | ||
| 1022 | pub const sigset_t = extern struct { | |
| 1023 | __bits: [SIG.WORDS]u32, | |
| 1024 | }; | |
| 1025 | ||
| 1026 | pub const empty_sigset = sigset_t{ .__bits = [_]u32{0} ** SIG.WORDS }; | |
| 1027 | ||
| 1028 | pub const fpregset_t = extern union { | |
| 1029 | regs: [130]u32, | |
| 1030 | chip_state: extern struct { | |
| 1031 | cw: u16, | |
| 1032 | sw: u16, | |
| 1033 | fctw: u8, | |
| 1034 | __fx_rsvd: u8, | |
| 1035 | fop: u16, | |
| 1036 | rip: u64, | |
| 1037 | rdp: u64, | |
| 1038 | mxcsr: u32, | |
| 1039 | mxcsr_mask: u32, | |
| 1040 | st: [8]extern union { | |
| 1041 | fpr_16: [5]u16, | |
| 1042 | __fpr_pad: u128, | |
| 1043 | }, | |
| 1044 | xmm: [16]u128, | |
| 1045 | __fx_ign2: [6]u128, | |
| 1046 | status: u32, | |
| 1047 | xstatus: u32, | |
| 1048 | }, | |
| 1049 | }; | |
| 1050 | ||
| 1051 | pub const mcontext_t = extern struct { | |
| 1052 | gregs: [28]u64, | |
| 1053 | fpregs: fpregset_t, | |
| 1054 | }; | |
| 1055 | ||
| 1056 | pub const REG = struct { | |
| 1057 | pub const RBP = 10; | |
| 1058 | pub const RIP = 17; | |
| 1059 | pub const RSP = 20; | |
| 1060 | }; | |
| 1061 | ||
| 1062 | pub const ucontext_t = extern struct { | |
| 1063 | flags: u64, | |
| 1064 | link: ?*ucontext_t, | |
| 1065 | sigmask: sigset_t, | |
| 1066 | stack: stack_t, | |
| 1067 | mcontext: mcontext_t, | |
| 1068 | brand_data: [3]?*c_void, | |
| 1069 | filler: [2]i64, | |
| 1070 | }; | |
| 1071 | ||
| 1072 | pub const GETCONTEXT = 0; | |
| 1073 | pub const SETCONTEXT = 1; | |
| 1074 | pub const GETUSTACK = 2; | |
| 1075 | pub const SETUSTACK = 3; | |
| 1076 | ||
| 1077 | pub const E = enum(u16) { | |
| 1078 | /// No error occurred. | |
| 1079 | SUCCESS = 0, | |
| 1080 | /// Not super-user | |
| 1081 | PERM = 1, | |
| 1082 | /// No such file or directory | |
| 1083 | NOENT = 2, | |
| 1084 | /// No such process | |
| 1085 | SRCH = 3, | |
| 1086 | /// interrupted system call | |
| 1087 | INTR = 4, | |
| 1088 | /// I/O error | |
| 1089 | IO = 5, | |
| 1090 | /// No such device or address | |
| 1091 | NXIO = 6, | |
| 1092 | /// Arg list too long | |
| 1093 | @"2BIG" = 7, | |
| 1094 | /// Exec format error | |
| 1095 | NOEXEC = 8, | |
| 1096 | /// Bad file number | |
| 1097 | BADF = 9, | |
| 1098 | /// No children | |
| 1099 | CHILD = 10, | |
| 1100 | /// Resource temporarily unavailable. | |
| 1101 | /// also: WOULDBLOCK: Operation would block. | |
| 1102 | AGAIN = 11, | |
| 1103 | /// Not enough core | |
| 1104 | NOMEM = 12, | |
| 1105 | /// Permission denied | |
| 1106 | ACCES = 13, | |
| 1107 | /// Bad address | |
| 1108 | FAULT = 14, | |
| 1109 | /// Block device required | |
| 1110 | NOTBLK = 15, | |
| 1111 | /// Mount device busy | |
| 1112 | BUSY = 16, | |
| 1113 | /// File exists | |
| 1114 | EXIST = 17, | |
| 1115 | /// Cross-device link | |
| 1116 | XDEV = 18, | |
| 1117 | /// No such device | |
| 1118 | NODEV = 19, | |
| 1119 | /// Not a directory | |
| 1120 | NOTDIR = 20, | |
| 1121 | /// Is a directory | |
| 1122 | ISDIR = 21, | |
| 1123 | /// Invalid argument | |
| 1124 | INVAL = 22, | |
| 1125 | /// File table overflow | |
| 1126 | NFILE = 23, | |
| 1127 | /// Too many open files | |
| 1128 | MFILE = 24, | |
| 1129 | /// Inappropriate ioctl for device | |
| 1130 | NOTTY = 25, | |
| 1131 | /// Text file busy | |
| 1132 | TXTBSY = 26, | |
| 1133 | /// File too large | |
| 1134 | FBIG = 27, | |
| 1135 | /// No space left on device | |
| 1136 | NOSPC = 28, | |
| 1137 | /// Illegal seek | |
| 1138 | SPIPE = 29, | |
| 1139 | /// Read only file system | |
| 1140 | ROFS = 30, | |
| 1141 | /// Too many links | |
| 1142 | MLINK = 31, | |
| 1143 | /// Broken pipe | |
| 1144 | PIPE = 32, | |
| 1145 | /// Math arg out of domain of func | |
| 1146 | DOM = 33, | |
| 1147 | /// Math result not representable | |
| 1148 | RANGE = 34, | |
| 1149 | /// No message of desired type | |
| 1150 | NOMSG = 35, | |
| 1151 | /// Identifier removed | |
| 1152 | IDRM = 36, | |
| 1153 | /// Channel number out of range | |
| 1154 | CHRNG = 37, | |
| 1155 | /// Level 2 not synchronized | |
| 1156 | L2NSYNC = 38, | |
| 1157 | /// Level 3 halted | |
| 1158 | L3HLT = 39, | |
| 1159 | /// Level 3 reset | |
| 1160 | L3RST = 40, | |
| 1161 | /// Link number out of range | |
| 1162 | LNRNG = 41, | |
| 1163 | /// Protocol driver not attached | |
| 1164 | UNATCH = 42, | |
| 1165 | /// No CSI structure available | |
| 1166 | NOCSI = 43, | |
| 1167 | /// Level 2 halted | |
| 1168 | L2HLT = 44, | |
| 1169 | /// Deadlock condition. | |
| 1170 | DEADLK = 45, | |
| 1171 | /// No record locks available. | |
| 1172 | NOLCK = 46, | |
| 1173 | /// Operation canceled | |
| 1174 | CANCELED = 47, | |
| 1175 | /// Operation not supported | |
| 1176 | NOTSUP = 48, | |
| 1177 | ||
| 1178 | // Filesystem Quotas | |
| 1179 | /// Disc quota exceeded | |
| 1180 | DQUOT = 49, | |
| 1181 | ||
| 1182 | // Convergent Error Returns | |
| 1183 | /// invalid exchange | |
| 1184 | BADE = 50, | |
| 1185 | /// invalid request descriptor | |
| 1186 | BADR = 51, | |
| 1187 | /// exchange full | |
| 1188 | XFULL = 52, | |
| 1189 | /// no anode | |
| 1190 | NOANO = 53, | |
| 1191 | /// invalid request code | |
| 1192 | BADRQC = 54, | |
| 1193 | /// invalid slot | |
| 1194 | BADSLT = 55, | |
| 1195 | /// file locking deadlock error | |
| 1196 | DEADLOCK = 56, | |
| 1197 | /// bad font file fmt | |
| 1198 | BFONT = 57, | |
| 1199 | ||
| 1200 | // Interprocess Robust Locks | |
| 1201 | /// process died with the lock | |
| 1202 | OWNERDEAD = 58, | |
| 1203 | /// lock is not recoverable | |
| 1204 | NOTRECOVERABLE = 59, | |
| 1205 | /// locked lock was unmapped | |
| 1206 | LOCKUNMAPPED = 72, | |
| 1207 | /// Facility is not active | |
| 1208 | NOTACTIVE = 73, | |
| 1209 | /// multihop attempted | |
| 1210 | MULTIHOP = 74, | |
| 1211 | /// trying to read unreadable message | |
| 1212 | BADMSG = 77, | |
| 1213 | /// path name is too long | |
| 1214 | NAMETOOLONG = 78, | |
| 1215 | /// value too large to be stored in data type | |
| 1216 | OVERFLOW = 79, | |
| 1217 | /// given log. name not unique | |
| 1218 | NOTUNIQ = 80, | |
| 1219 | /// f.d. invalid for this operation | |
| 1220 | BADFD = 81, | |
| 1221 | /// Remote address changed | |
| 1222 | REMCHG = 82, | |
| 1223 | ||
| 1224 | // Stream Problems | |
| 1225 | /// Device not a stream | |
| 1226 | NOSTR = 60, | |
| 1227 | /// no data (for no delay io) | |
| 1228 | NODATA = 61, | |
| 1229 | /// timer expired | |
| 1230 | TIME = 62, | |
| 1231 | /// out of streams resources | |
| 1232 | NOSR = 63, | |
| 1233 | /// Machine is not on the network | |
| 1234 | NONET = 64, | |
| 1235 | /// Package not installed | |
| 1236 | NOPKG = 65, | |
| 1237 | /// The object is remote | |
| 1238 | REMOTE = 66, | |
| 1239 | /// the link has been severed | |
| 1240 | NOLINK = 67, | |
| 1241 | /// advertise error | |
| 1242 | ADV = 68, | |
| 1243 | /// srmount error | |
| 1244 | SRMNT = 69, | |
| 1245 | /// Communication error on send | |
| 1246 | COMM = 70, | |
| 1247 | /// Protocol error | |
| 1248 | PROTO = 71, | |
| 1249 | ||
| 1250 | // Shared Library Problems | |
| 1251 | /// Can't access a needed shared lib. | |
| 1252 | LIBACC = 83, | |
| 1253 | /// Accessing a corrupted shared lib. | |
| 1254 | LIBBAD = 84, | |
| 1255 | /// .lib section in a.out corrupted. | |
| 1256 | LIBSCN = 85, | |
| 1257 | /// Attempting to link in too many libs. | |
| 1258 | LIBMAX = 86, | |
| 1259 | /// Attempting to exec a shared library. | |
| 1260 | LIBEXEC = 87, | |
| 1261 | /// Illegal byte sequence. | |
| 1262 | ILSEQ = 88, | |
| 1263 | /// Unsupported file system operation | |
| 1264 | NOSYS = 89, | |
| 1265 | /// Symbolic link loop | |
| 1266 | LOOP = 90, | |
| 1267 | /// Restartable system call | |
| 1268 | RESTART = 91, | |
| 1269 | /// if pipe/FIFO, don't sleep in stream head | |
| 1270 | STRPIPE = 92, | |
| 1271 | /// directory not empty | |
| 1272 | NOTEMPTY = 93, | |
| 1273 | /// Too many users (for UFS) | |
| 1274 | USERS = 94, | |
| 1275 | ||
| 1276 | // BSD Networking Software | |
| 1277 | // Argument Errors | |
| 1278 | /// Socket operation on non-socket | |
| 1279 | NOTSOCK = 95, | |
| 1280 | /// Destination address required | |
| 1281 | DESTADDRREQ = 96, | |
| 1282 | /// Message too long | |
| 1283 | MSGSIZE = 97, | |
| 1284 | /// Protocol wrong type for socket | |
| 1285 | PROTOTYPE = 98, | |
| 1286 | /// Protocol not available | |
| 1287 | NOPROTOOPT = 99, | |
| 1288 | /// Protocol not supported | |
| 1289 | PROTONOSUPPORT = 120, | |
| 1290 | /// Socket type not supported | |
| 1291 | SOCKTNOSUPPORT = 121, | |
| 1292 | /// Operation not supported on socket | |
| 1293 | OPNOTSUPP = 122, | |
| 1294 | /// Protocol family not supported | |
| 1295 | PFNOSUPPORT = 123, | |
| 1296 | /// Address family not supported by | |
| 1297 | AFNOSUPPORT = 124, | |
| 1298 | /// Address already in use | |
| 1299 | ADDRINUSE = 125, | |
| 1300 | /// Can't assign requested address | |
| 1301 | ADDRNOTAVAIL = 126, | |
| 1302 | ||
| 1303 | // Operational Errors | |
| 1304 | /// Network is down | |
| 1305 | NETDOWN = 127, | |
| 1306 | /// Network is unreachable | |
| 1307 | NETUNREACH = 128, | |
| 1308 | /// Network dropped connection because | |
| 1309 | NETRESET = 129, | |
| 1310 | /// Software caused connection abort | |
| 1311 | CONNABORTED = 130, | |
| 1312 | /// Connection reset by peer | |
| 1313 | CONNRESET = 131, | |
| 1314 | /// No buffer space available | |
| 1315 | NOBUFS = 132, | |
| 1316 | /// Socket is already connected | |
| 1317 | ISCONN = 133, | |
| 1318 | /// Socket is not connected | |
| 1319 | NOTCONN = 134, | |
| 1320 | /// Can't send after socket shutdown | |
| 1321 | SHUTDOWN = 143, | |
| 1322 | /// Too many references: can't splice | |
| 1323 | TOOMANYREFS = 144, | |
| 1324 | /// Connection timed out | |
| 1325 | TIMEDOUT = 145, | |
| 1326 | /// Connection refused | |
| 1327 | CONNREFUSED = 146, | |
| 1328 | /// Host is down | |
| 1329 | HOSTDOWN = 147, | |
| 1330 | /// No route to host | |
| 1331 | HOSTUNREACH = 148, | |
| 1332 | /// operation already in progress | |
| 1333 | ALREADY = 149, | |
| 1334 | /// operation now in progress | |
| 1335 | INPROGRESS = 150, | |
| 1336 | ||
| 1337 | // SUN Network File System | |
| 1338 | /// Stale NFS file handle | |
| 1339 | STALE = 151, | |
| 1340 | ||
| 1341 | _, | |
| 1342 | }; | |
| 1343 | ||
| 1344 | pub const MINSIGSTKSZ = 2048; | |
| 1345 | pub const SIGSTKSZ = 8192; | |
| 1346 | ||
| 1347 | pub const SS_ONSTACK = 0x1; | |
| 1348 | pub const SS_DISABLE = 0x2; | |
| 1349 | ||
| 1350 | pub const stack_t = extern struct { | |
| 1351 | sp: [*]u8, | |
| 1352 | size: isize, | |
| 1353 | flags: i32, | |
| 1354 | }; | |
| 1355 | ||
| 1356 | pub const S = struct { | |
| 1357 | pub const IFMT = 0o170000; | |
| 1358 | ||
| 1359 | pub const IFIFO = 0o010000; | |
| 1360 | pub const IFCHR = 0o020000; | |
| 1361 | pub const IFDIR = 0o040000; | |
| 1362 | pub const IFBLK = 0o060000; | |
| 1363 | pub const IFREG = 0o100000; | |
| 1364 | pub const IFLNK = 0o120000; | |
| 1365 | pub const IFSOCK = 0o140000; | |
| 1366 | /// SunOS 2.6 Door | |
| 1367 | pub const IFDOOR = 0o150000; | |
| 1368 | /// Solaris 10 Event Port | |
| 1369 | pub const IFPORT = 0o160000; | |
| 1370 | ||
| 1371 | pub const ISUID = 0o4000; | |
| 1372 | pub const ISGID = 0o2000; | |
| 1373 | pub const ISVTX = 0o1000; | |
| 1374 | pub const IRWXU = 0o700; | |
| 1375 | pub const IRUSR = 0o400; | |
| 1376 | pub const IWUSR = 0o200; | |
| 1377 | pub const IXUSR = 0o100; | |
| 1378 | pub const IRWXG = 0o070; | |
| 1379 | pub const IRGRP = 0o040; | |
| 1380 | pub const IWGRP = 0o020; | |
| 1381 | pub const IXGRP = 0o010; | |
| 1382 | pub const IRWXO = 0o007; | |
| 1383 | pub const IROTH = 0o004; | |
| 1384 | pub const IWOTH = 0o002; | |
| 1385 | pub const IXOTH = 0o001; | |
| 1386 | ||
| 1387 | pub fn ISFIFO(m: u32) bool { | |
| 1388 | return m & IFMT == IFIFO; | |
| 1389 | } | |
| 1390 | ||
| 1391 | pub fn ISCHR(m: u32) bool { | |
| 1392 | return m & IFMT == IFCHR; | |
| 1393 | } | |
| 1394 | ||
| 1395 | pub fn ISDIR(m: u32) bool { | |
| 1396 | return m & IFMT == IFDIR; | |
| 1397 | } | |
| 1398 | ||
| 1399 | pub fn ISBLK(m: u32) bool { | |
| 1400 | return m & IFMT == IFBLK; | |
| 1401 | } | |
| 1402 | ||
| 1403 | pub fn ISREG(m: u32) bool { | |
| 1404 | return m & IFMT == IFREG; | |
| 1405 | } | |
| 1406 | ||
| 1407 | pub fn ISLNK(m: u32) bool { | |
| 1408 | return m & IFMT == IFLNK; | |
| 1409 | } | |
| 1410 | ||
| 1411 | pub fn ISSOCK(m: u32) bool { | |
| 1412 | return m & IFMT == IFSOCK; | |
| 1413 | } | |
| 1414 | ||
| 1415 | pub fn ISDOOR(m: u32) bool { | |
| 1416 | return m & IFMT == IFDOOR; | |
| 1417 | } | |
| 1418 | ||
| 1419 | pub fn ISPORT(m: u32) bool { | |
| 1420 | return m & IFMT == IFPORT; | |
| 1421 | } | |
| 1422 | }; | |
| 1423 | ||
| 1424 | pub const AT = struct { | |
| 1425 | /// Magic value that specify the use of the current working directory | |
| 1426 | /// to determine the target of relative file paths in the openat() and | |
| 1427 | /// similar syscalls. | |
| 1428 | pub const FDCWD = @bitCast(fd_t, @as(u32, 0xffd19553)); | |
| 1429 | ||
| 1430 | /// Do not follow symbolic links | |
| 1431 | pub const SYMLINK_NOFOLLOW = 0x1000; | |
| 1432 | /// Follow symbolic link | |
| 1433 | pub const SYMLINK_FOLLOW = 0x2000; | |
| 1434 | /// Remove directory instead of file | |
| 1435 | pub const REMOVEDIR = 0x1; | |
| 1436 | pub const TRIGGER = 0x2; | |
| 1437 | /// Check access using effective user and group ID | |
| 1438 | pub const EACCESS = 0x4; | |
| 1439 | }; | |
| 1440 | ||
| 1441 | pub const POSIX_FADV = struct { | |
| 1442 | pub const NORMAL = 0; | |
| 1443 | pub const RANDOM = 1; | |
| 1444 | pub const SEQUENTIAL = 2; | |
| 1445 | pub const WILLNEED = 3; | |
| 1446 | pub const DONTNEED = 4; | |
| 1447 | pub const NOREUSE = 5; | |
| 1448 | }; | |
| 1449 | ||
| 1450 | pub const HOST_NAME_MAX = 255; | |
| 1451 | ||
| 1452 | pub const IPPROTO = struct { | |
| 1453 | /// dummy for IP | |
| 1454 | pub const IP = 0; | |
| 1455 | /// Hop by hop header for IPv6 | |
| 1456 | pub const HOPOPTS = 0; | |
| 1457 | /// control message protocol | |
| 1458 | pub const ICMP = 1; | |
| 1459 | /// group control protocol | |
| 1460 | pub const IGMP = 2; | |
| 1461 | /// gateway^2 (deprecated) | |
| 1462 | pub const GGP = 3; | |
| 1463 | /// IP in IP encapsulation | |
| 1464 | pub const ENCAP = 4; | |
| 1465 | /// tcp | |
| 1466 | pub const TCP = 6; | |
| 1467 | /// exterior gateway protocol | |
| 1468 | pub const EGP = 8; | |
| 1469 | /// pup | |
| 1470 | pub const PUP = 12; | |
| 1471 | /// user datagram protocol | |
| 1472 | pub const UDP = 17; | |
| 1473 | /// xns idp | |
| 1474 | pub const IDP = 22; | |
| 1475 | /// IPv6 encapsulated in IP | |
| 1476 | pub const IPV6 = 41; | |
| 1477 | /// Routing header for IPv6 | |
| 1478 | pub const ROUTING = 43; | |
| 1479 | /// Fragment header for IPv6 | |
| 1480 | pub const FRAGMENT = 44; | |
| 1481 | /// rsvp | |
| 1482 | pub const RSVP = 46; | |
| 1483 | /// IPsec Encap. Sec. Payload | |
| 1484 | pub const ESP = 50; | |
| 1485 | /// IPsec Authentication Hdr. | |
| 1486 | pub const AH = 51; | |
| 1487 | /// ICMP for IPv6 | |
| 1488 | pub const ICMPV6 = 58; | |
| 1489 | /// No next header for IPv6 | |
| 1490 | pub const NONE = 59; | |
| 1491 | /// Destination options | |
| 1492 | pub const DSTOPTS = 60; | |
| 1493 | /// "hello" routing protocol | |
| 1494 | pub const HELLO = 63; | |
| 1495 | /// UNOFFICIAL net disk proto | |
| 1496 | pub const ND = 77; | |
| 1497 | /// ISO clnp | |
| 1498 | pub const EON = 80; | |
| 1499 | /// OSPF | |
| 1500 | pub const OSPF = 89; | |
| 1501 | /// PIM routing protocol | |
| 1502 | pub const PIM = 103; | |
| 1503 | /// Stream Control | |
| 1504 | pub const SCTP = 132; | |
| 1505 | /// raw IP packet | |
| 1506 | pub const RAW = 255; | |
| 1507 | /// Sockets Direct Protocol | |
| 1508 | pub const PROTO_SDP = 257; | |
| 1509 | }; | |
| 1510 | ||
| 1511 | pub const priority = enum(c_int) { | |
| 1512 | PROCESS = 0, | |
| 1513 | PGRP = 1, | |
| 1514 | USER = 2, | |
| 1515 | GROUP = 3, | |
| 1516 | SESSION = 4, | |
| 1517 | LWP = 5, | |
| 1518 | TASK = 6, | |
| 1519 | PROJECT = 7, | |
| 1520 | ZONE = 8, | |
| 1521 | CONTRACT = 9, | |
| 1522 | }; | |
| 1523 | ||
| 1524 | pub const rlimit_resource = enum(c_int) { | |
| 1525 | CPU = 0, | |
| 1526 | FSIZE = 1, | |
| 1527 | DATA = 2, | |
| 1528 | STACK = 3, | |
| 1529 | CORE = 4, | |
| 1530 | NOFILE = 5, | |
| 1531 | VMEM = 6, | |
| 1532 | _, | |
| 1533 | ||
| 1534 | pub const AS: rlimit_resource = .VMEM; | |
| 1535 | }; | |
| 1536 | ||
| 1537 | pub const rlim_t = u64; | |
| 1538 | ||
| 1539 | pub const RLIM = struct { | |
| 1540 | /// No limit | |
| 1541 | pub const INFINITY: rlim_t = (1 << 63) - 3; | |
| 1542 | pub const SAVED_MAX: rlim_t = (1 << 63) - 2; | |
| 1543 | pub const SAVED_CUR: rlim_t = (1 << 63) - 1; | |
| 1544 | }; | |
| 1545 | ||
| 1546 | pub const rlimit = extern struct { | |
| 1547 | /// Soft limit | |
| 1548 | cur: rlim_t, | |
| 1549 | /// Hard limit | |
| 1550 | max: rlim_t, | |
| 1551 | }; | |
| 1552 | ||
| 1553 | pub const RUSAGE_SELF = 0; | |
| 1554 | pub const RUSAGE_CHILDREN = -1; | |
| 1555 | pub const RUSAGE_THREAD = 1; | |
| 1556 | ||
| 1557 | pub const rusage = extern struct { | |
| 1558 | utime: timeval, | |
| 1559 | stime: timeval, | |
| 1560 | maxrss: isize, | |
| 1561 | ixrss: isize, | |
| 1562 | idrss: isize, | |
| 1563 | isrss: isize, | |
| 1564 | minflt: isize, | |
| 1565 | majflt: isize, | |
| 1566 | nswap: isize, | |
| 1567 | inblock: isize, | |
| 1568 | oublock: isize, | |
| 1569 | msgsnd: isize, | |
| 1570 | msgrcv: isize, | |
| 1571 | nsignals: isize, | |
| 1572 | nvcsw: isize, | |
| 1573 | nivcsw: isize, | |
| 1574 | }; | |
| 1575 | ||
| 1576 | pub const SHUT = struct { | |
| 1577 | pub const RD = 0; | |
| 1578 | pub const WR = 1; | |
| 1579 | pub const RDWR = 2; | |
| 1580 | }; | |
| 1581 | ||
| 1582 | pub const pollfd = extern struct { | |
| 1583 | fd: fd_t, | |
| 1584 | events: i16, | |
| 1585 | revents: i16, | |
| 1586 | }; | |
| 1587 | ||
| 1588 | /// Testable events (may be specified in ::pollfd::events). | |
| 1589 | pub const POLL = struct { | |
| 1590 | pub const IN = 0x0001; | |
| 1591 | pub const PRI = 0x0002; | |
| 1592 | pub const OUT = 0x0004; | |
| 1593 | pub const RDNORM = 0x0040; | |
| 1594 | pub const WRNORM = .OUT; | |
| 1595 | pub const RDBAND = 0x0080; | |
| 1596 | pub const WRBAND = 0x0100; | |
| 1597 | /// Read-side hangup. | |
| 1598 | pub const RDHUP = 0x4000; | |
| 1599 | ||
| 1600 | /// Non-testable events (may not be specified in events). | |
| 1601 | pub const ERR = 0x0008; | |
| 1602 | pub const HUP = 0x0010; | |
| 1603 | pub const NVAL = 0x0020; | |
| 1604 | ||
| 1605 | /// Events to control `/dev/poll` (not specified in revents) | |
| 1606 | pub const REMOVE = 0x0800; | |
| 1607 | pub const ONESHOT = 0x1000; | |
| 1608 | pub const ET = 0x2000; | |
| 1609 | }; | |
| 1610 | ||
| 1611 | /// Extensions to the ELF auxiliary vector. | |
| 1612 | pub const AT_SUN = struct { | |
| 1613 | /// effective user id | |
| 1614 | pub const UID = 2000; | |
| 1615 | /// real user id | |
| 1616 | pub const RUID = 2001; | |
| 1617 | /// effective group id | |
| 1618 | pub const GID = 2002; | |
| 1619 | /// real group id | |
| 1620 | pub const RGID = 2003; | |
| 1621 | /// dynamic linker's ELF header | |
| 1622 | pub const LDELF = 2004; | |
| 1623 | /// dynamic linker's section headers | |
| 1624 | pub const LDSHDR = 2005; | |
| 1625 | /// name of dynamic linker | |
| 1626 | pub const LDNAME = 2006; | |
| 1627 | /// large pagesize | |
| 1628 | pub const LPAGESZ = 2007; | |
| 1629 | /// platform name | |
| 1630 | pub const PLATFORM = 2008; | |
| 1631 | /// hints about hardware capabilities. | |
| 1632 | pub const HWCAP = 2009; | |
| 1633 | pub const HWCAP2 = 2023; | |
| 1634 | /// flush icache? | |
| 1635 | pub const IFLUSH = 2010; | |
| 1636 | /// cpu name | |
| 1637 | pub const CPU = 2011; | |
| 1638 | /// exec() path name in the auxv, null terminated. | |
| 1639 | pub const EXECNAME = 2014; | |
| 1640 | /// mmu module name | |
| 1641 | pub const MMU = 2015; | |
| 1642 | /// dynamic linkers data segment | |
| 1643 | pub const LDDATA = 2016; | |
| 1644 | /// AF_SUN_ flags passed from the kernel | |
| 1645 | pub const AUXFLAGS = 2017; | |
| 1646 | /// name of the emulation binary for the linker | |
| 1647 | pub const EMULATOR = 2018; | |
| 1648 | /// name of the brand library for the linker | |
| 1649 | pub const BRANDNAME = 2019; | |
| 1650 | /// vectors for brand modules. | |
| 1651 | pub const BRAND_AUX1 = 2020; | |
| 1652 | pub const BRAND_AUX2 = 2021; | |
| 1653 | pub const BRAND_AUX3 = 2022; | |
| 1654 | pub const BRAND_AUX4 = 2025; | |
| 1655 | pub const BRAND_NROOT = 2024; | |
| 1656 | /// vector for comm page. | |
| 1657 | pub const COMMPAGE = 2026; | |
| 1658 | /// information about the x86 FPU. | |
| 1659 | pub const FPTYPE = 2027; | |
| 1660 | pub const FPSIZE = 2028; | |
| 1661 | }; | |
| 1662 | ||
| 1663 | /// ELF auxiliary vector flags. | |
| 1664 | pub const AF_SUN = struct { | |
| 1665 | /// tell ld.so.1 to run "secure" and ignore the environment. | |
| 1666 | pub const SETUGID = 0x00000001; | |
| 1667 | /// hardware capabilities can be verified against AT_SUN_HWCAP | |
| 1668 | pub const HWCAPVERIFY = 0x00000002; | |
| 1669 | pub const NOPLM = 0x00000004; | |
| 1670 | }; | |
| 1671 | ||
| 1672 | // TODO: Add sysconf numbers when the other OSs do. | |
| 1673 | pub const _SC = struct { | |
| 1674 | pub const NPROCESSORS_ONLN = 15; | |
| 1675 | }; | |
| 1676 | ||
| 1677 | pub const procfs = struct { | |
| 1678 | pub const misc_header = extern struct { | |
| 1679 | size: u32, | |
| 1680 | @"type": enum(u32) { | |
| 1681 | Pathname, | |
| 1682 | Socketname, | |
| 1683 | Peersockname, | |
| 1684 | SockoptsBoolOpts, | |
| 1685 | SockoptLinger, | |
| 1686 | SockoptSndbuf, | |
| 1687 | SockoptRcvbuf, | |
| 1688 | SockoptIpNexthop, | |
| 1689 | SockoptIpv6Nexthop, | |
| 1690 | SockoptType, | |
| 1691 | SockoptTcpCongestion, | |
| 1692 | SockfiltersPriv = 14, | |
| 1693 | }, | |
| 1694 | }; | |
| 1695 | ||
| 1696 | pub const fdinfo = extern struct { | |
| 1697 | fd: fd_t, | |
| 1698 | mode: mode_t, | |
| 1699 | ino: ino_t, | |
| 1700 | size: off_t, | |
| 1701 | offset: off_t, | |
| 1702 | uid: uid_t, | |
| 1703 | gid: gid_t, | |
| 1704 | dev_major: major_t, | |
| 1705 | dev_minor: minor_t, | |
| 1706 | special_major: major_t, | |
| 1707 | special_minor: minor_t, | |
| 1708 | fileflags: i32, | |
| 1709 | fdflags: i32, | |
| 1710 | locktype: i16, | |
| 1711 | lockpid: pid_t, | |
| 1712 | locksysid: i32, | |
| 1713 | peerpid: pid_t, | |
| 1714 | __filler: [25]c_int, | |
| 1715 | peername: [15:0]u8, | |
| 1716 | misc: [1]u8, | |
| 1717 | }; | |
| 1718 | }; | |
| 1719 | ||
| 1720 | pub const SFD = struct { | |
| 1721 | pub const CLOEXEC = 0o2000000; | |
| 1722 | pub const NONBLOCK = 0o4000; | |
| 1723 | }; | |
| 1724 | ||
| 1725 | pub const signalfd_siginfo = extern struct { | |
| 1726 | signo: u32, | |
| 1727 | errno: i32, | |
| 1728 | code: i32, | |
| 1729 | pid: u32, | |
| 1730 | uid: uid_t, | |
| 1731 | fd: i32, | |
| 1732 | tid: u32, // unused | |
| 1733 | band: u32, | |
| 1734 | overrun: u32, // unused | |
| 1735 | trapno: u32, | |
| 1736 | status: i32, | |
| 1737 | int: i32, // unused | |
| 1738 | ptr: u64, // unused | |
| 1739 | utime: u64, | |
| 1740 | stime: u64, | |
| 1741 | addr: u64, | |
| 1742 | __pad: [48]u8, | |
| 1743 | }; | |
| 1744 | ||
| 1745 | pub const PORT_SOURCE = struct { | |
| 1746 | pub const AIO = 1; | |
| 1747 | pub const TIMER = 2; | |
| 1748 | pub const USER = 3; | |
| 1749 | pub const FD = 4; | |
| 1750 | pub const ALERT = 5; | |
| 1751 | pub const MQ = 6; | |
| 1752 | pub const FILE = 7; | |
| 1753 | }; | |
| 1754 | ||
| 1755 | pub const PORT_ALERT = struct { | |
| 1756 | pub const SET = 0x01; | |
| 1757 | pub const UPDATE = 0x02; | |
| 1758 | }; | |
| 1759 | ||
| 1760 | /// User watchable file events. | |
| 1761 | pub const FILE_EVENT = struct { | |
| 1762 | pub const ACCESS = 0x00000001; | |
| 1763 | pub const MODIFIED = 0x00000002; | |
| 1764 | pub const ATTRIB = 0x00000004; | |
| 1765 | pub const DELETE = 0x00000010; | |
| 1766 | pub const RENAME_TO = 0x00000020; | |
| 1767 | pub const RENAME_FROM = 0x00000040; | |
| 1768 | pub const TRUNC = 0x00100000; | |
| 1769 | pub const NOFOLLOW = 0x10000000; | |
| 1770 | /// The filesystem holding the watched file was unmounted. | |
| 1771 | pub const UNMOUNTED = 0x20000000; | |
| 1772 | /// Some other file/filesystem got mounted over the watched file/directory. | |
| 1773 | pub const MOUNTEDOVER = 0x40000000; | |
| 1774 | ||
| 1775 | pub fn isException(event: u32) bool { | |
| 1776 | return event & (UNMOUNTED | DELETE | RENAME_TO | RENAME_FROM | MOUNTEDOVER) > 0; | |
| 1777 | } | |
| 1778 | }; | |
| 1779 | ||
| 1780 | pub const port_event = extern struct { | |
| 1781 | events: u32, | |
| 1782 | /// Event source. | |
| 1783 | source: u16, | |
| 1784 | __pad: u16, | |
| 1785 | /// Source-specific object. | |
| 1786 | object: ?*c_void, | |
| 1787 | /// User cookie. | |
| 1788 | cookie: ?*c_void, | |
| 1789 | }; | |
| 1790 | ||
| 1791 | pub const port_notify = extern struct { | |
| 1792 | /// Bind request(s) to port. | |
| 1793 | port: u32, | |
| 1794 | /// User defined variable. | |
| 1795 | user: ?*void, | |
| 1796 | }; | |
| 1797 | ||
| 1798 | pub const file_obj = extern struct { | |
| 1799 | /// Access time. | |
| 1800 | atim: timespec, | |
| 1801 | /// Modification time | |
| 1802 | mtim: timespec, | |
| 1803 | /// Change time | |
| 1804 | ctim: timespec, | |
| 1805 | __pad: [3]usize, | |
| 1806 | name: [*:0]u8, | |
| 1807 | }; | |
| 1808 | ||
| 1809 | // struct ifreq is marked obsolete, with struct lifreq prefered for interface requests. | |
| 1810 | // Here we alias lifreq to ifreq to avoid chainging existing code in os and x.os.IPv6. | |
| 1811 | pub const SIOCGLIFINDEX = IOWR('i', 133, lifreq); | |
| 1812 | pub const SIOCGIFINDEX = SIOCGLIFINDEX; | |
| 1813 | pub const MAX_HDW_LEN = 64; | |
| 1814 | pub const IFNAMESIZE = 32; | |
| 1815 | ||
| 1816 | pub const lif_nd_req = extern struct { | |
| 1817 | addr: sockaddr.storage, | |
| 1818 | state_create: u8, | |
| 1819 | state_same_lla: u8, | |
| 1820 | state_diff_lla: u8, | |
| 1821 | hdw_len: i32, | |
| 1822 | flags: i32, | |
| 1823 | __pad: i32, | |
| 1824 | hdw_addr: [MAX_HDW_LEN]u8, | |
| 1825 | }; | |
| 1826 | ||
| 1827 | pub const lif_ifinfo_req = extern struct { | |
| 1828 | maxhops: u8, | |
| 1829 | reachtime: u32, | |
| 1830 | reachretrans: u32, | |
| 1831 | maxmtu: u32, | |
| 1832 | }; | |
| 1833 | ||
| 1834 | /// IP interface request. See if_tcp(7p) for more info. | |
| 1835 | pub const lifreq = extern struct { | |
| 1836 | // Not actually in a union, but the stdlib expects one for ifreq | |
| 1837 | ifrn: extern union { | |
| 1838 | /// Interface name, e.g. "lo0", "en0". | |
| 1839 | name: [IFNAMESIZE]u8, | |
| 1840 | }, | |
| 1841 | ru1: extern union { | |
| 1842 | /// For subnet/token etc. | |
| 1843 | addrlen: i32, | |
| 1844 | /// Driver's PPA (physical point of attachment). | |
| 1845 | ppa: u32, | |
| 1846 | }, | |
| 1847 | /// One of the IFT types, e.g. IFT_ETHER. | |
| 1848 | @"type": u32, | |
| 1849 | ifru: extern union { | |
| 1850 | /// Address. | |
| 1851 | addr: sockaddr.storage, | |
| 1852 | /// Other end of a peer-to-peer link. | |
| 1853 | dstaddr: sockaddr.storage, | |
| 1854 | /// Broadcast address. | |
| 1855 | broadaddr: sockaddr.storage, | |
| 1856 | /// Address token. | |
| 1857 | token: sockaddr.storage, | |
| 1858 | /// Subnet prefix. | |
| 1859 | subnet: sockaddr.storage, | |
| 1860 | /// Interface index. | |
| 1861 | ivalue: i32, | |
| 1862 | /// Flags for SIOC?LIFFLAGS. | |
| 1863 | flags: u64, | |
| 1864 | /// Hop count metric | |
| 1865 | metric: i32, | |
| 1866 | /// Maximum transmission unit | |
| 1867 | mtu: u32, | |
| 1868 | // Technically [2]i32 | |
| 1869 | muxid: packed struct { ip: i32, arp: i32 }, | |
| 1870 | /// Neighbor reachability determination entries | |
| 1871 | nd_req: lif_nd_req, | |
| 1872 | /// Link info | |
| 1873 | ifinfo_req: lif_ifinfo_req, | |
| 1874 | /// Name of the multipath interface group | |
| 1875 | groupname: [IFNAMESIZE]u8, | |
| 1876 | binding: [IFNAMESIZE]u8, | |
| 1877 | /// Zone id associated with this interface. | |
| 1878 | zoneid: zoneid_t, | |
| 1879 | /// Duplicate address detection state. Either in progress or completed. | |
| 1880 | dadstate: u32, | |
| 1881 | }, | |
| 1882 | }; | |
| 1883 | ||
| 1884 | pub const ifreq = lifreq; | |
| 1885 | ||
| 1886 | const IoCtlCommand = enum(u32) { | |
| 1887 | none = 0x20000000, // no parameters | |
| 1888 | write = 0x40000000, // copy out parameters | |
| 1889 | read = 0x80000000, // copy in parameters | |
| 1890 | read_write = 0xc0000000, | |
| 1891 | }; | |
| 1892 | ||
| 1893 | fn ioImpl(cmd: IoCtlCommand, io_type: u8, nr: u8, comptime IOT: type) i32 { | |
| 1894 | const size = @intCast(u32, @truncate(u8, @sizeOf(IOT))) << 16; | |
| 1895 | const t = @intCast(u32, io_type) << 8; | |
| 1896 | return @bitCast(i32, @enumToInt(cmd) | size | t | nr); | |
| 1897 | } | |
| 1898 | ||
| 1899 | pub fn IO(io_type: u8, nr: u8) i32 { | |
| 1900 | return ioImpl(.none, io_type, nr, void); | |
| 1901 | } | |
| 1902 | ||
| 1903 | pub fn IOR(io_type: u8, nr: u8, comptime IOT: type) i32 { | |
| 1904 | return ioImpl(.write, io_type, nr, IOT); | |
| 1905 | } | |
| 1906 | ||
| 1907 | pub fn IOW(io_type: u8, nr: u8, comptime IOT: type) i32 { | |
| 1908 | return ioImpl(.read, io_type, nr, IOT); | |
| 1909 | } | |
| 1910 | ||
| 1911 | pub fn IOWR(io_type: u8, nr: u8, comptime IOT: type) i32 { | |
| 1912 | return ioImpl(.read_write, io_type, nr, IOT); | |
| 1913 | } |
lib/std/child_process.zig+6-5| ... | ... | @@ -195,7 +195,7 @@ pub const ChildProcess = struct { |
| 195 | 195 | }; |
| 196 | 196 | |
| 197 | 197 | var dead_fds: usize = 0; |
| 198 | // We ask for ensureCapacity with this much extra space. This has more of an | |
| 198 | // We ask for ensureTotalCapacity with this much extra space. This has more of an | |
| 199 | 199 | // effect on small reads because once the reads start to get larger the amount |
| 200 | 200 | // of space an ArrayList will allocate grows exponentially. |
| 201 | 201 | const bump_amt = 512; |
| ... | ... | @@ -215,7 +215,7 @@ pub const ChildProcess = struct { |
| 215 | 215 | if (poll_fds[0].revents & os.POLL.IN != 0) { |
| 216 | 216 | // stdout is ready. |
| 217 | 217 | const new_capacity = std.math.min(stdout.items.len + bump_amt, max_output_bytes); |
| 218 | try stdout.ensureCapacity(new_capacity); | |
| 218 | try stdout.ensureTotalCapacity(new_capacity); | |
| 219 | 219 | const buf = stdout.unusedCapacitySlice(); |
| 220 | 220 | if (buf.len == 0) return error.StdoutStreamTooLong; |
| 221 | 221 | const nread = try os.read(poll_fds[0].fd, buf); |
| ... | ... | @@ -230,7 +230,7 @@ pub const ChildProcess = struct { |
| 230 | 230 | if (poll_fds[1].revents & os.POLL.IN != 0) { |
| 231 | 231 | // stderr is ready. |
| 232 | 232 | const new_capacity = std.math.min(stderr.items.len + bump_amt, max_output_bytes); |
| 233 | try stderr.ensureCapacity(new_capacity); | |
| 233 | try stderr.ensureTotalCapacity(new_capacity); | |
| 234 | 234 | const buf = stderr.unusedCapacitySlice(); |
| 235 | 235 | if (buf.len == 0) return error.StderrStreamTooLong; |
| 236 | 236 | const nread = try os.read(poll_fds[1].fd, buf); |
| ... | ... | @@ -276,7 +276,8 @@ pub const ChildProcess = struct { |
| 276 | 276 | |
| 277 | 277 | // Windows Async IO requires an initial call to ReadFile before waiting on the handle |
| 278 | 278 | for ([_]u1{ 0, 1 }) |i| { |
| 279 | try outs[i].ensureCapacity(bump_amt); | |
| 279 | const new_capacity = std.math.min(outs[i].items.len + bump_amt, max_output_bytes); | |
| 280 | try outs[i].ensureTotalCapacity(new_capacity); | |
| 280 | 281 | const buf = outs[i].unusedCapacitySlice(); |
| 281 | 282 | _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]); |
| 282 | 283 | wait_objects[wait_object_count] = handles[i]; |
| ... | ... | @@ -318,7 +319,7 @@ pub const ChildProcess = struct { |
| 318 | 319 | |
| 319 | 320 | outs[i].items.len += read_bytes; |
| 320 | 321 | const new_capacity = std.math.min(outs[i].items.len + bump_amt, max_output_bytes); |
| 321 | try outs[i].ensureCapacity(new_capacity); | |
| 322 | try outs[i].ensureTotalCapacity(new_capacity); | |
| 322 | 323 | const buf = outs[i].unusedCapacitySlice(); |
| 323 | 324 | if (buf.len == 0) return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong; |
| 324 | 325 | _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]); |
lib/std/coff.zig+1-1| ... | ... | @@ -277,7 +277,7 @@ pub const Coff = struct { |
| 277 | 277 | if (self.sections.items.len == self.coff_header.number_of_sections) |
| 278 | 278 | return; |
| 279 | 279 | |
| 280 | try self.sections.ensureCapacity(self.coff_header.number_of_sections); | |
| 280 | try self.sections.ensureTotalCapacity(self.coff_header.number_of_sections); | |
| 281 | 281 | |
| 282 | 282 | const in = self.in_file.reader(); |
| 283 | 283 |
lib/std/compress/deflate.zig+26-6| ... | ... | @@ -58,8 +58,10 @@ const Huffman = struct { |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | 60 | // All zero. |
| 61 | if (self.count[0] == code_length.len) | |
| 61 | if (self.count[0] == code_length.len) { | |
| 62 | self.min_code_len = 0; | |
| 62 | 63 | return; |
| 64 | } | |
| 63 | 65 | |
| 64 | 66 | var left: isize = 1; |
| 65 | 67 | for (self.count[1..]) |val| { |
| ... | ... | @@ -280,7 +282,7 @@ pub fn InflateStream(comptime ReaderType: type) type { |
| 280 | 282 | return self.bits & mask; |
| 281 | 283 | } |
| 282 | 284 | fn readBits(self: *Self, bits: usize) !u32 { |
| 283 | const val = self.peekBits(bits); | |
| 285 | const val = try self.peekBits(bits); | |
| 284 | 286 | self.discardBits(bits); |
| 285 | 287 | return val; |
| 286 | 288 | } |
| ... | ... | @@ -487,6 +489,8 @@ pub fn InflateStream(comptime ReaderType: type) type { |
| 487 | 489 | // We can't read PREFIX_LUT_BITS as we don't want to read past the |
| 488 | 490 | // deflate stream end, use an incremental approach instead. |
| 489 | 491 | var code_len = h.min_code_len; |
| 492 | if (code_len == 0) | |
| 493 | return error.OutOfCodes; | |
| 490 | 494 | while (true) { |
| 491 | 495 | _ = try self.peekBits(code_len); |
| 492 | 496 | // Small optimization win, use as many bits as possible in the |
| ... | ... | @@ -658,11 +662,27 @@ test "lengths overflow" { |
| 658 | 662 | // f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6 |
| 659 | 663 | // 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11 |
| 660 | 664 | const stream = [_]u8{ 0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110 }; |
| 665 | try std.testing.expectError(error.InvalidLength, testInflate(stream[0..])); | |
| 666 | } | |
| 667 | ||
| 668 | test "empty distance alphabet" { | |
| 669 | // dynamic block with empty distance alphabet is valid if end of data symbol is used immediately | |
| 670 | // f dy hlit hdist hclen 16 17 18 0 8 7 9 6 10 5 11 4 12 3 13 2 14 1 15 (18) x128 (18) x128 (1) ( 0) (256) | |
| 671 | // 1 10 00000 00000 1111 000 000 010 010 000 000 000 000 000 000 000 000 000 000 000 000 000 001 000 (11) 1110101 (11) 1110101 (0) (10) (0) | |
| 672 | const stream = [_]u8{ 0b00000101, 0b11100000, 0b00000001, 0b00001001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00010000, 0b01011100, 0b10111111, 0b00101110 }; | |
| 673 | try testInflate(stream[0..]); | |
| 674 | } | |
| 661 | 675 | |
| 662 | const reader = std.io.fixedBufferStream(&stream).reader(); | |
| 676 | test "inflateStream fuzzing" { | |
| 677 | // see https://github.com/ziglang/zig/issues/9842 | |
| 678 | try std.testing.expectError(error.EndOfStream, testInflate("\x950000")); | |
| 679 | try std.testing.expectError(error.OutOfCodes, testInflate("\x950\x00\x0000000")); | |
| 680 | } | |
| 681 | ||
| 682 | fn testInflate(data: []const u8) !void { | |
| 663 | 683 | var window: [0x8000]u8 = undefined; |
| 684 | const reader = std.io.fixedBufferStream(data).reader(); | |
| 664 | 685 | var inflate = inflateStream(reader, &window); |
| 665 | ||
| 666 | var buf: [1]u8 = undefined; | |
| 667 | try std.testing.expectError(error.InvalidLength, inflate.read(&buf)); | |
| 686 | var inflated = try inflate.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); | |
| 687 | defer std.testing.allocator.free(inflated); | |
| 668 | 688 | } |
lib/std/crypto/benchmark.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | // zig run benchmark.zig --release-fast --zig-lib-dir .. | |
| 1 | // zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig | |
| 2 | 2 | |
| 3 | 3 | const std = @import("../std.zig"); |
| 4 | 4 | const builtin = std.builtin; |
lib/std/crypto/blake3.zig+4-4| ... | ... | @@ -398,10 +398,10 @@ pub const Blake3 = struct { |
| 398 | 398 | return Blake3.init_internal(context_key_words, DERIVE_KEY_MATERIAL); |
| 399 | 399 | } |
| 400 | 400 | |
| 401 | pub fn hash(in: []const u8, out: []u8, options: Options) void { | |
| 402 | var hasher = Blake3.init(options); | |
| 403 | hasher.update(in); | |
| 404 | hasher.final(out); | |
| 401 | pub fn hash(b: []const u8, out: []u8, options: Options) void { | |
| 402 | var d = Blake3.init(options); | |
| 403 | d.update(b); | |
| 404 | d.final(out); | |
| 405 | 405 | } |
| 406 | 406 | |
| 407 | 407 | fn pushCv(self: *Blake3, cv: [8]u32) void { |
lib/std/debug.zig+44-12| ... | ... | @@ -228,9 +228,32 @@ pub fn assert(ok: bool) void { |
| 228 | 228 | |
| 229 | 229 | pub fn panic(comptime format: []const u8, args: anytype) noreturn { |
| 230 | 230 | @setCold(true); |
| 231 | // TODO: remove conditional once wasi / LLVM defines __builtin_return_address | |
| 232 | const first_trace_addr = if (native_os == .wasi) null else @returnAddress(); | |
| 233 | panicExtra(null, first_trace_addr, format, args); | |
| 231 | ||
| 232 | panicExtra(null, format, args); | |
| 233 | } | |
| 234 | ||
| 235 | /// `panicExtra` is useful when you want to print out an `@errorReturnTrace` | |
| 236 | /// and also print out some values. | |
| 237 | pub fn panicExtra( | |
| 238 | trace: ?*builtin.StackTrace, | |
| 239 | comptime format: []const u8, | |
| 240 | args: anytype, | |
| 241 | ) noreturn { | |
| 242 | @setCold(true); | |
| 243 | ||
| 244 | const size = 0x1000; | |
| 245 | const trunc_msg = "(msg truncated)"; | |
| 246 | var buf: [size + trunc_msg.len]u8 = undefined; | |
| 247 | // a minor annoyance with this is that it will result in the NoSpaceLeft | |
| 248 | // error being part of the @panic stack trace (but that error should | |
| 249 | // only happen rarely) | |
| 250 | const msg = std.fmt.bufPrint(buf[0..size], format, args) catch |err| switch (err) { | |
| 251 | std.fmt.BufPrintError.NoSpaceLeft => blk: { | |
| 252 | std.mem.copy(u8, buf[size..], trunc_msg); | |
| 253 | break :blk &buf; | |
| 254 | }, | |
| 255 | }; | |
| 256 | builtin.panic(msg, trace); | |
| 234 | 257 | } |
| 235 | 258 | |
| 236 | 259 | /// Non-zero whenever the program triggered a panic. |
| ... | ... | @@ -244,7 +267,9 @@ var panic_mutex = std.Thread.Mutex{}; |
| 244 | 267 | /// This is used to catch and handle panics triggered by the panic handler. |
| 245 | 268 | threadlocal var panic_stage: usize = 0; |
| 246 | 269 | |
| 247 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: anytype) noreturn { | |
| 270 | // `panicImpl` could be useful in implementing a custom panic handler which | |
| 271 | // calls the default handler (on supported platforms) | |
| 272 | pub fn panicImpl(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, msg: []const u8) noreturn { | |
| 248 | 273 | @setCold(true); |
| 249 | 274 | |
| 250 | 275 | if (enable_segfault_handler) { |
| ... | ... | @@ -271,7 +296,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 271 | 296 | const current_thread_id = std.Thread.getCurrentId(); |
| 272 | 297 | stderr.print("thread {} panic: ", .{current_thread_id}) catch os.abort(); |
| 273 | 298 | } |
| 274 | stderr.print(format ++ "\n", args) catch os.abort(); | |
| 299 | stderr.print("{s}\n", .{msg}) catch os.abort(); | |
| 275 | 300 | if (trace) |t| { |
| 276 | 301 | dumpStackTrace(t.*); |
| 277 | 302 | } |
| ... | ... | @@ -654,6 +679,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { |
| 654 | 679 | .openbsd, |
| 655 | 680 | .macos, |
| 656 | 681 | .windows, |
| 682 | .solaris, | |
| 657 | 683 | => return DebugInfo.init(allocator), |
| 658 | 684 | else => return error.UnsupportedDebugInfo, |
| 659 | 685 | } |
| ... | ... | @@ -1420,7 +1446,7 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1420 | 1446 | }; |
| 1421 | 1447 | } |
| 1422 | 1448 | }, |
| 1423 | .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku => struct { | |
| 1449 | .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris => struct { | |
| 1424 | 1450 | base_address: usize, |
| 1425 | 1451 | dwarf: DW.DwarfInfo, |
| 1426 | 1452 | mapped_memory: []const u8, |
| ... | ... | @@ -1468,7 +1494,7 @@ fn getDebugInfoAllocator() *mem.Allocator { |
| 1468 | 1494 | |
| 1469 | 1495 | /// Whether or not the current target can print useful debug information when a segfault occurs. |
| 1470 | 1496 | pub const have_segfault_handling_support = switch (native_os) { |
| 1471 | .linux, .netbsd => true, | |
| 1497 | .linux, .netbsd, .solaris => true, | |
| 1472 | 1498 | .windows => true, |
| 1473 | 1499 | .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"), |
| 1474 | 1500 | else => false, |
| ... | ... | @@ -1535,6 +1561,7 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v |
| 1535 | 1561 | .freebsd => @ptrToInt(info.addr), |
| 1536 | 1562 | .netbsd => @ptrToInt(info.info.reason.fault.addr), |
| 1537 | 1563 | .openbsd => @ptrToInt(info.data.fault.addr), |
| 1564 | .solaris => @ptrToInt(info.reason.fault.addr), | |
| 1538 | 1565 | else => unreachable, |
| 1539 | 1566 | }; |
| 1540 | 1567 | |
| ... | ... | @@ -1559,13 +1586,13 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v |
| 1559 | 1586 | .x86_64 => { |
| 1560 | 1587 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); |
| 1561 | 1588 | const ip = switch (native_os) { |
| 1562 | .linux, .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG.RIP]), | |
| 1589 | .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RIP]), | |
| 1563 | 1590 | .freebsd => @intCast(usize, ctx.mcontext.rip), |
| 1564 | 1591 | .openbsd => @intCast(usize, ctx.sc_rip), |
| 1565 | 1592 | else => unreachable, |
| 1566 | 1593 | }; |
| 1567 | 1594 | const bp = switch (native_os) { |
| 1568 | .linux, .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG.RBP]), | |
| 1595 | .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RBP]), | |
| 1569 | 1596 | .openbsd => @intCast(usize, ctx.sc_rbp), |
| 1570 | 1597 | .freebsd => @intCast(usize, ctx.mcontext.rbp), |
| 1571 | 1598 | else => unreachable, |
| ... | ... | @@ -1624,9 +1651,14 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: u |
| 1624 | 1651 | os.abort(); |
| 1625 | 1652 | } else { |
| 1626 | 1653 | switch (msg) { |
| 1627 | 0 => panicExtra(null, exception_address, format.?, .{}), | |
| 1628 | 1 => panicExtra(null, exception_address, "Segmentation fault at address 0x{x}", .{info.ExceptionRecord.ExceptionInformation[1]}), | |
| 1629 | 2 => panicExtra(null, exception_address, "Illegal Instruction", .{}), | |
| 1654 | 0 => panicImpl(null, exception_address, format.?), | |
| 1655 | 1 => { | |
| 1656 | const format_item = "Segmentation fault at address 0x{x}"; | |
| 1657 | var buf: [format_item.len + 64]u8 = undefined; // 64 is arbitrary, but sufficiently large | |
| 1658 | const to_print = std.fmt.bufPrint(buf[0..buf.len], format_item, .{info.ExceptionRecord.ExceptionInformation[1]}) catch unreachable; | |
| 1659 | panicImpl(null, exception_address, to_print); | |
| 1660 | }, | |
| 1661 | 2 => panicImpl(null, exception_address, "Illegal Instruction"), | |
| 1630 | 1662 | else => unreachable, |
| 1631 | 1663 | } |
| 1632 | 1664 | } |
lib/std/dynamic_library.zig+1-1| ... | ... | @@ -14,7 +14,7 @@ const max = std.math.max; |
| 14 | 14 | pub const DynLib = switch (builtin.os.tag) { |
| 15 | 15 | .linux => if (builtin.link_libc) DlDynlib else ElfDynLib, |
| 16 | 16 | .windows => WindowsDynLib, |
| 17 | .macos, .tvos, .watchos, .ios, .freebsd, .netbsd, .openbsd, .dragonfly => DlDynlib, | |
| 17 | .macos, .tvos, .watchos, .ios, .freebsd, .netbsd, .openbsd, .dragonfly, .solaris => DlDynlib, | |
| 18 | 18 | else => void, |
| 19 | 19 | }; |
| 20 | 20 |
lib/std/elf.zig+97| ... | ... | @@ -1591,3 +1591,100 @@ pub const PF_MASKOS = 0x0ff00000; |
| 1591 | 1591 | |
| 1592 | 1592 | /// Bits for processor-specific semantics. |
| 1593 | 1593 | pub const PF_MASKPROC = 0xf0000000; |
| 1594 | ||
| 1595 | // Special section indexes used in Elf{32,64}_Sym. | |
| 1596 | pub const SHN_UNDEF = 0; | |
| 1597 | pub const SHN_LORESERVE = 0xff00; | |
| 1598 | pub const SHN_LOPROC = 0xff00; | |
| 1599 | pub const SHN_HIPROC = 0xff1f; | |
| 1600 | pub const SHN_LIVEPATCH = 0xff20; | |
| 1601 | pub const SHN_ABS = 0xfff1; | |
| 1602 | pub const SHN_COMMON = 0xfff2; | |
| 1603 | pub const SHN_HIRESERVE = 0xffff; | |
| 1604 | ||
| 1605 | /// AMD x86-64 relocations. | |
| 1606 | /// No reloc | |
| 1607 | pub const R_X86_64_NONE = 0; | |
| 1608 | /// Direct 64 bit | |
| 1609 | pub const R_X86_64_64 = 1; | |
| 1610 | /// PC relative 32 bit signed | |
| 1611 | pub const R_X86_64_PC32 = 2; | |
| 1612 | /// 32 bit GOT entry | |
| 1613 | pub const R_X86_64_GOT32 = 3; | |
| 1614 | /// 32 bit PLT address | |
| 1615 | pub const R_X86_64_PLT32 = 4; | |
| 1616 | /// Copy symbol at runtime | |
| 1617 | pub const R_X86_64_COPY = 5; | |
| 1618 | /// Create GOT entry | |
| 1619 | pub const R_X86_64_GLOB_DAT = 6; | |
| 1620 | /// Create PLT entry | |
| 1621 | pub const R_X86_64_JUMP_SLOT = 7; | |
| 1622 | /// Adjust by program base | |
| 1623 | pub const R_X86_64_RELATIVE = 8; | |
| 1624 | /// 32 bit signed PC relative offset to GOT | |
| 1625 | pub const R_X86_64_GOTPCREL = 9; | |
| 1626 | /// Direct 32 bit zero extended | |
| 1627 | pub const R_X86_64_32 = 10; | |
| 1628 | /// Direct 32 bit sign extended | |
| 1629 | pub const R_X86_64_32S = 11; | |
| 1630 | /// Direct 16 bit zero extended | |
| 1631 | pub const R_X86_64_16 = 12; | |
| 1632 | /// 16 bit sign extended pc relative | |
| 1633 | pub const R_X86_64_PC16 = 13; | |
| 1634 | /// Direct 8 bit sign extended | |
| 1635 | pub const R_X86_64_8 = 14; | |
| 1636 | /// 8 bit sign extended pc relative | |
| 1637 | pub const R_X86_64_PC8 = 15; | |
| 1638 | /// ID of module containing symbol | |
| 1639 | pub const R_X86_64_DTPMOD64 = 16; | |
| 1640 | /// Offset in module's TLS block | |
| 1641 | pub const R_X86_64_DTPOFF64 = 17; | |
| 1642 | /// Offset in initial TLS block | |
| 1643 | pub const R_X86_64_TPOFF64 = 18; | |
| 1644 | /// 32 bit signed PC relative offset to two GOT entries for GD symbol | |
| 1645 | pub const R_X86_64_TLSGD = 19; | |
| 1646 | /// 32 bit signed PC relative offset to two GOT entries for LD symbol | |
| 1647 | pub const R_X86_64_TLSLD = 20; | |
| 1648 | /// Offset in TLS block | |
| 1649 | pub const R_X86_64_DTPOFF32 = 21; | |
| 1650 | /// 32 bit signed PC relative offset to GOT entry for IE symbol | |
| 1651 | pub const R_X86_64_GOTTPOFF = 22; | |
| 1652 | /// Offset in initial TLS block | |
| 1653 | pub const R_X86_64_TPOFF32 = 23; | |
| 1654 | /// PC relative 64 bit | |
| 1655 | pub const R_X86_64_PC64 = 24; | |
| 1656 | /// 64 bit offset to GOT | |
| 1657 | pub const R_X86_64_GOTOFF64 = 25; | |
| 1658 | /// 32 bit signed pc relative offset to GOT | |
| 1659 | pub const R_X86_64_GOTPC32 = 26; | |
| 1660 | /// 64 bit GOT entry offset | |
| 1661 | pub const R_X86_64_GOT64 = 27; | |
| 1662 | /// 64 bit PC relative offset to GOT entry | |
| 1663 | pub const R_X86_64_GOTPCREL64 = 28; | |
| 1664 | /// 64 bit PC relative offset to GOT | |
| 1665 | pub const R_X86_64_GOTPC64 = 29; | |
| 1666 | /// Like GOT64, says PLT entry needed | |
| 1667 | pub const R_X86_64_GOTPLT64 = 30; | |
| 1668 | /// 64-bit GOT relative offset to PLT entry | |
| 1669 | pub const R_X86_64_PLTOFF64 = 31; | |
| 1670 | /// Size of symbol plus 32-bit addend | |
| 1671 | pub const R_X86_64_SIZE32 = 32; | |
| 1672 | /// Size of symbol plus 64-bit addend | |
| 1673 | pub const R_X86_64_SIZE64 = 33; | |
| 1674 | /// GOT offset for TLS descriptor | |
| 1675 | pub const R_X86_64_GOTPC32_TLSDESC = 34; | |
| 1676 | /// Marker for call through TLS descriptor | |
| 1677 | pub const R_X86_64_TLSDESC_CALL = 35; | |
| 1678 | /// TLS descriptor | |
| 1679 | pub const R_X86_64_TLSDESC = 36; | |
| 1680 | /// Adjust indirectly by program base | |
| 1681 | pub const R_X86_64_IRELATIVE = 37; | |
| 1682 | /// 64-bit adjust by program base | |
| 1683 | pub const R_X86_64_RELATIVE64 = 38; | |
| 1684 | /// 39 Reserved was R_X86_64_PC32_BND | |
| 1685 | /// 40 Reserved was R_X86_64_PLT32_BND | |
| 1686 | /// Load from 32 bit signed pc relative offset to GOT entry without REX prefix, relaxable | |
| 1687 | pub const R_X86_64_GOTPCRELX = 41; | |
| 1688 | /// Load from 32 bit signed PC relative offset to GOT entry with REX prefix, relaxable | |
| 1689 | pub const R_X86_64_REX_GOTPCRELX = 42; | |
| 1690 | pub const R_X86_64_NUM = 43; |
lib/std/fifo.zig+6-3| ... | ... | @@ -119,8 +119,11 @@ pub fn LinearFifo( |
| 119 | 119 | } |
| 120 | 120 | } |
| 121 | 121 | |
| 122 | /// Deprecated: call `ensureUnusedCapacity` or `ensureTotalCapacity`. | |
| 123 | pub const ensureCapacity = ensureTotalCapacity; | |
| 124 | ||
| 122 | 125 | /// Ensure that the buffer can fit at least `size` items |
| 123 | pub fn ensureCapacity(self: *Self, size: usize) !void { | |
| 126 | pub fn ensureTotalCapacity(self: *Self, size: usize) !void { | |
| 124 | 127 | if (self.buf.len >= size) return; |
| 125 | 128 | if (buffer_type == .Dynamic) { |
| 126 | 129 | self.realign(); |
| ... | ... | @@ -135,7 +138,7 @@ pub fn LinearFifo( |
| 135 | 138 | pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void { |
| 136 | 139 | if (self.writableLength() >= size) return; |
| 137 | 140 | |
| 138 | return try self.ensureCapacity(math.add(usize, self.count, size) catch return error.OutOfMemory); | |
| 141 | return try self.ensureTotalCapacity(math.add(usize, self.count, size) catch return error.OutOfMemory); | |
| 139 | 142 | } |
| 140 | 143 | |
| 141 | 144 | /// Returns number of items currently in fifo |
| ... | ... | @@ -471,7 +474,7 @@ test "LinearFifo(u8, .Dynamic)" { |
| 471 | 474 | } |
| 472 | 475 | |
| 473 | 476 | { |
| 474 | try fifo.ensureCapacity(1); | |
| 477 | try fifo.ensureTotalCapacity(1); | |
| 475 | 478 | var in_fbs = std.io.fixedBufferStream("pump test"); |
| 476 | 479 | var out_buf: [50]u8 = undefined; |
| 477 | 480 | var out_fbs = std.io.fixedBufferStream(&out_buf); |
lib/std/fs.zig+61-4| ... | ... | @@ -35,7 +35,7 @@ pub const Watch = @import("fs/watch.zig").Watch; |
| 35 | 35 | /// fit into a UTF-8 encoded array of this length. |
| 36 | 36 | /// The byte count includes room for a null sentinel byte. |
| 37 | 37 | pub const MAX_PATH_BYTES = switch (builtin.os.tag) { |
| 38 | .linux, .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .haiku => os.PATH_MAX, | |
| 38 | .linux, .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .haiku, .solaris => os.PATH_MAX, | |
| 39 | 39 | // Each UTF-16LE character may be expanded to 3 UTF-8 bytes. |
| 40 | 40 | // If it would require 4 UTF-8 bytes, then there would be a surrogate |
| 41 | 41 | // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. |
| ... | ... | @@ -298,10 +298,10 @@ pub const Dir = struct { |
| 298 | 298 | pub const Kind = File.Kind; |
| 299 | 299 | }; |
| 300 | 300 | |
| 301 | const IteratorError = error{AccessDenied} || os.UnexpectedError; | |
| 301 | const IteratorError = error{ AccessDenied, SystemResources } || os.UnexpectedError; | |
| 302 | 302 | |
| 303 | 303 | pub const Iterator = switch (builtin.os.tag) { |
| 304 | .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd => struct { | |
| 304 | .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris => struct { | |
| 305 | 305 | dir: Dir, |
| 306 | 306 | seek: i64, |
| 307 | 307 | buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)), |
| ... | ... | @@ -318,6 +318,7 @@ pub const Dir = struct { |
| 318 | 318 | switch (builtin.os.tag) { |
| 319 | 319 | .macos, .ios => return self.nextDarwin(), |
| 320 | 320 | .freebsd, .netbsd, .dragonfly, .openbsd => return self.nextBsd(), |
| 321 | .solaris => return self.nextSolaris(), | |
| 321 | 322 | else => @compileError("unimplemented"), |
| 322 | 323 | } |
| 323 | 324 | } |
| ... | ... | @@ -372,6 +373,60 @@ pub const Dir = struct { |
| 372 | 373 | } |
| 373 | 374 | } |
| 374 | 375 | |
| 376 | fn nextSolaris(self: *Self) !?Entry { | |
| 377 | start_over: while (true) { | |
| 378 | if (self.index >= self.end_index) { | |
| 379 | const rc = os.system.getdents(self.dir.fd, &self.buf, self.buf.len); | |
| 380 | switch (os.errno(rc)) { | |
| 381 | .SUCCESS => {}, | |
| 382 | .BADF => unreachable, // Dir is invalid or was opened without iteration ability | |
| 383 | .FAULT => unreachable, | |
| 384 | .NOTDIR => unreachable, | |
| 385 | .INVAL => unreachable, | |
| 386 | else => |err| return os.unexpectedErrno(err), | |
| 387 | } | |
| 388 | if (rc == 0) return null; | |
| 389 | self.index = 0; | |
| 390 | self.end_index = @intCast(usize, rc); | |
| 391 | } | |
| 392 | const entry = @ptrCast(*align(1) os.system.dirent, &self.buf[self.index]); | |
| 393 | const next_index = self.index + entry.reclen(); | |
| 394 | self.index = next_index; | |
| 395 | ||
| 396 | const name = mem.spanZ(@ptrCast([*:0]u8, &entry.d_name)); | |
| 397 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) | |
| 398 | continue :start_over; | |
| 399 | ||
| 400 | // Solaris dirent doesn't expose d_type, so we have to call stat to get it. | |
| 401 | const stat_info = os.fstatat( | |
| 402 | self.dir.fd, | |
| 403 | name, | |
| 404 | os.AT.SYMLINK_NOFOLLOW, | |
| 405 | ) catch |err| switch (err) { | |
| 406 | error.NameTooLong => unreachable, | |
| 407 | error.SymLinkLoop => unreachable, | |
| 408 | error.FileNotFound => unreachable, // lost the race | |
| 409 | else => |e| return e, | |
| 410 | }; | |
| 411 | const entry_kind = switch (stat_info.mode & os.S.IFMT) { | |
| 412 | os.S.IFIFO => Entry.Kind.NamedPipe, | |
| 413 | os.S.IFCHR => Entry.Kind.CharacterDevice, | |
| 414 | os.S.IFDIR => Entry.Kind.Directory, | |
| 415 | os.S.IFBLK => Entry.Kind.BlockDevice, | |
| 416 | os.S.IFREG => Entry.Kind.File, | |
| 417 | os.S.IFLNK => Entry.Kind.SymLink, | |
| 418 | os.S.IFSOCK => Entry.Kind.UnixDomainSocket, | |
| 419 | os.S.IFDOOR => Entry.Kind.Door, | |
| 420 | os.S.IFPORT => Entry.Kind.EventPort, | |
| 421 | else => Entry.Kind.Unknown, | |
| 422 | }; | |
| 423 | return Entry{ | |
| 424 | .name = name, | |
| 425 | .kind = entry_kind, | |
| 426 | }; | |
| 427 | } | |
| 428 | } | |
| 429 | ||
| 375 | 430 | fn nextBsd(self: *Self) !?Entry { |
| 376 | 431 | start_over: while (true) { |
| 377 | 432 | if (self.index >= self.end_index) { |
| ... | ... | @@ -704,6 +759,7 @@ pub const Dir = struct { |
| 704 | 759 | .netbsd, |
| 705 | 760 | .dragonfly, |
| 706 | 761 | .openbsd, |
| 762 | .solaris, | |
| 707 | 763 | => return Iterator{ |
| 708 | 764 | .dir = self, |
| 709 | 765 | .seek = 0, |
| ... | ... | @@ -1556,7 +1612,7 @@ pub const Dir = struct { |
| 1556 | 1612 | error.AccessDenied => |e| switch (builtin.os.tag) { |
| 1557 | 1613 | // non-Linux POSIX systems return EPERM when trying to delete a directory, so |
| 1558 | 1614 | // we need to handle that case specifically and translate the error |
| 1559 | .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd => { | |
| 1615 | .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris => { | |
| 1560 | 1616 | // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them) |
| 1561 | 1617 | const fstat = os.fstatatZ(self.fd, sub_path_c, os.AT.SYMLINK_NOFOLLOW) catch return e; |
| 1562 | 1618 | const is_dir = fstat.mode & os.S.IFMT == os.S.IFDIR; |
| ... | ... | @@ -2441,6 +2497,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { |
| 2441 | 2497 | } |
| 2442 | 2498 | switch (builtin.os.tag) { |
| 2443 | 2499 | .linux => return os.readlinkZ("/proc/self/exe", out_buffer), |
| 2500 | .solaris => return os.readlinkZ("/proc/self/path/a.out", out_buffer), | |
| 2444 | 2501 | .freebsd, .dragonfly => { |
| 2445 | 2502 | var mib = [4]c_int{ os.CTL.KERN, os.KERN.PROC, os.KERN.PROC_PATHNAME, -1 }; |
| 2446 | 2503 | var out_len: usize = out_buffer.len; |
lib/std/fs/file.zig+34-18| ... | ... | @@ -41,6 +41,8 @@ pub const File = struct { |
| 41 | 41 | File, |
| 42 | 42 | UnixDomainSocket, |
| 43 | 43 | Whiteout, |
| 44 | Door, | |
| 45 | EventPort, | |
| 44 | 46 | Unknown, |
| 45 | 47 | }; |
| 46 | 48 | |
| ... | ... | @@ -320,28 +322,40 @@ pub const File = struct { |
| 320 | 322 | const atime = st.atime(); |
| 321 | 323 | const mtime = st.mtime(); |
| 322 | 324 | const ctime = st.ctime(); |
| 325 | const kind: Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) { | |
| 326 | .BLOCK_DEVICE => Kind.BlockDevice, | |
| 327 | .CHARACTER_DEVICE => Kind.CharacterDevice, | |
| 328 | .DIRECTORY => Kind.Directory, | |
| 329 | .SYMBOLIC_LINK => Kind.SymLink, | |
| 330 | .REGULAR_FILE => Kind.File, | |
| 331 | .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket, | |
| 332 | else => Kind.Unknown, | |
| 333 | } else blk: { | |
| 334 | const m = st.mode & os.S.IFMT; | |
| 335 | switch (m) { | |
| 336 | os.S.IFBLK => break :blk Kind.BlockDevice, | |
| 337 | os.S.IFCHR => break :blk Kind.CharacterDevice, | |
| 338 | os.S.IFDIR => break :blk Kind.Directory, | |
| 339 | os.S.IFIFO => break :blk Kind.NamedPipe, | |
| 340 | os.S.IFLNK => break :blk Kind.SymLink, | |
| 341 | os.S.IFREG => break :blk Kind.File, | |
| 342 | os.S.IFSOCK => break :blk Kind.UnixDomainSocket, | |
| 343 | else => {}, | |
| 344 | } | |
| 345 | if (builtin.os.tag == .solaris) switch (m) { | |
| 346 | os.S.IFDOOR => break :blk Kind.Door, | |
| 347 | os.S.IFPORT => break :blk Kind.EventPort, | |
| 348 | else => {}, | |
| 349 | }; | |
| 350 | ||
| 351 | break :blk .Unknown; | |
| 352 | }; | |
| 353 | ||
| 323 | 354 | return Stat{ |
| 324 | 355 | .inode = st.ino, |
| 325 | 356 | .size = @bitCast(u64, st.size), |
| 326 | 357 | .mode = st.mode, |
| 327 | .kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) { | |
| 328 | .BLOCK_DEVICE => Kind.BlockDevice, | |
| 329 | .CHARACTER_DEVICE => Kind.CharacterDevice, | |
| 330 | .DIRECTORY => Kind.Directory, | |
| 331 | .SYMBOLIC_LINK => Kind.SymLink, | |
| 332 | .REGULAR_FILE => Kind.File, | |
| 333 | .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket, | |
| 334 | else => Kind.Unknown, | |
| 335 | } else switch (st.mode & os.S.IFMT) { | |
| 336 | os.S.IFBLK => Kind.BlockDevice, | |
| 337 | os.S.IFCHR => Kind.CharacterDevice, | |
| 338 | os.S.IFDIR => Kind.Directory, | |
| 339 | os.S.IFIFO => Kind.NamedPipe, | |
| 340 | os.S.IFLNK => Kind.SymLink, | |
| 341 | os.S.IFREG => Kind.File, | |
| 342 | os.S.IFSOCK => Kind.UnixDomainSocket, | |
| 343 | else => Kind.Unknown, | |
| 344 | }, | |
| 358 | .kind = kind, | |
| 345 | 359 | .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec, |
| 346 | 360 | .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec, |
| 347 | 361 | .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec, |
| ... | ... | @@ -852,6 +866,7 @@ pub const File = struct { |
| 852 | 866 | |
| 853 | 867 | pub const LockError = error{ |
| 854 | 868 | SystemResources, |
| 869 | FileLocksNotSupported, | |
| 855 | 870 | } || os.UnexpectedError; |
| 856 | 871 | |
| 857 | 872 | /// Blocks when an incompatible lock is held by another process. |
| ... | ... | @@ -914,6 +929,7 @@ pub const File = struct { |
| 914 | 929 | return os.flock(file.handle, os.LOCK.UN) catch |err| switch (err) { |
| 915 | 930 | error.WouldBlock => unreachable, // unlocking can't block |
| 916 | 931 | error.SystemResources => unreachable, // We are deallocating resources. |
| 932 | error.FileLocksNotSupported => unreachable, // We already got the lock. | |
| 917 | 933 | error.Unexpected => unreachable, // Resource deallocation must succeed. |
| 918 | 934 | }; |
| 919 | 935 | } |
lib/std/fs/get_app_data_dir.zig+1-1| ... | ... | @@ -44,7 +44,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD |
| 44 | 44 | }; |
| 45 | 45 | return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname }); |
| 46 | 46 | }, |
| 47 | .linux, .freebsd, .netbsd, .dragonfly, .openbsd => { | |
| 47 | .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris => { | |
| 48 | 48 | const home_dir = os.getenv("HOME") orelse { |
| 49 | 49 | // TODO look in /etc/passwd |
| 50 | 50 | return error.AppDataDirUnavailable; |
lib/std/fs/test.zig+1-1| ... | ... | @@ -188,7 +188,7 @@ fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool { |
| 188 | 188 | |
| 189 | 189 | test "Dir.realpath smoke test" { |
| 190 | 190 | switch (builtin.os.tag) { |
| 191 | .linux, .windows, .macos, .ios, .watchos, .tvos => {}, | |
| 191 | .linux, .windows, .macos, .ios, .watchos, .tvos, .solaris => {}, | |
| 192 | 192 | else => return error.SkipZigTest, |
| 193 | 193 | } |
| 194 | 194 |
lib/std/hash/benchmark.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | // zig run benchmark.zig --release-fast --zig-lib-dir .. | |
| 1 | // zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig | |
| 2 | 2 | |
| 3 | 3 | const builtin = std.builtin; |
| 4 | 4 | const std = @import("std"); |
lib/std/hash_map.zig+9-9| ... | ... | @@ -1568,11 +1568,11 @@ test "std.hash_map basic usage" { |
| 1568 | 1568 | try expectEqual(total, sum); |
| 1569 | 1569 | } |
| 1570 | 1570 | |
| 1571 | test "std.hash_map ensureCapacity" { | |
| 1571 | test "std.hash_map ensureTotalCapacity" { | |
| 1572 | 1572 | var map = AutoHashMap(i32, i32).init(std.testing.allocator); |
| 1573 | 1573 | defer map.deinit(); |
| 1574 | 1574 | |
| 1575 | try map.ensureCapacity(20); | |
| 1575 | try map.ensureTotalCapacity(20); | |
| 1576 | 1576 | const initial_capacity = map.capacity(); |
| 1577 | 1577 | try testing.expect(initial_capacity >= 20); |
| 1578 | 1578 | var i: i32 = 0; |
| ... | ... | @@ -1583,13 +1583,13 @@ test "std.hash_map ensureCapacity" { |
| 1583 | 1583 | try testing.expect(initial_capacity == map.capacity()); |
| 1584 | 1584 | } |
| 1585 | 1585 | |
| 1586 | test "std.hash_map ensureCapacity with tombstones" { | |
| 1586 | test "std.hash_map ensureUnusedCapacity with tombstones" { | |
| 1587 | 1587 | var map = AutoHashMap(i32, i32).init(std.testing.allocator); |
| 1588 | 1588 | defer map.deinit(); |
| 1589 | 1589 | |
| 1590 | 1590 | var i: i32 = 0; |
| 1591 | 1591 | while (i < 100) : (i += 1) { |
| 1592 | try map.ensureCapacity(@intCast(u32, map.count() + 1)); | |
| 1592 | try map.ensureUnusedCapacity(1); | |
| 1593 | 1593 | map.putAssumeCapacity(i, i); |
| 1594 | 1594 | // Remove to create tombstones that still count as load in the hashmap. |
| 1595 | 1595 | _ = map.remove(i); |
| ... | ... | @@ -1669,7 +1669,7 @@ test "std.hash_map clone" { |
| 1669 | 1669 | try expectEqual(b.get(3).?, 3); |
| 1670 | 1670 | } |
| 1671 | 1671 | |
| 1672 | test "std.hash_map ensureCapacity with existing elements" { | |
| 1672 | test "std.hash_map ensureTotalCapacity with existing elements" { | |
| 1673 | 1673 | var map = AutoHashMap(u32, u32).init(std.testing.allocator); |
| 1674 | 1674 | defer map.deinit(); |
| 1675 | 1675 | |
| ... | ... | @@ -1677,16 +1677,16 @@ test "std.hash_map ensureCapacity with existing elements" { |
| 1677 | 1677 | try expectEqual(map.count(), 1); |
| 1678 | 1678 | try expectEqual(map.capacity(), @TypeOf(map).Unmanaged.minimal_capacity); |
| 1679 | 1679 | |
| 1680 | try map.ensureCapacity(65); | |
| 1680 | try map.ensureTotalCapacity(65); | |
| 1681 | 1681 | try expectEqual(map.count(), 1); |
| 1682 | 1682 | try expectEqual(map.capacity(), 128); |
| 1683 | 1683 | } |
| 1684 | 1684 | |
| 1685 | test "std.hash_map ensureCapacity satisfies max load factor" { | |
| 1685 | test "std.hash_map ensureTotalCapacity satisfies max load factor" { | |
| 1686 | 1686 | var map = AutoHashMap(u32, u32).init(std.testing.allocator); |
| 1687 | 1687 | defer map.deinit(); |
| 1688 | 1688 | |
| 1689 | try map.ensureCapacity(127); | |
| 1689 | try map.ensureTotalCapacity(127); | |
| 1690 | 1690 | try expectEqual(map.capacity(), 256); |
| 1691 | 1691 | } |
| 1692 | 1692 | |
| ... | ... | @@ -1870,7 +1870,7 @@ test "std.hash_map putAssumeCapacity" { |
| 1870 | 1870 | var map = AutoHashMap(u32, u32).init(std.testing.allocator); |
| 1871 | 1871 | defer map.deinit(); |
| 1872 | 1872 | |
| 1873 | try map.ensureCapacity(20); | |
| 1873 | try map.ensureTotalCapacity(20); | |
| 1874 | 1874 | var i: u32 = 0; |
| 1875 | 1875 | while (i < 20) : (i += 1) { |
| 1876 | 1876 | map.putAssumeCapacityNoClobber(i, i); |
lib/std/heap/general_purpose_allocator.zig+1-4| ... | ... | @@ -746,10 +746,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 746 | 746 | |
| 747 | 747 | const new_aligned_size = math.max(len, ptr_align); |
| 748 | 748 | if (new_aligned_size > largest_bucket_object_size) { |
| 749 | try self.large_allocations.ensureCapacity( | |
| 750 | self.backing_allocator, | |
| 751 | self.large_allocations.count() + 1, | |
| 752 | ); | |
| 749 | try self.large_allocations.ensureUnusedCapacity(self.backing_allocator, 1); | |
| 753 | 750 | |
| 754 | 751 | const slice = try self.backing_allocator.allocFn(self.backing_allocator, len, ptr_align, len_align, ret_addr); |
| 755 | 752 |
lib/std/io/reader.zig+2-2| ... | ... | @@ -61,7 +61,7 @@ pub fn Reader( |
| 61 | 61 | array_list: *std.ArrayListAligned(u8, alignment), |
| 62 | 62 | max_append_size: usize, |
| 63 | 63 | ) !void { |
| 64 | try array_list.ensureCapacity(math.min(max_append_size, 4096)); | |
| 64 | try array_list.ensureTotalCapacity(math.min(max_append_size, 4096)); | |
| 65 | 65 | const original_len = array_list.items.len; |
| 66 | 66 | var start_index: usize = original_len; |
| 67 | 67 | while (true) { |
| ... | ... | @@ -81,7 +81,7 @@ pub fn Reader( |
| 81 | 81 | } |
| 82 | 82 | |
| 83 | 83 | // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. |
| 84 | try array_list.ensureCapacity(start_index + 1); | |
| 84 | try array_list.ensureTotalCapacity(start_index + 1); | |
| 85 | 85 | } |
| 86 | 86 | } |
| 87 | 87 |
lib/std/json.zig+1-1| ... | ... | @@ -1838,7 +1838,7 @@ fn parseInternal( |
| 1838 | 1838 | else => {}, |
| 1839 | 1839 | } |
| 1840 | 1840 | |
| 1841 | try arraylist.ensureCapacity(arraylist.items.len + 1); | |
| 1841 | try arraylist.ensureUnusedCapacity(1); | |
| 1842 | 1842 | const v = try parseInternal(ptrInfo.child, tok, tokens, options); |
| 1843 | 1843 | arraylist.appendAssumeCapacity(v); |
| 1844 | 1844 | } |
lib/std/leb128.zig+12-2| ... | ... | @@ -76,6 +76,14 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T { |
| 76 | 76 | const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift)); |
| 77 | 77 | const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift; |
| 78 | 78 | if (remaining_bits != -1) return error.Overflow; |
| 79 | } else { | |
| 80 | // If we don't overflow and this is the last byte and the number being decoded | |
| 81 | // is negative, check that the remaining bits are 1 | |
| 82 | if ((byte & 0x80 == 0) and (@bitCast(S, temp) < 0)) { | |
| 83 | const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift)); | |
| 84 | const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift; | |
| 85 | if (remaining_bits != -1) return error.Overflow; | |
| 86 | } | |
| 79 | 87 | } |
| 80 | 88 | |
| 81 | 89 | value |= temp; |
| ... | ... | @@ -215,6 +223,8 @@ test "deserialize signed LEB128" { |
| 215 | 223 | try testing.expectError(error.Overflow, test_read_ileb128(i32, "\x80\x80\x80\x80\x40")); |
| 216 | 224 | try testing.expectError(error.Overflow, test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40")); |
| 217 | 225 | try testing.expectError(error.Overflow, test_read_ileb128(i8, "\xff\x7e")); |
| 226 | try testing.expectError(error.Overflow, test_read_ileb128(i32, "\x80\x80\x80\x80\x08")); | |
| 227 | try testing.expectError(error.Overflow, test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")); | |
| 218 | 228 | |
| 219 | 229 | // Decode SLEB128 |
| 220 | 230 | try testing.expect((try test_read_ileb128(i64, "\x00")) == 0); |
| ... | ... | @@ -233,8 +243,8 @@ test "deserialize signed LEB128" { |
| 233 | 243 | try testing.expect((try test_read_ileb128(i8, "\xff\x7f")) == -1); |
| 234 | 244 | try testing.expect((try test_read_ileb128(i16, "\xff\xff\x7f")) == -1); |
| 235 | 245 | try testing.expect((try test_read_ileb128(i32, "\xff\xff\xff\xff\x7f")) == -1); |
| 236 | try testing.expect((try test_read_ileb128(i32, "\x80\x80\x80\x80\x08")) == -0x80000000); | |
| 237 | try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")) == @bitCast(i64, @intCast(u64, 0x8000000000000000))); | |
| 246 | try testing.expect((try test_read_ileb128(i32, "\x80\x80\x80\x80\x78")) == -0x80000000); | |
| 247 | try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7f")) == @bitCast(i64, @intCast(u64, 0x8000000000000000))); | |
| 238 | 248 | try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x40")) == -0x4000000000000000); |
| 239 | 249 | try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7f")) == -0x8000000000000000); |
| 240 | 250 |
lib/std/math/big/int.zig+330-36| ... | ... | @@ -552,47 +552,78 @@ pub const Mutable = struct { |
| 552 | 552 | r.positive = a.positive; |
| 553 | 553 | } |
| 554 | 554 | |
| 555 | /// r = a | b | |
| 555 | /// r = a | b under 2s complement semantics. | |
| 556 | 556 | /// r may alias with a or b. |
| 557 | 557 | /// |
| 558 | 558 | /// a and b are zero-extended to the longer of a or b. |
| 559 | 559 | /// |
| 560 | 560 | /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`. |
| 561 | 561 | pub fn bitOr(r: *Mutable, a: Const, b: Const) void { |
| 562 | if (a.limbs.len > b.limbs.len) { | |
| 563 | llor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | |
| 564 | r.len = a.limbs.len; | |
| 562 | // Trivial cases, llsignedor does not support zero. | |
| 563 | if (a.eqZero()) { | |
| 564 | r.copy(b); | |
| 565 | return; | |
| 566 | } else if (b.eqZero()) { | |
| 567 | r.copy(a); | |
| 568 | return; | |
| 569 | } | |
| 570 | ||
| 571 | if (a.limbs.len >= b.limbs.len) { | |
| 572 | r.positive = llsignedor(r.limbs, a.limbs, a.positive, b.limbs, b.positive); | |
| 573 | r.normalize(if (b.positive) a.limbs.len else b.limbs.len); | |
| 565 | 574 | } else { |
| 566 | llor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | |
| 567 | r.len = b.limbs.len; | |
| 575 | r.positive = llsignedor(r.limbs, b.limbs, b.positive, a.limbs, a.positive); | |
| 576 | r.normalize(if (a.positive) b.limbs.len else a.limbs.len); | |
| 568 | 577 | } |
| 569 | 578 | } |
| 570 | 579 | |
| 571 | /// r = a & b | |
| 580 | /// r = a & b under 2s complement semantics. | |
| 572 | 581 | /// r may alias with a or b. |
| 573 | 582 | /// |
| 574 | /// Asserts that r has enough limbs to store the result. Upper bound is `math.min(a.limbs.len, b.limbs.len)`. | |
| 583 | /// Asserts that r has enough limbs to store the result. | |
| 584 | /// If a or b is positive, the upper bound is `math.min(a.limbs.len, b.limbs.len)`. | |
| 585 | /// If a and b are negative, the upper bound is `math.max(a.limbs.len, b.limbs.len) + 1`. | |
| 575 | 586 | pub fn bitAnd(r: *Mutable, a: Const, b: Const) void { |
| 576 | if (a.limbs.len > b.limbs.len) { | |
| 577 | lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | |
| 578 | r.normalize(b.limbs.len); | |
| 587 | // Trivial cases, llsignedand does not support zero. | |
| 588 | if (a.eqZero()) { | |
| 589 | r.copy(a); | |
| 590 | return; | |
| 591 | } else if (b.eqZero()) { | |
| 592 | r.copy(b); | |
| 593 | return; | |
| 594 | } | |
| 595 | ||
| 596 | if (a.limbs.len >= b.limbs.len) { | |
| 597 | r.positive = llsignedand(r.limbs, a.limbs, a.positive, b.limbs, b.positive); | |
| 598 | r.normalize(if (a.positive or b.positive) b.limbs.len else a.limbs.len + 1); | |
| 579 | 599 | } else { |
| 580 | lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | |
| 581 | r.normalize(a.limbs.len); | |
| 600 | r.positive = llsignedand(r.limbs, b.limbs, b.positive, a.limbs, a.positive); | |
| 601 | r.normalize(if (a.positive or b.positive) a.limbs.len else b.limbs.len + 1); | |
| 582 | 602 | } |
| 583 | 603 | } |
| 584 | 604 | |
| 585 | /// r = a ^ b | |
| 605 | /// r = a ^ b under 2s complement semantics. | |
| 586 | 606 | /// r may alias with a or b. |
| 587 | 607 | /// |
| 588 | /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`. | |
| 608 | /// Asserts that r has enough limbs to store the result. If a and b share the same signedness, the | |
| 609 | /// upper bound is `math.max(a.limbs.len, b.limbs.len)`. Otherwise, if either a or b is negative | |
| 610 | /// but not both, the upper bound is `math.max(a.limbs.len, b.limbs.len) + 1`. | |
| 589 | 611 | pub fn bitXor(r: *Mutable, a: Const, b: Const) void { |
| 612 | // Trivial cases, because llsignedxor does not support negative zero. | |
| 613 | if (a.eqZero()) { | |
| 614 | r.copy(b); | |
| 615 | return; | |
| 616 | } else if (b.eqZero()) { | |
| 617 | r.copy(a); | |
| 618 | return; | |
| 619 | } | |
| 620 | ||
| 590 | 621 | if (a.limbs.len > b.limbs.len) { |
| 591 | llxor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | |
| 592 | r.normalize(a.limbs.len); | |
| 622 | r.positive = llsignedxor(r.limbs, a.limbs, a.positive, b.limbs, b.positive); | |
| 623 | r.normalize(a.limbs.len + @boolToInt(a.positive != b.positive)); | |
| 593 | 624 | } else { |
| 594 | llxor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | |
| 595 | r.normalize(b.limbs.len); | |
| 625 | r.positive = llsignedxor(r.limbs, b.limbs, b.positive, a.limbs, a.positive); | |
| 626 | r.normalize(b.limbs.len + @boolToInt(a.positive != b.positive)); | |
| 596 | 627 | } |
| 597 | 628 | } |
| 598 | 629 | |
| ... | ... | @@ -1834,7 +1865,11 @@ pub const Managed = struct { |
| 1834 | 1865 | |
| 1835 | 1866 | /// r = a & b |
| 1836 | 1867 | pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void { |
| 1837 | try r.ensureCapacity(math.min(a.len(), b.len())); | |
| 1868 | const cap = if (a.isPositive() or b.isPositive()) | |
| 1869 | math.min(a.len(), b.len()) | |
| 1870 | else | |
| 1871 | math.max(a.len(), b.len()) + 1; | |
| 1872 | try r.ensureCapacity(cap); | |
| 1838 | 1873 | var m = r.toMutable(); |
| 1839 | 1874 | m.bitAnd(a.toConst(), b.toConst()); |
| 1840 | 1875 | r.setMetadata(m.positive, m.len); |
| ... | ... | @@ -1842,7 +1877,9 @@ pub const Managed = struct { |
| 1842 | 1877 | |
| 1843 | 1878 | /// r = a ^ b |
| 1844 | 1879 | pub fn bitXor(r: *Managed, a: Managed, b: Managed) !void { |
| 1845 | try r.ensureCapacity(math.max(a.len(), b.len())); | |
| 1880 | var cap = math.max(a.len(), b.len()) + @boolToInt(a.isPositive() != b.isPositive()); | |
| 1881 | try r.ensureCapacity(cap); | |
| 1882 | ||
| 1846 | 1883 | var m = r.toMutable(); |
| 1847 | 1884 | m.bitXor(a.toConst(), b.toConst()); |
| 1848 | 1885 | r.setMetadata(m.positive, m.len); |
| ... | ... | @@ -2221,42 +2258,299 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { |
| 2221 | 2258 | } |
| 2222 | 2259 | } |
| 2223 | 2260 | |
| 2224 | fn llor(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 2261 | // r = a | b with 2s complement semantics. | |
| 2262 | // r may alias. | |
| 2263 | // a and b must not be 0. | |
| 2264 | // Returns `true` when the result is positive. | |
| 2265 | // When b is positive, r requires at least `a.len` limbs of storage. | |
| 2266 | // When b is negative, r requires at least `b.len` limbs of storage. | |
| 2267 | fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { | |
| 2225 | 2268 | @setRuntimeSafety(debug_safety); |
| 2226 | 2269 | assert(r.len >= a.len); |
| 2227 | 2270 | assert(a.len >= b.len); |
| 2228 | 2271 | |
| 2229 | var i: usize = 0; | |
| 2230 | while (i < b.len) : (i += 1) { | |
| 2231 | r[i] = a[i] | b[i]; | |
| 2232 | } | |
| 2233 | while (i < a.len) : (i += 1) { | |
| 2234 | r[i] = a[i]; | |
| 2272 | if (a_positive and b_positive) { | |
| 2273 | // Trivial case, result is positive. | |
| 2274 | var i: usize = 0; | |
| 2275 | while (i < b.len) : (i += 1) { | |
| 2276 | r[i] = a[i] | b[i]; | |
| 2277 | } | |
| 2278 | while (i < a.len) : (i += 1) { | |
| 2279 | r[i] = a[i]; | |
| 2280 | } | |
| 2281 | ||
| 2282 | return true; | |
| 2283 | } else if (!a_positive and b_positive) { | |
| 2284 | // Result is negative. | |
| 2285 | // r = (--a) | b | |
| 2286 | // = ~(-a - 1) | b | |
| 2287 | // = ~(-a - 1) | ~~b | |
| 2288 | // = ~((-a - 1) & ~b) | |
| 2289 | // = -(((-a - 1) & ~b) + 1) | |
| 2290 | ||
| 2291 | var i: usize = 0; | |
| 2292 | var a_borrow: u1 = 1; | |
| 2293 | var r_carry: u1 = 1; | |
| 2294 | ||
| 2295 | while (i < b.len) : (i += 1) { | |
| 2296 | var a_limb: Limb = undefined; | |
| 2297 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); | |
| 2298 | ||
| 2299 | r[i] = a_limb & ~b[i]; | |
| 2300 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); | |
| 2301 | } | |
| 2302 | ||
| 2303 | // In order for r_carry to be nonzero at this point, ~b[i] would need to be | |
| 2304 | // all ones, which would require b[i] to be zero. This cannot be when | |
| 2305 | // b is normalized, so there cannot be a carry here. | |
| 2306 | // Also, x & ~b can only clear bits, so (x & ~b) <= x, meaning (-a - 1) + 1 never overflows. | |
| 2307 | assert(r_carry == 0); | |
| 2308 | ||
| 2309 | // With b = 0, we get (-a - 1) & ~0 = -a - 1. | |
| 2310 | // Note, if a_borrow is zero we do not need to compute anything for | |
| 2311 | // the higher limbs so we can early return here. | |
| 2312 | while (i < a.len and a_borrow == 1) : (i += 1) { | |
| 2313 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i])); | |
| 2314 | } | |
| 2315 | ||
| 2316 | assert(a_borrow == 0); // a was 0. | |
| 2317 | ||
| 2318 | return false; | |
| 2319 | } else if (a_positive and !b_positive) { | |
| 2320 | // Result is negative. | |
| 2321 | // r = a | (--b) | |
| 2322 | // = a | ~(-b - 1) | |
| 2323 | // = ~~a | ~(-b - 1) | |
| 2324 | // = ~(~a & (-b - 1)) | |
| 2325 | // = -((~a & (-b - 1)) + 1) | |
| 2326 | ||
| 2327 | var i: usize = 0; | |
| 2328 | var b_borrow: u1 = 1; | |
| 2329 | var r_carry: u1 = 1; | |
| 2330 | ||
| 2331 | while (i < b.len) : (i += 1) { | |
| 2332 | var b_limb: Limb = undefined; | |
| 2333 | b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); | |
| 2334 | ||
| 2335 | r[i] = ~a[i] & b_limb; | |
| 2336 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); | |
| 2337 | } | |
| 2338 | ||
| 2339 | // b is at least 1, so this should never underflow. | |
| 2340 | assert(b_borrow == 0); // b was 0 | |
| 2341 | ||
| 2342 | // x & ~a can only clear bits, so (x & ~a) <= x, meaning (-b - 1) + 1 never overflows. | |
| 2343 | assert(r_carry == 0); | |
| 2344 | ||
| 2345 | // With b = 0 and b_borrow = 0, we get ~a & (-0 - 0) = ~a & 0 = 0. | |
| 2346 | // Omit setting the upper bytes, just deal with those when calling llsignedor. | |
| 2347 | ||
| 2348 | return false; | |
| 2349 | } else { | |
| 2350 | // Result is negative. | |
| 2351 | // r = (--a) | (--b) | |
| 2352 | // = ~(-a - 1) | ~(-b - 1) | |
| 2353 | // = ~((-a - 1) & (-b - 1)) | |
| 2354 | // = -(~(~((-a - 1) & (-b - 1))) + 1) | |
| 2355 | // = -((-a - 1) & (-b - 1) + 1) | |
| 2356 | ||
| 2357 | var i: usize = 0; | |
| 2358 | var a_borrow: u1 = 1; | |
| 2359 | var b_borrow: u1 = 1; | |
| 2360 | var r_carry: u1 = 1; | |
| 2361 | ||
| 2362 | while (i < b.len) : (i += 1) { | |
| 2363 | var a_limb: Limb = undefined; | |
| 2364 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); | |
| 2365 | ||
| 2366 | var b_limb: Limb = undefined; | |
| 2367 | b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); | |
| 2368 | ||
| 2369 | r[i] = a_limb & b_limb; | |
| 2370 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); | |
| 2371 | } | |
| 2372 | ||
| 2373 | // b is at least 1, so this should never underflow. | |
| 2374 | assert(b_borrow == 0); // b was 0 | |
| 2375 | ||
| 2376 | // Can never overflow because in order for b_limb to be maxInt(Limb), | |
| 2377 | // b_borrow would need to equal 1. | |
| 2378 | ||
| 2379 | // x & y can only clear bits, meaning x & y <= x and x & y <= y. This implies that | |
| 2380 | // for x = a - 1 and y = b - 1, the +1 term would never cause an overflow. | |
| 2381 | assert(r_carry == 0); | |
| 2382 | ||
| 2383 | // With b = 0 and b_borrow = 0 we get (-a - 1) & (-0 - 0) = (-a - 1) & 0 = 0. | |
| 2384 | // Omit setting the upper bytes, just deal with those when calling llsignedor. | |
| 2385 | return false; | |
| 2235 | 2386 | } |
| 2236 | 2387 | } |
| 2237 | 2388 | |
| 2238 | fn lland(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 2389 | // r = a & b with 2s complement semantics. | |
| 2390 | // r may alias. | |
| 2391 | // a and b must not be 0. | |
| 2392 | // Returns `true` when the result is positive. | |
| 2393 | // When either or both of a and b are positive, r requires at least `b.len` limbs of storage. | |
| 2394 | // When both a and b are negative, r requires at least `a.limbs.len + 1` limbs of storage. | |
| 2395 | fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { | |
| 2239 | 2396 | @setRuntimeSafety(debug_safety); |
| 2240 | assert(r.len >= b.len); | |
| 2397 | assert(a.len != 0 and b.len != 0); | |
| 2398 | assert(r.len >= a.len); | |
| 2241 | 2399 | assert(a.len >= b.len); |
| 2242 | 2400 | |
| 2243 | var i: usize = 0; | |
| 2244 | while (i < b.len) : (i += 1) { | |
| 2245 | r[i] = a[i] & b[i]; | |
| 2401 | if (a_positive and b_positive) { | |
| 2402 | // Trivial case, result is positive. | |
| 2403 | var i: usize = 0; | |
| 2404 | while (i < b.len) : (i += 1) { | |
| 2405 | r[i] = a[i] & b[i]; | |
| 2406 | } | |
| 2407 | ||
| 2408 | // With b = 0 we have a & 0 = 0, so the upper bytes are zero. | |
| 2409 | // Omit setting them here and simply discard them whenever | |
| 2410 | // llsignedand is called. | |
| 2411 | ||
| 2412 | return true; | |
| 2413 | } else if (!a_positive and b_positive) { | |
| 2414 | // Result is positive. | |
| 2415 | // r = (--a) & b | |
| 2416 | // = ~(-a - 1) & b | |
| 2417 | ||
| 2418 | var i: usize = 0; | |
| 2419 | var a_borrow: u1 = 1; | |
| 2420 | ||
| 2421 | while (i < b.len) : (i += 1) { | |
| 2422 | var a_limb: Limb = undefined; | |
| 2423 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); | |
| 2424 | r[i] = ~a_limb & b[i]; | |
| 2425 | } | |
| 2426 | ||
| 2427 | // With b = 0 we have ~(a - 1) & 0 = 0, so the upper bytes are zero. | |
| 2428 | // Omit setting them here and simply discard them whenever | |
| 2429 | // llsignedand is called. | |
| 2430 | ||
| 2431 | return true; | |
| 2432 | } else if (a_positive and !b_positive) { | |
| 2433 | // Result is positive. | |
| 2434 | // r = a & (--b) | |
| 2435 | // = a & ~(-b - 1) | |
| 2436 | ||
| 2437 | var i: usize = 0; | |
| 2438 | var b_borrow: u1 = 1; | |
| 2439 | ||
| 2440 | while (i < b.len) : (i += 1) { | |
| 2441 | var a_limb: Limb = undefined; | |
| 2442 | b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &a_limb)); | |
| 2443 | r[i] = a[i] & ~a_limb; | |
| 2444 | } | |
| 2445 | ||
| 2446 | assert(b_borrow == 0); // b was 0 | |
| 2447 | ||
| 2448 | // With b = 0 and b_borrow = 0 we have a & ~(-0 - 0) = a & 0 = 0, so | |
| 2449 | // the upper bytes are zero. Omit setting them here and simply discard | |
| 2450 | // them whenever llsignedand is called. | |
| 2451 | ||
| 2452 | return true; | |
| 2453 | } else { | |
| 2454 | // Result is negative. | |
| 2455 | // r = (--a) & (--b) | |
| 2456 | // = ~(-a - 1) & ~(-b - 1) | |
| 2457 | // = ~((-a - 1) | (-b - 1)) | |
| 2458 | // = -(((-a - 1) | (-b - 1)) + 1) | |
| 2459 | ||
| 2460 | var i: usize = 0; | |
| 2461 | var a_borrow: u1 = 1; | |
| 2462 | var b_borrow: u1 = 1; | |
| 2463 | var r_carry: u1 = 1; | |
| 2464 | ||
| 2465 | while (i < b.len) : (i += 1) { | |
| 2466 | var a_limb: Limb = undefined; | |
| 2467 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); | |
| 2468 | ||
| 2469 | var b_limb: Limb = undefined; | |
| 2470 | b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); | |
| 2471 | ||
| 2472 | r[i] = a_limb | b_limb; | |
| 2473 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); | |
| 2474 | } | |
| 2475 | ||
| 2476 | // b is at least 1, so this should never underflow. | |
| 2477 | assert(b_borrow == 0); // b was 0 | |
| 2478 | ||
| 2479 | // With b = 0 and b_borrow = 0 we get (-a - 1) | (-0 - 0) = (-a - 1) | 0 = -a - 1. | |
| 2480 | while (i < a.len) : (i += 1) { | |
| 2481 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i])); | |
| 2482 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); | |
| 2483 | } | |
| 2484 | ||
| 2485 | assert(a_borrow == 0); // a was 0. | |
| 2486 | ||
| 2487 | // The final addition can overflow here, so we need to keep that in mind. | |
| 2488 | r[i] = r_carry; | |
| 2489 | ||
| 2490 | return false; | |
| 2246 | 2491 | } |
| 2247 | 2492 | } |
| 2248 | 2493 | |
| 2249 | fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 2494 | // r = a ^ b with 2s complement semantics. | |
| 2495 | // r may alias. | |
| 2496 | // a and b must not be -0. | |
| 2497 | // Returns `true` when the result is positive. | |
| 2498 | // If the sign of a and b is equal, then r requires at least `max(a.len, b.len)` limbs are required. | |
| 2499 | // Otherwise, r requires at least `max(a.len, b.len) + 1` limbs. | |
| 2500 | fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool { | |
| 2501 | @setRuntimeSafety(debug_safety); | |
| 2502 | assert(a.len != 0 and b.len != 0); | |
| 2250 | 2503 | assert(r.len >= a.len); |
| 2251 | 2504 | assert(a.len >= b.len); |
| 2252 | 2505 | |
| 2506 | // If a and b are positive, the result is positive and r = a ^ b. | |
| 2507 | // If a negative, b positive, result is negative and we have | |
| 2508 | // r = --(--a ^ b) | |
| 2509 | // = --(~(-a - 1) ^ b) | |
| 2510 | // = -(~(~(-a - 1) ^ b) + 1) | |
| 2511 | // = -(((-a - 1) ^ b) + 1) | |
| 2512 | // Same if a is positive and b is negative, sides switched. | |
| 2513 | // If both a and b are negative, the result is positive and we have | |
| 2514 | // r = (--a) ^ (--b) | |
| 2515 | // = ~(-a - 1) ^ ~(-b - 1) | |
| 2516 | // = (-a - 1) ^ (-b - 1) | |
| 2517 | // These operations can be made more generic as follows: | |
| 2518 | // - If a is negative, subtract 1 from |a| before the xor. | |
| 2519 | // - If b is negative, subtract 1 from |b| before the xor. | |
| 2520 | // - if the result is supposed to be negative, add 1. | |
| 2521 | ||
| 2253 | 2522 | var i: usize = 0; |
| 2523 | var a_borrow = @boolToInt(!a_positive); | |
| 2524 | var b_borrow = @boolToInt(!b_positive); | |
| 2525 | var r_carry = @boolToInt(a_positive != b_positive); | |
| 2526 | ||
| 2254 | 2527 | while (i < b.len) : (i += 1) { |
| 2255 | r[i] = a[i] ^ b[i]; | |
| 2528 | var a_limb: Limb = undefined; | |
| 2529 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); | |
| 2530 | ||
| 2531 | var b_limb: Limb = undefined; | |
| 2532 | b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); | |
| 2533 | ||
| 2534 | r[i] = a_limb ^ b_limb; | |
| 2535 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); | |
| 2256 | 2536 | } |
| 2537 | ||
| 2257 | 2538 | while (i < a.len) : (i += 1) { |
| 2258 | r[i] = a[i]; | |
| 2539 | a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i])); | |
| 2540 | r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); | |
| 2259 | 2541 | } |
| 2542 | ||
| 2543 | // If both inputs don't share the same sign, an extra limb is required. | |
| 2544 | if (a_positive != b_positive) { | |
| 2545 | r[i] = r_carry; | |
| 2546 | } else { | |
| 2547 | assert(r_carry == 0); | |
| 2548 | } | |
| 2549 | ||
| 2550 | assert(a_borrow == 0); | |
| 2551 | assert(b_borrow == 0); | |
| 2552 | ||
| 2553 | return a_positive == b_positive; | |
| 2260 | 2554 | } |
| 2261 | 2555 | |
| 2262 | 2556 | /// r MUST NOT alias x. |
lib/std/math/big/int_test.zig+210| ... | ... | @@ -5,6 +5,7 @@ const Managed = std.math.big.int.Managed; |
| 5 | 5 | const Mutable = std.math.big.int.Mutable; |
| 6 | 6 | const Limb = std.math.big.Limb; |
| 7 | 7 | const DoubleLimb = std.math.big.DoubleLimb; |
| 8 | const SignedDoubleLimb = std.math.big.SignedDoubleLimb; | |
| 8 | 9 | const maxInt = std.math.maxInt; |
| 9 | 10 | const minInt = std.math.minInt; |
| 10 | 11 | |
| ... | ... | @@ -1364,6 +1365,83 @@ test "big.int bitwise and multi-limb" { |
| 1364 | 1365 | try testing.expect((try a.to(u128)) == 0); |
| 1365 | 1366 | } |
| 1366 | 1367 | |
| 1368 | test "big.int bitwise and negative-positive simple" { | |
| 1369 | var a = try Managed.initSet(testing.allocator, -0xffffffff11111111); | |
| 1370 | defer a.deinit(); | |
| 1371 | var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222); | |
| 1372 | defer b.deinit(); | |
| 1373 | ||
| 1374 | try a.bitAnd(a, b); | |
| 1375 | ||
| 1376 | try testing.expect((try a.to(u64)) == 0x22222222); | |
| 1377 | } | |
| 1378 | ||
| 1379 | test "big.int bitwise and negative-positive multi-limb" { | |
| 1380 | var a = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1); | |
| 1381 | defer a.deinit(); | |
| 1382 | var b = try Managed.initSet(testing.allocator, maxInt(Limb)); | |
| 1383 | defer b.deinit(); | |
| 1384 | ||
| 1385 | try a.bitAnd(a, b); | |
| 1386 | ||
| 1387 | try testing.expect(a.eqZero()); | |
| 1388 | } | |
| 1389 | ||
| 1390 | test "big.int bitwise and positive-negative simple" { | |
| 1391 | var a = try Managed.initSet(testing.allocator, 0xffffffff11111111); | |
| 1392 | defer a.deinit(); | |
| 1393 | var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222); | |
| 1394 | defer b.deinit(); | |
| 1395 | ||
| 1396 | try a.bitAnd(a, b); | |
| 1397 | ||
| 1398 | try testing.expect((try a.to(u64)) == 0x1111111111111110); | |
| 1399 | } | |
| 1400 | ||
| 1401 | test "big.int bitwise and positive-negative multi-limb" { | |
| 1402 | var a = try Managed.initSet(testing.allocator, maxInt(Limb)); | |
| 1403 | defer a.deinit(); | |
| 1404 | var b = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1); | |
| 1405 | defer b.deinit(); | |
| 1406 | ||
| 1407 | try a.bitAnd(a, b); | |
| 1408 | ||
| 1409 | try testing.expect(a.eqZero()); | |
| 1410 | } | |
| 1411 | ||
| 1412 | test "big.int bitwise and negative-negative simple" { | |
| 1413 | var a = try Managed.initSet(testing.allocator, -0xffffffff11111111); | |
| 1414 | defer a.deinit(); | |
| 1415 | var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222); | |
| 1416 | defer b.deinit(); | |
| 1417 | ||
| 1418 | try a.bitAnd(a, b); | |
| 1419 | ||
| 1420 | try testing.expect((try a.to(i128)) == -0xffffffff33333332); | |
| 1421 | } | |
| 1422 | ||
| 1423 | test "big.int bitwise and negative-negative multi-limb" { | |
| 1424 | var a = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1); | |
| 1425 | defer a.deinit(); | |
| 1426 | var b = try Managed.initSet(testing.allocator, -maxInt(Limb) - 2); | |
| 1427 | defer b.deinit(); | |
| 1428 | ||
| 1429 | try a.bitAnd(a, b); | |
| 1430 | ||
| 1431 | try testing.expect((try a.to(i128)) == -maxInt(Limb) * 2 - 2); | |
| 1432 | } | |
| 1433 | ||
| 1434 | test "big.int bitwise and negative overflow" { | |
| 1435 | var a = try Managed.initSet(testing.allocator, -maxInt(Limb)); | |
| 1436 | defer a.deinit(); | |
| 1437 | var b = try Managed.initSet(testing.allocator, -2); | |
| 1438 | defer b.deinit(); | |
| 1439 | ||
| 1440 | try a.bitAnd(a, b); | |
| 1441 | ||
| 1442 | try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb) - 1); | |
| 1443 | } | |
| 1444 | ||
| 1367 | 1445 | test "big.int bitwise xor simple" { |
| 1368 | 1446 | var a = try Managed.initSet(testing.allocator, 0xffffffff11111111); |
| 1369 | 1447 | defer a.deinit(); |
| ... | ... | @@ -1386,6 +1464,72 @@ test "big.int bitwise xor multi-limb" { |
| 1386 | 1464 | try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb)); |
| 1387 | 1465 | } |
| 1388 | 1466 | |
| 1467 | test "big.int bitwise xor single negative simple" { | |
| 1468 | var a = try Managed.initSet(testing.allocator, 0x6b03e381328a3154); | |
| 1469 | defer a.deinit(); | |
| 1470 | var b = try Managed.initSet(testing.allocator, -0x45fd3acef9191fad); | |
| 1471 | defer b.deinit(); | |
| 1472 | ||
| 1473 | try a.bitXor(a, b); | |
| 1474 | ||
| 1475 | try testing.expect((try a.to(i64)) == -0x2efed94fcb932ef9); | |
| 1476 | } | |
| 1477 | ||
| 1478 | test "big.int bitwise xor single negative zero" { | |
| 1479 | var a = try Managed.initSet(testing.allocator, 0); | |
| 1480 | defer a.deinit(); | |
| 1481 | var b = try Managed.initSet(testing.allocator, -0); | |
| 1482 | defer b.deinit(); | |
| 1483 | ||
| 1484 | try a.bitXor(a, b); | |
| 1485 | ||
| 1486 | try testing.expect(a.eqZero()); | |
| 1487 | } | |
| 1488 | ||
| 1489 | test "big.int bitwise xor single negative multi-limb" { | |
| 1490 | var a = try Managed.initSet(testing.allocator, -0x9849c6e7a10d66d0e4260d4846254c32); | |
| 1491 | defer a.deinit(); | |
| 1492 | var b = try Managed.initSet(testing.allocator, 0xf2194e7d1c855272a997fcde16f6d5a8); | |
| 1493 | defer b.deinit(); | |
| 1494 | ||
| 1495 | try a.bitXor(a, b); | |
| 1496 | ||
| 1497 | try testing.expect((try a.to(i128)) == -0x6a50889abd8834a24db1f19650d3999a); | |
| 1498 | } | |
| 1499 | ||
| 1500 | test "big.int bitwise xor single negative overflow" { | |
| 1501 | var a = try Managed.initSet(testing.allocator, maxInt(Limb)); | |
| 1502 | defer a.deinit(); | |
| 1503 | var b = try Managed.initSet(testing.allocator, -1); | |
| 1504 | defer b.deinit(); | |
| 1505 | ||
| 1506 | try a.bitXor(a, b); | |
| 1507 | ||
| 1508 | try testing.expect((try a.to(SignedDoubleLimb)) == -(maxInt(Limb) + 1)); | |
| 1509 | } | |
| 1510 | ||
| 1511 | test "big.int bitwise xor double negative simple" { | |
| 1512 | var a = try Managed.initSet(testing.allocator, -0x8e48bd5f755ef1f3); | |
| 1513 | defer a.deinit(); | |
| 1514 | var b = try Managed.initSet(testing.allocator, -0x4dd4fa576f3046ac); | |
| 1515 | defer b.deinit(); | |
| 1516 | ||
| 1517 | try a.bitXor(a, b); | |
| 1518 | ||
| 1519 | try testing.expect((try a.to(u64)) == 0xc39c47081a6eb759); | |
| 1520 | } | |
| 1521 | ||
| 1522 | test "big.int bitwise xor double negative multi-limb" { | |
| 1523 | var a = try Managed.initSet(testing.allocator, -0x684e5da8f500ec8ca7204c33ccc51c9c); | |
| 1524 | defer a.deinit(); | |
| 1525 | var b = try Managed.initSet(testing.allocator, -0xcb07736a7b62289c78d967c3985eebeb); | |
| 1526 | defer b.deinit(); | |
| 1527 | ||
| 1528 | try a.bitXor(a, b); | |
| 1529 | ||
| 1530 | try testing.expect((try a.to(u128)) == 0xa3492ec28e62c410dff92bf0549bf771); | |
| 1531 | } | |
| 1532 | ||
| 1389 | 1533 | test "big.int bitwise or simple" { |
| 1390 | 1534 | var a = try Managed.initSet(testing.allocator, 0xffffffff11111111); |
| 1391 | 1535 | defer a.deinit(); |
| ... | ... | @@ -1409,6 +1553,72 @@ test "big.int bitwise or multi-limb" { |
| 1409 | 1553 | try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb)); |
| 1410 | 1554 | } |
| 1411 | 1555 | |
| 1556 | test "big.int bitwise or negative-positive simple" { | |
| 1557 | var a = try Managed.initSet(testing.allocator, -0xffffffff11111111); | |
| 1558 | defer a.deinit(); | |
| 1559 | var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222); | |
| 1560 | defer b.deinit(); | |
| 1561 | ||
| 1562 | try a.bitOr(a, b); | |
| 1563 | ||
| 1564 | try testing.expect((try a.to(i64)) == -0x1111111111111111); | |
| 1565 | } | |
| 1566 | ||
| 1567 | test "big.int bitwise or negative-positive multi-limb" { | |
| 1568 | var a = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1); | |
| 1569 | defer a.deinit(); | |
| 1570 | var b = try Managed.initSet(testing.allocator, 1); | |
| 1571 | defer b.deinit(); | |
| 1572 | ||
| 1573 | try a.bitOr(a, b); | |
| 1574 | ||
| 1575 | try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb)); | |
| 1576 | } | |
| 1577 | ||
| 1578 | test "big.int bitwise or positive-negative simple" { | |
| 1579 | var a = try Managed.initSet(testing.allocator, 0xffffffff11111111); | |
| 1580 | defer a.deinit(); | |
| 1581 | var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222); | |
| 1582 | defer b.deinit(); | |
| 1583 | ||
| 1584 | try a.bitOr(a, b); | |
| 1585 | ||
| 1586 | try testing.expect((try a.to(i64)) == -0x22222221); | |
| 1587 | } | |
| 1588 | ||
| 1589 | test "big.int bitwise or positive-negative multi-limb" { | |
| 1590 | var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 1591 | defer a.deinit(); | |
| 1592 | var b = try Managed.initSet(testing.allocator, -1); | |
| 1593 | defer b.deinit(); | |
| 1594 | ||
| 1595 | try a.bitOr(a, b); | |
| 1596 | ||
| 1597 | try testing.expect((try a.to(SignedDoubleLimb)) == -1); | |
| 1598 | } | |
| 1599 | ||
| 1600 | test "big.int bitwise or negative-negative simple" { | |
| 1601 | var a = try Managed.initSet(testing.allocator, -0xffffffff11111111); | |
| 1602 | defer a.deinit(); | |
| 1603 | var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222); | |
| 1604 | defer b.deinit(); | |
| 1605 | ||
| 1606 | try a.bitOr(a, b); | |
| 1607 | ||
| 1608 | try testing.expect((try a.to(i128)) == -0xeeeeeeee00000001); | |
| 1609 | } | |
| 1610 | ||
| 1611 | test "big.int bitwise or negative-negative multi-limb" { | |
| 1612 | var a = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1); | |
| 1613 | defer a.deinit(); | |
| 1614 | var b = try Managed.initSet(testing.allocator, -maxInt(Limb)); | |
| 1615 | defer b.deinit(); | |
| 1616 | ||
| 1617 | try a.bitOr(a, b); | |
| 1618 | ||
| 1619 | try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb)); | |
| 1620 | } | |
| 1621 | ||
| 1412 | 1622 | test "big.int var args" { |
| 1413 | 1623 | var a = try Managed.initSet(testing.allocator, 5); |
| 1414 | 1624 | defer a.deinit(); |
lib/std/mem.zig+42| ... | ... | @@ -147,6 +147,46 @@ test "mem.Allocator basics" { |
| 147 | 147 | try testing.expectError(error.OutOfMemory, failAllocator.allocSentinel(u8, 1, 0)); |
| 148 | 148 | } |
| 149 | 149 | |
| 150 | test "Allocator.resize" { | |
| 151 | const primitiveIntTypes = .{ | |
| 152 | i8, | |
| 153 | u8, | |
| 154 | i16, | |
| 155 | u16, | |
| 156 | i32, | |
| 157 | u32, | |
| 158 | i64, | |
| 159 | u64, | |
| 160 | i128, | |
| 161 | u128, | |
| 162 | isize, | |
| 163 | usize, | |
| 164 | }; | |
| 165 | inline for (primitiveIntTypes) |T| { | |
| 166 | var values = try testing.allocator.alloc(T, 100); | |
| 167 | defer testing.allocator.free(values); | |
| 168 | ||
| 169 | for (values) |*v, i| v.* = @intCast(T, i); | |
| 170 | values = try testing.allocator.resize(values, values.len + 10); | |
| 171 | try testing.expect(values.len == 110); | |
| 172 | } | |
| 173 | ||
| 174 | const primitiveFloatTypes = .{ | |
| 175 | f16, | |
| 176 | f32, | |
| 177 | f64, | |
| 178 | f128, | |
| 179 | }; | |
| 180 | inline for (primitiveFloatTypes) |T| { | |
| 181 | var values = try testing.allocator.alloc(T, 100); | |
| 182 | defer testing.allocator.free(values); | |
| 183 | ||
| 184 | for (values) |*v, i| v.* = @intToFloat(T, i); | |
| 185 | values = try testing.allocator.resize(values, values.len + 10); | |
| 186 | try testing.expect(values.len == 110); | |
| 187 | } | |
| 188 | } | |
| 189 | ||
| 150 | 190 | /// Copy all of source into dest at position 0. |
| 151 | 191 | /// dest.len must be >= source.len. |
| 152 | 192 | /// If the slices overlap, dest.ptr must be <= src.ptr. |
| ... | ... | @@ -2472,6 +2512,7 @@ fn CopyPtrAttrs(comptime source: type, comptime size: std.builtin.TypeInfo.Point |
| 2472 | 2512 | .is_volatile = info.is_volatile, |
| 2473 | 2513 | .is_allowzero = info.is_allowzero, |
| 2474 | 2514 | .alignment = info.alignment, |
| 2515 | .address_space = info.address_space, | |
| 2475 | 2516 | .child = child, |
| 2476 | 2517 | .sentinel = null, |
| 2477 | 2518 | }, |
| ... | ... | @@ -2960,6 +3001,7 @@ fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: u29) typ |
| 2960 | 3001 | .is_volatile = info.is_volatile, |
| 2961 | 3002 | .is_allowzero = info.is_allowzero, |
| 2962 | 3003 | .alignment = new_alignment, |
| 3004 | .address_space = info.address_space, | |
| 2963 | 3005 | .child = info.child, |
| 2964 | 3006 | .sentinel = null, |
| 2965 | 3007 | }, |
lib/std/mem/Allocator.zig+1-1| ... | ... | @@ -313,7 +313,7 @@ pub fn resize(self: *Allocator, old_mem: anytype, new_n: usize) Error!@TypeOf(ol |
| 313 | 313 | const new_byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory; |
| 314 | 314 | const rc = try self.resizeFn(self, old_byte_slice, Slice.alignment, new_byte_count, 0, @returnAddress()); |
| 315 | 315 | assert(rc == new_byte_count); |
| 316 | const new_byte_slice = old_mem.ptr[0..new_byte_count]; | |
| 316 | const new_byte_slice = old_byte_slice.ptr[0..new_byte_count]; | |
| 317 | 317 | return mem.bytesAsSlice(T, new_byte_slice); |
| 318 | 318 | } |
| 319 | 319 |
lib/std/meta.zig+3| ... | ... | @@ -235,6 +235,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type { |
| 235 | 235 | .is_const = info.is_const, |
| 236 | 236 | .is_volatile = info.is_volatile, |
| 237 | 237 | .alignment = info.alignment, |
| 238 | .address_space = info.address_space, | |
| 238 | 239 | .child = @Type(.{ |
| 239 | 240 | .Array = .{ |
| 240 | 241 | .len = array_info.len, |
| ... | ... | @@ -254,6 +255,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type { |
| 254 | 255 | .is_const = info.is_const, |
| 255 | 256 | .is_volatile = info.is_volatile, |
| 256 | 257 | .alignment = info.alignment, |
| 258 | .address_space = info.address_space, | |
| 257 | 259 | .child = info.child, |
| 258 | 260 | .is_allowzero = info.is_allowzero, |
| 259 | 261 | .sentinel = sentinel_val, |
| ... | ... | @@ -271,6 +273,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type { |
| 271 | 273 | .is_const = ptr_info.is_const, |
| 272 | 274 | .is_volatile = ptr_info.is_volatile, |
| 273 | 275 | .alignment = ptr_info.alignment, |
| 276 | .address_space = ptr_info.address_space, | |
| 274 | 277 | .child = ptr_info.child, |
| 275 | 278 | .is_allowzero = ptr_info.is_allowzero, |
| 276 | 279 | .sentinel = sentinel_val, |
lib/std/multi_array_list.zig+2-2| ... | ... | @@ -189,7 +189,7 @@ pub fn MultiArrayList(comptime S: type) type { |
| 189 | 189 | /// sets the given index to the specified element. May reallocate |
| 190 | 190 | /// and invalidate iterators. |
| 191 | 191 | pub fn insert(self: *Self, gpa: *Allocator, index: usize, elem: S) void { |
| 192 | try self.ensureCapacity(gpa, self.len + 1); | |
| 192 | try self.ensureUnusedCapacity(gpa, 1); | |
| 193 | 193 | self.insertAssumeCapacity(index, elem); |
| 194 | 194 | } |
| 195 | 195 | |
| ... | ... | @@ -376,7 +376,7 @@ pub fn MultiArrayList(comptime S: type) type { |
| 376 | 376 | pub fn clone(self: Self, gpa: *Allocator) !Self { |
| 377 | 377 | var result = Self{}; |
| 378 | 378 | errdefer result.deinit(gpa); |
| 379 | try result.ensureCapacity(gpa, self.len); | |
| 379 | try result.ensureTotalCapacity(gpa, self.len); | |
| 380 | 380 | result.len = self.len; |
| 381 | 381 | const self_slice = self.slice(); |
| 382 | 382 | const result_slice = result.slice(); |
lib/std/net.zig+1-1| ... | ... | @@ -157,7 +157,7 @@ pub const Address = extern union { |
| 157 | 157 | unreachable; |
| 158 | 158 | } |
| 159 | 159 | |
| 160 | try std.fmt.format(out_stream, "{s}", .{&self.un.path}); | |
| 160 | try std.fmt.format(out_stream, "{s}", .{std.mem.sliceTo(&self.un.path, 0)}); | |
| 161 | 161 | }, |
| 162 | 162 | else => unreachable, |
| 163 | 163 | } |
lib/std/net/test.zig+13| ... | ... | @@ -90,6 +90,19 @@ test "parse and render IPv4 addresses" { |
| 90 | 90 | try testing.expectError(error.NonCanonical, net.Address.parseIp4("127.01.0.1", 0)); |
| 91 | 91 | } |
| 92 | 92 | |
| 93 | test "parse and render UNIX addresses" { | |
| 94 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 95 | if (!net.has_unix_sockets) return error.SkipZigTest; | |
| 96 | ||
| 97 | var buffer: [14]u8 = undefined; | |
| 98 | const addr = net.Address.initUnix("/tmp/testpath") catch unreachable; | |
| 99 | const fmt_addr = std.fmt.bufPrint(buffer[0..], "{}", .{addr}) catch unreachable; | |
| 100 | try std.testing.expectEqualSlices(u8, "/tmp/testpath", fmt_addr); | |
| 101 | ||
| 102 | const too_long = [_]u8{'a'} ** (addr.un.path.len + 1); | |
| 103 | try testing.expectError(error.NameTooLong, net.Address.initUnix(too_long[0..])); | |
| 104 | } | |
| 105 | ||
| 93 | 106 | test "resolve DNS" { |
| 94 | 107 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 95 | 108 |
lib/std/os.zig+24| ... | ... | @@ -31,6 +31,7 @@ pub const freebsd = std.c; |
| 31 | 31 | pub const haiku = std.c; |
| 32 | 32 | pub const netbsd = std.c; |
| 33 | 33 | pub const openbsd = std.c; |
| 34 | pub const solaris = std.c; | |
| 34 | 35 | pub const linux = @import("os/linux.zig"); |
| 35 | 36 | pub const uefi = @import("os/uefi.zig"); |
| 36 | 37 | pub const wasi = @import("os/wasi.zig"); |
| ... | ... | @@ -64,8 +65,10 @@ else switch (builtin.os.tag) { |
| 64 | 65 | }; |
| 65 | 66 | |
| 66 | 67 | pub const AF = system.AF; |
| 68 | pub const AF_SUN = system.AF_SUN; | |
| 67 | 69 | pub const ARCH = system.ARCH; |
| 68 | 70 | pub const AT = system.AT; |
| 71 | pub const AT_SUN = system.AT_SUN; | |
| 69 | 72 | pub const CLOCK = system.CLOCK; |
| 70 | 73 | pub const CPU_COUNT = system.CPU_COUNT; |
| 71 | 74 | pub const CTL = system.CTL; |
| ... | ... | @@ -101,6 +104,7 @@ pub const RR = system.RR; |
| 101 | 104 | pub const S = system.S; |
| 102 | 105 | pub const SA = system.SA; |
| 103 | 106 | pub const SC = system.SC; |
| 107 | pub const _SC = system._SC; | |
| 104 | 108 | pub const SEEK = system.SEEK; |
| 105 | 109 | pub const SHUT = system.SHUT; |
| 106 | 110 | pub const SIG = system.SIG; |
| ... | ... | @@ -143,6 +147,10 @@ pub const off_t = system.off_t; |
| 143 | 147 | pub const oflags_t = system.oflags_t; |
| 144 | 148 | pub const pid_t = system.pid_t; |
| 145 | 149 | pub const pollfd = system.pollfd; |
| 150 | pub const port_t = system.port_t; | |
| 151 | pub const port_event = system.port_event; | |
| 152 | pub const port_notify = system.port_notify; | |
| 153 | pub const file_obj = system.file_obj; | |
| 146 | 154 | pub const rights_t = system.rights_t; |
| 147 | 155 | pub const rlim_t = system.rlim_t; |
| 148 | 156 | pub const rlimit = system.rlimit; |
| ... | ... | @@ -2038,6 +2046,7 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr |
| 2038 | 2046 | .NOTDIR => return error.NotDir, |
| 2039 | 2047 | .NOMEM => return error.SystemResources, |
| 2040 | 2048 | .ROFS => return error.ReadOnlyFileSystem, |
| 2049 | .EXIST => return error.DirNotEmpty, | |
| 2041 | 2050 | .NOTEMPTY => return error.DirNotEmpty, |
| 2042 | 2051 | |
| 2043 | 2052 | .INVAL => unreachable, // invalid flags, or pathname has . as last component |
| ... | ... | @@ -4492,8 +4501,12 @@ pub const FlockError = error{ |
| 4492 | 4501 | |
| 4493 | 4502 | /// The kernel ran out of memory for allocating file locks |
| 4494 | 4503 | SystemResources, |
| 4504 | ||
| 4505 | /// The underlying filesystem does not support file locks | |
| 4506 | FileLocksNotSupported, | |
| 4495 | 4507 | } || UnexpectedError; |
| 4496 | 4508 | |
| 4509 | /// Depending on the operating system `flock` may or may not interact with `fcntl` locks made by other processes. | |
| 4497 | 4510 | pub fn flock(fd: fd_t, operation: i32) FlockError!void { |
| 4498 | 4511 | while (true) { |
| 4499 | 4512 | const rc = system.flock(fd, operation); |
| ... | ... | @@ -4504,6 +4517,7 @@ pub fn flock(fd: fd_t, operation: i32) FlockError!void { |
| 4504 | 4517 | .INVAL => unreachable, // invalid parameters |
| 4505 | 4518 | .NOLCK => return error.SystemResources, |
| 4506 | 4519 | .AGAIN => return error.WouldBlock, // TODO: integrate with async instead of just returning an error |
| 4520 | .OPNOTSUPP => return error.FileLocksNotSupported, | |
| 4507 | 4521 | else => |err| return unexpectedErrno(err), |
| 4508 | 4522 | } |
| 4509 | 4523 | } |
| ... | ... | @@ -4667,6 +4681,16 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 4667 | 4681 | }; |
| 4668 | 4682 | return target; |
| 4669 | 4683 | }, |
| 4684 | .solaris => { | |
| 4685 | var procfs_buf: ["/proc/self/path/-2147483648".len:0]u8 = undefined; | |
| 4686 | const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/path/{d}", .{fd}) catch unreachable; | |
| 4687 | ||
| 4688 | const target = readlinkZ(proc_path, out_buffer) catch |err| switch (err) { | |
| 4689 | error.UnsupportedReparsePointType => unreachable, | |
| 4690 | else => |e| return e, | |
| 4691 | }; | |
| 4692 | return target; | |
| 4693 | }, | |
| 4670 | 4694 | else => @compileError("querying for canonical path of a handle is unsupported on this host"), |
| 4671 | 4695 | } |
| 4672 | 4696 | } |
lib/std/os/linux.zig+9-7| ... | ... | @@ -89,6 +89,7 @@ pub const user_desc = arch_bits.user_desc; |
| 89 | 89 | pub const tls = @import("linux/tls.zig"); |
| 90 | 90 | pub const pie = @import("linux/start_pie.zig"); |
| 91 | 91 | pub const BPF = @import("linux/bpf.zig"); |
| 92 | pub const IOCTL = @import("linux/ioctl.zig"); | |
| 92 | 93 | |
| 93 | 94 | pub const MAP = struct { |
| 94 | 95 | pub usingnamespace arch_bits.MAP; |
| ... | ... | @@ -2585,18 +2586,18 @@ pub const T = struct { |
| 2585 | 2586 | pub const IOCGSID = 0x5429; |
| 2586 | 2587 | pub const IOCGRS485 = 0x542E; |
| 2587 | 2588 | pub const IOCSRS485 = 0x542F; |
| 2588 | pub const IOCGPTN = 0x80045430; | |
| 2589 | pub const IOCSPTLCK = 0x40045431; | |
| 2590 | pub const IOCGDEV = 0x80045432; | |
| 2589 | pub const IOCGPTN = IOCTL.IOR('T', 0x30, c_uint); | |
| 2590 | pub const IOCSPTLCK = IOCTL.IOW('T', 0x31, c_int); | |
| 2591 | pub const IOCGDEV = IOCTL.IOR('T', 0x32, c_uint); | |
| 2591 | 2592 | pub const CGETX = 0x5432; |
| 2592 | 2593 | pub const CSETX = 0x5433; |
| 2593 | 2594 | pub const CSETXF = 0x5434; |
| 2594 | 2595 | pub const CSETXW = 0x5435; |
| 2595 | pub const IOCSIG = 0x40045436; | |
| 2596 | pub const IOCSIG = IOCTL.IOW('T', 0x36, c_int); | |
| 2596 | 2597 | pub const IOCVHANGUP = 0x5437; |
| 2597 | pub const IOCGPKT = 0x80045438; | |
| 2598 | pub const IOCGPTLCK = 0x80045439; | |
| 2599 | pub const IOCGEXCL = 0x80045440; | |
| 2598 | pub const IOCGPKT = IOCTL.IOR('T', 0x38, c_int); | |
| 2599 | pub const IOCGPTLCK = IOCTL.IOR('T', 0x39, c_int); | |
| 2600 | pub const IOCGEXCL = IOCTL.IOR('T', 0x40, c_int); | |
| 2600 | 2601 | }; |
| 2601 | 2602 | |
| 2602 | 2603 | pub const EPOLL = struct { |
| ... | ... | @@ -2923,6 +2924,7 @@ pub const sockaddr = extern struct { |
| 2923 | 2924 | family: sa_family_t, |
| 2924 | 2925 | data: [14]u8, |
| 2925 | 2926 | |
| 2927 | pub const SS_MAXSIZE = 128; | |
| 2926 | 2928 | pub const storage = std.x.os.Socket.Address.Native.Storage; |
| 2927 | 2929 | |
| 2928 | 2930 | /// IPv4 socket address |
lib/std/os/linux/io_uring.zig+128| ... | ... | @@ -404,6 +404,25 @@ pub const IO_Uring = struct { |
| 404 | 404 | return sqe; |
| 405 | 405 | } |
| 406 | 406 | |
| 407 | /// Queues (but does not submit) an SQE to perform a IORING_OP_READ_FIXED. | |
| 408 | /// The `buffer` provided must be registered with the kernel by calling `register_buffers` first. | |
| 409 | /// The `buffer_index` must be the same as its index in the array provided to `register_buffers`. | |
| 410 | /// | |
| 411 | /// Returns a pointer to the SQE so that you can further modify the SQE for advanced use cases. | |
| 412 | pub fn read_fixed( | |
| 413 | self: *IO_Uring, | |
| 414 | user_data: u64, | |
| 415 | fd: os.fd_t, | |
| 416 | buffer: *os.iovec, | |
| 417 | offset: u64, | |
| 418 | buffer_index: u16, | |
| 419 | ) !*io_uring_sqe { | |
| 420 | const sqe = try self.get_sqe(); | |
| 421 | io_uring_prep_read_fixed(sqe, fd, buffer, offset, buffer_index); | |
| 422 | sqe.user_data = user_data; | |
| 423 | return sqe; | |
| 424 | } | |
| 425 | ||
| 407 | 426 | /// Queues (but does not submit) an SQE to perform a `pwritev()`. |
| 408 | 427 | /// Returns a pointer to the SQE so that you can further modify the SQE for advanced use cases. |
| 409 | 428 | /// For example, if you want to do a `pwritev2()` then set `rw_flags` on the returned SQE. |
| ... | ... | @@ -421,6 +440,25 @@ pub const IO_Uring = struct { |
| 421 | 440 | return sqe; |
| 422 | 441 | } |
| 423 | 442 | |
| 443 | /// Queues (but does not submit) an SQE to perform a IORING_OP_WRITE_FIXED. | |
| 444 | /// The `buffer` provided must be registered with the kernel by calling `register_buffers` first. | |
| 445 | /// The `buffer_index` must be the same as its index in the array provided to `register_buffers`. | |
| 446 | /// | |
| 447 | /// Returns a pointer to the SQE so that you can further modify the SQE for advanced use cases. | |
| 448 | pub fn write_fixed( | |
| 449 | self: *IO_Uring, | |
| 450 | user_data: u64, | |
| 451 | fd: os.fd_t, | |
| 452 | buffer: *os.iovec, | |
| 453 | offset: u64, | |
| 454 | buffer_index: u16, | |
| 455 | ) !*io_uring_sqe { | |
| 456 | const sqe = try self.get_sqe(); | |
| 457 | io_uring_prep_write_fixed(sqe, fd, buffer, offset, buffer_index); | |
| 458 | sqe.user_data = user_data; | |
| 459 | return sqe; | |
| 460 | } | |
| 461 | ||
| 424 | 462 | /// Queues (but does not submit) an SQE to perform an `accept4(2)` on a socket. |
| 425 | 463 | /// Returns a pointer to the SQE. |
| 426 | 464 | pub fn accept( |
| ... | ... | @@ -674,6 +712,29 @@ pub const IO_Uring = struct { |
| 674 | 712 | try handle_registration_result(res); |
| 675 | 713 | } |
| 676 | 714 | |
| 715 | /// Registers an array of buffers for use with `read_fixed` and `write_fixed`. | |
| 716 | pub fn register_buffers(self: *IO_Uring, buffers: []const os.iovec) !void { | |
| 717 | assert(self.fd >= 0); | |
| 718 | const res = linux.io_uring_register( | |
| 719 | self.fd, | |
| 720 | .REGISTER_BUFFERS, | |
| 721 | buffers.ptr, | |
| 722 | @intCast(u32, buffers.len), | |
| 723 | ); | |
| 724 | try handle_registration_result(res); | |
| 725 | } | |
| 726 | ||
| 727 | /// Unregister the registered buffers. | |
| 728 | pub fn unregister_buffers(self: *IO_Uring) !void { | |
| 729 | assert(self.fd >= 0); | |
| 730 | const res = linux.io_uring_register(self.fd, .UNREGISTER_BUFFERS, null, 0); | |
| 731 | switch (linux.getErrno(res)) { | |
| 732 | .SUCCESS => {}, | |
| 733 | .NXIO => return error.BuffersNotRegistered, | |
| 734 | else => |errno| return os.unexpectedErrno(errno), | |
| 735 | } | |
| 736 | } | |
| 737 | ||
| 677 | 738 | fn handle_registration_result(res: usize) !void { |
| 678 | 739 | switch (linux.getErrno(res)) { |
| 679 | 740 | .SUCCESS => {}, |
| ... | ... | @@ -905,6 +966,16 @@ pub fn io_uring_prep_writev( |
| 905 | 966 | io_uring_prep_rw(.WRITEV, sqe, fd, @ptrToInt(iovecs.ptr), iovecs.len, offset); |
| 906 | 967 | } |
| 907 | 968 | |
| 969 | pub fn io_uring_prep_read_fixed(sqe: *io_uring_sqe, fd: os.fd_t, buffer: *os.iovec, offset: u64, buffer_index: u16) void { | |
| 970 | io_uring_prep_rw(.READ_FIXED, sqe, fd, @ptrToInt(buffer.iov_base), buffer.iov_len, offset); | |
| 971 | sqe.buf_index = buffer_index; | |
| 972 | } | |
| 973 | ||
| 974 | pub fn io_uring_prep_write_fixed(sqe: *io_uring_sqe, fd: os.fd_t, buffer: *os.iovec, offset: u64, buffer_index: u16) void { | |
| 975 | io_uring_prep_rw(.WRITE_FIXED, sqe, fd, @ptrToInt(buffer.iov_base), buffer.iov_len, offset); | |
| 976 | sqe.buf_index = buffer_index; | |
| 977 | } | |
| 978 | ||
| 908 | 979 | pub fn io_uring_prep_accept( |
| 909 | 980 | sqe: *io_uring_sqe, |
| 910 | 981 | fd: os.fd_t, |
| ... | ... | @@ -1282,6 +1353,63 @@ test "write/read" { |
| 1282 | 1353 | try testing.expectEqualSlices(u8, buffer_write[0..], buffer_read[0..]); |
| 1283 | 1354 | } |
| 1284 | 1355 | |
| 1356 | test "write_fixed/read_fixed" { | |
| 1357 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 1358 | ||
| 1359 | var ring = IO_Uring.init(2, 0) catch |err| switch (err) { | |
| 1360 | error.SystemOutdated => return error.SkipZigTest, | |
| 1361 | error.PermissionDenied => return error.SkipZigTest, | |
| 1362 | else => return err, | |
| 1363 | }; | |
| 1364 | defer ring.deinit(); | |
| 1365 | ||
| 1366 | const path = "test_io_uring_write_read_fixed"; | |
| 1367 | const file = try std.fs.cwd().createFile(path, .{ .read = true, .truncate = true }); | |
| 1368 | defer file.close(); | |
| 1369 | defer std.fs.cwd().deleteFile(path) catch {}; | |
| 1370 | const fd = file.handle; | |
| 1371 | ||
| 1372 | var raw_buffers: [2][11]u8 = undefined; | |
| 1373 | // First buffer will be written to the file. | |
| 1374 | std.mem.set(u8, &raw_buffers[0], 'z'); | |
| 1375 | std.mem.copy(u8, &raw_buffers[0], "foobar"); | |
| 1376 | ||
| 1377 | var buffers = [2]os.iovec{ | |
| 1378 | .{ .iov_base = &raw_buffers[0], .iov_len = raw_buffers[0].len }, | |
| 1379 | .{ .iov_base = &raw_buffers[1], .iov_len = raw_buffers[1].len }, | |
| 1380 | }; | |
| 1381 | try ring.register_buffers(&buffers); | |
| 1382 | ||
| 1383 | const sqe_write = try ring.write_fixed(0x45454545, fd, &buffers[0], 3, 0); | |
| 1384 | try testing.expectEqual(linux.IORING_OP.WRITE_FIXED, sqe_write.opcode); | |
| 1385 | try testing.expectEqual(@as(u64, 3), sqe_write.off); | |
| 1386 | sqe_write.flags |= linux.IOSQE_IO_LINK; | |
| 1387 | ||
| 1388 | const sqe_read = try ring.read_fixed(0x12121212, fd, &buffers[1], 0, 1); | |
| 1389 | try testing.expectEqual(linux.IORING_OP.READ_FIXED, sqe_read.opcode); | |
| 1390 | try testing.expectEqual(@as(u64, 0), sqe_read.off); | |
| 1391 | ||
| 1392 | try testing.expectEqual(@as(u32, 2), try ring.submit()); | |
| 1393 | ||
| 1394 | const cqe_write = try ring.copy_cqe(); | |
| 1395 | const cqe_read = try ring.copy_cqe(); | |
| 1396 | ||
| 1397 | try testing.expectEqual(linux.io_uring_cqe{ | |
| 1398 | .user_data = 0x45454545, | |
| 1399 | .res = @intCast(i32, buffers[0].iov_len), | |
| 1400 | .flags = 0, | |
| 1401 | }, cqe_write); | |
| 1402 | try testing.expectEqual(linux.io_uring_cqe{ | |
| 1403 | .user_data = 0x12121212, | |
| 1404 | .res = @intCast(i32, buffers[1].iov_len), | |
| 1405 | .flags = 0, | |
| 1406 | }, cqe_read); | |
| 1407 | ||
| 1408 | try testing.expectEqualSlices(u8, "\x00\x00\x00", buffers[1].iov_base[0..3]); | |
| 1409 | try testing.expectEqualSlices(u8, "foobar", buffers[1].iov_base[3..9]); | |
| 1410 | try testing.expectEqualSlices(u8, "zz", buffers[1].iov_base[9..11]); | |
| 1411 | } | |
| 1412 | ||
| 1285 | 1413 | test "openat" { |
| 1286 | 1414 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1287 | 1415 |
lib/std/os/linux/ioctl.zig created+56| ... | ... | @@ -0,0 +1,56 @@ |
| 1 | const std = @import("../../std.zig"); | |
| 2 | ||
| 3 | const bits = switch (@import("builtin").cpu.arch) { | |
| 4 | .mips, | |
| 5 | .mipsel, | |
| 6 | .mips64, | |
| 7 | .mips64el, | |
| 8 | .powerpc, | |
| 9 | .powerpcle, | |
| 10 | .powerpc64, | |
| 11 | .powerpc64le, | |
| 12 | .sparc, | |
| 13 | .sparcv9, | |
| 14 | .sparcel, | |
| 15 | => .{ .size = 13, .dir = 3, .none = 1, .read = 2, .write = 4 }, | |
| 16 | else => .{ .size = 14, .dir = 2, .none = 0, .read = 2, .write = 1 }, | |
| 17 | }; | |
| 18 | ||
| 19 | const Direction = std.meta.Int(.unsigned, bits.dir); | |
| 20 | ||
| 21 | pub const Request = packed struct { | |
| 22 | nr: u8, | |
| 23 | io_type: u8, | |
| 24 | size: std.meta.Int(.unsigned, bits.size), | |
| 25 | dir: Direction, | |
| 26 | }; | |
| 27 | ||
| 28 | fn io_impl(dir: Direction, io_type: u8, nr: u8, comptime T: type) u32 { | |
| 29 | const request = Request{ | |
| 30 | .dir = dir, | |
| 31 | .size = @sizeOf(T), | |
| 32 | .io_type = io_type, | |
| 33 | .nr = nr, | |
| 34 | }; | |
| 35 | return @bitCast(u32, request); | |
| 36 | } | |
| 37 | ||
| 38 | pub fn IO(io_type: u8, nr: u8) u32 { | |
| 39 | return io_impl(bits.none, io_type, nr, void); | |
| 40 | } | |
| 41 | ||
| 42 | pub fn IOR(io_type: u8, nr: u8, comptime T: type) u32 { | |
| 43 | return io_impl(bits.read, io_type, nr, T); | |
| 44 | } | |
| 45 | ||
| 46 | pub fn IOW(io_type: u8, nr: u8, comptime T: type) u32 { | |
| 47 | return io_impl(bits.write, io_type, nr, T); | |
| 48 | } | |
| 49 | ||
| 50 | pub fn IOWR(io_type: u8, nr: u8, comptime T: type) u32 { | |
| 51 | return io_impl(bits.read | bits.write, io_type, nr, T); | |
| 52 | } | |
| 53 | ||
| 54 | comptime { | |
| 55 | std.debug.assert(@bitSizeOf(Request) == 32); | |
| 56 | } |
lib/std/os/linux/sparc64.zig+4| ... | ... | @@ -674,6 +674,10 @@ pub const msghdr_const = extern struct { |
| 674 | 674 | pub const off_t = i64; |
| 675 | 675 | pub const ino_t = u64; |
| 676 | 676 | pub const mode_t = u32; |
| 677 | pub const dev_t = usize; | |
| 678 | pub const nlink_t = u32; | |
| 679 | pub const blksize_t = isize; | |
| 680 | pub const blkcnt_t = isize; | |
| 677 | 681 | |
| 678 | 682 | // The `stat64` definition used by the kernel. |
| 679 | 683 | pub const Stat = extern struct { |
lib/std/os/test.zig+20-7| ... | ... | @@ -188,7 +188,10 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { |
| 188 | 188 | } |
| 189 | 189 | |
| 190 | 190 | test "link with relative paths" { |
| 191 | if (native_os != .linux) return error.SkipZigTest; | |
| 191 | switch (native_os) { | |
| 192 | .linux, .solaris => {}, | |
| 193 | else => return error.SkipZigTest, | |
| 194 | } | |
| 192 | 195 | var cwd = fs.cwd(); |
| 193 | 196 | |
| 194 | 197 | cwd.deleteFile("example.txt") catch {}; |
| ... | ... | @@ -222,7 +225,10 @@ test "link with relative paths" { |
| 222 | 225 | } |
| 223 | 226 | |
| 224 | 227 | test "linkat with different directories" { |
| 225 | if (native_os != .linux) return error.SkipZigTest; | |
| 228 | switch (native_os) { | |
| 229 | .linux, .solaris => {}, | |
| 230 | else => return error.SkipZigTest, | |
| 231 | } | |
| 226 | 232 | var cwd = fs.cwd(); |
| 227 | 233 | var tmp = tmpDir(.{}); |
| 228 | 234 | |
| ... | ... | @@ -634,8 +640,10 @@ test "fcntl" { |
| 634 | 640 | } |
| 635 | 641 | |
| 636 | 642 | test "signalfd" { |
| 637 | if (native_os != .linux) | |
| 638 | return error.SkipZigTest; | |
| 643 | switch (native_os) { | |
| 644 | .linux, .solaris => {}, | |
| 645 | else => return error.SkipZigTest, | |
| 646 | } | |
| 639 | 647 | _ = std.os.signalfd; |
| 640 | 648 | } |
| 641 | 649 | |
| ... | ... | @@ -658,8 +666,10 @@ test "sync" { |
| 658 | 666 | } |
| 659 | 667 | |
| 660 | 668 | test "fsync" { |
| 661 | if (native_os != .linux and native_os != .windows) | |
| 662 | return error.SkipZigTest; | |
| 669 | switch (native_os) { | |
| 670 | .linux, .windows, .solaris => {}, | |
| 671 | else => return error.SkipZigTest, | |
| 672 | } | |
| 663 | 673 | |
| 664 | 674 | var tmp = tmpDir(.{}); |
| 665 | 675 | defer tmp.cleanup(); |
| ... | ... | @@ -754,7 +764,10 @@ test "sigaction" { |
| 754 | 764 | } |
| 755 | 765 | |
| 756 | 766 | test "dup & dup2" { |
| 757 | if (native_os != .linux) return error.SkipZigTest; | |
| 767 | switch (native_os) { | |
| 768 | .linux, .solaris => {}, | |
| 769 | else => return error.SkipZigTest, | |
| 770 | } | |
| 758 | 771 | |
| 759 | 772 | var tmp = tmpDir(.{}); |
| 760 | 773 | defer tmp.cleanup(); |
lib/std/os/windows/kernel32.zig+4| ... | ... | @@ -152,6 +152,8 @@ pub extern "kernel32" fn GetCommandLineW() callconv(WINAPI) LPWSTR; |
| 152 | 152 | |
| 153 | 153 | pub extern "kernel32" fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) callconv(WINAPI) BOOL; |
| 154 | 154 | |
| 155 | pub extern "kernel32" fn GetConsoleOutputCP() callconv(WINAPI) UINT; | |
| 156 | ||
| 155 | 157 | pub extern "kernel32" fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) callconv(WINAPI) BOOL; |
| 156 | 158 | pub extern "kernel32" fn FillConsoleOutputCharacterA(hConsoleOutput: HANDLE, cCharacter: CHAR, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfCharsWritten: *DWORD) callconv(WINAPI) BOOL; |
| 157 | 159 | pub extern "kernel32" fn FillConsoleOutputCharacterW(hConsoleOutput: HANDLE, cCharacter: WCHAR, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfCharsWritten: *DWORD) callconv(WINAPI) BOOL; |
| ... | ... | @@ -286,6 +288,8 @@ pub extern "kernel32" fn SetConsoleCtrlHandler( |
| 286 | 288 | Add: BOOL, |
| 287 | 289 | ) callconv(WINAPI) BOOL; |
| 288 | 290 | |
| 291 | pub extern "kernel32" fn SetConsoleOutputCP(wCodePageID: UINT) callconv(WINAPI) BOOL; | |
| 292 | ||
| 289 | 293 | pub extern "kernel32" fn SetFileCompletionNotificationModes( |
| 290 | 294 | FileHandle: HANDLE, |
| 291 | 295 | Flags: UCHAR, |
lib/std/os/windows/ws2_32.zig+1| ... | ... | @@ -1105,6 +1105,7 @@ pub const sockaddr = extern struct { |
| 1105 | 1105 | family: ADDRESS_FAMILY, |
| 1106 | 1106 | data: [14]u8, |
| 1107 | 1107 | |
| 1108 | pub const SS_MAXSIZE = 128; | |
| 1108 | 1109 | pub const storage = std.x.os.Socket.Address.Native.Storage; |
| 1109 | 1110 | |
| 1110 | 1111 | /// IPv4 socket address |
lib/std/priority_dequeue.zig+14-5| ... | ... | @@ -43,13 +43,13 @@ pub fn PriorityDequeue(comptime T: type) type { |
| 43 | 43 | |
| 44 | 44 | /// Insert a new element, maintaining priority. |
| 45 | 45 | pub fn add(self: *Self, elem: T) !void { |
| 46 | try ensureCapacity(self, self.len + 1); | |
| 46 | try self.ensureUnusedCapacity(1); | |
| 47 | 47 | addUnchecked(self, elem); |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | 50 | /// Add each element in `items` to the dequeue. |
| 51 | 51 | pub fn addSlice(self: *Self, items: []const T) !void { |
| 52 | try self.ensureCapacity(self.len + items.len); | |
| 52 | try self.ensureUnusedCapacity(items.len); | |
| 53 | 53 | for (items) |e| { |
| 54 | 54 | self.addUnchecked(e); |
| 55 | 55 | } |
| ... | ... | @@ -359,7 +359,11 @@ pub fn PriorityDequeue(comptime T: type) type { |
| 359 | 359 | return queue; |
| 360 | 360 | } |
| 361 | 361 | |
| 362 | pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { | |
| 362 | /// Deprecated: call `ensureUnusedCapacity` or `ensureTotalCapacity`. | |
| 363 | pub const ensureCapacity = ensureTotalCapacity; | |
| 364 | ||
| 365 | /// Ensure that the dequeue can fit at least `new_capacity` items. | |
| 366 | pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) !void { | |
| 363 | 367 | var better_capacity = self.capacity(); |
| 364 | 368 | if (better_capacity >= new_capacity) return; |
| 365 | 369 | while (true) { |
| ... | ... | @@ -369,6 +373,11 @@ pub fn PriorityDequeue(comptime T: type) type { |
| 369 | 373 | self.items = try self.allocator.realloc(self.items, better_capacity); |
| 370 | 374 | } |
| 371 | 375 | |
| 376 | /// Ensure that the dequeue can fit at least `additional_count` **more** items. | |
| 377 | pub fn ensureUnusedCapacity(self: *Self, additional_count: usize) !void { | |
| 378 | return self.ensureTotalCapacity(self.len + additional_count); | |
| 379 | } | |
| 380 | ||
| 372 | 381 | /// Reduce allocated capacity to `new_len`. |
| 373 | 382 | pub fn shrinkAndFree(self: *Self, new_len: usize) void { |
| 374 | 383 | assert(new_len <= self.items.len); |
| ... | ... | @@ -824,7 +833,7 @@ test "std.PriorityDequeue: shrinkAndFree" { |
| 824 | 833 | var queue = PDQ.init(testing.allocator, lessThanComparison); |
| 825 | 834 | defer queue.deinit(); |
| 826 | 835 | |
| 827 | try queue.ensureCapacity(4); | |
| 836 | try queue.ensureTotalCapacity(4); | |
| 828 | 837 | try expect(queue.capacity() >= 4); |
| 829 | 838 | |
| 830 | 839 | try queue.add(1); |
| ... | ... | @@ -940,7 +949,7 @@ fn fuzzTestMinMax(rng: *std.rand.Random, queue_size: usize) !void { |
| 940 | 949 | |
| 941 | 950 | fn generateRandomSlice(allocator: *std.mem.Allocator, rng: *std.rand.Random, size: usize) ![]u32 { |
| 942 | 951 | var array = std.ArrayList(u32).init(allocator); |
| 943 | try array.ensureCapacity(size); | |
| 952 | try array.ensureTotalCapacity(size); | |
| 944 | 953 | |
| 945 | 954 | var i: usize = 0; |
| 946 | 955 | while (i < size) : (i += 1) { |
lib/std/priority_queue.zig+13-4| ... | ... | @@ -42,7 +42,7 @@ pub fn PriorityQueue(comptime T: type) type { |
| 42 | 42 | |
| 43 | 43 | /// Insert a new element, maintaining priority. |
| 44 | 44 | pub fn add(self: *Self, elem: T) !void { |
| 45 | try ensureCapacity(self, self.len + 1); | |
| 45 | try self.ensureUnusedCapacity(1); | |
| 46 | 46 | addUnchecked(self, elem); |
| 47 | 47 | } |
| 48 | 48 | |
| ... | ... | @@ -69,7 +69,7 @@ pub fn PriorityQueue(comptime T: type) type { |
| 69 | 69 | |
| 70 | 70 | /// Add each element in `items` to the queue. |
| 71 | 71 | pub fn addSlice(self: *Self, items: []const T) !void { |
| 72 | try self.ensureCapacity(self.len + items.len); | |
| 72 | try self.ensureUnusedCapacity(items.len); | |
| 73 | 73 | for (items) |e| { |
| 74 | 74 | self.addUnchecked(e); |
| 75 | 75 | } |
| ... | ... | @@ -175,7 +175,11 @@ pub fn PriorityQueue(comptime T: type) type { |
| 175 | 175 | return queue; |
| 176 | 176 | } |
| 177 | 177 | |
| 178 | pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { | |
| 178 | /// Deprecated: call `ensureUnusedCapacity` or `ensureTotalCapacity`. | |
| 179 | pub const ensureCapacity = ensureTotalCapacity; | |
| 180 | ||
| 181 | /// Ensure that the queue can fit at least `new_capacity` items. | |
| 182 | pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) !void { | |
| 179 | 183 | var better_capacity = self.capacity(); |
| 180 | 184 | if (better_capacity >= new_capacity) return; |
| 181 | 185 | while (true) { |
| ... | ... | @@ -185,6 +189,11 @@ pub fn PriorityQueue(comptime T: type) type { |
| 185 | 189 | self.items = try self.allocator.realloc(self.items, better_capacity); |
| 186 | 190 | } |
| 187 | 191 | |
| 192 | /// Ensure that the queue can fit at least `additional_count` **more** item. | |
| 193 | pub fn ensureUnusedCapacity(self: *Self, additional_count: usize) !void { | |
| 194 | return self.ensureTotalCapacity(self.len + additional_count); | |
| 195 | } | |
| 196 | ||
| 188 | 197 | /// Reduce allocated capacity to `new_len`. |
| 189 | 198 | pub fn shrinkAndFree(self: *Self, new_len: usize) void { |
| 190 | 199 | assert(new_len <= self.items.len); |
| ... | ... | @@ -483,7 +492,7 @@ test "std.PriorityQueue: shrinkAndFree" { |
| 483 | 492 | var queue = PQ.init(testing.allocator, lessThan); |
| 484 | 493 | defer queue.deinit(); |
| 485 | 494 | |
| 486 | try queue.ensureCapacity(4); | |
| 495 | try queue.ensureTotalCapacity(4); | |
| 487 | 496 | try expect(queue.capacity() >= 4); |
| 488 | 497 | |
| 489 | 498 | try queue.add(1); |
lib/std/process.zig+2-1| ... | ... | @@ -625,7 +625,7 @@ pub const UserInfo = struct { |
| 625 | 625 | /// POSIX function which gets a uid from username. |
| 626 | 626 | pub fn getUserInfo(name: []const u8) !UserInfo { |
| 627 | 627 | return switch (builtin.os.tag) { |
| 628 | .linux, .macos, .watchos, .tvos, .ios, .freebsd, .netbsd, .openbsd, .haiku => posixGetUserInfo(name), | |
| 628 | .linux, .macos, .watchos, .tvos, .ios, .freebsd, .netbsd, .openbsd, .haiku, .solaris => posixGetUserInfo(name), | |
| 629 | 629 | else => @compileError("Unsupported OS"), |
| 630 | 630 | }; |
| 631 | 631 | } |
| ... | ... | @@ -753,6 +753,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0] |
| 753 | 753 | .netbsd, |
| 754 | 754 | .dragonfly, |
| 755 | 755 | .openbsd, |
| 756 | .solaris, | |
| 756 | 757 | => { |
| 757 | 758 | var paths = List.init(allocator); |
| 758 | 759 | errdefer { |
lib/std/special/c.zig+43-1174| ... | ... | @@ -1,177 +1,36 @@ |
| 1 | // This is Zig's multi-target implementation of libc. | |
| 2 | // When builtin.link_libc is true, we need to export all the functions and | |
| 3 | // provide an entire C API. | |
| 4 | // Otherwise, only the functions which LLVM generates calls to need to be generated, | |
| 5 | // such as memcpy, memset, and some math functions. | |
| 1 | //! This is Zig's multi-target implementation of libc. | |
| 2 | //! When builtin.link_libc is true, we need to export all the functions and | |
| 3 | //! provide an entire C API. | |
| 4 | //! Otherwise, only the functions which LLVM generates calls to need to be generated, | |
| 5 | //! such as memcpy, memset, and some math functions. | |
| 6 | 6 | |
| 7 | 7 | const std = @import("std"); |
| 8 | const builtin = std.builtin; | |
| 9 | const maxInt = std.math.maxInt; | |
| 10 | const isNan = std.math.isNan; | |
| 11 | const native_arch = std.Target.current.cpu.arch; | |
| 12 | const native_abi = std.Target.current.abi; | |
| 13 | const native_os = std.Target.current.os.tag; | |
| 8 | const builtin = @import("builtin"); | |
| 9 | const native_os = builtin.os.tag; | |
| 14 | 10 | |
| 15 | const is_wasm = switch (native_arch) { | |
| 16 | .wasm32, .wasm64 => true, | |
| 17 | else => false, | |
| 18 | }; | |
| 19 | const is_msvc = switch (native_abi) { | |
| 20 | .msvc => true, | |
| 21 | else => false, | |
| 22 | }; | |
| 23 | const is_freestanding = switch (native_os) { | |
| 24 | .freestanding => true, | |
| 25 | else => false, | |
| 26 | }; | |
| 27 | 11 | comptime { |
| 28 | if (is_freestanding and is_wasm and builtin.link_libc) { | |
| 29 | @export(wasm_start, .{ .name = "_start", .linkage = .Strong }); | |
| 30 | } | |
| 31 | if (builtin.link_libc) { | |
| 32 | @export(strcmp, .{ .name = "strcmp", .linkage = .Strong }); | |
| 33 | @export(strncmp, .{ .name = "strncmp", .linkage = .Strong }); | |
| 34 | @export(strerror, .{ .name = "strerror", .linkage = .Strong }); | |
| 35 | @export(strlen, .{ .name = "strlen", .linkage = .Strong }); | |
| 36 | @export(strcpy, .{ .name = "strcpy", .linkage = .Strong }); | |
| 37 | @export(strncpy, .{ .name = "strncpy", .linkage = .Strong }); | |
| 38 | @export(strcat, .{ .name = "strcat", .linkage = .Strong }); | |
| 39 | @export(strncat, .{ .name = "strncat", .linkage = .Strong }); | |
| 40 | } else if (is_msvc) { | |
| 41 | @export(_fltused, .{ .name = "_fltused", .linkage = .Strong }); | |
| 42 | } | |
| 43 | } | |
| 44 | ||
| 45 | var _fltused: c_int = 1; | |
| 46 | ||
| 47 | extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; | |
| 48 | fn wasm_start() callconv(.C) void { | |
| 49 | _ = main(0, undefined); | |
| 50 | } | |
| 51 | ||
| 52 | fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 { | |
| 53 | var i: usize = 0; | |
| 54 | while (src[i] != 0) : (i += 1) { | |
| 55 | dest[i] = src[i]; | |
| 56 | } | |
| 57 | dest[i] = 0; | |
| 58 | ||
| 59 | return dest; | |
| 60 | } | |
| 61 | ||
| 62 | test "strcpy" { | |
| 63 | var s1: [9:0]u8 = undefined; | |
| 64 | ||
| 65 | s1[0] = 0; | |
| 66 | _ = strcpy(&s1, "foobarbaz"); | |
| 67 | try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); | |
| 68 | } | |
| 69 | ||
| 70 | fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8 { | |
| 71 | var i: usize = 0; | |
| 72 | while (i < n and src[i] != 0) : (i += 1) { | |
| 73 | dest[i] = src[i]; | |
| 74 | } | |
| 75 | while (i < n) : (i += 1) { | |
| 76 | dest[i] = 0; | |
| 77 | } | |
| 78 | ||
| 79 | return dest; | |
| 80 | } | |
| 81 | ||
| 82 | test "strncpy" { | |
| 83 | var s1: [9:0]u8 = undefined; | |
| 84 | ||
| 85 | s1[0] = 0; | |
| 86 | _ = strncpy(&s1, "foobarbaz", @sizeOf(@TypeOf(s1))); | |
| 87 | try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); | |
| 88 | } | |
| 89 | ||
| 90 | fn strcat(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 { | |
| 91 | var dest_end: usize = 0; | |
| 92 | while (dest[dest_end] != 0) : (dest_end += 1) {} | |
| 93 | ||
| 94 | var i: usize = 0; | |
| 95 | while (src[i] != 0) : (i += 1) { | |
| 96 | dest[dest_end + i] = src[i]; | |
| 97 | } | |
| 98 | dest[dest_end + i] = 0; | |
| 99 | ||
| 100 | return dest; | |
| 101 | } | |
| 102 | ||
| 103 | test "strcat" { | |
| 104 | var s1: [9:0]u8 = undefined; | |
| 105 | ||
| 106 | s1[0] = 0; | |
| 107 | _ = strcat(&s1, "foo"); | |
| 108 | _ = strcat(&s1, "bar"); | |
| 109 | _ = strcat(&s1, "baz"); | |
| 110 | try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); | |
| 111 | } | |
| 112 | ||
| 113 | fn strncat(dest: [*:0]u8, src: [*:0]const u8, avail: usize) callconv(.C) [*:0]u8 { | |
| 114 | var dest_end: usize = 0; | |
| 115 | while (dest[dest_end] != 0) : (dest_end += 1) {} | |
| 116 | ||
| 117 | var i: usize = 0; | |
| 118 | while (i < avail and src[i] != 0) : (i += 1) { | |
| 119 | dest[dest_end + i] = src[i]; | |
| 120 | } | |
| 121 | dest[dest_end + i] = 0; | |
| 122 | ||
| 123 | return dest; | |
| 124 | } | |
| 125 | ||
| 126 | test "strncat" { | |
| 127 | var s1: [9:0]u8 = undefined; | |
| 128 | ||
| 129 | s1[0] = 0; | |
| 130 | _ = strncat(&s1, "foo1111", 3); | |
| 131 | _ = strncat(&s1, "bar1111", 3); | |
| 132 | _ = strncat(&s1, "baz1111", 3); | |
| 133 | try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); | |
| 134 | } | |
| 135 | ||
| 136 | fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int { | |
| 137 | return std.cstr.cmp(s1, s2); | |
| 138 | } | |
| 139 | ||
| 140 | fn strlen(s: [*:0]const u8) callconv(.C) usize { | |
| 141 | return std.mem.len(s); | |
| 142 | } | |
| 143 | ||
| 144 | fn strncmp(_l: [*:0]const u8, _r: [*:0]const u8, _n: usize) callconv(.C) c_int { | |
| 145 | if (_n == 0) return 0; | |
| 146 | var l = _l; | |
| 147 | var r = _r; | |
| 148 | var n = _n - 1; | |
| 149 | while (l[0] != 0 and r[0] != 0 and n != 0 and l[0] == r[0]) { | |
| 150 | l += 1; | |
| 151 | r += 1; | |
| 152 | n -= 1; | |
| 12 | // When the self-hosted compiler is further along, all the logic from c_stage1.zig will | |
| 13 | // be migrated to this file and then c_stage1.zig will be deleted. Until then we have a | |
| 14 | // simpler implementation of c.zig that only uses features already implemented in self-hosted. | |
| 15 | if (builtin.zig_is_stage2) { | |
| 16 | @export(memset, .{ .name = "memset", .linkage = .Strong }); | |
| 17 | @export(memcpy, .{ .name = "memcpy", .linkage = .Strong }); | |
| 18 | } else { | |
| 19 | _ = @import("c_stage1.zig"); | |
| 153 | 20 | } |
| 154 | return @as(c_int, l[0]) - @as(c_int, r[0]); | |
| 155 | } | |
| 156 | ||
| 157 | fn strerror(errnum: c_int) callconv(.C) [*:0]const u8 { | |
| 158 | _ = errnum; | |
| 159 | return "TODO strerror implementation"; | |
| 160 | } | |
| 161 | ||
| 162 | test "strncmp" { | |
| 163 | try std.testing.expect(strncmp("a", "b", 1) == -1); | |
| 164 | try std.testing.expect(strncmp("a", "c", 1) == -2); | |
| 165 | try std.testing.expect(strncmp("b", "a", 1) == 1); | |
| 166 | try std.testing.expect(strncmp("\xff", "\x02", 1) == 253); | |
| 167 | 21 | } |
| 168 | 22 | |
| 169 | 23 | // Avoid dragging in the runtime safety mechanisms into this .o file, |
| 170 | 24 | // unless we're trying to test this file. |
| 171 | pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { | |
| 25 | pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { | |
| 26 | @setCold(true); | |
| 172 | 27 | _ = error_return_trace; |
| 28 | if (builtin.zig_is_stage2) { | |
| 29 | while (true) { | |
| 30 | @breakpoint(); | |
| 31 | } | |
| 32 | } | |
| 173 | 33 | if (builtin.is_test) { |
| 174 | @setCold(true); | |
| 175 | 34 | std.debug.panic("{s}", .{msg}); |
| 176 | 35 | } |
| 177 | 36 | if (native_os != .freestanding and native_os != .other) { |
| ... | ... | @@ -180,1028 +39,38 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn |
| 180 | 39 | while (true) {} |
| 181 | 40 | } |
| 182 | 41 | |
| 183 | export fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8 { | |
| 184 | @setRuntimeSafety(false); | |
| 185 | ||
| 186 | var index: usize = 0; | |
| 187 | while (index != n) : (index += 1) | |
| 188 | dest.?[index] = c; | |
| 189 | ||
| 190 | return dest; | |
| 191 | } | |
| 192 | ||
| 193 | export fn __memset(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { | |
| 194 | if (dest_n < n) | |
| 195 | @panic("buffer overflow"); | |
| 196 | return memset(dest, c, n); | |
| 197 | } | |
| 198 | ||
| 199 | export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 { | |
| 200 | @setRuntimeSafety(false); | |
| 201 | ||
| 202 | var index: usize = 0; | |
| 203 | while (index != n) : (index += 1) | |
| 204 | dest.?[index] = src.?[index]; | |
| 205 | ||
| 206 | return dest; | |
| 207 | } | |
| 208 | ||
| 209 | export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 { | |
| 42 | fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.C) ?[*]u8 { | |
| 210 | 43 | @setRuntimeSafety(false); |
| 211 | 44 | |
| 212 | if (@ptrToInt(dest) < @ptrToInt(src)) { | |
| 213 | var index: usize = 0; | |
| 214 | while (index != n) : (index += 1) { | |
| 215 | dest.?[index] = src.?[index]; | |
| 216 | } | |
| 217 | } else { | |
| 218 | var index = n; | |
| 219 | while (index != 0) { | |
| 220 | index -= 1; | |
| 221 | dest.?[index] = src.?[index]; | |
| 45 | if (len != 0) { | |
| 46 | var d = dest.?; | |
| 47 | var n = len; | |
| 48 | while (true) { | |
| 49 | d.* = c; | |
| 50 | n -= 1; | |
| 51 | if (n == 0) break; | |
| 52 | d += 1; | |
| 222 | 53 | } |
| 223 | 54 | } |
| 224 | 55 | |
| 225 | 56 | return dest; |
| 226 | 57 | } |
| 227 | 58 | |
| 228 | export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) c_int { | |
| 229 | @setRuntimeSafety(false); | |
| 230 | ||
| 231 | var index: usize = 0; | |
| 232 | while (index != n) : (index += 1) { | |
| 233 | const compare_val = @bitCast(i8, vl.?[index] -% vr.?[index]); | |
| 234 | if (compare_val != 0) { | |
| 235 | return compare_val; | |
| 236 | } | |
| 237 | } | |
| 238 | ||
| 239 | return 0; | |
| 240 | } | |
| 241 | ||
| 242 | test "memcmp" { | |
| 243 | const base_arr = &[_]u8{ 1, 1, 1 }; | |
| 244 | const arr1 = &[_]u8{ 1, 1, 1 }; | |
| 245 | const arr2 = &[_]u8{ 1, 0, 1 }; | |
| 246 | const arr3 = &[_]u8{ 1, 2, 1 }; | |
| 247 | ||
| 248 | try std.testing.expect(memcmp(base_arr[0..], arr1[0..], base_arr.len) == 0); | |
| 249 | try std.testing.expect(memcmp(base_arr[0..], arr2[0..], base_arr.len) > 0); | |
| 250 | try std.testing.expect(memcmp(base_arr[0..], arr3[0..], base_arr.len) < 0); | |
| 251 | } | |
| 252 | ||
| 253 | export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) callconv(.C) c_int { | |
| 254 | @setRuntimeSafety(false); | |
| 255 | ||
| 256 | var index: usize = 0; | |
| 257 | while (index != n) : (index += 1) { | |
| 258 | if (vl[index] != vr[index]) { | |
| 259 | return 1; | |
| 260 | } | |
| 261 | } | |
| 262 | ||
| 263 | return 0; | |
| 264 | } | |
| 265 | ||
| 266 | test "bcmp" { | |
| 267 | const base_arr = &[_]u8{ 1, 1, 1 }; | |
| 268 | const arr1 = &[_]u8{ 1, 1, 1 }; | |
| 269 | const arr2 = &[_]u8{ 1, 0, 1 }; | |
| 270 | const arr3 = &[_]u8{ 1, 2, 1 }; | |
| 271 | ||
| 272 | try std.testing.expect(bcmp(base_arr[0..], arr1[0..], base_arr.len) == 0); | |
| 273 | try std.testing.expect(bcmp(base_arr[0..], arr2[0..], base_arr.len) != 0); | |
| 274 | try std.testing.expect(bcmp(base_arr[0..], arr3[0..], base_arr.len) != 0); | |
| 275 | } | |
| 276 | ||
| 277 | comptime { | |
| 278 | if (native_os == .linux) { | |
| 279 | @export(clone, .{ .name = "clone" }); | |
| 280 | } | |
| 281 | } | |
| 282 | ||
| 283 | // TODO we should be able to put this directly in std/linux/x86_64.zig but | |
| 284 | // it causes a segfault in release mode. this is a workaround of calling it | |
| 285 | // across .o file boundaries. fix comptime @ptrCast of nakedcc functions. | |
| 286 | fn clone() callconv(.Naked) void { | |
| 287 | switch (native_arch) { | |
| 288 | .i386 => { | |
| 289 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 290 | // +8, +12, +16, +20, +24, +28, +32 | |
| 291 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 292 | // eax, ebx, ecx, edx, esi, edi | |
| 293 | asm volatile ( | |
| 294 | \\ push %%ebp | |
| 295 | \\ mov %%esp,%%ebp | |
| 296 | \\ push %%ebx | |
| 297 | \\ push %%esi | |
| 298 | \\ push %%edi | |
| 299 | \\ // Setup the arguments | |
| 300 | \\ mov 16(%%ebp),%%ebx | |
| 301 | \\ mov 12(%%ebp),%%ecx | |
| 302 | \\ and $-16,%%ecx | |
| 303 | \\ sub $20,%%ecx | |
| 304 | \\ mov 20(%%ebp),%%eax | |
| 305 | \\ mov %%eax,4(%%ecx) | |
| 306 | \\ mov 8(%%ebp),%%eax | |
| 307 | \\ mov %%eax,0(%%ecx) | |
| 308 | \\ mov 24(%%ebp),%%edx | |
| 309 | \\ mov 28(%%ebp),%%esi | |
| 310 | \\ mov 32(%%ebp),%%edi | |
| 311 | \\ mov $120,%%eax | |
| 312 | \\ int $128 | |
| 313 | \\ test %%eax,%%eax | |
| 314 | \\ jnz 1f | |
| 315 | \\ pop %%eax | |
| 316 | \\ xor %%ebp,%%ebp | |
| 317 | \\ call *%%eax | |
| 318 | \\ mov %%eax,%%ebx | |
| 319 | \\ xor %%eax,%%eax | |
| 320 | \\ inc %%eax | |
| 321 | \\ int $128 | |
| 322 | \\ hlt | |
| 323 | \\1: | |
| 324 | \\ pop %%edi | |
| 325 | \\ pop %%esi | |
| 326 | \\ pop %%ebx | |
| 327 | \\ pop %%ebp | |
| 328 | \\ ret | |
| 329 | ); | |
| 330 | }, | |
| 331 | .x86_64 => { | |
| 332 | asm volatile ( | |
| 333 | \\ xor %%eax,%%eax | |
| 334 | \\ mov $56,%%al // SYS_clone | |
| 335 | \\ mov %%rdi,%%r11 | |
| 336 | \\ mov %%rdx,%%rdi | |
| 337 | \\ mov %%r8,%%rdx | |
| 338 | \\ mov %%r9,%%r8 | |
| 339 | \\ mov 8(%%rsp),%%r10 | |
| 340 | \\ mov %%r11,%%r9 | |
| 341 | \\ and $-16,%%rsi | |
| 342 | \\ sub $8,%%rsi | |
| 343 | \\ mov %%rcx,(%%rsi) | |
| 344 | \\ syscall | |
| 345 | \\ test %%eax,%%eax | |
| 346 | \\ jnz 1f | |
| 347 | \\ xor %%ebp,%%ebp | |
| 348 | \\ pop %%rdi | |
| 349 | \\ call *%%r9 | |
| 350 | \\ mov %%eax,%%edi | |
| 351 | \\ xor %%eax,%%eax | |
| 352 | \\ mov $60,%%al // SYS_exit | |
| 353 | \\ syscall | |
| 354 | \\ hlt | |
| 355 | \\1: ret | |
| 356 | \\ | |
| 357 | ); | |
| 358 | }, | |
| 359 | .aarch64 => { | |
| 360 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 361 | // x0, x1, w2, x3, x4, x5, x6 | |
| 362 | ||
| 363 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 364 | // x8, x0, x1, x2, x3, x4 | |
| 365 | asm volatile ( | |
| 366 | \\ // align stack and save func,arg | |
| 367 | \\ and x1,x1,#-16 | |
| 368 | \\ stp x0,x3,[x1,#-16]! | |
| 369 | \\ | |
| 370 | \\ // syscall | |
| 371 | \\ uxtw x0,w2 | |
| 372 | \\ mov x2,x4 | |
| 373 | \\ mov x3,x5 | |
| 374 | \\ mov x4,x6 | |
| 375 | \\ mov x8,#220 // SYS_clone | |
| 376 | \\ svc #0 | |
| 377 | \\ | |
| 378 | \\ cbz x0,1f | |
| 379 | \\ // parent | |
| 380 | \\ ret | |
| 381 | \\ // child | |
| 382 | \\1: ldp x1,x0,[sp],#16 | |
| 383 | \\ blr x1 | |
| 384 | \\ mov x8,#93 // SYS_exit | |
| 385 | \\ svc #0 | |
| 386 | ); | |
| 387 | }, | |
| 388 | .arm, .thumb => { | |
| 389 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 390 | // r0, r1, r2, r3, +0, +4, +8 | |
| 391 | ||
| 392 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 393 | // r7 r0, r1, r2, r3, r4 | |
| 394 | asm volatile ( | |
| 395 | \\ stmfd sp!,{r4,r5,r6,r7} | |
| 396 | \\ mov r7,#120 | |
| 397 | \\ mov r6,r3 | |
| 398 | \\ mov r5,r0 | |
| 399 | \\ mov r0,r2 | |
| 400 | \\ and r1,r1,#-16 | |
| 401 | \\ ldr r2,[sp,#16] | |
| 402 | \\ ldr r3,[sp,#20] | |
| 403 | \\ ldr r4,[sp,#24] | |
| 404 | \\ svc 0 | |
| 405 | \\ tst r0,r0 | |
| 406 | \\ beq 1f | |
| 407 | \\ ldmfd sp!,{r4,r5,r6,r7} | |
| 408 | \\ bx lr | |
| 409 | \\ | |
| 410 | \\1: mov r0,r6 | |
| 411 | \\ bl 3f | |
| 412 | \\2: mov r7,#1 | |
| 413 | \\ svc 0 | |
| 414 | \\ b 2b | |
| 415 | \\3: bx r5 | |
| 416 | ); | |
| 417 | }, | |
| 418 | .riscv64 => { | |
| 419 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 420 | // a0, a1, a2, a3, a4, a5, a6 | |
| 421 | ||
| 422 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 423 | // a7 a0, a1, a2, a3, a4 | |
| 424 | asm volatile ( | |
| 425 | \\ # Save func and arg to stack | |
| 426 | \\ addi a1, a1, -16 | |
| 427 | \\ sd a0, 0(a1) | |
| 428 | \\ sd a3, 8(a1) | |
| 429 | \\ | |
| 430 | \\ # Call SYS_clone | |
| 431 | \\ mv a0, a2 | |
| 432 | \\ mv a2, a4 | |
| 433 | \\ mv a3, a5 | |
| 434 | \\ mv a4, a6 | |
| 435 | \\ li a7, 220 # SYS_clone | |
| 436 | \\ ecall | |
| 437 | \\ | |
| 438 | \\ beqz a0, 1f | |
| 439 | \\ # Parent | |
| 440 | \\ ret | |
| 441 | \\ | |
| 442 | \\ # Child | |
| 443 | \\1: ld a1, 0(sp) | |
| 444 | \\ ld a0, 8(sp) | |
| 445 | \\ jalr a1 | |
| 446 | \\ | |
| 447 | \\ # Exit | |
| 448 | \\ li a7, 93 # SYS_exit | |
| 449 | \\ ecall | |
| 450 | ); | |
| 451 | }, | |
| 452 | .mips, .mipsel => { | |
| 453 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 454 | // 3, 4, 5, 6, 7, 8, 9 | |
| 455 | ||
| 456 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 457 | // 2 4, 5, 6, 7, 8 | |
| 458 | asm volatile ( | |
| 459 | \\ # Save function pointer and argument pointer on new thread stack | |
| 460 | \\ and $5, $5, -8 | |
| 461 | \\ subu $5, $5, 16 | |
| 462 | \\ sw $4, 0($5) | |
| 463 | \\ sw $7, 4($5) | |
| 464 | \\ # Shuffle (fn,sp,fl,arg,ptid,tls,ctid) to (fl,sp,ptid,tls,ctid) | |
| 465 | \\ move $4, $6 | |
| 466 | \\ lw $6, 16($sp) | |
| 467 | \\ lw $7, 20($sp) | |
| 468 | \\ lw $9, 24($sp) | |
| 469 | \\ subu $sp, $sp, 16 | |
| 470 | \\ sw $9, 16($sp) | |
| 471 | \\ li $2, 4120 | |
| 472 | \\ syscall | |
| 473 | \\ beq $7, $0, 1f | |
| 474 | \\ nop | |
| 475 | \\ addu $sp, $sp, 16 | |
| 476 | \\ jr $ra | |
| 477 | \\ subu $2, $0, $2 | |
| 478 | \\1: | |
| 479 | \\ beq $2, $0, 1f | |
| 480 | \\ nop | |
| 481 | \\ addu $sp, $sp, 16 | |
| 482 | \\ jr $ra | |
| 483 | \\ nop | |
| 484 | \\1: | |
| 485 | \\ lw $25, 0($sp) | |
| 486 | \\ lw $4, 4($sp) | |
| 487 | \\ jalr $25 | |
| 488 | \\ nop | |
| 489 | \\ move $4, $2 | |
| 490 | \\ li $2, 4001 | |
| 491 | \\ syscall | |
| 492 | ); | |
| 493 | }, | |
| 494 | .powerpc => { | |
| 495 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 496 | // 3, 4, 5, 6, 7, 8, 9 | |
| 497 | ||
| 498 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 499 | // 0 3, 4, 5, 6, 7 | |
| 500 | asm volatile ( | |
| 501 | \\# store non-volatile regs r30, r31 on stack in order to put our | |
| 502 | \\# start func and its arg there | |
| 503 | \\stwu 30, -16(1) | |
| 504 | \\stw 31, 4(1) | |
| 505 | \\ | |
| 506 | \\# save r3 (func) into r30, and r6(arg) into r31 | |
| 507 | \\mr 30, 3 | |
| 508 | \\mr 31, 6 | |
| 509 | \\ | |
| 510 | \\# create initial stack frame for new thread | |
| 511 | \\clrrwi 4, 4, 4 | |
| 512 | \\li 0, 0 | |
| 513 | \\stwu 0, -16(4) | |
| 514 | \\ | |
| 515 | \\#move c into first arg | |
| 516 | \\mr 3, 5 | |
| 517 | \\#mr 4, 4 | |
| 518 | \\mr 5, 7 | |
| 519 | \\mr 6, 8 | |
| 520 | \\mr 7, 9 | |
| 521 | \\ | |
| 522 | \\# move syscall number into r0 | |
| 523 | \\li 0, 120 | |
| 524 | \\ | |
| 525 | \\sc | |
| 526 | \\ | |
| 527 | \\# check for syscall error | |
| 528 | \\bns+ 1f # jump to label 1 if no summary overflow. | |
| 529 | \\#else | |
| 530 | \\neg 3, 3 #negate the result (errno) | |
| 531 | \\1: | |
| 532 | \\# compare sc result with 0 | |
| 533 | \\cmpwi cr7, 3, 0 | |
| 534 | \\ | |
| 535 | \\# if not 0, jump to end | |
| 536 | \\bne cr7, 2f | |
| 537 | \\ | |
| 538 | \\#else: we're the child | |
| 539 | \\#call funcptr: move arg (d) into r3 | |
| 540 | \\mr 3, 31 | |
| 541 | \\#move r30 (funcptr) into CTR reg | |
| 542 | \\mtctr 30 | |
| 543 | \\# call CTR reg | |
| 544 | \\bctrl | |
| 545 | \\# mov SYS_exit into r0 (the exit param is already in r3) | |
| 546 | \\li 0, 1 | |
| 547 | \\sc | |
| 548 | \\ | |
| 549 | \\2: | |
| 550 | \\ | |
| 551 | \\# restore stack | |
| 552 | \\lwz 30, 0(1) | |
| 553 | \\lwz 31, 4(1) | |
| 554 | \\addi 1, 1, 16 | |
| 555 | \\ | |
| 556 | \\blr | |
| 557 | ); | |
| 558 | }, | |
| 559 | .powerpc64, .powerpc64le => { | |
| 560 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 561 | // 3, 4, 5, 6, 7, 8, 9 | |
| 562 | ||
| 563 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 564 | // 0 3, 4, 5, 6, 7 | |
| 565 | asm volatile ( | |
| 566 | \\ # create initial stack frame for new thread | |
| 567 | \\ clrrdi 4, 4, 4 | |
| 568 | \\ li 0, 0 | |
| 569 | \\ stdu 0,-32(4) | |
| 570 | \\ | |
| 571 | \\ # save fn and arg to child stack | |
| 572 | \\ std 3, 8(4) | |
| 573 | \\ std 6, 16(4) | |
| 574 | \\ | |
| 575 | \\ # shuffle args into correct registers and call SYS_clone | |
| 576 | \\ mr 3, 5 | |
| 577 | \\ #mr 4, 4 | |
| 578 | \\ mr 5, 7 | |
| 579 | \\ mr 6, 8 | |
| 580 | \\ mr 7, 9 | |
| 581 | \\ li 0, 120 # SYS_clone = 120 | |
| 582 | \\ sc | |
| 583 | \\ | |
| 584 | \\ # if error, negate return (errno) | |
| 585 | \\ bns+ 1f | |
| 586 | \\ neg 3, 3 | |
| 587 | \\ | |
| 588 | \\1: | |
| 589 | \\ # if we're the parent, return | |
| 590 | \\ cmpwi cr7, 3, 0 | |
| 591 | \\ bnelr cr7 | |
| 592 | \\ | |
| 593 | \\ # we're the child. call fn(arg) | |
| 594 | \\ ld 3, 16(1) | |
| 595 | \\ ld 12, 8(1) | |
| 596 | \\ mtctr 12 | |
| 597 | \\ bctrl | |
| 598 | \\ | |
| 599 | \\ # call SYS_exit. exit code is already in r3 from fn return value | |
| 600 | \\ li 0, 1 # SYS_exit = 1 | |
| 601 | \\ sc | |
| 602 | ); | |
| 603 | }, | |
| 604 | .sparcv9 => { | |
| 605 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 606 | // i0, i1, i2, i3, i4, i5, sp | |
| 607 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 608 | // g1 o0, o1, o2, o3, o4 | |
| 609 | asm volatile ( | |
| 610 | \\ save %%sp, -192, %%sp | |
| 611 | \\ # Save the func pointer and the arg pointer | |
| 612 | \\ mov %%i0, %%g2 | |
| 613 | \\ mov %%i3, %%g3 | |
| 614 | \\ # Shuffle the arguments | |
| 615 | \\ mov 217, %%g1 | |
| 616 | \\ mov %%i2, %%o0 | |
| 617 | \\ # Add some extra space for the initial frame | |
| 618 | \\ sub %%i1, 176 + 2047, %%o1 | |
| 619 | \\ mov %%i4, %%o2 | |
| 620 | \\ mov %%i5, %%o3 | |
| 621 | \\ ldx [%%fp + 0x8af], %%o4 | |
| 622 | \\ t 0x6d | |
| 623 | \\ bcs,pn %%xcc, 2f | |
| 624 | \\ nop | |
| 625 | \\ # The child pid is returned in o0 while o1 tells if this | |
| 626 | \\ # process is # the child (=1) or the parent (=0). | |
| 627 | \\ brnz %%o1, 1f | |
| 628 | \\ nop | |
| 629 | \\ # Parent process, return the child pid | |
| 630 | \\ mov %%o0, %%i0 | |
| 631 | \\ ret | |
| 632 | \\ restore | |
| 633 | \\1: | |
| 634 | \\ # Child process, call func(arg) | |
| 635 | \\ mov %%g0, %%fp | |
| 636 | \\ call %%g2 | |
| 637 | \\ mov %%g3, %%o0 | |
| 638 | \\ # Exit | |
| 639 | \\ mov 1, %%g1 | |
| 640 | \\ t 0x6d | |
| 641 | \\2: | |
| 642 | \\ # The syscall failed | |
| 643 | \\ sub %%g0, %%o0, %%i0 | |
| 644 | \\ ret | |
| 645 | \\ restore | |
| 646 | ); | |
| 647 | }, | |
| 648 | else => @compileError("Implement clone() for this arch."), | |
| 649 | } | |
| 650 | } | |
| 651 | ||
| 652 | const math = std.math; | |
| 653 | ||
| 654 | export fn fmodf(x: f32, y: f32) f32 { | |
| 655 | return generic_fmod(f32, x, y); | |
| 656 | } | |
| 657 | export fn fmod(x: f64, y: f64) f64 { | |
| 658 | return generic_fmod(f64, x, y); | |
| 659 | } | |
| 660 | ||
| 661 | // TODO add intrinsics for these (and probably the double version too) | |
| 662 | // and have the math stuff use the intrinsic. same as @mod and @rem | |
| 663 | export fn floorf(x: f32) f32 { | |
| 664 | return math.floor(x); | |
| 665 | } | |
| 666 | ||
| 667 | export fn ceilf(x: f32) f32 { | |
| 668 | return math.ceil(x); | |
| 669 | } | |
| 670 | ||
| 671 | export fn floor(x: f64) f64 { | |
| 672 | return math.floor(x); | |
| 673 | } | |
| 674 | ||
| 675 | export fn ceil(x: f64) f64 { | |
| 676 | return math.ceil(x); | |
| 677 | } | |
| 678 | ||
| 679 | export fn fma(a: f64, b: f64, c: f64) f64 { | |
| 680 | return math.fma(f64, a, b, c); | |
| 681 | } | |
| 682 | ||
| 683 | export fn fmaf(a: f32, b: f32, c: f32) f32 { | |
| 684 | return math.fma(f32, a, b, c); | |
| 685 | } | |
| 686 | ||
| 687 | export fn sin(a: f64) f64 { | |
| 688 | return math.sin(a); | |
| 689 | } | |
| 690 | ||
| 691 | export fn sinf(a: f32) f32 { | |
| 692 | return math.sin(a); | |
| 693 | } | |
| 694 | ||
| 695 | export fn cos(a: f64) f64 { | |
| 696 | return math.cos(a); | |
| 697 | } | |
| 698 | ||
| 699 | export fn cosf(a: f32) f32 { | |
| 700 | return math.cos(a); | |
| 701 | } | |
| 702 | ||
| 703 | export fn sincos(a: f64, r_sin: *f64, r_cos: *f64) void { | |
| 704 | r_sin.* = math.sin(a); | |
| 705 | r_cos.* = math.cos(a); | |
| 706 | } | |
| 707 | ||
| 708 | export fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) void { | |
| 709 | r_sin.* = math.sin(a); | |
| 710 | r_cos.* = math.cos(a); | |
| 711 | } | |
| 712 | ||
| 713 | export fn exp(a: f64) f64 { | |
| 714 | return math.exp(a); | |
| 715 | } | |
| 716 | ||
| 717 | export fn expf(a: f32) f32 { | |
| 718 | return math.exp(a); | |
| 719 | } | |
| 720 | ||
| 721 | export fn exp2(a: f64) f64 { | |
| 722 | return math.exp2(a); | |
| 723 | } | |
| 724 | ||
| 725 | export fn exp2f(a: f32) f32 { | |
| 726 | return math.exp2(a); | |
| 727 | } | |
| 728 | ||
| 729 | export fn log(a: f64) f64 { | |
| 730 | return math.ln(a); | |
| 731 | } | |
| 732 | ||
| 733 | export fn logf(a: f32) f32 { | |
| 734 | return math.ln(a); | |
| 735 | } | |
| 736 | ||
| 737 | export fn log2(a: f64) f64 { | |
| 738 | return math.log2(a); | |
| 739 | } | |
| 740 | ||
| 741 | export fn log2f(a: f32) f32 { | |
| 742 | return math.log2(a); | |
| 743 | } | |
| 744 | ||
| 745 | export fn log10(a: f64) f64 { | |
| 746 | return math.log10(a); | |
| 747 | } | |
| 748 | ||
| 749 | export fn log10f(a: f32) f32 { | |
| 750 | return math.log10(a); | |
| 751 | } | |
| 752 | ||
| 753 | export fn fabs(a: f64) f64 { | |
| 754 | return math.fabs(a); | |
| 755 | } | |
| 756 | ||
| 757 | export fn fabsf(a: f32) f32 { | |
| 758 | return math.fabs(a); | |
| 759 | } | |
| 760 | ||
| 761 | export fn trunc(a: f64) f64 { | |
| 762 | return math.trunc(a); | |
| 763 | } | |
| 764 | ||
| 765 | export fn truncf(a: f32) f32 { | |
| 766 | return math.trunc(a); | |
| 767 | } | |
| 768 | ||
| 769 | export fn round(a: f64) f64 { | |
| 770 | return math.round(a); | |
| 771 | } | |
| 772 | ||
| 773 | export fn roundf(a: f32) f32 { | |
| 774 | return math.round(a); | |
| 775 | } | |
| 776 | ||
| 777 | fn generic_fmod(comptime T: type, x: T, y: T) T { | |
| 59 | fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, len: usize) callconv(.C) ?[*]u8 { | |
| 778 | 60 | @setRuntimeSafety(false); |
| 779 | 61 | |
| 780 | const bits = @typeInfo(T).Float.bits; | |
| 781 | const uint = std.meta.Int(.unsigned, bits); | |
| 782 | const log2uint = math.Log2Int(uint); | |
| 783 | const digits = if (T == f32) 23 else 52; | |
| 784 | const exp_bits = if (T == f32) 9 else 12; | |
| 785 | const bits_minus_1 = bits - 1; | |
| 786 | const mask = if (T == f32) 0xff else 0x7ff; | |
| 787 | var ux = @bitCast(uint, x); | |
| 788 | var uy = @bitCast(uint, y); | |
| 789 | var ex = @intCast(i32, (ux >> digits) & mask); | |
| 790 | var ey = @intCast(i32, (uy >> digits) & mask); | |
| 791 | const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1); | |
| 792 | var i: uint = undefined; | |
| 793 | ||
| 794 | if (uy << 1 == 0 or isNan(@bitCast(T, uy)) or ex == mask) | |
| 795 | return (x * y) / (x * y); | |
| 796 | ||
| 797 | if (ux << 1 <= uy << 1) { | |
| 798 | if (ux << 1 == uy << 1) | |
| 799 | return 0 * x; | |
| 800 | return x; | |
| 801 | } | |
| 802 | ||
| 803 | // normalize x and y | |
| 804 | if (ex == 0) { | |
| 805 | i = ux << exp_bits; | |
| 806 | while (i >> bits_minus_1 == 0) : ({ | |
| 807 | ex -= 1; | |
| 808 | i <<= 1; | |
| 809 | }) {} | |
| 810 | ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1)); | |
| 811 | } else { | |
| 812 | ux &= maxInt(uint) >> exp_bits; | |
| 813 | ux |= 1 << digits; | |
| 814 | } | |
| 815 | if (ey == 0) { | |
| 816 | i = uy << exp_bits; | |
| 817 | while (i >> bits_minus_1 == 0) : ({ | |
| 818 | ey -= 1; | |
| 819 | i <<= 1; | |
| 820 | }) {} | |
| 821 | uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1)); | |
| 822 | } else { | |
| 823 | uy &= maxInt(uint) >> exp_bits; | |
| 824 | uy |= 1 << digits; | |
| 825 | } | |
| 826 | ||
| 827 | // x mod y | |
| 828 | while (ex > ey) : (ex -= 1) { | |
| 829 | i = ux -% uy; | |
| 830 | if (i >> bits_minus_1 == 0) { | |
| 831 | if (i == 0) | |
| 832 | return 0 * x; | |
| 833 | ux = i; | |
| 834 | } | |
| 835 | ux <<= 1; | |
| 836 | } | |
| 837 | i = ux -% uy; | |
| 838 | if (i >> bits_minus_1 == 0) { | |
| 839 | if (i == 0) | |
| 840 | return 0 * x; | |
| 841 | ux = i; | |
| 842 | } | |
| 843 | while (ux >> digits == 0) : ({ | |
| 844 | ux <<= 1; | |
| 845 | ex -= 1; | |
| 846 | }) {} | |
| 847 | ||
| 848 | // scale result up | |
| 849 | if (ex > 0) { | |
| 850 | ux -%= 1 << digits; | |
| 851 | ux |= @as(uint, @bitCast(u32, ex)) << digits; | |
| 852 | } else { | |
| 853 | ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1)); | |
| 854 | } | |
| 855 | if (T == f32) { | |
| 856 | ux |= sx; | |
| 857 | } else { | |
| 858 | ux |= @intCast(uint, sx) << bits_minus_1; | |
| 859 | } | |
| 860 | return @bitCast(T, ux); | |
| 861 | } | |
| 862 | ||
| 863 | test "fmod, fmodf" { | |
| 864 | inline for ([_]type{ f32, f64 }) |T| { | |
| 865 | const nan_val = math.nan(T); | |
| 866 | const inf_val = math.inf(T); | |
| 867 | ||
| 868 | try std.testing.expect(isNan(generic_fmod(T, nan_val, 1.0))); | |
| 869 | try std.testing.expect(isNan(generic_fmod(T, 1.0, nan_val))); | |
| 870 | try std.testing.expect(isNan(generic_fmod(T, inf_val, 1.0))); | |
| 871 | try std.testing.expect(isNan(generic_fmod(T, 0.0, 0.0))); | |
| 872 | try std.testing.expect(isNan(generic_fmod(T, 1.0, 0.0))); | |
| 873 | ||
| 874 | try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0)); | |
| 875 | try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0)); | |
| 876 | ||
| 877 | try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0)); | |
| 878 | try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0)); | |
| 879 | try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0)); | |
| 880 | try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0)); | |
| 881 | } | |
| 882 | } | |
| 883 | ||
| 884 | fn generic_fmin(comptime T: type, x: T, y: T) T { | |
| 885 | if (isNan(x)) | |
| 886 | return y; | |
| 887 | if (isNan(y)) | |
| 888 | return x; | |
| 889 | return if (x < y) x else y; | |
| 890 | } | |
| 891 | ||
| 892 | export fn fminf(x: f32, y: f32) callconv(.C) f32 { | |
| 893 | return generic_fmin(f32, x, y); | |
| 894 | } | |
| 895 | ||
| 896 | export fn fmin(x: f64, y: f64) callconv(.C) f64 { | |
| 897 | return generic_fmin(f64, x, y); | |
| 898 | } | |
| 899 | ||
| 900 | test "fmin, fminf" { | |
| 901 | inline for ([_]type{ f32, f64 }) |T| { | |
| 902 | const nan_val = math.nan(T); | |
| 903 | ||
| 904 | try std.testing.expect(isNan(generic_fmin(T, nan_val, nan_val))); | |
| 905 | try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0)); | |
| 906 | try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val)); | |
| 907 | ||
| 908 | try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0)); | |
| 909 | try std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0)); | |
| 910 | } | |
| 911 | } | |
| 912 | ||
| 913 | fn generic_fmax(comptime T: type, x: T, y: T) T { | |
| 914 | if (isNan(x)) | |
| 915 | return y; | |
| 916 | if (isNan(y)) | |
| 917 | return x; | |
| 918 | return if (x < y) y else x; | |
| 919 | } | |
| 920 | ||
| 921 | export fn fmaxf(x: f32, y: f32) callconv(.C) f32 { | |
| 922 | return generic_fmax(f32, x, y); | |
| 923 | } | |
| 924 | ||
| 925 | export fn fmax(x: f64, y: f64) callconv(.C) f64 { | |
| 926 | return generic_fmax(f64, x, y); | |
| 927 | } | |
| 928 | ||
| 929 | test "fmax, fmaxf" { | |
| 930 | inline for ([_]type{ f32, f64 }) |T| { | |
| 931 | const nan_val = math.nan(T); | |
| 932 | ||
| 933 | try std.testing.expect(isNan(generic_fmax(T, nan_val, nan_val))); | |
| 934 | try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0)); | |
| 935 | try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val)); | |
| 936 | ||
| 937 | try std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0)); | |
| 938 | try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0)); | |
| 939 | } | |
| 940 | } | |
| 941 | ||
| 942 | // NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound | |
| 943 | // behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are | |
| 944 | // potentially some edge cases remaining that are not handled in the same way. | |
| 945 | export fn sqrt(x: f64) f64 { | |
| 946 | const tiny: f64 = 1.0e-300; | |
| 947 | const sign: u32 = 0x80000000; | |
| 948 | const u = @bitCast(u64, x); | |
| 949 | ||
| 950 | var ix0 = @intCast(u32, u >> 32); | |
| 951 | var ix1 = @intCast(u32, u & 0xFFFFFFFF); | |
| 952 | ||
| 953 | // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan | |
| 954 | if (ix0 & 0x7FF00000 == 0x7FF00000) { | |
| 955 | return x * x + x; | |
| 956 | } | |
| 957 | ||
| 958 | // sqrt(+-0) = +-0 | |
| 959 | if (x == 0.0) { | |
| 960 | return x; | |
| 961 | } | |
| 962 | // sqrt(-ve) = snan | |
| 963 | if (ix0 & sign != 0) { | |
| 964 | return math.snan(f64); | |
| 965 | } | |
| 966 | ||
| 967 | // normalize x | |
| 968 | var m = @intCast(i32, ix0 >> 20); | |
| 969 | if (m == 0) { | |
| 970 | // subnormal | |
| 971 | while (ix0 == 0) { | |
| 972 | m -= 21; | |
| 973 | ix0 |= ix1 >> 11; | |
| 974 | ix1 <<= 21; | |
| 975 | } | |
| 976 | ||
| 977 | // subnormal | |
| 978 | var i: u32 = 0; | |
| 979 | while (ix0 & 0x00100000 == 0) : (i += 1) { | |
| 980 | ix0 <<= 1; | |
| 62 | if (len != 0) { | |
| 63 | var d = dest.?; | |
| 64 | var s = src.?; | |
| 65 | var n = len; | |
| 66 | while (true) { | |
| 67 | d.* = s.*; | |
| 68 | n -= 1; | |
| 69 | if (n == 0) break; | |
| 70 | d += 1; | |
| 71 | s += 1; | |
| 981 | 72 | } |
| 982 | m -= @intCast(i32, i) - 1; | |
| 983 | ix0 |= ix1 >> @intCast(u5, 32 - i); | |
| 984 | ix1 <<= @intCast(u5, i); | |
| 985 | 73 | } |
| 986 | 74 | |
| 987 | // unbias exponent | |
| 988 | m -= 1023; | |
| 989 | ix0 = (ix0 & 0x000FFFFF) | 0x00100000; | |
| 990 | if (m & 1 != 0) { | |
| 991 | ix0 += ix0 + (ix1 >> 31); | |
| 992 | ix1 = ix1 +% ix1; | |
| 993 | } | |
| 994 | m >>= 1; | |
| 995 | ||
| 996 | // sqrt(x) bit by bit | |
| 997 | ix0 += ix0 + (ix1 >> 31); | |
| 998 | ix1 = ix1 +% ix1; | |
| 999 | ||
| 1000 | var q: u32 = 0; | |
| 1001 | var q1: u32 = 0; | |
| 1002 | var s0: u32 = 0; | |
| 1003 | var s1: u32 = 0; | |
| 1004 | var r: u32 = 0x00200000; | |
| 1005 | var t: u32 = undefined; | |
| 1006 | var t1: u32 = undefined; | |
| 1007 | ||
| 1008 | while (r != 0) { | |
| 1009 | t = s0 +% r; | |
| 1010 | if (t <= ix0) { | |
| 1011 | s0 = t + r; | |
| 1012 | ix0 -= t; | |
| 1013 | q += r; | |
| 1014 | } | |
| 1015 | ix0 = ix0 +% ix0 +% (ix1 >> 31); | |
| 1016 | ix1 = ix1 +% ix1; | |
| 1017 | r >>= 1; | |
| 1018 | } | |
| 1019 | ||
| 1020 | r = sign; | |
| 1021 | while (r != 0) { | |
| 1022 | t1 = s1 +% r; | |
| 1023 | t = s0; | |
| 1024 | if (t < ix0 or (t == ix0 and t1 <= ix1)) { | |
| 1025 | s1 = t1 +% r; | |
| 1026 | if (t1 & sign == sign and s1 & sign == 0) { | |
| 1027 | s0 += 1; | |
| 1028 | } | |
| 1029 | ix0 -= t; | |
| 1030 | if (ix1 < t1) { | |
| 1031 | ix0 -= 1; | |
| 1032 | } | |
| 1033 | ix1 = ix1 -% t1; | |
| 1034 | q1 += r; | |
| 1035 | } | |
| 1036 | ix0 = ix0 +% ix0 +% (ix1 >> 31); | |
| 1037 | ix1 = ix1 +% ix1; | |
| 1038 | r >>= 1; | |
| 1039 | } | |
| 1040 | ||
| 1041 | // rounding direction | |
| 1042 | if (ix0 | ix1 != 0) { | |
| 1043 | var z = 1.0 - tiny; // raise inexact | |
| 1044 | if (z >= 1.0) { | |
| 1045 | z = 1.0 + tiny; | |
| 1046 | if (q1 == 0xFFFFFFFF) { | |
| 1047 | q1 = 0; | |
| 1048 | q += 1; | |
| 1049 | } else if (z > 1.0) { | |
| 1050 | if (q1 == 0xFFFFFFFE) { | |
| 1051 | q += 1; | |
| 1052 | } | |
| 1053 | q1 += 2; | |
| 1054 | } else { | |
| 1055 | q1 += q1 & 1; | |
| 1056 | } | |
| 1057 | } | |
| 1058 | } | |
| 1059 | ||
| 1060 | ix0 = (q >> 1) + 0x3FE00000; | |
| 1061 | ix1 = q1 >> 1; | |
| 1062 | if (q & 1 != 0) { | |
| 1063 | ix1 |= 0x80000000; | |
| 1064 | } | |
| 1065 | ||
| 1066 | // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same | |
| 1067 | // behaviour at least. | |
| 1068 | var iix0 = @intCast(i32, ix0); | |
| 1069 | iix0 = iix0 +% (m << 20); | |
| 1070 | ||
| 1071 | const uz = (@intCast(u64, iix0) << 32) | ix1; | |
| 1072 | return @bitCast(f64, uz); | |
| 1073 | } | |
| 1074 | ||
| 1075 | test "sqrt" { | |
| 1076 | const V = [_]f64{ | |
| 1077 | 0.0, | |
| 1078 | 4.089288054930154, | |
| 1079 | 7.538757127071935, | |
| 1080 | 8.97780793672623, | |
| 1081 | 5.304443821913729, | |
| 1082 | 5.682408965311888, | |
| 1083 | 0.5846878579110049, | |
| 1084 | 3.650338664297043, | |
| 1085 | 0.3178091951800732, | |
| 1086 | 7.1505232436382835, | |
| 1087 | 3.6589165881946464, | |
| 1088 | }; | |
| 1089 | ||
| 1090 | // Note that @sqrt will either generate the sqrt opcode (if supported by the | |
| 1091 | // target ISA) or a call to `sqrtf` otherwise. | |
| 1092 | for (V) |val| | |
| 1093 | try std.testing.expectEqual(@sqrt(val), sqrt(val)); | |
| 1094 | } | |
| 1095 | ||
| 1096 | test "sqrt special" { | |
| 1097 | try std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64)))); | |
| 1098 | try std.testing.expect(sqrt(0.0) == 0.0); | |
| 1099 | try std.testing.expect(sqrt(-0.0) == -0.0); | |
| 1100 | try std.testing.expect(isNan(sqrt(-1.0))); | |
| 1101 | try std.testing.expect(isNan(sqrt(std.math.nan(f64)))); | |
| 1102 | } | |
| 1103 | ||
| 1104 | export fn sqrtf(x: f32) f32 { | |
| 1105 | const tiny: f32 = 1.0e-30; | |
| 1106 | const sign: i32 = @bitCast(i32, @as(u32, 0x80000000)); | |
| 1107 | var ix: i32 = @bitCast(i32, x); | |
| 1108 | ||
| 1109 | if ((ix & 0x7F800000) == 0x7F800000) { | |
| 1110 | return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan | |
| 1111 | } | |
| 1112 | ||
| 1113 | // zero | |
| 1114 | if (ix <= 0) { | |
| 1115 | if (ix & ~sign == 0) { | |
| 1116 | return x; // sqrt (+-0) = +-0 | |
| 1117 | } | |
| 1118 | if (ix < 0) { | |
| 1119 | return math.snan(f32); | |
| 1120 | } | |
| 1121 | } | |
| 1122 | ||
| 1123 | // normalize | |
| 1124 | var m = ix >> 23; | |
| 1125 | if (m == 0) { | |
| 1126 | // subnormal | |
| 1127 | var i: i32 = 0; | |
| 1128 | while (ix & 0x00800000 == 0) : (i += 1) { | |
| 1129 | ix <<= 1; | |
| 1130 | } | |
| 1131 | m -= i - 1; | |
| 1132 | } | |
| 1133 | ||
| 1134 | m -= 127; // unbias exponent | |
| 1135 | ix = (ix & 0x007FFFFF) | 0x00800000; | |
| 1136 | ||
| 1137 | if (m & 1 != 0) { // odd m, double x to even | |
| 1138 | ix += ix; | |
| 1139 | } | |
| 1140 | ||
| 1141 | m >>= 1; // m = [m / 2] | |
| 1142 | ||
| 1143 | // sqrt(x) bit by bit | |
| 1144 | ix += ix; | |
| 1145 | var q: i32 = 0; // q = sqrt(x) | |
| 1146 | var s: i32 = 0; | |
| 1147 | var r: i32 = 0x01000000; // r = moving bit right -> left | |
| 1148 | ||
| 1149 | while (r != 0) { | |
| 1150 | const t = s + r; | |
| 1151 | if (t <= ix) { | |
| 1152 | s = t + r; | |
| 1153 | ix -= t; | |
| 1154 | q += r; | |
| 1155 | } | |
| 1156 | ix += ix; | |
| 1157 | r >>= 1; | |
| 1158 | } | |
| 1159 | ||
| 1160 | // floating add to find rounding direction | |
| 1161 | if (ix != 0) { | |
| 1162 | var z = 1.0 - tiny; // inexact | |
| 1163 | if (z >= 1.0) { | |
| 1164 | z = 1.0 + tiny; | |
| 1165 | if (z > 1.0) { | |
| 1166 | q += 2; | |
| 1167 | } else { | |
| 1168 | if (q & 1 != 0) { | |
| 1169 | q += 1; | |
| 1170 | } | |
| 1171 | } | |
| 1172 | } | |
| 1173 | } | |
| 1174 | ||
| 1175 | ix = (q >> 1) + 0x3f000000; | |
| 1176 | ix += m << 23; | |
| 1177 | return @bitCast(f32, ix); | |
| 1178 | } | |
| 1179 | ||
| 1180 | test "sqrtf" { | |
| 1181 | const V = [_]f32{ | |
| 1182 | 0.0, | |
| 1183 | 4.089288054930154, | |
| 1184 | 7.538757127071935, | |
| 1185 | 8.97780793672623, | |
| 1186 | 5.304443821913729, | |
| 1187 | 5.682408965311888, | |
| 1188 | 0.5846878579110049, | |
| 1189 | 3.650338664297043, | |
| 1190 | 0.3178091951800732, | |
| 1191 | 7.1505232436382835, | |
| 1192 | 3.6589165881946464, | |
| 1193 | }; | |
| 1194 | ||
| 1195 | // Note that @sqrt will either generate the sqrt opcode (if supported by the | |
| 1196 | // target ISA) or a call to `sqrtf` otherwise. | |
| 1197 | for (V) |val| | |
| 1198 | try std.testing.expectEqual(@sqrt(val), sqrtf(val)); | |
| 1199 | } | |
| 1200 | ||
| 1201 | test "sqrtf special" { | |
| 1202 | try std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32)))); | |
| 1203 | try std.testing.expect(sqrtf(0.0) == 0.0); | |
| 1204 | try std.testing.expect(sqrtf(-0.0) == -0.0); | |
| 1205 | try std.testing.expect(isNan(sqrtf(-1.0))); | |
| 1206 | try std.testing.expect(isNan(sqrtf(std.math.nan(f32)))); | |
| 75 | return dest; | |
| 1207 | 76 | } |
lib/std/special/c_stage1.zig created+1187| ... | ... | @@ -0,0 +1,1187 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const maxInt = std.math.maxInt; | |
| 4 | const isNan = std.math.isNan; | |
| 5 | const native_arch = builtin.cpu.arch; | |
| 6 | const native_abi = builtin.abi; | |
| 7 | const native_os = builtin.os.tag; | |
| 8 | ||
| 9 | const is_wasm = switch (native_arch) { | |
| 10 | .wasm32, .wasm64 => true, | |
| 11 | else => false, | |
| 12 | }; | |
| 13 | const is_msvc = switch (native_abi) { | |
| 14 | .msvc => true, | |
| 15 | else => false, | |
| 16 | }; | |
| 17 | const is_freestanding = switch (native_os) { | |
| 18 | .freestanding => true, | |
| 19 | else => false, | |
| 20 | }; | |
| 21 | comptime { | |
| 22 | if (is_freestanding and is_wasm and builtin.link_libc) { | |
| 23 | @export(wasm_start, .{ .name = "_start", .linkage = .Strong }); | |
| 24 | } | |
| 25 | if (builtin.link_libc) { | |
| 26 | @export(strcmp, .{ .name = "strcmp", .linkage = .Strong }); | |
| 27 | @export(strncmp, .{ .name = "strncmp", .linkage = .Strong }); | |
| 28 | @export(strerror, .{ .name = "strerror", .linkage = .Strong }); | |
| 29 | @export(strlen, .{ .name = "strlen", .linkage = .Strong }); | |
| 30 | @export(strcpy, .{ .name = "strcpy", .linkage = .Strong }); | |
| 31 | @export(strncpy, .{ .name = "strncpy", .linkage = .Strong }); | |
| 32 | @export(strcat, .{ .name = "strcat", .linkage = .Strong }); | |
| 33 | @export(strncat, .{ .name = "strncat", .linkage = .Strong }); | |
| 34 | } else if (is_msvc) { | |
| 35 | @export(_fltused, .{ .name = "_fltused", .linkage = .Strong }); | |
| 36 | } | |
| 37 | } | |
| 38 | ||
| 39 | var _fltused: c_int = 1; | |
| 40 | ||
| 41 | extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; | |
| 42 | fn wasm_start() callconv(.C) void { | |
| 43 | _ = main(0, undefined); | |
| 44 | } | |
| 45 | ||
| 46 | fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 { | |
| 47 | var i: usize = 0; | |
| 48 | while (src[i] != 0) : (i += 1) { | |
| 49 | dest[i] = src[i]; | |
| 50 | } | |
| 51 | dest[i] = 0; | |
| 52 | ||
| 53 | return dest; | |
| 54 | } | |
| 55 | ||
| 56 | test "strcpy" { | |
| 57 | var s1: [9:0]u8 = undefined; | |
| 58 | ||
| 59 | s1[0] = 0; | |
| 60 | _ = strcpy(&s1, "foobarbaz"); | |
| 61 | try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); | |
| 62 | } | |
| 63 | ||
| 64 | fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8 { | |
| 65 | var i: usize = 0; | |
| 66 | while (i < n and src[i] != 0) : (i += 1) { | |
| 67 | dest[i] = src[i]; | |
| 68 | } | |
| 69 | while (i < n) : (i += 1) { | |
| 70 | dest[i] = 0; | |
| 71 | } | |
| 72 | ||
| 73 | return dest; | |
| 74 | } | |
| 75 | ||
| 76 | test "strncpy" { | |
| 77 | var s1: [9:0]u8 = undefined; | |
| 78 | ||
| 79 | s1[0] = 0; | |
| 80 | _ = strncpy(&s1, "foobarbaz", @sizeOf(@TypeOf(s1))); | |
| 81 | try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); | |
| 82 | } | |
| 83 | ||
| 84 | fn strcat(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 { | |
| 85 | var dest_end: usize = 0; | |
| 86 | while (dest[dest_end] != 0) : (dest_end += 1) {} | |
| 87 | ||
| 88 | var i: usize = 0; | |
| 89 | while (src[i] != 0) : (i += 1) { | |
| 90 | dest[dest_end + i] = src[i]; | |
| 91 | } | |
| 92 | dest[dest_end + i] = 0; | |
| 93 | ||
| 94 | return dest; | |
| 95 | } | |
| 96 | ||
| 97 | test "strcat" { | |
| 98 | var s1: [9:0]u8 = undefined; | |
| 99 | ||
| 100 | s1[0] = 0; | |
| 101 | _ = strcat(&s1, "foo"); | |
| 102 | _ = strcat(&s1, "bar"); | |
| 103 | _ = strcat(&s1, "baz"); | |
| 104 | try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); | |
| 105 | } | |
| 106 | ||
| 107 | fn strncat(dest: [*:0]u8, src: [*:0]const u8, avail: usize) callconv(.C) [*:0]u8 { | |
| 108 | var dest_end: usize = 0; | |
| 109 | while (dest[dest_end] != 0) : (dest_end += 1) {} | |
| 110 | ||
| 111 | var i: usize = 0; | |
| 112 | while (i < avail and src[i] != 0) : (i += 1) { | |
| 113 | dest[dest_end + i] = src[i]; | |
| 114 | } | |
| 115 | dest[dest_end + i] = 0; | |
| 116 | ||
| 117 | return dest; | |
| 118 | } | |
| 119 | ||
| 120 | test "strncat" { | |
| 121 | var s1: [9:0]u8 = undefined; | |
| 122 | ||
| 123 | s1[0] = 0; | |
| 124 | _ = strncat(&s1, "foo1111", 3); | |
| 125 | _ = strncat(&s1, "bar1111", 3); | |
| 126 | _ = strncat(&s1, "baz1111", 3); | |
| 127 | try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); | |
| 128 | } | |
| 129 | ||
| 130 | fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int { | |
| 131 | return std.cstr.cmp(s1, s2); | |
| 132 | } | |
| 133 | ||
| 134 | fn strlen(s: [*:0]const u8) callconv(.C) usize { | |
| 135 | return std.mem.len(s); | |
| 136 | } | |
| 137 | ||
| 138 | fn strncmp(_l: [*:0]const u8, _r: [*:0]const u8, _n: usize) callconv(.C) c_int { | |
| 139 | if (_n == 0) return 0; | |
| 140 | var l = _l; | |
| 141 | var r = _r; | |
| 142 | var n = _n - 1; | |
| 143 | while (l[0] != 0 and r[0] != 0 and n != 0 and l[0] == r[0]) { | |
| 144 | l += 1; | |
| 145 | r += 1; | |
| 146 | n -= 1; | |
| 147 | } | |
| 148 | return @as(c_int, l[0]) - @as(c_int, r[0]); | |
| 149 | } | |
| 150 | ||
| 151 | fn strerror(errnum: c_int) callconv(.C) [*:0]const u8 { | |
| 152 | _ = errnum; | |
| 153 | return "TODO strerror implementation"; | |
| 154 | } | |
| 155 | ||
| 156 | test "strncmp" { | |
| 157 | try std.testing.expect(strncmp("a", "b", 1) == -1); | |
| 158 | try std.testing.expect(strncmp("a", "c", 1) == -2); | |
| 159 | try std.testing.expect(strncmp("b", "a", 1) == 1); | |
| 160 | try std.testing.expect(strncmp("\xff", "\x02", 1) == 253); | |
| 161 | } | |
| 162 | ||
| 163 | export fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8 { | |
| 164 | @setRuntimeSafety(false); | |
| 165 | ||
| 166 | var index: usize = 0; | |
| 167 | while (index != n) : (index += 1) | |
| 168 | dest.?[index] = c; | |
| 169 | ||
| 170 | return dest; | |
| 171 | } | |
| 172 | ||
| 173 | export fn __memset(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { | |
| 174 | if (dest_n < n) | |
| 175 | @panic("buffer overflow"); | |
| 176 | return memset(dest, c, n); | |
| 177 | } | |
| 178 | ||
| 179 | export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 { | |
| 180 | @setRuntimeSafety(false); | |
| 181 | ||
| 182 | var index: usize = 0; | |
| 183 | while (index != n) : (index += 1) | |
| 184 | dest.?[index] = src.?[index]; | |
| 185 | ||
| 186 | return dest; | |
| 187 | } | |
| 188 | ||
| 189 | export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 { | |
| 190 | @setRuntimeSafety(false); | |
| 191 | ||
| 192 | if (@ptrToInt(dest) < @ptrToInt(src)) { | |
| 193 | var index: usize = 0; | |
| 194 | while (index != n) : (index += 1) { | |
| 195 | dest.?[index] = src.?[index]; | |
| 196 | } | |
| 197 | } else { | |
| 198 | var index = n; | |
| 199 | while (index != 0) { | |
| 200 | index -= 1; | |
| 201 | dest.?[index] = src.?[index]; | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | return dest; | |
| 206 | } | |
| 207 | ||
| 208 | export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) c_int { | |
| 209 | @setRuntimeSafety(false); | |
| 210 | ||
| 211 | var index: usize = 0; | |
| 212 | while (index != n) : (index += 1) { | |
| 213 | const compare_val = @bitCast(i8, vl.?[index] -% vr.?[index]); | |
| 214 | if (compare_val != 0) { | |
| 215 | return compare_val; | |
| 216 | } | |
| 217 | } | |
| 218 | ||
| 219 | return 0; | |
| 220 | } | |
| 221 | ||
| 222 | test "memcmp" { | |
| 223 | const base_arr = &[_]u8{ 1, 1, 1 }; | |
| 224 | const arr1 = &[_]u8{ 1, 1, 1 }; | |
| 225 | const arr2 = &[_]u8{ 1, 0, 1 }; | |
| 226 | const arr3 = &[_]u8{ 1, 2, 1 }; | |
| 227 | ||
| 228 | try std.testing.expect(memcmp(base_arr[0..], arr1[0..], base_arr.len) == 0); | |
| 229 | try std.testing.expect(memcmp(base_arr[0..], arr2[0..], base_arr.len) > 0); | |
| 230 | try std.testing.expect(memcmp(base_arr[0..], arr3[0..], base_arr.len) < 0); | |
| 231 | } | |
| 232 | ||
| 233 | export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) callconv(.C) c_int { | |
| 234 | @setRuntimeSafety(false); | |
| 235 | ||
| 236 | var index: usize = 0; | |
| 237 | while (index != n) : (index += 1) { | |
| 238 | if (vl[index] != vr[index]) { | |
| 239 | return 1; | |
| 240 | } | |
| 241 | } | |
| 242 | ||
| 243 | return 0; | |
| 244 | } | |
| 245 | ||
| 246 | test "bcmp" { | |
| 247 | const base_arr = &[_]u8{ 1, 1, 1 }; | |
| 248 | const arr1 = &[_]u8{ 1, 1, 1 }; | |
| 249 | const arr2 = &[_]u8{ 1, 0, 1 }; | |
| 250 | const arr3 = &[_]u8{ 1, 2, 1 }; | |
| 251 | ||
| 252 | try std.testing.expect(bcmp(base_arr[0..], arr1[0..], base_arr.len) == 0); | |
| 253 | try std.testing.expect(bcmp(base_arr[0..], arr2[0..], base_arr.len) != 0); | |
| 254 | try std.testing.expect(bcmp(base_arr[0..], arr3[0..], base_arr.len) != 0); | |
| 255 | } | |
| 256 | ||
| 257 | comptime { | |
| 258 | if (native_os == .linux) { | |
| 259 | @export(clone, .{ .name = "clone" }); | |
| 260 | } | |
| 261 | } | |
| 262 | ||
| 263 | // TODO we should be able to put this directly in std/linux/x86_64.zig but | |
| 264 | // it causes a segfault in release mode. this is a workaround of calling it | |
| 265 | // across .o file boundaries. fix comptime @ptrCast of nakedcc functions. | |
| 266 | fn clone() callconv(.Naked) void { | |
| 267 | switch (native_arch) { | |
| 268 | .i386 => { | |
| 269 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 270 | // +8, +12, +16, +20, +24, +28, +32 | |
| 271 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 272 | // eax, ebx, ecx, edx, esi, edi | |
| 273 | asm volatile ( | |
| 274 | \\ push %%ebp | |
| 275 | \\ mov %%esp,%%ebp | |
| 276 | \\ push %%ebx | |
| 277 | \\ push %%esi | |
| 278 | \\ push %%edi | |
| 279 | \\ // Setup the arguments | |
| 280 | \\ mov 16(%%ebp),%%ebx | |
| 281 | \\ mov 12(%%ebp),%%ecx | |
| 282 | \\ and $-16,%%ecx | |
| 283 | \\ sub $20,%%ecx | |
| 284 | \\ mov 20(%%ebp),%%eax | |
| 285 | \\ mov %%eax,4(%%ecx) | |
| 286 | \\ mov 8(%%ebp),%%eax | |
| 287 | \\ mov %%eax,0(%%ecx) | |
| 288 | \\ mov 24(%%ebp),%%edx | |
| 289 | \\ mov 28(%%ebp),%%esi | |
| 290 | \\ mov 32(%%ebp),%%edi | |
| 291 | \\ mov $120,%%eax | |
| 292 | \\ int $128 | |
| 293 | \\ test %%eax,%%eax | |
| 294 | \\ jnz 1f | |
| 295 | \\ pop %%eax | |
| 296 | \\ xor %%ebp,%%ebp | |
| 297 | \\ call *%%eax | |
| 298 | \\ mov %%eax,%%ebx | |
| 299 | \\ xor %%eax,%%eax | |
| 300 | \\ inc %%eax | |
| 301 | \\ int $128 | |
| 302 | \\ hlt | |
| 303 | \\1: | |
| 304 | \\ pop %%edi | |
| 305 | \\ pop %%esi | |
| 306 | \\ pop %%ebx | |
| 307 | \\ pop %%ebp | |
| 308 | \\ ret | |
| 309 | ); | |
| 310 | }, | |
| 311 | .x86_64 => { | |
| 312 | asm volatile ( | |
| 313 | \\ xor %%eax,%%eax | |
| 314 | \\ mov $56,%%al // SYS_clone | |
| 315 | \\ mov %%rdi,%%r11 | |
| 316 | \\ mov %%rdx,%%rdi | |
| 317 | \\ mov %%r8,%%rdx | |
| 318 | \\ mov %%r9,%%r8 | |
| 319 | \\ mov 8(%%rsp),%%r10 | |
| 320 | \\ mov %%r11,%%r9 | |
| 321 | \\ and $-16,%%rsi | |
| 322 | \\ sub $8,%%rsi | |
| 323 | \\ mov %%rcx,(%%rsi) | |
| 324 | \\ syscall | |
| 325 | \\ test %%eax,%%eax | |
| 326 | \\ jnz 1f | |
| 327 | \\ xor %%ebp,%%ebp | |
| 328 | \\ pop %%rdi | |
| 329 | \\ call *%%r9 | |
| 330 | \\ mov %%eax,%%edi | |
| 331 | \\ xor %%eax,%%eax | |
| 332 | \\ mov $60,%%al // SYS_exit | |
| 333 | \\ syscall | |
| 334 | \\ hlt | |
| 335 | \\1: ret | |
| 336 | \\ | |
| 337 | ); | |
| 338 | }, | |
| 339 | .aarch64 => { | |
| 340 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 341 | // x0, x1, w2, x3, x4, x5, x6 | |
| 342 | ||
| 343 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 344 | // x8, x0, x1, x2, x3, x4 | |
| 345 | asm volatile ( | |
| 346 | \\ // align stack and save func,arg | |
| 347 | \\ and x1,x1,#-16 | |
| 348 | \\ stp x0,x3,[x1,#-16]! | |
| 349 | \\ | |
| 350 | \\ // syscall | |
| 351 | \\ uxtw x0,w2 | |
| 352 | \\ mov x2,x4 | |
| 353 | \\ mov x3,x5 | |
| 354 | \\ mov x4,x6 | |
| 355 | \\ mov x8,#220 // SYS_clone | |
| 356 | \\ svc #0 | |
| 357 | \\ | |
| 358 | \\ cbz x0,1f | |
| 359 | \\ // parent | |
| 360 | \\ ret | |
| 361 | \\ // child | |
| 362 | \\1: ldp x1,x0,[sp],#16 | |
| 363 | \\ blr x1 | |
| 364 | \\ mov x8,#93 // SYS_exit | |
| 365 | \\ svc #0 | |
| 366 | ); | |
| 367 | }, | |
| 368 | .arm, .thumb => { | |
| 369 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 370 | // r0, r1, r2, r3, +0, +4, +8 | |
| 371 | ||
| 372 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 373 | // r7 r0, r1, r2, r3, r4 | |
| 374 | asm volatile ( | |
| 375 | \\ stmfd sp!,{r4,r5,r6,r7} | |
| 376 | \\ mov r7,#120 | |
| 377 | \\ mov r6,r3 | |
| 378 | \\ mov r5,r0 | |
| 379 | \\ mov r0,r2 | |
| 380 | \\ and r1,r1,#-16 | |
| 381 | \\ ldr r2,[sp,#16] | |
| 382 | \\ ldr r3,[sp,#20] | |
| 383 | \\ ldr r4,[sp,#24] | |
| 384 | \\ svc 0 | |
| 385 | \\ tst r0,r0 | |
| 386 | \\ beq 1f | |
| 387 | \\ ldmfd sp!,{r4,r5,r6,r7} | |
| 388 | \\ bx lr | |
| 389 | \\ | |
| 390 | \\1: mov r0,r6 | |
| 391 | \\ bl 3f | |
| 392 | \\2: mov r7,#1 | |
| 393 | \\ svc 0 | |
| 394 | \\ b 2b | |
| 395 | \\3: bx r5 | |
| 396 | ); | |
| 397 | }, | |
| 398 | .riscv64 => { | |
| 399 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 400 | // a0, a1, a2, a3, a4, a5, a6 | |
| 401 | ||
| 402 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 403 | // a7 a0, a1, a2, a3, a4 | |
| 404 | asm volatile ( | |
| 405 | \\ # Save func and arg to stack | |
| 406 | \\ addi a1, a1, -16 | |
| 407 | \\ sd a0, 0(a1) | |
| 408 | \\ sd a3, 8(a1) | |
| 409 | \\ | |
| 410 | \\ # Call SYS_clone | |
| 411 | \\ mv a0, a2 | |
| 412 | \\ mv a2, a4 | |
| 413 | \\ mv a3, a5 | |
| 414 | \\ mv a4, a6 | |
| 415 | \\ li a7, 220 # SYS_clone | |
| 416 | \\ ecall | |
| 417 | \\ | |
| 418 | \\ beqz a0, 1f | |
| 419 | \\ # Parent | |
| 420 | \\ ret | |
| 421 | \\ | |
| 422 | \\ # Child | |
| 423 | \\1: ld a1, 0(sp) | |
| 424 | \\ ld a0, 8(sp) | |
| 425 | \\ jalr a1 | |
| 426 | \\ | |
| 427 | \\ # Exit | |
| 428 | \\ li a7, 93 # SYS_exit | |
| 429 | \\ ecall | |
| 430 | ); | |
| 431 | }, | |
| 432 | .mips, .mipsel => { | |
| 433 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 434 | // 3, 4, 5, 6, 7, 8, 9 | |
| 435 | ||
| 436 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 437 | // 2 4, 5, 6, 7, 8 | |
| 438 | asm volatile ( | |
| 439 | \\ # Save function pointer and argument pointer on new thread stack | |
| 440 | \\ and $5, $5, -8 | |
| 441 | \\ subu $5, $5, 16 | |
| 442 | \\ sw $4, 0($5) | |
| 443 | \\ sw $7, 4($5) | |
| 444 | \\ # Shuffle (fn,sp,fl,arg,ptid,tls,ctid) to (fl,sp,ptid,tls,ctid) | |
| 445 | \\ move $4, $6 | |
| 446 | \\ lw $6, 16($sp) | |
| 447 | \\ lw $7, 20($sp) | |
| 448 | \\ lw $9, 24($sp) | |
| 449 | \\ subu $sp, $sp, 16 | |
| 450 | \\ sw $9, 16($sp) | |
| 451 | \\ li $2, 4120 | |
| 452 | \\ syscall | |
| 453 | \\ beq $7, $0, 1f | |
| 454 | \\ nop | |
| 455 | \\ addu $sp, $sp, 16 | |
| 456 | \\ jr $ra | |
| 457 | \\ subu $2, $0, $2 | |
| 458 | \\1: | |
| 459 | \\ beq $2, $0, 1f | |
| 460 | \\ nop | |
| 461 | \\ addu $sp, $sp, 16 | |
| 462 | \\ jr $ra | |
| 463 | \\ nop | |
| 464 | \\1: | |
| 465 | \\ lw $25, 0($sp) | |
| 466 | \\ lw $4, 4($sp) | |
| 467 | \\ jalr $25 | |
| 468 | \\ nop | |
| 469 | \\ move $4, $2 | |
| 470 | \\ li $2, 4001 | |
| 471 | \\ syscall | |
| 472 | ); | |
| 473 | }, | |
| 474 | .powerpc => { | |
| 475 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 476 | // 3, 4, 5, 6, 7, 8, 9 | |
| 477 | ||
| 478 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 479 | // 0 3, 4, 5, 6, 7 | |
| 480 | asm volatile ( | |
| 481 | \\# store non-volatile regs r30, r31 on stack in order to put our | |
| 482 | \\# start func and its arg there | |
| 483 | \\stwu 30, -16(1) | |
| 484 | \\stw 31, 4(1) | |
| 485 | \\ | |
| 486 | \\# save r3 (func) into r30, and r6(arg) into r31 | |
| 487 | \\mr 30, 3 | |
| 488 | \\mr 31, 6 | |
| 489 | \\ | |
| 490 | \\# create initial stack frame for new thread | |
| 491 | \\clrrwi 4, 4, 4 | |
| 492 | \\li 0, 0 | |
| 493 | \\stwu 0, -16(4) | |
| 494 | \\ | |
| 495 | \\#move c into first arg | |
| 496 | \\mr 3, 5 | |
| 497 | \\#mr 4, 4 | |
| 498 | \\mr 5, 7 | |
| 499 | \\mr 6, 8 | |
| 500 | \\mr 7, 9 | |
| 501 | \\ | |
| 502 | \\# move syscall number into r0 | |
| 503 | \\li 0, 120 | |
| 504 | \\ | |
| 505 | \\sc | |
| 506 | \\ | |
| 507 | \\# check for syscall error | |
| 508 | \\bns+ 1f # jump to label 1 if no summary overflow. | |
| 509 | \\#else | |
| 510 | \\neg 3, 3 #negate the result (errno) | |
| 511 | \\1: | |
| 512 | \\# compare sc result with 0 | |
| 513 | \\cmpwi cr7, 3, 0 | |
| 514 | \\ | |
| 515 | \\# if not 0, jump to end | |
| 516 | \\bne cr7, 2f | |
| 517 | \\ | |
| 518 | \\#else: we're the child | |
| 519 | \\#call funcptr: move arg (d) into r3 | |
| 520 | \\mr 3, 31 | |
| 521 | \\#move r30 (funcptr) into CTR reg | |
| 522 | \\mtctr 30 | |
| 523 | \\# call CTR reg | |
| 524 | \\bctrl | |
| 525 | \\# mov SYS_exit into r0 (the exit param is already in r3) | |
| 526 | \\li 0, 1 | |
| 527 | \\sc | |
| 528 | \\ | |
| 529 | \\2: | |
| 530 | \\ | |
| 531 | \\# restore stack | |
| 532 | \\lwz 30, 0(1) | |
| 533 | \\lwz 31, 4(1) | |
| 534 | \\addi 1, 1, 16 | |
| 535 | \\ | |
| 536 | \\blr | |
| 537 | ); | |
| 538 | }, | |
| 539 | .powerpc64, .powerpc64le => { | |
| 540 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 541 | // 3, 4, 5, 6, 7, 8, 9 | |
| 542 | ||
| 543 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 544 | // 0 3, 4, 5, 6, 7 | |
| 545 | asm volatile ( | |
| 546 | \\ # create initial stack frame for new thread | |
| 547 | \\ clrrdi 4, 4, 4 | |
| 548 | \\ li 0, 0 | |
| 549 | \\ stdu 0,-32(4) | |
| 550 | \\ | |
| 551 | \\ # save fn and arg to child stack | |
| 552 | \\ std 3, 8(4) | |
| 553 | \\ std 6, 16(4) | |
| 554 | \\ | |
| 555 | \\ # shuffle args into correct registers and call SYS_clone | |
| 556 | \\ mr 3, 5 | |
| 557 | \\ #mr 4, 4 | |
| 558 | \\ mr 5, 7 | |
| 559 | \\ mr 6, 8 | |
| 560 | \\ mr 7, 9 | |
| 561 | \\ li 0, 120 # SYS_clone = 120 | |
| 562 | \\ sc | |
| 563 | \\ | |
| 564 | \\ # if error, negate return (errno) | |
| 565 | \\ bns+ 1f | |
| 566 | \\ neg 3, 3 | |
| 567 | \\ | |
| 568 | \\1: | |
| 569 | \\ # if we're the parent, return | |
| 570 | \\ cmpwi cr7, 3, 0 | |
| 571 | \\ bnelr cr7 | |
| 572 | \\ | |
| 573 | \\ # we're the child. call fn(arg) | |
| 574 | \\ ld 3, 16(1) | |
| 575 | \\ ld 12, 8(1) | |
| 576 | \\ mtctr 12 | |
| 577 | \\ bctrl | |
| 578 | \\ | |
| 579 | \\ # call SYS_exit. exit code is already in r3 from fn return value | |
| 580 | \\ li 0, 1 # SYS_exit = 1 | |
| 581 | \\ sc | |
| 582 | ); | |
| 583 | }, | |
| 584 | .sparcv9 => { | |
| 585 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 586 | // i0, i1, i2, i3, i4, i5, sp | |
| 587 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 588 | // g1 o0, o1, o2, o3, o4 | |
| 589 | asm volatile ( | |
| 590 | \\ save %%sp, -192, %%sp | |
| 591 | \\ # Save the func pointer and the arg pointer | |
| 592 | \\ mov %%i0, %%g2 | |
| 593 | \\ mov %%i3, %%g3 | |
| 594 | \\ # Shuffle the arguments | |
| 595 | \\ mov 217, %%g1 | |
| 596 | \\ mov %%i2, %%o0 | |
| 597 | \\ # Add some extra space for the initial frame | |
| 598 | \\ sub %%i1, 176 + 2047, %%o1 | |
| 599 | \\ mov %%i4, %%o2 | |
| 600 | \\ mov %%i5, %%o3 | |
| 601 | \\ ldx [%%fp + 0x8af], %%o4 | |
| 602 | \\ t 0x6d | |
| 603 | \\ bcs,pn %%xcc, 2f | |
| 604 | \\ nop | |
| 605 | \\ # The child pid is returned in o0 while o1 tells if this | |
| 606 | \\ # process is # the child (=1) or the parent (=0). | |
| 607 | \\ brnz %%o1, 1f | |
| 608 | \\ nop | |
| 609 | \\ # Parent process, return the child pid | |
| 610 | \\ mov %%o0, %%i0 | |
| 611 | \\ ret | |
| 612 | \\ restore | |
| 613 | \\1: | |
| 614 | \\ # Child process, call func(arg) | |
| 615 | \\ mov %%g0, %%fp | |
| 616 | \\ call %%g2 | |
| 617 | \\ mov %%g3, %%o0 | |
| 618 | \\ # Exit | |
| 619 | \\ mov 1, %%g1 | |
| 620 | \\ t 0x6d | |
| 621 | \\2: | |
| 622 | \\ # The syscall failed | |
| 623 | \\ sub %%g0, %%o0, %%i0 | |
| 624 | \\ ret | |
| 625 | \\ restore | |
| 626 | ); | |
| 627 | }, | |
| 628 | else => @compileError("Implement clone() for this arch."), | |
| 629 | } | |
| 630 | } | |
| 631 | ||
| 632 | const math = std.math; | |
| 633 | ||
| 634 | export fn fmodf(x: f32, y: f32) f32 { | |
| 635 | return generic_fmod(f32, x, y); | |
| 636 | } | |
| 637 | export fn fmod(x: f64, y: f64) f64 { | |
| 638 | return generic_fmod(f64, x, y); | |
| 639 | } | |
| 640 | ||
| 641 | // TODO add intrinsics for these (and probably the double version too) | |
| 642 | // and have the math stuff use the intrinsic. same as @mod and @rem | |
| 643 | export fn floorf(x: f32) f32 { | |
| 644 | return math.floor(x); | |
| 645 | } | |
| 646 | ||
| 647 | export fn ceilf(x: f32) f32 { | |
| 648 | return math.ceil(x); | |
| 649 | } | |
| 650 | ||
| 651 | export fn floor(x: f64) f64 { | |
| 652 | return math.floor(x); | |
| 653 | } | |
| 654 | ||
| 655 | export fn ceil(x: f64) f64 { | |
| 656 | return math.ceil(x); | |
| 657 | } | |
| 658 | ||
| 659 | export fn fma(a: f64, b: f64, c: f64) f64 { | |
| 660 | return math.fma(f64, a, b, c); | |
| 661 | } | |
| 662 | ||
| 663 | export fn fmaf(a: f32, b: f32, c: f32) f32 { | |
| 664 | return math.fma(f32, a, b, c); | |
| 665 | } | |
| 666 | ||
| 667 | export fn sin(a: f64) f64 { | |
| 668 | return math.sin(a); | |
| 669 | } | |
| 670 | ||
| 671 | export fn sinf(a: f32) f32 { | |
| 672 | return math.sin(a); | |
| 673 | } | |
| 674 | ||
| 675 | export fn cos(a: f64) f64 { | |
| 676 | return math.cos(a); | |
| 677 | } | |
| 678 | ||
| 679 | export fn cosf(a: f32) f32 { | |
| 680 | return math.cos(a); | |
| 681 | } | |
| 682 | ||
| 683 | export fn sincos(a: f64, r_sin: *f64, r_cos: *f64) void { | |
| 684 | r_sin.* = math.sin(a); | |
| 685 | r_cos.* = math.cos(a); | |
| 686 | } | |
| 687 | ||
| 688 | export fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) void { | |
| 689 | r_sin.* = math.sin(a); | |
| 690 | r_cos.* = math.cos(a); | |
| 691 | } | |
| 692 | ||
| 693 | export fn exp(a: f64) f64 { | |
| 694 | return math.exp(a); | |
| 695 | } | |
| 696 | ||
| 697 | export fn expf(a: f32) f32 { | |
| 698 | return math.exp(a); | |
| 699 | } | |
| 700 | ||
| 701 | export fn exp2(a: f64) f64 { | |
| 702 | return math.exp2(a); | |
| 703 | } | |
| 704 | ||
| 705 | export fn exp2f(a: f32) f32 { | |
| 706 | return math.exp2(a); | |
| 707 | } | |
| 708 | ||
| 709 | export fn log(a: f64) f64 { | |
| 710 | return math.ln(a); | |
| 711 | } | |
| 712 | ||
| 713 | export fn logf(a: f32) f32 { | |
| 714 | return math.ln(a); | |
| 715 | } | |
| 716 | ||
| 717 | export fn log2(a: f64) f64 { | |
| 718 | return math.log2(a); | |
| 719 | } | |
| 720 | ||
| 721 | export fn log2f(a: f32) f32 { | |
| 722 | return math.log2(a); | |
| 723 | } | |
| 724 | ||
| 725 | export fn log10(a: f64) f64 { | |
| 726 | return math.log10(a); | |
| 727 | } | |
| 728 | ||
| 729 | export fn log10f(a: f32) f32 { | |
| 730 | return math.log10(a); | |
| 731 | } | |
| 732 | ||
| 733 | export fn fabs(a: f64) f64 { | |
| 734 | return math.fabs(a); | |
| 735 | } | |
| 736 | ||
| 737 | export fn fabsf(a: f32) f32 { | |
| 738 | return math.fabs(a); | |
| 739 | } | |
| 740 | ||
| 741 | export fn trunc(a: f64) f64 { | |
| 742 | return math.trunc(a); | |
| 743 | } | |
| 744 | ||
| 745 | export fn truncf(a: f32) f32 { | |
| 746 | return math.trunc(a); | |
| 747 | } | |
| 748 | ||
| 749 | export fn round(a: f64) f64 { | |
| 750 | return math.round(a); | |
| 751 | } | |
| 752 | ||
| 753 | export fn roundf(a: f32) f32 { | |
| 754 | return math.round(a); | |
| 755 | } | |
| 756 | ||
| 757 | fn generic_fmod(comptime T: type, x: T, y: T) T { | |
| 758 | @setRuntimeSafety(false); | |
| 759 | ||
| 760 | const bits = @typeInfo(T).Float.bits; | |
| 761 | const uint = std.meta.Int(.unsigned, bits); | |
| 762 | const log2uint = math.Log2Int(uint); | |
| 763 | const digits = if (T == f32) 23 else 52; | |
| 764 | const exp_bits = if (T == f32) 9 else 12; | |
| 765 | const bits_minus_1 = bits - 1; | |
| 766 | const mask = if (T == f32) 0xff else 0x7ff; | |
| 767 | var ux = @bitCast(uint, x); | |
| 768 | var uy = @bitCast(uint, y); | |
| 769 | var ex = @intCast(i32, (ux >> digits) & mask); | |
| 770 | var ey = @intCast(i32, (uy >> digits) & mask); | |
| 771 | const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1); | |
| 772 | var i: uint = undefined; | |
| 773 | ||
| 774 | if (uy << 1 == 0 or isNan(@bitCast(T, uy)) or ex == mask) | |
| 775 | return (x * y) / (x * y); | |
| 776 | ||
| 777 | if (ux << 1 <= uy << 1) { | |
| 778 | if (ux << 1 == uy << 1) | |
| 779 | return 0 * x; | |
| 780 | return x; | |
| 781 | } | |
| 782 | ||
| 783 | // normalize x and y | |
| 784 | if (ex == 0) { | |
| 785 | i = ux << exp_bits; | |
| 786 | while (i >> bits_minus_1 == 0) : ({ | |
| 787 | ex -= 1; | |
| 788 | i <<= 1; | |
| 789 | }) {} | |
| 790 | ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1)); | |
| 791 | } else { | |
| 792 | ux &= maxInt(uint) >> exp_bits; | |
| 793 | ux |= 1 << digits; | |
| 794 | } | |
| 795 | if (ey == 0) { | |
| 796 | i = uy << exp_bits; | |
| 797 | while (i >> bits_minus_1 == 0) : ({ | |
| 798 | ey -= 1; | |
| 799 | i <<= 1; | |
| 800 | }) {} | |
| 801 | uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1)); | |
| 802 | } else { | |
| 803 | uy &= maxInt(uint) >> exp_bits; | |
| 804 | uy |= 1 << digits; | |
| 805 | } | |
| 806 | ||
| 807 | // x mod y | |
| 808 | while (ex > ey) : (ex -= 1) { | |
| 809 | i = ux -% uy; | |
| 810 | if (i >> bits_minus_1 == 0) { | |
| 811 | if (i == 0) | |
| 812 | return 0 * x; | |
| 813 | ux = i; | |
| 814 | } | |
| 815 | ux <<= 1; | |
| 816 | } | |
| 817 | i = ux -% uy; | |
| 818 | if (i >> bits_minus_1 == 0) { | |
| 819 | if (i == 0) | |
| 820 | return 0 * x; | |
| 821 | ux = i; | |
| 822 | } | |
| 823 | while (ux >> digits == 0) : ({ | |
| 824 | ux <<= 1; | |
| 825 | ex -= 1; | |
| 826 | }) {} | |
| 827 | ||
| 828 | // scale result up | |
| 829 | if (ex > 0) { | |
| 830 | ux -%= 1 << digits; | |
| 831 | ux |= @as(uint, @bitCast(u32, ex)) << digits; | |
| 832 | } else { | |
| 833 | ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1)); | |
| 834 | } | |
| 835 | if (T == f32) { | |
| 836 | ux |= sx; | |
| 837 | } else { | |
| 838 | ux |= @intCast(uint, sx) << bits_minus_1; | |
| 839 | } | |
| 840 | return @bitCast(T, ux); | |
| 841 | } | |
| 842 | ||
| 843 | test "fmod, fmodf" { | |
| 844 | inline for ([_]type{ f32, f64 }) |T| { | |
| 845 | const nan_val = math.nan(T); | |
| 846 | const inf_val = math.inf(T); | |
| 847 | ||
| 848 | try std.testing.expect(isNan(generic_fmod(T, nan_val, 1.0))); | |
| 849 | try std.testing.expect(isNan(generic_fmod(T, 1.0, nan_val))); | |
| 850 | try std.testing.expect(isNan(generic_fmod(T, inf_val, 1.0))); | |
| 851 | try std.testing.expect(isNan(generic_fmod(T, 0.0, 0.0))); | |
| 852 | try std.testing.expect(isNan(generic_fmod(T, 1.0, 0.0))); | |
| 853 | ||
| 854 | try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0)); | |
| 855 | try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0)); | |
| 856 | ||
| 857 | try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0)); | |
| 858 | try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0)); | |
| 859 | try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0)); | |
| 860 | try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0)); | |
| 861 | } | |
| 862 | } | |
| 863 | ||
| 864 | fn generic_fmin(comptime T: type, x: T, y: T) T { | |
| 865 | if (isNan(x)) | |
| 866 | return y; | |
| 867 | if (isNan(y)) | |
| 868 | return x; | |
| 869 | return if (x < y) x else y; | |
| 870 | } | |
| 871 | ||
| 872 | export fn fminf(x: f32, y: f32) callconv(.C) f32 { | |
| 873 | return generic_fmin(f32, x, y); | |
| 874 | } | |
| 875 | ||
| 876 | export fn fmin(x: f64, y: f64) callconv(.C) f64 { | |
| 877 | return generic_fmin(f64, x, y); | |
| 878 | } | |
| 879 | ||
| 880 | test "fmin, fminf" { | |
| 881 | inline for ([_]type{ f32, f64 }) |T| { | |
| 882 | const nan_val = math.nan(T); | |
| 883 | ||
| 884 | try std.testing.expect(isNan(generic_fmin(T, nan_val, nan_val))); | |
| 885 | try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0)); | |
| 886 | try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val)); | |
| 887 | ||
| 888 | try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0)); | |
| 889 | try std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0)); | |
| 890 | } | |
| 891 | } | |
| 892 | ||
| 893 | fn generic_fmax(comptime T: type, x: T, y: T) T { | |
| 894 | if (isNan(x)) | |
| 895 | return y; | |
| 896 | if (isNan(y)) | |
| 897 | return x; | |
| 898 | return if (x < y) y else x; | |
| 899 | } | |
| 900 | ||
| 901 | export fn fmaxf(x: f32, y: f32) callconv(.C) f32 { | |
| 902 | return generic_fmax(f32, x, y); | |
| 903 | } | |
| 904 | ||
| 905 | export fn fmax(x: f64, y: f64) callconv(.C) f64 { | |
| 906 | return generic_fmax(f64, x, y); | |
| 907 | } | |
| 908 | ||
| 909 | test "fmax, fmaxf" { | |
| 910 | inline for ([_]type{ f32, f64 }) |T| { | |
| 911 | const nan_val = math.nan(T); | |
| 912 | ||
| 913 | try std.testing.expect(isNan(generic_fmax(T, nan_val, nan_val))); | |
| 914 | try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0)); | |
| 915 | try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val)); | |
| 916 | ||
| 917 | try std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0)); | |
| 918 | try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0)); | |
| 919 | } | |
| 920 | } | |
| 921 | ||
| 922 | // NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound | |
| 923 | // behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are | |
| 924 | // potentially some edge cases remaining that are not handled in the same way. | |
| 925 | export fn sqrt(x: f64) f64 { | |
| 926 | const tiny: f64 = 1.0e-300; | |
| 927 | const sign: u32 = 0x80000000; | |
| 928 | const u = @bitCast(u64, x); | |
| 929 | ||
| 930 | var ix0 = @intCast(u32, u >> 32); | |
| 931 | var ix1 = @intCast(u32, u & 0xFFFFFFFF); | |
| 932 | ||
| 933 | // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan | |
| 934 | if (ix0 & 0x7FF00000 == 0x7FF00000) { | |
| 935 | return x * x + x; | |
| 936 | } | |
| 937 | ||
| 938 | // sqrt(+-0) = +-0 | |
| 939 | if (x == 0.0) { | |
| 940 | return x; | |
| 941 | } | |
| 942 | // sqrt(-ve) = snan | |
| 943 | if (ix0 & sign != 0) { | |
| 944 | return math.snan(f64); | |
| 945 | } | |
| 946 | ||
| 947 | // normalize x | |
| 948 | var m = @intCast(i32, ix0 >> 20); | |
| 949 | if (m == 0) { | |
| 950 | // subnormal | |
| 951 | while (ix0 == 0) { | |
| 952 | m -= 21; | |
| 953 | ix0 |= ix1 >> 11; | |
| 954 | ix1 <<= 21; | |
| 955 | } | |
| 956 | ||
| 957 | // subnormal | |
| 958 | var i: u32 = 0; | |
| 959 | while (ix0 & 0x00100000 == 0) : (i += 1) { | |
| 960 | ix0 <<= 1; | |
| 961 | } | |
| 962 | m -= @intCast(i32, i) - 1; | |
| 963 | ix0 |= ix1 >> @intCast(u5, 32 - i); | |
| 964 | ix1 <<= @intCast(u5, i); | |
| 965 | } | |
| 966 | ||
| 967 | // unbias exponent | |
| 968 | m -= 1023; | |
| 969 | ix0 = (ix0 & 0x000FFFFF) | 0x00100000; | |
| 970 | if (m & 1 != 0) { | |
| 971 | ix0 += ix0 + (ix1 >> 31); | |
| 972 | ix1 = ix1 +% ix1; | |
| 973 | } | |
| 974 | m >>= 1; | |
| 975 | ||
| 976 | // sqrt(x) bit by bit | |
| 977 | ix0 += ix0 + (ix1 >> 31); | |
| 978 | ix1 = ix1 +% ix1; | |
| 979 | ||
| 980 | var q: u32 = 0; | |
| 981 | var q1: u32 = 0; | |
| 982 | var s0: u32 = 0; | |
| 983 | var s1: u32 = 0; | |
| 984 | var r: u32 = 0x00200000; | |
| 985 | var t: u32 = undefined; | |
| 986 | var t1: u32 = undefined; | |
| 987 | ||
| 988 | while (r != 0) { | |
| 989 | t = s0 +% r; | |
| 990 | if (t <= ix0) { | |
| 991 | s0 = t + r; | |
| 992 | ix0 -= t; | |
| 993 | q += r; | |
| 994 | } | |
| 995 | ix0 = ix0 +% ix0 +% (ix1 >> 31); | |
| 996 | ix1 = ix1 +% ix1; | |
| 997 | r >>= 1; | |
| 998 | } | |
| 999 | ||
| 1000 | r = sign; | |
| 1001 | while (r != 0) { | |
| 1002 | t1 = s1 +% r; | |
| 1003 | t = s0; | |
| 1004 | if (t < ix0 or (t == ix0 and t1 <= ix1)) { | |
| 1005 | s1 = t1 +% r; | |
| 1006 | if (t1 & sign == sign and s1 & sign == 0) { | |
| 1007 | s0 += 1; | |
| 1008 | } | |
| 1009 | ix0 -= t; | |
| 1010 | if (ix1 < t1) { | |
| 1011 | ix0 -= 1; | |
| 1012 | } | |
| 1013 | ix1 = ix1 -% t1; | |
| 1014 | q1 += r; | |
| 1015 | } | |
| 1016 | ix0 = ix0 +% ix0 +% (ix1 >> 31); | |
| 1017 | ix1 = ix1 +% ix1; | |
| 1018 | r >>= 1; | |
| 1019 | } | |
| 1020 | ||
| 1021 | // rounding direction | |
| 1022 | if (ix0 | ix1 != 0) { | |
| 1023 | var z = 1.0 - tiny; // raise inexact | |
| 1024 | if (z >= 1.0) { | |
| 1025 | z = 1.0 + tiny; | |
| 1026 | if (q1 == 0xFFFFFFFF) { | |
| 1027 | q1 = 0; | |
| 1028 | q += 1; | |
| 1029 | } else if (z > 1.0) { | |
| 1030 | if (q1 == 0xFFFFFFFE) { | |
| 1031 | q += 1; | |
| 1032 | } | |
| 1033 | q1 += 2; | |
| 1034 | } else { | |
| 1035 | q1 += q1 & 1; | |
| 1036 | } | |
| 1037 | } | |
| 1038 | } | |
| 1039 | ||
| 1040 | ix0 = (q >> 1) + 0x3FE00000; | |
| 1041 | ix1 = q1 >> 1; | |
| 1042 | if (q & 1 != 0) { | |
| 1043 | ix1 |= 0x80000000; | |
| 1044 | } | |
| 1045 | ||
| 1046 | // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same | |
| 1047 | // behaviour at least. | |
| 1048 | var iix0 = @intCast(i32, ix0); | |
| 1049 | iix0 = iix0 +% (m << 20); | |
| 1050 | ||
| 1051 | const uz = (@intCast(u64, iix0) << 32) | ix1; | |
| 1052 | return @bitCast(f64, uz); | |
| 1053 | } | |
| 1054 | ||
| 1055 | test "sqrt" { | |
| 1056 | const V = [_]f64{ | |
| 1057 | 0.0, | |
| 1058 | 4.089288054930154, | |
| 1059 | 7.538757127071935, | |
| 1060 | 8.97780793672623, | |
| 1061 | 5.304443821913729, | |
| 1062 | 5.682408965311888, | |
| 1063 | 0.5846878579110049, | |
| 1064 | 3.650338664297043, | |
| 1065 | 0.3178091951800732, | |
| 1066 | 7.1505232436382835, | |
| 1067 | 3.6589165881946464, | |
| 1068 | }; | |
| 1069 | ||
| 1070 | // Note that @sqrt will either generate the sqrt opcode (if supported by the | |
| 1071 | // target ISA) or a call to `sqrtf` otherwise. | |
| 1072 | for (V) |val| | |
| 1073 | try std.testing.expectEqual(@sqrt(val), sqrt(val)); | |
| 1074 | } | |
| 1075 | ||
| 1076 | test "sqrt special" { | |
| 1077 | try std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64)))); | |
| 1078 | try std.testing.expect(sqrt(0.0) == 0.0); | |
| 1079 | try std.testing.expect(sqrt(-0.0) == -0.0); | |
| 1080 | try std.testing.expect(isNan(sqrt(-1.0))); | |
| 1081 | try std.testing.expect(isNan(sqrt(std.math.nan(f64)))); | |
| 1082 | } | |
| 1083 | ||
| 1084 | export fn sqrtf(x: f32) f32 { | |
| 1085 | const tiny: f32 = 1.0e-30; | |
| 1086 | const sign: i32 = @bitCast(i32, @as(u32, 0x80000000)); | |
| 1087 | var ix: i32 = @bitCast(i32, x); | |
| 1088 | ||
| 1089 | if ((ix & 0x7F800000) == 0x7F800000) { | |
| 1090 | return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan | |
| 1091 | } | |
| 1092 | ||
| 1093 | // zero | |
| 1094 | if (ix <= 0) { | |
| 1095 | if (ix & ~sign == 0) { | |
| 1096 | return x; // sqrt (+-0) = +-0 | |
| 1097 | } | |
| 1098 | if (ix < 0) { | |
| 1099 | return math.snan(f32); | |
| 1100 | } | |
| 1101 | } | |
| 1102 | ||
| 1103 | // normalize | |
| 1104 | var m = ix >> 23; | |
| 1105 | if (m == 0) { | |
| 1106 | // subnormal | |
| 1107 | var i: i32 = 0; | |
| 1108 | while (ix & 0x00800000 == 0) : (i += 1) { | |
| 1109 | ix <<= 1; | |
| 1110 | } | |
| 1111 | m -= i - 1; | |
| 1112 | } | |
| 1113 | ||
| 1114 | m -= 127; // unbias exponent | |
| 1115 | ix = (ix & 0x007FFFFF) | 0x00800000; | |
| 1116 | ||
| 1117 | if (m & 1 != 0) { // odd m, double x to even | |
| 1118 | ix += ix; | |
| 1119 | } | |
| 1120 | ||
| 1121 | m >>= 1; // m = [m / 2] | |
| 1122 | ||
| 1123 | // sqrt(x) bit by bit | |
| 1124 | ix += ix; | |
| 1125 | var q: i32 = 0; // q = sqrt(x) | |
| 1126 | var s: i32 = 0; | |
| 1127 | var r: i32 = 0x01000000; // r = moving bit right -> left | |
| 1128 | ||
| 1129 | while (r != 0) { | |
| 1130 | const t = s + r; | |
| 1131 | if (t <= ix) { | |
| 1132 | s = t + r; | |
| 1133 | ix -= t; | |
| 1134 | q += r; | |
| 1135 | } | |
| 1136 | ix += ix; | |
| 1137 | r >>= 1; | |
| 1138 | } | |
| 1139 | ||
| 1140 | // floating add to find rounding direction | |
| 1141 | if (ix != 0) { | |
| 1142 | var z = 1.0 - tiny; // inexact | |
| 1143 | if (z >= 1.0) { | |
| 1144 | z = 1.0 + tiny; | |
| 1145 | if (z > 1.0) { | |
| 1146 | q += 2; | |
| 1147 | } else { | |
| 1148 | if (q & 1 != 0) { | |
| 1149 | q += 1; | |
| 1150 | } | |
| 1151 | } | |
| 1152 | } | |
| 1153 | } | |
| 1154 | ||
| 1155 | ix = (q >> 1) + 0x3f000000; | |
| 1156 | ix += m << 23; | |
| 1157 | return @bitCast(f32, ix); | |
| 1158 | } | |
| 1159 | ||
| 1160 | test "sqrtf" { | |
| 1161 | const V = [_]f32{ | |
| 1162 | 0.0, | |
| 1163 | 4.089288054930154, | |
| 1164 | 7.538757127071935, | |
| 1165 | 8.97780793672623, | |
| 1166 | 5.304443821913729, | |
| 1167 | 5.682408965311888, | |
| 1168 | 0.5846878579110049, | |
| 1169 | 3.650338664297043, | |
| 1170 | 0.3178091951800732, | |
| 1171 | 7.1505232436382835, | |
| 1172 | 3.6589165881946464, | |
| 1173 | }; | |
| 1174 | ||
| 1175 | // Note that @sqrt will either generate the sqrt opcode (if supported by the | |
| 1176 | // target ISA) or a call to `sqrtf` otherwise. | |
| 1177 | for (V) |val| | |
| 1178 | try std.testing.expectEqual(@sqrt(val), sqrtf(val)); | |
| 1179 | } | |
| 1180 | ||
| 1181 | test "sqrtf special" { | |
| 1182 | try std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32)))); | |
| 1183 | try std.testing.expect(sqrtf(0.0) == 0.0); | |
| 1184 | try std.testing.expect(sqrtf(-0.0) == -0.0); | |
| 1185 | try std.testing.expect(isNan(sqrtf(-1.0))); | |
| 1186 | try std.testing.expect(isNan(sqrtf(std.math.nan(f32)))); | |
| 1187 | } |
lib/std/special/compiler_rt.zig+591-573| ... | ... | @@ -1,171 +1,24 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const builtin = std.builtin; | |
| 2 | const builtin = @import("builtin"); | |
| 3 | 3 | const is_test = builtin.is_test; |
| 4 | 4 | const os_tag = std.Target.current.os.tag; |
| 5 | const arch = std.Target.current.cpu.arch; | |
| 5 | const arch = builtin.stage2_arch; | |
| 6 | 6 | const abi = std.Target.current.abi; |
| 7 | 7 | |
| 8 | 8 | const is_gnu = abi.isGnu(); |
| 9 | 9 | const is_mingw = os_tag == .windows and is_gnu; |
| 10 | 10 | |
| 11 | comptime { | |
| 12 | const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak; | |
| 13 | const strong_linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong; | |
| 14 | ||
| 15 | switch (arch) { | |
| 16 | .i386, | |
| 17 | .x86_64, | |
| 18 | => { | |
| 19 | const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack; | |
| 20 | @export(zig_probe_stack, .{ | |
| 21 | .name = "__zig_probe_stack", | |
| 22 | .linkage = linkage, | |
| 23 | }); | |
| 24 | }, | |
| 25 | ||
| 26 | else => {}, | |
| 27 | } | |
| 11 | const linkage = if (is_test) | |
| 12 | std.builtin.GlobalLinkage.Internal | |
| 13 | else | |
| 14 | std.builtin.GlobalLinkage.Weak; | |
| 28 | 15 | |
| 29 | // __clear_cache manages its own logic about whether to be exported or not. | |
| 30 | _ = @import("compiler_rt/clear_cache.zig").clear_cache; | |
| 31 | ||
| 32 | const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2; | |
| 33 | @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage }); | |
| 34 | const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2; | |
| 35 | @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage }); | |
| 36 | const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2; | |
| 37 | @export(__letf2, .{ .name = "__letf2", .linkage = linkage }); | |
| 38 | ||
| 39 | const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2; | |
| 40 | @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage }); | |
| 41 | const __gedf2 = @import("compiler_rt/compareXf2.zig").__gedf2; | |
| 42 | @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage }); | |
| 43 | const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2; | |
| 44 | @export(__getf2, .{ .name = "__getf2", .linkage = linkage }); | |
| 45 | ||
| 46 | if (!is_test) { | |
| 47 | @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage }); | |
| 48 | @export(__ledf2, .{ .name = "__cmpdf2", .linkage = linkage }); | |
| 49 | @export(__letf2, .{ .name = "__cmptf2", .linkage = linkage }); | |
| 50 | ||
| 51 | const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2; | |
| 52 | @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); | |
| 53 | const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2; | |
| 54 | @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); | |
| 55 | @export(__letf2, .{ .name = "__eqtf2", .linkage = linkage }); | |
| 56 | ||
| 57 | const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2; | |
| 58 | @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); | |
| 59 | const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2; | |
| 60 | @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); | |
| 61 | @export(__letf2, .{ .name = "__lttf2", .linkage = linkage }); | |
| 62 | ||
| 63 | const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2; | |
| 64 | @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage }); | |
| 65 | const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2; | |
| 66 | @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage }); | |
| 67 | @export(__letf2, .{ .name = "__netf2", .linkage = linkage }); | |
| 68 | ||
| 69 | const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2; | |
| 70 | @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); | |
| 71 | const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2; | |
| 72 | @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); | |
| 73 | @export(__getf2, .{ .name = "__gttf2", .linkage = linkage }); | |
| 74 | ||
| 75 | const __extendhfsf2 = @import("compiler_rt/extendXfYf2.zig").__extendhfsf2; | |
| 76 | @export(__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage }); | |
| 77 | const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2; | |
| 78 | @export(__truncsfhf2, .{ .name = "__gnu_f2h_ieee", .linkage = linkage }); | |
| 79 | } | |
| 80 | ||
| 81 | const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2; | |
| 82 | @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage }); | |
| 83 | const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2; | |
| 84 | @export(__unorddf2, .{ .name = "__unorddf2", .linkage = linkage }); | |
| 85 | const __unordtf2 = @import("compiler_rt/compareXf2.zig").__unordtf2; | |
| 86 | @export(__unordtf2, .{ .name = "__unordtf2", .linkage = linkage }); | |
| 87 | ||
| 88 | const __addsf3 = @import("compiler_rt/addXf3.zig").__addsf3; | |
| 89 | @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage }); | |
| 90 | const __adddf3 = @import("compiler_rt/addXf3.zig").__adddf3; | |
| 91 | @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); | |
| 92 | const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3; | |
| 93 | @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); | |
| 94 | const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3; | |
| 95 | @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); | |
| 96 | const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3; | |
| 97 | @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); | |
| 98 | const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3; | |
| 99 | @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); | |
| 100 | ||
| 101 | const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3; | |
| 102 | @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); | |
| 103 | const __muldf3 = @import("compiler_rt/mulXf3.zig").__muldf3; | |
| 104 | @export(__muldf3, .{ .name = "__muldf3", .linkage = linkage }); | |
| 105 | const __multf3 = @import("compiler_rt/mulXf3.zig").__multf3; | |
| 106 | @export(__multf3, .{ .name = "__multf3", .linkage = linkage }); | |
| 107 | ||
| 108 | const __divsf3 = @import("compiler_rt/divsf3.zig").__divsf3; | |
| 109 | @export(__divsf3, .{ .name = "__divsf3", .linkage = linkage }); | |
| 110 | const __divdf3 = @import("compiler_rt/divdf3.zig").__divdf3; | |
| 111 | @export(__divdf3, .{ .name = "__divdf3", .linkage = linkage }); | |
| 112 | const __divtf3 = @import("compiler_rt/divtf3.zig").__divtf3; | |
| 113 | @export(__divtf3, .{ .name = "__divtf3", .linkage = linkage }); | |
| 114 | ||
| 115 | const __ashldi3 = @import("compiler_rt/shift.zig").__ashldi3; | |
| 116 | @export(__ashldi3, .{ .name = "__ashldi3", .linkage = linkage }); | |
| 117 | const __ashlti3 = @import("compiler_rt/shift.zig").__ashlti3; | |
| 118 | @export(__ashlti3, .{ .name = "__ashlti3", .linkage = linkage }); | |
| 119 | const __ashrdi3 = @import("compiler_rt/shift.zig").__ashrdi3; | |
| 120 | @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = linkage }); | |
| 121 | const __ashrti3 = @import("compiler_rt/shift.zig").__ashrti3; | |
| 122 | @export(__ashrti3, .{ .name = "__ashrti3", .linkage = linkage }); | |
| 123 | const __lshrdi3 = @import("compiler_rt/shift.zig").__lshrdi3; | |
| 124 | @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = linkage }); | |
| 125 | const __lshrti3 = @import("compiler_rt/shift.zig").__lshrti3; | |
| 126 | @export(__lshrti3, .{ .name = "__lshrti3", .linkage = linkage }); | |
| 127 | ||
| 128 | const __floatsidf = @import("compiler_rt/floatsiXf.zig").__floatsidf; | |
| 129 | @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); | |
| 130 | const __floatsisf = @import("compiler_rt/floatsiXf.zig").__floatsisf; | |
| 131 | @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage }); | |
| 132 | const __floatdidf = @import("compiler_rt/floatdidf.zig").__floatdidf; | |
| 133 | @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); | |
| 134 | const __floatsitf = @import("compiler_rt/floatsiXf.zig").__floatsitf; | |
| 135 | @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); | |
| 136 | ||
| 137 | const __floatunsisf = @import("compiler_rt/floatunsisf.zig").__floatunsisf; | |
| 138 | @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage }); | |
| 139 | const __floatundisf = @import("compiler_rt/floatundisf.zig").__floatundisf; | |
| 140 | @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage }); | |
| 141 | const __floatunsidf = @import("compiler_rt/floatunsidf.zig").__floatunsidf; | |
| 142 | @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); | |
| 143 | const __floatundidf = @import("compiler_rt/floatundidf.zig").__floatundidf; | |
| 144 | @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage }); | |
| 145 | ||
| 146 | const __floatditf = @import("compiler_rt/floatditf.zig").__floatditf; | |
| 147 | @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage }); | |
| 148 | const __floattitf = @import("compiler_rt/floattitf.zig").__floattitf; | |
| 149 | @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage }); | |
| 150 | const __floattidf = @import("compiler_rt/floattidf.zig").__floattidf; | |
| 151 | @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage }); | |
| 152 | const __floattisf = @import("compiler_rt/floatXisf.zig").__floattisf; | |
| 153 | @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage }); | |
| 154 | const __floatdisf = @import("compiler_rt/floatXisf.zig").__floatdisf; | |
| 155 | @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage }); | |
| 156 | ||
| 157 | const __floatunditf = @import("compiler_rt/floatunditf.zig").__floatunditf; | |
| 158 | @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); | |
| 159 | const __floatunsitf = @import("compiler_rt/floatunsitf.zig").__floatunsitf; | |
| 160 | @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage }); | |
| 161 | ||
| 162 | const __floatuntitf = @import("compiler_rt/floatuntitf.zig").__floatuntitf; | |
| 163 | @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage }); | |
| 164 | const __floatuntidf = @import("compiler_rt/floatuntidf.zig").__floatuntidf; | |
| 165 | @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); | |
| 166 | const __floatuntisf = @import("compiler_rt/floatuntisf.zig").__floatuntisf; | |
| 167 | @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); | |
| 16 | const strong_linkage = if (is_test) | |
| 17 | std.builtin.GlobalLinkage.Internal | |
| 18 | else | |
| 19 | std.builtin.GlobalLinkage.Strong; | |
| 168 | 20 | |
| 21 | comptime { | |
| 169 | 22 | const __extenddftf2 = @import("compiler_rt/extendXfYf2.zig").__extenddftf2; |
| 170 | 23 | @export(__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage }); |
| 171 | 24 | const __extendsftf2 = @import("compiler_rt/extendXfYf2.zig").__extendsftf2; |
| ... | ... | @@ -175,446 +28,611 @@ comptime { |
| 175 | 28 | const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2; |
| 176 | 29 | @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage }); |
| 177 | 30 | |
| 178 | const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2; | |
| 179 | @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); | |
| 180 | const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2; | |
| 181 | @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); | |
| 182 | const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2; | |
| 183 | @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage }); | |
| 184 | const __trunctfdf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfdf2; | |
| 185 | @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); | |
| 186 | const __trunctfsf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfsf2; | |
| 187 | @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); | |
| 188 | ||
| 189 | const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2; | |
| 190 | @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); | |
| 191 | ||
| 192 | const __extendsfdf2 = @import("compiler_rt/extendXfYf2.zig").__extendsfdf2; | |
| 193 | @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage }); | |
| 194 | ||
| 195 | const __fixunssfsi = @import("compiler_rt/fixunssfsi.zig").__fixunssfsi; | |
| 196 | @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); | |
| 197 | const __fixunssfdi = @import("compiler_rt/fixunssfdi.zig").__fixunssfdi; | |
| 198 | @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); | |
| 199 | const __fixunssfti = @import("compiler_rt/fixunssfti.zig").__fixunssfti; | |
| 200 | @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); | |
| 201 | ||
| 202 | const __fixunsdfsi = @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi; | |
| 203 | @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); | |
| 204 | const __fixunsdfdi = @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi; | |
| 205 | @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); | |
| 206 | const __fixunsdfti = @import("compiler_rt/fixunsdfti.zig").__fixunsdfti; | |
| 207 | @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); | |
| 208 | ||
| 209 | const __fixunstfsi = @import("compiler_rt/fixunstfsi.zig").__fixunstfsi; | |
| 210 | @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage }); | |
| 211 | const __fixunstfdi = @import("compiler_rt/fixunstfdi.zig").__fixunstfdi; | |
| 212 | @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); | |
| 213 | const __fixunstfti = @import("compiler_rt/fixunstfti.zig").__fixunstfti; | |
| 214 | @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); | |
| 215 | ||
| 216 | const __fixdfdi = @import("compiler_rt/fixdfdi.zig").__fixdfdi; | |
| 217 | @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); | |
| 218 | const __fixdfsi = @import("compiler_rt/fixdfsi.zig").__fixdfsi; | |
| 219 | @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); | |
| 220 | const __fixdfti = @import("compiler_rt/fixdfti.zig").__fixdfti; | |
| 221 | @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); | |
| 222 | const __fixsfdi = @import("compiler_rt/fixsfdi.zig").__fixsfdi; | |
| 223 | @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); | |
| 224 | const __fixsfsi = @import("compiler_rt/fixsfsi.zig").__fixsfsi; | |
| 225 | @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); | |
| 226 | const __fixsfti = @import("compiler_rt/fixsfti.zig").__fixsfti; | |
| 227 | @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); | |
| 228 | const __fixtfdi = @import("compiler_rt/fixtfdi.zig").__fixtfdi; | |
| 229 | @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage }); | |
| 230 | const __fixtfsi = @import("compiler_rt/fixtfsi.zig").__fixtfsi; | |
| 231 | @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); | |
| 232 | const __fixtfti = @import("compiler_rt/fixtfti.zig").__fixtfti; | |
| 233 | @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); | |
| 234 | ||
| 235 | const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; | |
| 236 | @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); | |
| 237 | const __popcountdi2 = @import("compiler_rt/popcountdi2.zig").__popcountdi2; | |
| 238 | @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage }); | |
| 239 | ||
| 240 | const __mulsi3 = @import("compiler_rt/int.zig").__mulsi3; | |
| 241 | @export(__mulsi3, .{ .name = "__mulsi3", .linkage = linkage }); | |
| 242 | const __muldi3 = @import("compiler_rt/muldi3.zig").__muldi3; | |
| 243 | @export(__muldi3, .{ .name = "__muldi3", .linkage = linkage }); | |
| 244 | const __divmoddi4 = @import("compiler_rt/int.zig").__divmoddi4; | |
| 245 | @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = linkage }); | |
| 246 | const __divsi3 = @import("compiler_rt/int.zig").__divsi3; | |
| 247 | @export(__divsi3, .{ .name = "__divsi3", .linkage = linkage }); | |
| 248 | const __divdi3 = @import("compiler_rt/int.zig").__divdi3; | |
| 249 | @export(__divdi3, .{ .name = "__divdi3", .linkage = linkage }); | |
| 250 | const __udivsi3 = @import("compiler_rt/int.zig").__udivsi3; | |
| 251 | @export(__udivsi3, .{ .name = "__udivsi3", .linkage = linkage }); | |
| 252 | const __udivdi3 = @import("compiler_rt/int.zig").__udivdi3; | |
| 253 | @export(__udivdi3, .{ .name = "__udivdi3", .linkage = linkage }); | |
| 254 | const __modsi3 = @import("compiler_rt/int.zig").__modsi3; | |
| 255 | @export(__modsi3, .{ .name = "__modsi3", .linkage = linkage }); | |
| 256 | const __moddi3 = @import("compiler_rt/int.zig").__moddi3; | |
| 257 | @export(__moddi3, .{ .name = "__moddi3", .linkage = linkage }); | |
| 258 | const __umodsi3 = @import("compiler_rt/int.zig").__umodsi3; | |
| 259 | @export(__umodsi3, .{ .name = "__umodsi3", .linkage = linkage }); | |
| 260 | const __umoddi3 = @import("compiler_rt/int.zig").__umoddi3; | |
| 261 | @export(__umoddi3, .{ .name = "__umoddi3", .linkage = linkage }); | |
| 262 | const __divmodsi4 = @import("compiler_rt/int.zig").__divmodsi4; | |
| 263 | @export(__divmodsi4, .{ .name = "__divmodsi4", .linkage = linkage }); | |
| 264 | const __udivmodsi4 = @import("compiler_rt/int.zig").__udivmodsi4; | |
| 265 | @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = linkage }); | |
| 266 | ||
| 267 | const __negsf2 = @import("compiler_rt/negXf2.zig").__negsf2; | |
| 268 | @export(__negsf2, .{ .name = "__negsf2", .linkage = linkage }); | |
| 269 | const __negdf2 = @import("compiler_rt/negXf2.zig").__negdf2; | |
| 270 | @export(__negdf2, .{ .name = "__negdf2", .linkage = linkage }); | |
| 271 | ||
| 272 | const __clzsi2 = @import("compiler_rt/count0bits.zig").__clzsi2; | |
| 273 | @export(__clzsi2, .{ .name = "__clzsi2", .linkage = linkage }); | |
| 274 | const __clzdi2 = @import("compiler_rt/count0bits.zig").__clzdi2; | |
| 275 | @export(__clzdi2, .{ .name = "__clzdi2", .linkage = linkage }); | |
| 276 | const __clzti2 = @import("compiler_rt/count0bits.zig").__clzti2; | |
| 277 | @export(__clzti2, .{ .name = "__clzti2", .linkage = linkage }); | |
| 278 | ||
| 279 | if (builtin.link_libc and os_tag == .openbsd) { | |
| 280 | const __emutls_get_address = @import("compiler_rt/emutls.zig").__emutls_get_address; | |
| 281 | @export(__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = linkage }); | |
| 282 | } | |
| 31 | if (!builtin.zig_is_stage2) { | |
| 32 | switch (arch) { | |
| 33 | .i386, | |
| 34 | .x86_64, | |
| 35 | => { | |
| 36 | const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack; | |
| 37 | @export(zig_probe_stack, .{ | |
| 38 | .name = "__zig_probe_stack", | |
| 39 | .linkage = linkage, | |
| 40 | }); | |
| 41 | }, | |
| 283 | 42 | |
| 284 | if ((arch.isARM() or arch.isThumb()) and !is_test) { | |
| 285 | const __aeabi_unwind_cpp_pr0 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0; | |
| 286 | @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage }); | |
| 287 | const __aeabi_unwind_cpp_pr1 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1; | |
| 288 | @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage }); | |
| 289 | const __aeabi_unwind_cpp_pr2 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr2; | |
| 290 | @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage }); | |
| 291 | ||
| 292 | @export(__muldi3, .{ .name = "__aeabi_lmul", .linkage = linkage }); | |
| 293 | ||
| 294 | const __aeabi_ldivmod = @import("compiler_rt/arm.zig").__aeabi_ldivmod; | |
| 295 | @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage }); | |
| 296 | const __aeabi_uldivmod = @import("compiler_rt/arm.zig").__aeabi_uldivmod; | |
| 297 | @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage }); | |
| 298 | ||
| 299 | @export(__divsi3, .{ .name = "__aeabi_idiv", .linkage = linkage }); | |
| 300 | const __aeabi_idivmod = @import("compiler_rt/arm.zig").__aeabi_idivmod; | |
| 301 | @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage }); | |
| 302 | @export(__udivsi3, .{ .name = "__aeabi_uidiv", .linkage = linkage }); | |
| 303 | const __aeabi_uidivmod = @import("compiler_rt/arm.zig").__aeabi_uidivmod; | |
| 304 | @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage }); | |
| 305 | ||
| 306 | const __aeabi_memcpy = @import("compiler_rt/arm.zig").__aeabi_memcpy; | |
| 307 | @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage }); | |
| 308 | @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy4", .linkage = linkage }); | |
| 309 | @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy8", .linkage = linkage }); | |
| 310 | ||
| 311 | const __aeabi_memmove = @import("compiler_rt/arm.zig").__aeabi_memmove; | |
| 312 | @export(__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = linkage }); | |
| 313 | @export(__aeabi_memmove, .{ .name = "__aeabi_memmove4", .linkage = linkage }); | |
| 314 | @export(__aeabi_memmove, .{ .name = "__aeabi_memmove8", .linkage = linkage }); | |
| 315 | ||
| 316 | const __aeabi_memset = @import("compiler_rt/arm.zig").__aeabi_memset; | |
| 317 | @export(__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = linkage }); | |
| 318 | @export(__aeabi_memset, .{ .name = "__aeabi_memset4", .linkage = linkage }); | |
| 319 | @export(__aeabi_memset, .{ .name = "__aeabi_memset8", .linkage = linkage }); | |
| 320 | ||
| 321 | const __aeabi_memclr = @import("compiler_rt/arm.zig").__aeabi_memclr; | |
| 322 | @export(__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = linkage }); | |
| 323 | @export(__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage }); | |
| 324 | @export(__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage }); | |
| 325 | ||
| 326 | if (os_tag == .linux) { | |
| 327 | const __aeabi_read_tp = @import("compiler_rt/arm.zig").__aeabi_read_tp; | |
| 328 | @export(__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); | |
| 43 | else => {}, | |
| 329 | 44 | } |
| 330 | 45 | |
| 331 | const __aeabi_f2d = @import("compiler_rt/extendXfYf2.zig").__aeabi_f2d; | |
| 332 | @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage }); | |
| 333 | const __aeabi_i2d = @import("compiler_rt/floatsiXf.zig").__aeabi_i2d; | |
| 334 | @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); | |
| 335 | const __aeabi_l2d = @import("compiler_rt/floatdidf.zig").__aeabi_l2d; | |
| 336 | @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage }); | |
| 337 | const __aeabi_l2f = @import("compiler_rt/floatXisf.zig").__aeabi_l2f; | |
| 338 | @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage }); | |
| 339 | const __aeabi_ui2d = @import("compiler_rt/floatunsidf.zig").__aeabi_ui2d; | |
| 340 | @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage }); | |
| 341 | const __aeabi_ul2d = @import("compiler_rt/floatundidf.zig").__aeabi_ul2d; | |
| 342 | @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage }); | |
| 343 | const __aeabi_ui2f = @import("compiler_rt/floatunsisf.zig").__aeabi_ui2f; | |
| 344 | @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage }); | |
| 345 | const __aeabi_ul2f = @import("compiler_rt/floatundisf.zig").__aeabi_ul2f; | |
| 346 | @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage }); | |
| 347 | ||
| 348 | const __aeabi_fneg = @import("compiler_rt/negXf2.zig").__aeabi_fneg; | |
| 349 | @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage }); | |
| 350 | const __aeabi_dneg = @import("compiler_rt/negXf2.zig").__aeabi_dneg; | |
| 351 | @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage }); | |
| 352 | ||
| 353 | const __aeabi_fmul = @import("compiler_rt/mulXf3.zig").__aeabi_fmul; | |
| 354 | @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage }); | |
| 355 | const __aeabi_dmul = @import("compiler_rt/mulXf3.zig").__aeabi_dmul; | |
| 356 | @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage }); | |
| 357 | ||
| 358 | const __aeabi_d2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2h; | |
| 359 | @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage }); | |
| 360 | ||
| 361 | const __aeabi_f2ulz = @import("compiler_rt/fixunssfdi.zig").__aeabi_f2ulz; | |
| 362 | @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage }); | |
| 363 | const __aeabi_d2ulz = @import("compiler_rt/fixunsdfdi.zig").__aeabi_d2ulz; | |
| 364 | @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage }); | |
| 365 | ||
| 366 | const __aeabi_f2lz = @import("compiler_rt/fixsfdi.zig").__aeabi_f2lz; | |
| 367 | @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage }); | |
| 368 | const __aeabi_d2lz = @import("compiler_rt/fixdfdi.zig").__aeabi_d2lz; | |
| 369 | @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage }); | |
| 370 | ||
| 371 | const __aeabi_d2uiz = @import("compiler_rt/fixunsdfsi.zig").__aeabi_d2uiz; | |
| 372 | @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage }); | |
| 373 | ||
| 374 | const __aeabi_h2f = @import("compiler_rt/extendXfYf2.zig").__aeabi_h2f; | |
| 375 | @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage }); | |
| 376 | const __aeabi_f2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_f2h; | |
| 377 | @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage }); | |
| 378 | ||
| 379 | const __aeabi_i2f = @import("compiler_rt/floatsiXf.zig").__aeabi_i2f; | |
| 380 | @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage }); | |
| 381 | const __aeabi_d2f = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2f; | |
| 382 | @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage }); | |
| 383 | ||
| 384 | const __aeabi_fadd = @import("compiler_rt/addXf3.zig").__aeabi_fadd; | |
| 385 | @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage }); | |
| 386 | const __aeabi_dadd = @import("compiler_rt/addXf3.zig").__aeabi_dadd; | |
| 387 | @export(__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = linkage }); | |
| 388 | const __aeabi_fsub = @import("compiler_rt/addXf3.zig").__aeabi_fsub; | |
| 389 | @export(__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = linkage }); | |
| 390 | const __aeabi_dsub = @import("compiler_rt/addXf3.zig").__aeabi_dsub; | |
| 391 | @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage }); | |
| 392 | ||
| 393 | const __aeabi_f2uiz = @import("compiler_rt/fixunssfsi.zig").__aeabi_f2uiz; | |
| 394 | @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage }); | |
| 395 | ||
| 396 | const __aeabi_f2iz = @import("compiler_rt/fixsfsi.zig").__aeabi_f2iz; | |
| 397 | @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage }); | |
| 398 | const __aeabi_d2iz = @import("compiler_rt/fixdfsi.zig").__aeabi_d2iz; | |
| 399 | @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage }); | |
| 400 | ||
| 401 | const __aeabi_fdiv = @import("compiler_rt/divsf3.zig").__aeabi_fdiv; | |
| 402 | @export(__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage }); | |
| 403 | const __aeabi_ddiv = @import("compiler_rt/divdf3.zig").__aeabi_ddiv; | |
| 404 | @export(__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage }); | |
| 405 | ||
| 406 | const __aeabi_llsl = @import("compiler_rt/shift.zig").__aeabi_llsl; | |
| 407 | @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = linkage }); | |
| 408 | const __aeabi_lasr = @import("compiler_rt/shift.zig").__aeabi_lasr; | |
| 409 | @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = linkage }); | |
| 410 | const __aeabi_llsr = @import("compiler_rt/shift.zig").__aeabi_llsr; | |
| 411 | @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = linkage }); | |
| 412 | ||
| 413 | const __aeabi_fcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpeq; | |
| 414 | @export(__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage }); | |
| 415 | const __aeabi_fcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmplt; | |
| 416 | @export(__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage }); | |
| 417 | const __aeabi_fcmple = @import("compiler_rt/compareXf2.zig").__aeabi_fcmple; | |
| 418 | @export(__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage }); | |
| 419 | const __aeabi_fcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpge; | |
| 420 | @export(__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage }); | |
| 421 | const __aeabi_fcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpgt; | |
| 422 | @export(__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage }); | |
| 423 | const __aeabi_fcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpun; | |
| 424 | @export(__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage }); | |
| 425 | ||
| 426 | const __aeabi_dcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpeq; | |
| 427 | @export(__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage }); | |
| 428 | const __aeabi_dcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmplt; | |
| 429 | @export(__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage }); | |
| 430 | const __aeabi_dcmple = @import("compiler_rt/compareXf2.zig").__aeabi_dcmple; | |
| 431 | @export(__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage }); | |
| 432 | const __aeabi_dcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpge; | |
| 433 | @export(__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage }); | |
| 434 | const __aeabi_dcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpgt; | |
| 435 | @export(__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage }); | |
| 436 | const __aeabi_dcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpun; | |
| 437 | @export(__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage }); | |
| 438 | } | |
| 46 | // __clear_cache manages its own logic about whether to be exported or not. | |
| 47 | _ = @import("compiler_rt/clear_cache.zig").clear_cache; | |
| 48 | ||
| 49 | const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2; | |
| 50 | @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage }); | |
| 51 | const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2; | |
| 52 | @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage }); | |
| 53 | const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2; | |
| 54 | @export(__letf2, .{ .name = "__letf2", .linkage = linkage }); | |
| 55 | ||
| 56 | const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2; | |
| 57 | @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage }); | |
| 58 | const __gedf2 = @import("compiler_rt/compareXf2.zig").__gedf2; | |
| 59 | @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage }); | |
| 60 | const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2; | |
| 61 | @export(__getf2, .{ .name = "__getf2", .linkage = linkage }); | |
| 62 | ||
| 63 | if (!is_test) { | |
| 64 | @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage }); | |
| 65 | @export(__ledf2, .{ .name = "__cmpdf2", .linkage = linkage }); | |
| 66 | @export(__letf2, .{ .name = "__cmptf2", .linkage = linkage }); | |
| 67 | ||
| 68 | const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2; | |
| 69 | @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); | |
| 70 | const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2; | |
| 71 | @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); | |
| 72 | @export(__letf2, .{ .name = "__eqtf2", .linkage = linkage }); | |
| 73 | ||
| 74 | const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2; | |
| 75 | @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); | |
| 76 | const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2; | |
| 77 | @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); | |
| 78 | @export(__letf2, .{ .name = "__lttf2", .linkage = linkage }); | |
| 79 | ||
| 80 | const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2; | |
| 81 | @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage }); | |
| 82 | const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2; | |
| 83 | @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage }); | |
| 84 | @export(__letf2, .{ .name = "__netf2", .linkage = linkage }); | |
| 85 | ||
| 86 | const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2; | |
| 87 | @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); | |
| 88 | const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2; | |
| 89 | @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); | |
| 90 | @export(__getf2, .{ .name = "__gttf2", .linkage = linkage }); | |
| 91 | ||
| 92 | @export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{ | |
| 93 | .name = "__gnu_h2f_ieee", | |
| 94 | .linkage = linkage, | |
| 95 | }); | |
| 96 | @export(@import("compiler_rt/truncXfYf2.zig").__truncsfhf2, .{ | |
| 97 | .name = "__gnu_f2h_ieee", | |
| 98 | .linkage = linkage, | |
| 99 | }); | |
| 100 | } | |
| 439 | 101 | |
| 440 | if (arch == .i386 and abi == .msvc) { | |
| 441 | // Don't let LLVM apply the stdcall name mangling on those MSVC builtins | |
| 442 | const _alldiv = @import("compiler_rt/aulldiv.zig")._alldiv; | |
| 443 | @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage }); | |
| 444 | const _aulldiv = @import("compiler_rt/aulldiv.zig")._aulldiv; | |
| 445 | @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage }); | |
| 446 | const _allrem = @import("compiler_rt/aullrem.zig")._allrem; | |
| 447 | @export(_allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage }); | |
| 448 | const _aullrem = @import("compiler_rt/aullrem.zig")._aullrem; | |
| 449 | @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); | |
| 450 | } | |
| 102 | const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2; | |
| 103 | @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage }); | |
| 104 | const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2; | |
| 105 | @export(__unorddf2, .{ .name = "__unorddf2", .linkage = linkage }); | |
| 106 | const __unordtf2 = @import("compiler_rt/compareXf2.zig").__unordtf2; | |
| 107 | @export(__unordtf2, .{ .name = "__unordtf2", .linkage = linkage }); | |
| 108 | ||
| 109 | const __addsf3 = @import("compiler_rt/addXf3.zig").__addsf3; | |
| 110 | @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage }); | |
| 111 | const __adddf3 = @import("compiler_rt/addXf3.zig").__adddf3; | |
| 112 | @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); | |
| 113 | const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3; | |
| 114 | @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); | |
| 115 | const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3; | |
| 116 | @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); | |
| 117 | const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3; | |
| 118 | @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); | |
| 119 | const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3; | |
| 120 | @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); | |
| 121 | ||
| 122 | const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3; | |
| 123 | @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); | |
| 124 | const __muldf3 = @import("compiler_rt/mulXf3.zig").__muldf3; | |
| 125 | @export(__muldf3, .{ .name = "__muldf3", .linkage = linkage }); | |
| 126 | const __multf3 = @import("compiler_rt/mulXf3.zig").__multf3; | |
| 127 | @export(__multf3, .{ .name = "__multf3", .linkage = linkage }); | |
| 128 | ||
| 129 | const __divsf3 = @import("compiler_rt/divsf3.zig").__divsf3; | |
| 130 | @export(__divsf3, .{ .name = "__divsf3", .linkage = linkage }); | |
| 131 | const __divdf3 = @import("compiler_rt/divdf3.zig").__divdf3; | |
| 132 | @export(__divdf3, .{ .name = "__divdf3", .linkage = linkage }); | |
| 133 | const __divtf3 = @import("compiler_rt/divtf3.zig").__divtf3; | |
| 134 | @export(__divtf3, .{ .name = "__divtf3", .linkage = linkage }); | |
| 135 | ||
| 136 | const __ashldi3 = @import("compiler_rt/shift.zig").__ashldi3; | |
| 137 | @export(__ashldi3, .{ .name = "__ashldi3", .linkage = linkage }); | |
| 138 | const __ashlti3 = @import("compiler_rt/shift.zig").__ashlti3; | |
| 139 | @export(__ashlti3, .{ .name = "__ashlti3", .linkage = linkage }); | |
| 140 | const __ashrdi3 = @import("compiler_rt/shift.zig").__ashrdi3; | |
| 141 | @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = linkage }); | |
| 142 | const __ashrti3 = @import("compiler_rt/shift.zig").__ashrti3; | |
| 143 | @export(__ashrti3, .{ .name = "__ashrti3", .linkage = linkage }); | |
| 144 | const __lshrdi3 = @import("compiler_rt/shift.zig").__lshrdi3; | |
| 145 | @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = linkage }); | |
| 146 | const __lshrti3 = @import("compiler_rt/shift.zig").__lshrti3; | |
| 147 | @export(__lshrti3, .{ .name = "__lshrti3", .linkage = linkage }); | |
| 148 | ||
| 149 | const __floatsidf = @import("compiler_rt/floatsiXf.zig").__floatsidf; | |
| 150 | @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); | |
| 151 | const __floatsisf = @import("compiler_rt/floatsiXf.zig").__floatsisf; | |
| 152 | @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage }); | |
| 153 | const __floatdidf = @import("compiler_rt/floatdidf.zig").__floatdidf; | |
| 154 | @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); | |
| 155 | const __floatsitf = @import("compiler_rt/floatsiXf.zig").__floatsitf; | |
| 156 | @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); | |
| 157 | ||
| 158 | const __floatunsisf = @import("compiler_rt/floatunsisf.zig").__floatunsisf; | |
| 159 | @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage }); | |
| 160 | const __floatundisf = @import("compiler_rt/floatundisf.zig").__floatundisf; | |
| 161 | @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage }); | |
| 162 | const __floatunsidf = @import("compiler_rt/floatunsidf.zig").__floatunsidf; | |
| 163 | @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); | |
| 164 | const __floatundidf = @import("compiler_rt/floatundidf.zig").__floatundidf; | |
| 165 | @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage }); | |
| 166 | ||
| 167 | const __floatditf = @import("compiler_rt/floatditf.zig").__floatditf; | |
| 168 | @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage }); | |
| 169 | const __floattitf = @import("compiler_rt/floattitf.zig").__floattitf; | |
| 170 | @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage }); | |
| 171 | const __floattidf = @import("compiler_rt/floattidf.zig").__floattidf; | |
| 172 | @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage }); | |
| 173 | const __floattisf = @import("compiler_rt/floatXisf.zig").__floattisf; | |
| 174 | @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage }); | |
| 175 | const __floatdisf = @import("compiler_rt/floatXisf.zig").__floatdisf; | |
| 176 | @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage }); | |
| 177 | ||
| 178 | const __floatunditf = @import("compiler_rt/floatunditf.zig").__floatunditf; | |
| 179 | @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); | |
| 180 | const __floatunsitf = @import("compiler_rt/floatunsitf.zig").__floatunsitf; | |
| 181 | @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage }); | |
| 182 | ||
| 183 | const __floatuntitf = @import("compiler_rt/floatuntitf.zig").__floatuntitf; | |
| 184 | @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage }); | |
| 185 | const __floatuntidf = @import("compiler_rt/floatuntidf.zig").__floatuntidf; | |
| 186 | @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); | |
| 187 | const __floatuntisf = @import("compiler_rt/floatuntisf.zig").__floatuntisf; | |
| 188 | @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); | |
| 451 | 189 | |
| 452 | if (arch.isSPARC()) { | |
| 453 | // SPARC systems use a different naming scheme | |
| 454 | const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add; | |
| 455 | @export(_Qp_add, .{ .name = "_Qp_add", .linkage = linkage }); | |
| 456 | const _Qp_div = @import("compiler_rt/sparc.zig")._Qp_div; | |
| 457 | @export(_Qp_div, .{ .name = "_Qp_div", .linkage = linkage }); | |
| 458 | const _Qp_mul = @import("compiler_rt/sparc.zig")._Qp_mul; | |
| 459 | @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage }); | |
| 460 | const _Qp_sub = @import("compiler_rt/sparc.zig")._Qp_sub; | |
| 461 | @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage }); | |
| 462 | ||
| 463 | const _Qp_cmp = @import("compiler_rt/sparc.zig")._Qp_cmp; | |
| 464 | @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage }); | |
| 465 | const _Qp_feq = @import("compiler_rt/sparc.zig")._Qp_feq; | |
| 466 | @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage }); | |
| 467 | const _Qp_fne = @import("compiler_rt/sparc.zig")._Qp_fne; | |
| 468 | @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage }); | |
| 469 | const _Qp_flt = @import("compiler_rt/sparc.zig")._Qp_flt; | |
| 470 | @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage }); | |
| 471 | const _Qp_fle = @import("compiler_rt/sparc.zig")._Qp_fle; | |
| 472 | @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage }); | |
| 473 | const _Qp_fgt = @import("compiler_rt/sparc.zig")._Qp_fgt; | |
| 474 | @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage }); | |
| 475 | const _Qp_fge = @import("compiler_rt/sparc.zig")._Qp_fge; | |
| 476 | @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage }); | |
| 477 | ||
| 478 | const _Qp_itoq = @import("compiler_rt/sparc.zig")._Qp_itoq; | |
| 479 | @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage }); | |
| 480 | const _Qp_uitoq = @import("compiler_rt/sparc.zig")._Qp_uitoq; | |
| 481 | @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage }); | |
| 482 | const _Qp_xtoq = @import("compiler_rt/sparc.zig")._Qp_xtoq; | |
| 483 | @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage }); | |
| 484 | const _Qp_uxtoq = @import("compiler_rt/sparc.zig")._Qp_uxtoq; | |
| 485 | @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage }); | |
| 486 | const _Qp_stoq = @import("compiler_rt/sparc.zig")._Qp_stoq; | |
| 487 | @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage }); | |
| 488 | const _Qp_dtoq = @import("compiler_rt/sparc.zig")._Qp_dtoq; | |
| 489 | @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage }); | |
| 490 | const _Qp_qtoi = @import("compiler_rt/sparc.zig")._Qp_qtoi; | |
| 491 | @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage }); | |
| 492 | const _Qp_qtoui = @import("compiler_rt/sparc.zig")._Qp_qtoui; | |
| 493 | @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage }); | |
| 494 | const _Qp_qtox = @import("compiler_rt/sparc.zig")._Qp_qtox; | |
| 495 | @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage }); | |
| 496 | const _Qp_qtoux = @import("compiler_rt/sparc.zig")._Qp_qtoux; | |
| 497 | @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage }); | |
| 498 | const _Qp_qtos = @import("compiler_rt/sparc.zig")._Qp_qtos; | |
| 499 | @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage }); | |
| 500 | const _Qp_qtod = @import("compiler_rt/sparc.zig")._Qp_qtod; | |
| 501 | @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); | |
| 502 | } | |
| 190 | const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2; | |
| 191 | @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); | |
| 192 | const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2; | |
| 193 | @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); | |
| 194 | const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2; | |
| 195 | @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage }); | |
| 196 | const __trunctfdf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfdf2; | |
| 197 | @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); | |
| 198 | const __trunctfsf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfsf2; | |
| 199 | @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); | |
| 200 | ||
| 201 | const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2; | |
| 202 | @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); | |
| 203 | ||
| 204 | const __extendsfdf2 = @import("compiler_rt/extendXfYf2.zig").__extendsfdf2; | |
| 205 | @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage }); | |
| 206 | ||
| 207 | const __fixunssfsi = @import("compiler_rt/fixunssfsi.zig").__fixunssfsi; | |
| 208 | @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); | |
| 209 | const __fixunssfdi = @import("compiler_rt/fixunssfdi.zig").__fixunssfdi; | |
| 210 | @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); | |
| 211 | const __fixunssfti = @import("compiler_rt/fixunssfti.zig").__fixunssfti; | |
| 212 | @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); | |
| 213 | ||
| 214 | const __fixunsdfsi = @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi; | |
| 215 | @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); | |
| 216 | const __fixunsdfdi = @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi; | |
| 217 | @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); | |
| 218 | const __fixunsdfti = @import("compiler_rt/fixunsdfti.zig").__fixunsdfti; | |
| 219 | @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); | |
| 220 | ||
| 221 | const __fixunstfsi = @import("compiler_rt/fixunstfsi.zig").__fixunstfsi; | |
| 222 | @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage }); | |
| 223 | const __fixunstfdi = @import("compiler_rt/fixunstfdi.zig").__fixunstfdi; | |
| 224 | @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); | |
| 225 | const __fixunstfti = @import("compiler_rt/fixunstfti.zig").__fixunstfti; | |
| 226 | @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); | |
| 227 | ||
| 228 | const __fixdfdi = @import("compiler_rt/fixdfdi.zig").__fixdfdi; | |
| 229 | @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); | |
| 230 | const __fixdfsi = @import("compiler_rt/fixdfsi.zig").__fixdfsi; | |
| 231 | @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); | |
| 232 | const __fixdfti = @import("compiler_rt/fixdfti.zig").__fixdfti; | |
| 233 | @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); | |
| 234 | const __fixsfdi = @import("compiler_rt/fixsfdi.zig").__fixsfdi; | |
| 235 | @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); | |
| 236 | const __fixsfsi = @import("compiler_rt/fixsfsi.zig").__fixsfsi; | |
| 237 | @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); | |
| 238 | const __fixsfti = @import("compiler_rt/fixsfti.zig").__fixsfti; | |
| 239 | @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); | |
| 240 | const __fixtfdi = @import("compiler_rt/fixtfdi.zig").__fixtfdi; | |
| 241 | @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage }); | |
| 242 | const __fixtfsi = @import("compiler_rt/fixtfsi.zig").__fixtfsi; | |
| 243 | @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); | |
| 244 | const __fixtfti = @import("compiler_rt/fixtfti.zig").__fixtfti; | |
| 245 | @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); | |
| 246 | ||
| 247 | const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; | |
| 248 | @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); | |
| 249 | const __popcountdi2 = @import("compiler_rt/popcountdi2.zig").__popcountdi2; | |
| 250 | @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage }); | |
| 251 | ||
| 252 | const __mulsi3 = @import("compiler_rt/int.zig").__mulsi3; | |
| 253 | @export(__mulsi3, .{ .name = "__mulsi3", .linkage = linkage }); | |
| 254 | const __muldi3 = @import("compiler_rt/muldi3.zig").__muldi3; | |
| 255 | @export(__muldi3, .{ .name = "__muldi3", .linkage = linkage }); | |
| 256 | const __divmoddi4 = @import("compiler_rt/int.zig").__divmoddi4; | |
| 257 | @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = linkage }); | |
| 258 | const __divsi3 = @import("compiler_rt/int.zig").__divsi3; | |
| 259 | @export(__divsi3, .{ .name = "__divsi3", .linkage = linkage }); | |
| 260 | const __divdi3 = @import("compiler_rt/int.zig").__divdi3; | |
| 261 | @export(__divdi3, .{ .name = "__divdi3", .linkage = linkage }); | |
| 262 | const __udivsi3 = @import("compiler_rt/int.zig").__udivsi3; | |
| 263 | @export(__udivsi3, .{ .name = "__udivsi3", .linkage = linkage }); | |
| 264 | const __udivdi3 = @import("compiler_rt/int.zig").__udivdi3; | |
| 265 | @export(__udivdi3, .{ .name = "__udivdi3", .linkage = linkage }); | |
| 266 | const __modsi3 = @import("compiler_rt/int.zig").__modsi3; | |
| 267 | @export(__modsi3, .{ .name = "__modsi3", .linkage = linkage }); | |
| 268 | const __moddi3 = @import("compiler_rt/int.zig").__moddi3; | |
| 269 | @export(__moddi3, .{ .name = "__moddi3", .linkage = linkage }); | |
| 270 | const __umodsi3 = @import("compiler_rt/int.zig").__umodsi3; | |
| 271 | @export(__umodsi3, .{ .name = "__umodsi3", .linkage = linkage }); | |
| 272 | const __umoddi3 = @import("compiler_rt/int.zig").__umoddi3; | |
| 273 | @export(__umoddi3, .{ .name = "__umoddi3", .linkage = linkage }); | |
| 274 | const __divmodsi4 = @import("compiler_rt/int.zig").__divmodsi4; | |
| 275 | @export(__divmodsi4, .{ .name = "__divmodsi4", .linkage = linkage }); | |
| 276 | const __udivmodsi4 = @import("compiler_rt/int.zig").__udivmodsi4; | |
| 277 | @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = linkage }); | |
| 278 | ||
| 279 | const __negsf2 = @import("compiler_rt/negXf2.zig").__negsf2; | |
| 280 | @export(__negsf2, .{ .name = "__negsf2", .linkage = linkage }); | |
| 281 | const __negdf2 = @import("compiler_rt/negXf2.zig").__negdf2; | |
| 282 | @export(__negdf2, .{ .name = "__negdf2", .linkage = linkage }); | |
| 283 | ||
| 284 | const __clzsi2 = @import("compiler_rt/count0bits.zig").__clzsi2; | |
| 285 | @export(__clzsi2, .{ .name = "__clzsi2", .linkage = linkage }); | |
| 286 | const __clzdi2 = @import("compiler_rt/count0bits.zig").__clzdi2; | |
| 287 | @export(__clzdi2, .{ .name = "__clzdi2", .linkage = linkage }); | |
| 288 | const __clzti2 = @import("compiler_rt/count0bits.zig").__clzti2; | |
| 289 | @export(__clzti2, .{ .name = "__clzti2", .linkage = linkage }); | |
| 290 | ||
| 291 | if (builtin.link_libc and os_tag == .openbsd) { | |
| 292 | const __emutls_get_address = @import("compiler_rt/emutls.zig").__emutls_get_address; | |
| 293 | @export(__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = linkage }); | |
| 294 | } | |
| 503 | 295 | |
| 504 | if ((arch == .powerpc or arch.isPPC64()) and !is_test) { | |
| 505 | @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage }); | |
| 506 | @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage }); | |
| 507 | @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage }); | |
| 508 | @export(__divtf3, .{ .name = "__divkf3", .linkage = linkage }); | |
| 509 | @export(__extendsftf2, .{ .name = "__extendsfkf2", .linkage = linkage }); | |
| 510 | @export(__extenddftf2, .{ .name = "__extenddfkf2", .linkage = linkage }); | |
| 511 | @export(__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = linkage }); | |
| 512 | @export(__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = linkage }); | |
| 513 | @export(__fixtfdi, .{ .name = "__fixkfdi", .linkage = linkage }); | |
| 514 | @export(__fixtfsi, .{ .name = "__fixkfsi", .linkage = linkage }); | |
| 515 | @export(__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = linkage }); | |
| 516 | @export(__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = linkage }); | |
| 517 | @export(__floatsitf, .{ .name = "__floatsikf", .linkage = linkage }); | |
| 518 | @export(__floatditf, .{ .name = "__floatdikf", .linkage = linkage }); | |
| 519 | @export(__floatunditf, .{ .name = "__floatundikf", .linkage = linkage }); | |
| 520 | @export(__floatunsitf, .{ .name = "__floatunsikf", .linkage = linkage }); | |
| 521 | ||
| 522 | @export(__letf2, .{ .name = "__eqkf2", .linkage = linkage }); | |
| 523 | @export(__letf2, .{ .name = "__nekf2", .linkage = linkage }); | |
| 524 | @export(__getf2, .{ .name = "__gekf2", .linkage = linkage }); | |
| 525 | @export(__letf2, .{ .name = "__ltkf2", .linkage = linkage }); | |
| 526 | @export(__letf2, .{ .name = "__lekf2", .linkage = linkage }); | |
| 527 | @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage }); | |
| 528 | @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage }); | |
| 529 | } | |
| 296 | if ((arch.isARM() or arch.isThumb()) and !is_test) { | |
| 297 | const __aeabi_unwind_cpp_pr0 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0; | |
| 298 | @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage }); | |
| 299 | const __aeabi_unwind_cpp_pr1 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1; | |
| 300 | @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage }); | |
| 301 | const __aeabi_unwind_cpp_pr2 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr2; | |
| 302 | @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage }); | |
| 303 | ||
| 304 | @export(__muldi3, .{ .name = "__aeabi_lmul", .linkage = linkage }); | |
| 305 | ||
| 306 | const __aeabi_ldivmod = @import("compiler_rt/arm.zig").__aeabi_ldivmod; | |
| 307 | @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage }); | |
| 308 | const __aeabi_uldivmod = @import("compiler_rt/arm.zig").__aeabi_uldivmod; | |
| 309 | @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage }); | |
| 310 | ||
| 311 | @export(__divsi3, .{ .name = "__aeabi_idiv", .linkage = linkage }); | |
| 312 | const __aeabi_idivmod = @import("compiler_rt/arm.zig").__aeabi_idivmod; | |
| 313 | @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage }); | |
| 314 | @export(__udivsi3, .{ .name = "__aeabi_uidiv", .linkage = linkage }); | |
| 315 | const __aeabi_uidivmod = @import("compiler_rt/arm.zig").__aeabi_uidivmod; | |
| 316 | @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage }); | |
| 317 | ||
| 318 | const __aeabi_memcpy = @import("compiler_rt/arm.zig").__aeabi_memcpy; | |
| 319 | @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage }); | |
| 320 | @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy4", .linkage = linkage }); | |
| 321 | @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy8", .linkage = linkage }); | |
| 322 | ||
| 323 | const __aeabi_memmove = @import("compiler_rt/arm.zig").__aeabi_memmove; | |
| 324 | @export(__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = linkage }); | |
| 325 | @export(__aeabi_memmove, .{ .name = "__aeabi_memmove4", .linkage = linkage }); | |
| 326 | @export(__aeabi_memmove, .{ .name = "__aeabi_memmove8", .linkage = linkage }); | |
| 327 | ||
| 328 | const __aeabi_memset = @import("compiler_rt/arm.zig").__aeabi_memset; | |
| 329 | @export(__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = linkage }); | |
| 330 | @export(__aeabi_memset, .{ .name = "__aeabi_memset4", .linkage = linkage }); | |
| 331 | @export(__aeabi_memset, .{ .name = "__aeabi_memset8", .linkage = linkage }); | |
| 332 | ||
| 333 | const __aeabi_memclr = @import("compiler_rt/arm.zig").__aeabi_memclr; | |
| 334 | @export(__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = linkage }); | |
| 335 | @export(__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage }); | |
| 336 | @export(__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage }); | |
| 337 | ||
| 338 | if (os_tag == .linux) { | |
| 339 | const __aeabi_read_tp = @import("compiler_rt/arm.zig").__aeabi_read_tp; | |
| 340 | @export(__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); | |
| 341 | } | |
| 342 | ||
| 343 | const __aeabi_f2d = @import("compiler_rt/extendXfYf2.zig").__aeabi_f2d; | |
| 344 | @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage }); | |
| 345 | const __aeabi_i2d = @import("compiler_rt/floatsiXf.zig").__aeabi_i2d; | |
| 346 | @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); | |
| 347 | const __aeabi_l2d = @import("compiler_rt/floatdidf.zig").__aeabi_l2d; | |
| 348 | @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage }); | |
| 349 | const __aeabi_l2f = @import("compiler_rt/floatXisf.zig").__aeabi_l2f; | |
| 350 | @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage }); | |
| 351 | const __aeabi_ui2d = @import("compiler_rt/floatunsidf.zig").__aeabi_ui2d; | |
| 352 | @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage }); | |
| 353 | const __aeabi_ul2d = @import("compiler_rt/floatundidf.zig").__aeabi_ul2d; | |
| 354 | @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage }); | |
| 355 | const __aeabi_ui2f = @import("compiler_rt/floatunsisf.zig").__aeabi_ui2f; | |
| 356 | @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage }); | |
| 357 | const __aeabi_ul2f = @import("compiler_rt/floatundisf.zig").__aeabi_ul2f; | |
| 358 | @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage }); | |
| 359 | ||
| 360 | const __aeabi_fneg = @import("compiler_rt/negXf2.zig").__aeabi_fneg; | |
| 361 | @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage }); | |
| 362 | const __aeabi_dneg = @import("compiler_rt/negXf2.zig").__aeabi_dneg; | |
| 363 | @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage }); | |
| 364 | ||
| 365 | const __aeabi_fmul = @import("compiler_rt/mulXf3.zig").__aeabi_fmul; | |
| 366 | @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage }); | |
| 367 | const __aeabi_dmul = @import("compiler_rt/mulXf3.zig").__aeabi_dmul; | |
| 368 | @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage }); | |
| 369 | ||
| 370 | const __aeabi_d2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2h; | |
| 371 | @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage }); | |
| 372 | ||
| 373 | const __aeabi_f2ulz = @import("compiler_rt/fixunssfdi.zig").__aeabi_f2ulz; | |
| 374 | @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage }); | |
| 375 | const __aeabi_d2ulz = @import("compiler_rt/fixunsdfdi.zig").__aeabi_d2ulz; | |
| 376 | @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage }); | |
| 377 | ||
| 378 | const __aeabi_f2lz = @import("compiler_rt/fixsfdi.zig").__aeabi_f2lz; | |
| 379 | @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage }); | |
| 380 | const __aeabi_d2lz = @import("compiler_rt/fixdfdi.zig").__aeabi_d2lz; | |
| 381 | @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage }); | |
| 382 | ||
| 383 | const __aeabi_d2uiz = @import("compiler_rt/fixunsdfsi.zig").__aeabi_d2uiz; | |
| 384 | @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage }); | |
| 385 | ||
| 386 | const __aeabi_h2f = @import("compiler_rt/extendXfYf2.zig").__aeabi_h2f; | |
| 387 | @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage }); | |
| 388 | const __aeabi_f2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_f2h; | |
| 389 | @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage }); | |
| 390 | ||
| 391 | const __aeabi_i2f = @import("compiler_rt/floatsiXf.zig").__aeabi_i2f; | |
| 392 | @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage }); | |
| 393 | const __aeabi_d2f = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2f; | |
| 394 | @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage }); | |
| 395 | ||
| 396 | const __aeabi_fadd = @import("compiler_rt/addXf3.zig").__aeabi_fadd; | |
| 397 | @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage }); | |
| 398 | const __aeabi_dadd = @import("compiler_rt/addXf3.zig").__aeabi_dadd; | |
| 399 | @export(__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = linkage }); | |
| 400 | const __aeabi_fsub = @import("compiler_rt/addXf3.zig").__aeabi_fsub; | |
| 401 | @export(__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = linkage }); | |
| 402 | const __aeabi_dsub = @import("compiler_rt/addXf3.zig").__aeabi_dsub; | |
| 403 | @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage }); | |
| 404 | ||
| 405 | const __aeabi_f2uiz = @import("compiler_rt/fixunssfsi.zig").__aeabi_f2uiz; | |
| 406 | @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage }); | |
| 407 | ||
| 408 | const __aeabi_f2iz = @import("compiler_rt/fixsfsi.zig").__aeabi_f2iz; | |
| 409 | @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage }); | |
| 410 | const __aeabi_d2iz = @import("compiler_rt/fixdfsi.zig").__aeabi_d2iz; | |
| 411 | @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage }); | |
| 412 | ||
| 413 | const __aeabi_fdiv = @import("compiler_rt/divsf3.zig").__aeabi_fdiv; | |
| 414 | @export(__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage }); | |
| 415 | const __aeabi_ddiv = @import("compiler_rt/divdf3.zig").__aeabi_ddiv; | |
| 416 | @export(__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage }); | |
| 417 | ||
| 418 | const __aeabi_llsl = @import("compiler_rt/shift.zig").__aeabi_llsl; | |
| 419 | @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = linkage }); | |
| 420 | const __aeabi_lasr = @import("compiler_rt/shift.zig").__aeabi_lasr; | |
| 421 | @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = linkage }); | |
| 422 | const __aeabi_llsr = @import("compiler_rt/shift.zig").__aeabi_llsr; | |
| 423 | @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = linkage }); | |
| 424 | ||
| 425 | const __aeabi_fcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpeq; | |
| 426 | @export(__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage }); | |
| 427 | const __aeabi_fcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmplt; | |
| 428 | @export(__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage }); | |
| 429 | const __aeabi_fcmple = @import("compiler_rt/compareXf2.zig").__aeabi_fcmple; | |
| 430 | @export(__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage }); | |
| 431 | const __aeabi_fcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpge; | |
| 432 | @export(__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage }); | |
| 433 | const __aeabi_fcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpgt; | |
| 434 | @export(__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage }); | |
| 435 | const __aeabi_fcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpun; | |
| 436 | @export(__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage }); | |
| 437 | ||
| 438 | const __aeabi_dcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpeq; | |
| 439 | @export(__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage }); | |
| 440 | const __aeabi_dcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmplt; | |
| 441 | @export(__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage }); | |
| 442 | const __aeabi_dcmple = @import("compiler_rt/compareXf2.zig").__aeabi_dcmple; | |
| 443 | @export(__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage }); | |
| 444 | const __aeabi_dcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpge; | |
| 445 | @export(__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage }); | |
| 446 | const __aeabi_dcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpgt; | |
| 447 | @export(__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage }); | |
| 448 | const __aeabi_dcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpun; | |
| 449 | @export(__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage }); | |
| 450 | } | |
| 530 | 451 | |
| 531 | if (builtin.os.tag == .windows) { | |
| 532 | // Default stack-probe functions emitted by LLVM | |
| 533 | if (is_mingw) { | |
| 534 | const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk; | |
| 535 | @export(_chkstk, .{ .name = "_alloca", .linkage = strong_linkage }); | |
| 536 | const ___chkstk_ms = @import("compiler_rt/stack_probe.zig").___chkstk_ms; | |
| 537 | @export(___chkstk_ms, .{ .name = "___chkstk_ms", .linkage = strong_linkage }); | |
| 538 | } else if (!builtin.link_libc) { | |
| 539 | // This symbols are otherwise exported by MSVCRT.lib | |
| 540 | const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk; | |
| 541 | @export(_chkstk, .{ .name = "_chkstk", .linkage = strong_linkage }); | |
| 542 | const __chkstk = @import("compiler_rt/stack_probe.zig").__chkstk; | |
| 543 | @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage }); | |
| 452 | if (arch == .i386 and abi == .msvc) { | |
| 453 | // Don't let LLVM apply the stdcall name mangling on those MSVC builtins | |
| 454 | const _alldiv = @import("compiler_rt/aulldiv.zig")._alldiv; | |
| 455 | @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage }); | |
| 456 | const _aulldiv = @import("compiler_rt/aulldiv.zig")._aulldiv; | |
| 457 | @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage }); | |
| 458 | const _allrem = @import("compiler_rt/aullrem.zig")._allrem; | |
| 459 | @export(_allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage }); | |
| 460 | const _aullrem = @import("compiler_rt/aullrem.zig")._aullrem; | |
| 461 | @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); | |
| 544 | 462 | } |
| 545 | 463 | |
| 546 | switch (arch) { | |
| 547 | .i386 => { | |
| 548 | const __divti3 = @import("compiler_rt/divti3.zig").__divti3; | |
| 549 | @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); | |
| 464 | if (arch.isSPARC()) { | |
| 465 | // SPARC systems use a different naming scheme | |
| 466 | const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add; | |
| 467 | @export(_Qp_add, .{ .name = "_Qp_add", .linkage = linkage }); | |
| 468 | const _Qp_div = @import("compiler_rt/sparc.zig")._Qp_div; | |
| 469 | @export(_Qp_div, .{ .name = "_Qp_div", .linkage = linkage }); | |
| 470 | const _Qp_mul = @import("compiler_rt/sparc.zig")._Qp_mul; | |
| 471 | @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage }); | |
| 472 | const _Qp_sub = @import("compiler_rt/sparc.zig")._Qp_sub; | |
| 473 | @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage }); | |
| 474 | ||
| 475 | const _Qp_cmp = @import("compiler_rt/sparc.zig")._Qp_cmp; | |
| 476 | @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage }); | |
| 477 | const _Qp_feq = @import("compiler_rt/sparc.zig")._Qp_feq; | |
| 478 | @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage }); | |
| 479 | const _Qp_fne = @import("compiler_rt/sparc.zig")._Qp_fne; | |
| 480 | @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage }); | |
| 481 | const _Qp_flt = @import("compiler_rt/sparc.zig")._Qp_flt; | |
| 482 | @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage }); | |
| 483 | const _Qp_fle = @import("compiler_rt/sparc.zig")._Qp_fle; | |
| 484 | @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage }); | |
| 485 | const _Qp_fgt = @import("compiler_rt/sparc.zig")._Qp_fgt; | |
| 486 | @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage }); | |
| 487 | const _Qp_fge = @import("compiler_rt/sparc.zig")._Qp_fge; | |
| 488 | @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage }); | |
| 489 | ||
| 490 | const _Qp_itoq = @import("compiler_rt/sparc.zig")._Qp_itoq; | |
| 491 | @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage }); | |
| 492 | const _Qp_uitoq = @import("compiler_rt/sparc.zig")._Qp_uitoq; | |
| 493 | @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage }); | |
| 494 | const _Qp_xtoq = @import("compiler_rt/sparc.zig")._Qp_xtoq; | |
| 495 | @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage }); | |
| 496 | const _Qp_uxtoq = @import("compiler_rt/sparc.zig")._Qp_uxtoq; | |
| 497 | @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage }); | |
| 498 | const _Qp_stoq = @import("compiler_rt/sparc.zig")._Qp_stoq; | |
| 499 | @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage }); | |
| 500 | const _Qp_dtoq = @import("compiler_rt/sparc.zig")._Qp_dtoq; | |
| 501 | @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage }); | |
| 502 | const _Qp_qtoi = @import("compiler_rt/sparc.zig")._Qp_qtoi; | |
| 503 | @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage }); | |
| 504 | const _Qp_qtoui = @import("compiler_rt/sparc.zig")._Qp_qtoui; | |
| 505 | @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage }); | |
| 506 | const _Qp_qtox = @import("compiler_rt/sparc.zig")._Qp_qtox; | |
| 507 | @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage }); | |
| 508 | const _Qp_qtoux = @import("compiler_rt/sparc.zig")._Qp_qtoux; | |
| 509 | @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage }); | |
| 510 | const _Qp_qtos = @import("compiler_rt/sparc.zig")._Qp_qtos; | |
| 511 | @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage }); | |
| 512 | const _Qp_qtod = @import("compiler_rt/sparc.zig")._Qp_qtod; | |
| 513 | @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); | |
| 514 | } | |
| 515 | ||
| 516 | if ((arch == .powerpc or arch.isPPC64()) and !is_test) { | |
| 517 | @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage }); | |
| 518 | @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage }); | |
| 519 | @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage }); | |
| 520 | @export(__divtf3, .{ .name = "__divkf3", .linkage = linkage }); | |
| 521 | @export(__extendsftf2, .{ .name = "__extendsfkf2", .linkage = linkage }); | |
| 522 | @export(__extenddftf2, .{ .name = "__extenddfkf2", .linkage = linkage }); | |
| 523 | @export(__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = linkage }); | |
| 524 | @export(__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = linkage }); | |
| 525 | @export(__fixtfdi, .{ .name = "__fixkfdi", .linkage = linkage }); | |
| 526 | @export(__fixtfsi, .{ .name = "__fixkfsi", .linkage = linkage }); | |
| 527 | @export(__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = linkage }); | |
| 528 | @export(__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = linkage }); | |
| 529 | @export(__floatsitf, .{ .name = "__floatsikf", .linkage = linkage }); | |
| 530 | @export(__floatditf, .{ .name = "__floatdikf", .linkage = linkage }); | |
| 531 | @export(__floatunditf, .{ .name = "__floatundikf", .linkage = linkage }); | |
| 532 | @export(__floatunsitf, .{ .name = "__floatunsikf", .linkage = linkage }); | |
| 533 | ||
| 534 | @export(__letf2, .{ .name = "__eqkf2", .linkage = linkage }); | |
| 535 | @export(__letf2, .{ .name = "__nekf2", .linkage = linkage }); | |
| 536 | @export(__getf2, .{ .name = "__gekf2", .linkage = linkage }); | |
| 537 | @export(__letf2, .{ .name = "__ltkf2", .linkage = linkage }); | |
| 538 | @export(__letf2, .{ .name = "__lekf2", .linkage = linkage }); | |
| 539 | @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage }); | |
| 540 | @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage }); | |
| 541 | } | |
| 542 | ||
| 543 | if (builtin.os.tag == .windows) { | |
| 544 | // Default stack-probe functions emitted by LLVM | |
| 545 | if (is_mingw) { | |
| 546 | const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk; | |
| 547 | @export(_chkstk, .{ .name = "_alloca", .linkage = strong_linkage }); | |
| 548 | const ___chkstk_ms = @import("compiler_rt/stack_probe.zig").___chkstk_ms; | |
| 549 | @export(___chkstk_ms, .{ .name = "___chkstk_ms", .linkage = strong_linkage }); | |
| 550 | } else if (!builtin.link_libc) { | |
| 551 | // This symbols are otherwise exported by MSVCRT.lib | |
| 552 | const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk; | |
| 553 | @export(_chkstk, .{ .name = "_chkstk", .linkage = strong_linkage }); | |
| 554 | const __chkstk = @import("compiler_rt/stack_probe.zig").__chkstk; | |
| 555 | @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage }); | |
| 556 | } | |
| 557 | ||
| 558 | switch (arch) { | |
| 559 | .i386 => { | |
| 560 | const __divti3 = @import("compiler_rt/divti3.zig").__divti3; | |
| 561 | @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); | |
| 562 | const __modti3 = @import("compiler_rt/modti3.zig").__modti3; | |
| 563 | @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); | |
| 564 | const __multi3 = @import("compiler_rt/multi3.zig").__multi3; | |
| 565 | @export(__multi3, .{ .name = "__multi3", .linkage = linkage }); | |
| 566 | const __udivti3 = @import("compiler_rt/udivti3.zig").__udivti3; | |
| 567 | @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); | |
| 568 | const __udivmodti4 = @import("compiler_rt/udivmodti4.zig").__udivmodti4; | |
| 569 | @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); | |
| 570 | const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3; | |
| 571 | @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); | |
| 572 | }, | |
| 573 | .x86_64 => { | |
| 574 | // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI | |
| 575 | // that LLVM expects compiler-rt to have. | |
| 576 | const __divti3_windows_x86_64 = @import("compiler_rt/divti3.zig").__divti3_windows_x86_64; | |
| 577 | @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = linkage }); | |
| 578 | const __modti3_windows_x86_64 = @import("compiler_rt/modti3.zig").__modti3_windows_x86_64; | |
| 579 | @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = linkage }); | |
| 580 | const __multi3_windows_x86_64 = @import("compiler_rt/multi3.zig").__multi3_windows_x86_64; | |
| 581 | @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = linkage }); | |
| 582 | const __udivti3_windows_x86_64 = @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64; | |
| 583 | @export(__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = linkage }); | |
| 584 | const __udivmodti4_windows_x86_64 = @import("compiler_rt/udivmodti4.zig").__udivmodti4_windows_x86_64; | |
| 585 | @export(__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = linkage }); | |
| 586 | const __umodti3_windows_x86_64 = @import("compiler_rt/umodti3.zig").__umodti3_windows_x86_64; | |
| 587 | @export(__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = linkage }); | |
| 588 | }, | |
| 589 | else => {}, | |
| 590 | } | |
| 591 | if (arch.isAARCH64()) { | |
| 592 | const __chkstk = @import("compiler_rt/stack_probe.zig").__chkstk; | |
| 593 | @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage }); | |
| 594 | const __divti3_windows = @import("compiler_rt/divti3.zig").__divti3; | |
| 595 | @export(__divti3_windows, .{ .name = "__divti3", .linkage = linkage }); | |
| 550 | 596 | const __modti3 = @import("compiler_rt/modti3.zig").__modti3; |
| 551 | 597 | @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); |
| 552 | const __multi3 = @import("compiler_rt/multi3.zig").__multi3; | |
| 553 | @export(__multi3, .{ .name = "__multi3", .linkage = linkage }); | |
| 554 | const __udivti3 = @import("compiler_rt/udivti3.zig").__udivti3; | |
| 555 | @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); | |
| 556 | const __udivmodti4 = @import("compiler_rt/udivmodti4.zig").__udivmodti4; | |
| 557 | @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); | |
| 598 | const __udivti3_windows = @import("compiler_rt/udivti3.zig").__udivti3; | |
| 599 | @export(__udivti3_windows, .{ .name = "__udivti3", .linkage = linkage }); | |
| 558 | 600 | const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3; |
| 559 | 601 | @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); |
| 560 | }, | |
| 561 | .x86_64 => { | |
| 562 | // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI | |
| 563 | // that LLVM expects compiler-rt to have. | |
| 564 | const __divti3_windows_x86_64 = @import("compiler_rt/divti3.zig").__divti3_windows_x86_64; | |
| 565 | @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = linkage }); | |
| 566 | const __modti3_windows_x86_64 = @import("compiler_rt/modti3.zig").__modti3_windows_x86_64; | |
| 567 | @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = linkage }); | |
| 568 | const __multi3_windows_x86_64 = @import("compiler_rt/multi3.zig").__multi3_windows_x86_64; | |
| 569 | @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = linkage }); | |
| 570 | const __udivti3_windows_x86_64 = @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64; | |
| 571 | @export(__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = linkage }); | |
| 572 | const __udivmodti4_windows_x86_64 = @import("compiler_rt/udivmodti4.zig").__udivmodti4_windows_x86_64; | |
| 573 | @export(__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = linkage }); | |
| 574 | const __umodti3_windows_x86_64 = @import("compiler_rt/umodti3.zig").__umodti3_windows_x86_64; | |
| 575 | @export(__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = linkage }); | |
| 576 | }, | |
| 577 | else => {}, | |
| 578 | } | |
| 579 | if (arch.isAARCH64()) { | |
| 580 | const __chkstk = @import("compiler_rt/stack_probe.zig").__chkstk; | |
| 581 | @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage }); | |
| 582 | const __divti3_windows = @import("compiler_rt/divti3.zig").__divti3; | |
| 583 | @export(__divti3_windows, .{ .name = "__divti3", .linkage = linkage }); | |
| 602 | } | |
| 603 | } else { | |
| 604 | const __divti3 = @import("compiler_rt/divti3.zig").__divti3; | |
| 605 | @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); | |
| 584 | 606 | const __modti3 = @import("compiler_rt/modti3.zig").__modti3; |
| 585 | 607 | @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); |
| 586 | const __udivti3_windows = @import("compiler_rt/udivti3.zig").__udivti3; | |
| 587 | @export(__udivti3_windows, .{ .name = "__udivti3", .linkage = linkage }); | |
| 608 | const __multi3 = @import("compiler_rt/multi3.zig").__multi3; | |
| 609 | @export(__multi3, .{ .name = "__multi3", .linkage = linkage }); | |
| 610 | const __udivti3 = @import("compiler_rt/udivti3.zig").__udivti3; | |
| 611 | @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); | |
| 612 | const __udivmodti4 = @import("compiler_rt/udivmodti4.zig").__udivmodti4; | |
| 613 | @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); | |
| 588 | 614 | const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3; |
| 589 | 615 | @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); |
| 590 | 616 | } |
| 591 | } else { | |
| 592 | const __divti3 = @import("compiler_rt/divti3.zig").__divti3; | |
| 593 | @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); | |
| 594 | const __modti3 = @import("compiler_rt/modti3.zig").__modti3; | |
| 595 | @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); | |
| 596 | const __multi3 = @import("compiler_rt/multi3.zig").__multi3; | |
| 597 | @export(__multi3, .{ .name = "__multi3", .linkage = linkage }); | |
| 598 | const __udivti3 = @import("compiler_rt/udivti3.zig").__udivti3; | |
| 599 | @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); | |
| 600 | const __udivmodti4 = @import("compiler_rt/udivmodti4.zig").__udivmodti4; | |
| 601 | @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); | |
| 602 | const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3; | |
| 603 | @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); | |
| 604 | } | |
| 605 | const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4; | |
| 606 | @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage }); | |
| 607 | const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4; | |
| 608 | @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); | |
| 617 | const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4; | |
| 618 | @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage }); | |
| 619 | const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4; | |
| 620 | @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); | |
| 609 | 621 | |
| 610 | _ = @import("compiler_rt/atomics.zig"); | |
| 622 | _ = @import("compiler_rt/atomics.zig"); | |
| 623 | } | |
| 611 | 624 | } |
| 612 | 625 | |
| 613 | 626 | // Avoid dragging in the runtime safety mechanisms into this .o file, |
| 614 | 627 | // unless we're trying to test this file. |
| 615 | pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { | |
| 628 | pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { | |
| 616 | 629 | _ = error_return_trace; |
| 617 | 630 | @setCold(true); |
| 631 | if (builtin.zig_is_stage2) { | |
| 632 | while (true) { | |
| 633 | @breakpoint(); | |
| 634 | } | |
| 635 | } | |
| 618 | 636 | if (is_test) { |
| 619 | 637 | std.debug.panic("{s}", .{msg}); |
| 620 | 638 | } else { |
lib/std/special/compiler_rt/extendXfYf2.zig+6-6| ... | ... | @@ -3,23 +3,23 @@ const builtin = @import("builtin"); |
| 3 | 3 | const is_test = builtin.is_test; |
| 4 | 4 | |
| 5 | 5 | pub fn __extendsfdf2(a: f32) callconv(.C) f64 { |
| 6 | return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f64, f32, @bitCast(u32, a) }); | |
| 6 | return extendXfYf2(f64, f32, @bitCast(u32, a)); | |
| 7 | 7 | } |
| 8 | 8 | |
| 9 | 9 | pub fn __extenddftf2(a: f64) callconv(.C) f128 { |
| 10 | return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f64, @bitCast(u64, a) }); | |
| 10 | return extendXfYf2(f128, f64, @bitCast(u64, a)); | |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | 13 | pub fn __extendsftf2(a: f32) callconv(.C) f128 { |
| 14 | return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f32, @bitCast(u32, a) }); | |
| 14 | return extendXfYf2(f128, f32, @bitCast(u32, a)); | |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | 17 | pub fn __extendhfsf2(a: u16) callconv(.C) f32 { |
| 18 | return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, a }); | |
| 18 | return extendXfYf2(f32, f16, a); | |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | pub fn __extendhftf2(a: u16) callconv(.C) f128 { |
| 22 | return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f16, a }); | |
| 22 | return extendXfYf2(f128, f16, a); | |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | 25 | pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 { |
| ... | ... | @@ -34,7 +34,7 @@ pub fn __aeabi_f2d(arg: f32) callconv(.AAPCS) f64 { |
| 34 | 34 | |
| 35 | 35 | const CHAR_BIT = 8; |
| 36 | 36 | |
| 37 | fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits)) dst_t { | |
| 37 | inline fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits)) dst_t { | |
| 38 | 38 | @setRuntimeSafety(builtin.is_test); |
| 39 | 39 | |
| 40 | 40 | const src_rep_t = std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits); |
lib/std/special/compiler_rt/stack_probe.zig+18-14| ... | ... | @@ -53,19 +53,6 @@ pub fn zig_probe_stack() callconv(.Naked) void { |
| 53 | 53 | }, |
| 54 | 54 | else => {}, |
| 55 | 55 | } |
| 56 | if (comptime native_arch.isAARCH64()) { | |
| 57 | asm volatile ( | |
| 58 | \\ lsl x16, x15, #4 | |
| 59 | \\ mov x17, sp | |
| 60 | \\1: | |
| 61 | \\ sub x17, x17, #PAGE_SIZE | |
| 62 | \\ subs x16, x16, #PAGE_SIZE | |
| 63 | \\ ldr xzr, [x17] | |
| 64 | \\ b.gt 1b | |
| 65 | \\ | |
| 66 | \\ ret | |
| 67 | ); | |
| 68 | } | |
| 69 | 56 | |
| 70 | 57 | unreachable; |
| 71 | 58 | } |
| ... | ... | @@ -118,6 +105,21 @@ fn win_probe_stack_only() void { |
| 118 | 105 | }, |
| 119 | 106 | else => {}, |
| 120 | 107 | } |
| 108 | if (comptime native_arch.isAARCH64()) { | |
| 109 | // NOTE: page size hardcoded to 4096 for now | |
| 110 | asm volatile ( | |
| 111 | \\ lsl x16, x15, #4 | |
| 112 | \\ mov x17, sp | |
| 113 | \\1: | |
| 114 | \\ | |
| 115 | \\ sub x17, x17, 4096 | |
| 116 | \\ subs x16, x16, 4096 | |
| 117 | \\ ldr xzr, [x17] | |
| 118 | \\ b.gt 1b | |
| 119 | \\ | |
| 120 | \\ ret | |
| 121 | ); | |
| 122 | } | |
| 121 | 123 | |
| 122 | 124 | unreachable; |
| 123 | 125 | } |
| ... | ... | @@ -199,7 +201,9 @@ pub fn _chkstk() callconv(.Naked) void { |
| 199 | 201 | } |
| 200 | 202 | pub fn __chkstk() callconv(.Naked) void { |
| 201 | 203 | @setRuntimeSafety(false); |
| 202 | switch (native_arch) { | |
| 204 | if (comptime native_arch.isAARCH64()) { | |
| 205 | @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{}); | |
| 206 | } else switch (native_arch) { | |
| 203 | 207 | .i386 => @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{}), |
| 204 | 208 | .x86_64 => @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{}), |
| 205 | 209 | else => unreachable, |
lib/std/special/test_runner.zig+16-2| ... | ... | @@ -13,6 +13,12 @@ fn processArgs() void { |
| 13 | 13 | const args = std.process.argsAlloc(&args_allocator.allocator) catch { |
| 14 | 14 | @panic("Too many bytes passed over the CLI to the test runner"); |
| 15 | 15 | }; |
| 16 | if (args.len != 2) { | |
| 17 | const self_name = if (args.len >= 1) args[0] else if (builtin.os.tag == .windows) "test.exe" else "test"; | |
| 18 | const zig_ext = if (builtin.os.tag == .windows) ".exe" else ""; | |
| 19 | std.debug.print("Usage: {s} path/to/zig{s}\n", .{ self_name, zig_ext }); | |
| 20 | @panic("Wrong number of command line arguments"); | |
| 21 | } | |
| 16 | 22 | std.testing.zig_exe_path = args[1]; |
| 17 | 23 | } |
| 18 | 24 | |
| ... | ... | @@ -56,7 +62,7 @@ pub fn main() void { |
| 56 | 62 | .evented => blk: { |
| 57 | 63 | if (async_frame_buffer.len < size) { |
| 58 | 64 | std.heap.page_allocator.free(async_frame_buffer); |
| 59 | async_frame_buffer = try std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size); | |
| 65 | async_frame_buffer = std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size) catch @panic("out of memory"); | |
| 60 | 66 | } |
| 61 | 67 | const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func); |
| 62 | 68 | break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{}); |
| ... | ... | @@ -123,8 +129,16 @@ pub fn log( |
| 123 | 129 | } |
| 124 | 130 | |
| 125 | 131 | pub fn main2() anyerror!void { |
| 132 | var bad = false; | |
| 126 | 133 | // Simpler main(), exercising fewer language features, so that stage2 can handle it. |
| 127 | 134 | for (builtin.test_functions) |test_fn| { |
| 128 | try test_fn.func(); | |
| 135 | test_fn.func() catch |err| { | |
| 136 | if (err != error.SkipZigTest) { | |
| 137 | bad = true; | |
| 138 | } | |
| 139 | }; | |
| 140 | } | |
| 141 | if (bad) { | |
| 142 | return error.TestsFailed; | |
| 129 | 143 | } |
| 130 | 144 | } |
lib/std/start.zig+17| ... | ... | @@ -28,6 +28,8 @@ comptime { |
| 28 | 28 | if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { |
| 29 | 29 | @export(main2, .{ .name = "main" }); |
| 30 | 30 | } |
| 31 | } else if (builtin.os.tag == .windows) { | |
| 32 | @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" }); | |
| 31 | 33 | } else { |
| 32 | 34 | if (!@hasDecl(root, "_start")) { |
| 33 | 35 | @export(_start2, .{ .name = "_start" }); |
| ... | ... | @@ -87,6 +89,16 @@ fn main2() callconv(.C) c_int { |
| 87 | 89 | } |
| 88 | 90 | |
| 89 | 91 | fn _start2() callconv(.Naked) noreturn { |
| 92 | callMain2(); | |
| 93 | } | |
| 94 | ||
| 95 | fn callMain2() noreturn { | |
| 96 | @setAlignStack(16); | |
| 97 | root.main(); | |
| 98 | exit2(0); | |
| 99 | } | |
| 100 | ||
| 101 | fn wWinMainCRTStartup2() callconv(.C) noreturn { | |
| 90 | 102 | root.main(); |
| 91 | 103 | exit2(0); |
| 92 | 104 | } |
| ... | ... | @@ -143,11 +155,16 @@ fn exit2(code: usize) noreturn { |
| 143 | 155 | }, |
| 144 | 156 | else => @compileError("TODO"), |
| 145 | 157 | }, |
| 158 | .windows => { | |
| 159 | ExitProcess(@truncate(u32, code)); | |
| 160 | }, | |
| 146 | 161 | else => @compileError("TODO"), |
| 147 | 162 | } |
| 148 | 163 | unreachable; |
| 149 | 164 | } |
| 150 | 165 | |
| 166 | extern "kernel32" fn ExitProcess(exit_code: u32) callconv(.C) noreturn; | |
| 167 | ||
| 151 | 168 | //////////////////////////////////////////////////////////////////////////////// |
| 152 | 169 | |
| 153 | 170 | fn _DllMainCRTStartup( |
lib/std/target.zig+9-3| ... | ... | @@ -235,7 +235,6 @@ pub const Target = struct { |
| 235 | 235 | .fuchsia, |
| 236 | 236 | .kfreebsd, |
| 237 | 237 | .lv2, |
| 238 | .solaris, | |
| 239 | 238 | .zos, |
| 240 | 239 | .haiku, |
| 241 | 240 | .minix, |
| ... | ... | @@ -310,6 +309,12 @@ pub const Target = struct { |
| 310 | 309 | .max = .{ .major = 6, .minor = 0 }, |
| 311 | 310 | }, |
| 312 | 311 | }, |
| 312 | .solaris => return .{ | |
| 313 | .semver = .{ | |
| 314 | .min = .{ .major = 5, .minor = 11 }, | |
| 315 | .max = .{ .major = 5, .minor = 11 }, | |
| 316 | }, | |
| 317 | }, | |
| 313 | 318 | |
| 314 | 319 | .linux => return .{ |
| 315 | 320 | .linux = .{ |
| ... | ... | @@ -353,6 +358,7 @@ pub const Target = struct { |
| 353 | 358 | .netbsd, |
| 354 | 359 | .openbsd, |
| 355 | 360 | .dragonfly, |
| 361 | .solaris, | |
| 356 | 362 | => return TaggedVersionRange{ .semver = self.version_range.semver }, |
| 357 | 363 | |
| 358 | 364 | else => return .none, |
| ... | ... | @@ -385,6 +391,7 @@ pub const Target = struct { |
| 385 | 391 | .dragonfly, |
| 386 | 392 | .openbsd, |
| 387 | 393 | .haiku, |
| 394 | .solaris, | |
| 388 | 395 | => true, |
| 389 | 396 | |
| 390 | 397 | .linux, |
| ... | ... | @@ -395,7 +402,6 @@ pub const Target = struct { |
| 395 | 402 | .fuchsia, |
| 396 | 403 | .kfreebsd, |
| 397 | 404 | .lv2, |
| 398 | .solaris, | |
| 399 | 405 | .zos, |
| 400 | 406 | .minix, |
| 401 | 407 | .rtems, |
| ... | ... | @@ -1523,6 +1529,7 @@ pub const Target = struct { |
| 1523 | 1529 | .netbsd => return copy(&result, "/libexec/ld.elf_so"), |
| 1524 | 1530 | .openbsd => return copy(&result, "/usr/libexec/ld.so"), |
| 1525 | 1531 | .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"), |
| 1532 | .solaris => return copy(&result, "/lib/64/ld.so.1"), | |
| 1526 | 1533 | .linux => switch (self.cpu.arch) { |
| 1527 | 1534 | .i386, |
| 1528 | 1535 | .sparc, |
| ... | ... | @@ -1642,7 +1649,6 @@ pub const Target = struct { |
| 1642 | 1649 | .fuchsia, |
| 1643 | 1650 | .kfreebsd, |
| 1644 | 1651 | .lv2, |
| 1645 | .solaris, | |
| 1646 | 1652 | .zos, |
| 1647 | 1653 | .minix, |
| 1648 | 1654 | .rtems, |
lib/std/unicode.zig+22-4| ... | ... | @@ -553,8 +553,9 @@ fn testDecode(bytes: []const u8) !u21 { |
| 553 | 553 | /// Caller must free returned memory. |
| 554 | 554 | pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 { |
| 555 | 555 | var result = std.ArrayList(u8).init(allocator); |
| 556 | errdefer result.deinit(); | |
| 556 | 557 | // optimistically guess that it will all be ascii. |
| 557 | try result.ensureCapacity(utf16le.len); | |
| 558 | try result.ensureTotalCapacity(utf16le.len); | |
| 558 | 559 | var out_index: usize = 0; |
| 559 | 560 | var it = Utf16LeIterator.init(utf16le); |
| 560 | 561 | while (try it.nextCodepoint()) |codepoint| { |
| ... | ... | @@ -569,9 +570,10 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 |
| 569 | 570 | |
| 570 | 571 | /// Caller must free returned memory. |
| 571 | 572 | pub fn utf16leToUtf8AllocZ(allocator: *mem.Allocator, utf16le: []const u16) ![:0]u8 { |
| 572 | var result = try std.ArrayList(u8).initCapacity(allocator, utf16le.len); | |
| 573 | var result = std.ArrayList(u8).init(allocator); | |
| 574 | errdefer result.deinit(); | |
| 573 | 575 | // optimistically guess that it will all be ascii. |
| 574 | try result.ensureCapacity(utf16le.len); | |
| 576 | try result.ensureTotalCapacity(utf16le.len); | |
| 575 | 577 | var out_index: usize = 0; |
| 576 | 578 | var it = Utf16LeIterator.init(utf16le); |
| 577 | 579 | while (try it.nextCodepoint()) |codepoint| { |
| ... | ... | @@ -653,12 +655,20 @@ test "utf16leToUtf8" { |
| 653 | 655 | defer std.testing.allocator.free(utf8); |
| 654 | 656 | try testing.expect(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80")); |
| 655 | 657 | } |
| 658 | ||
| 659 | { | |
| 660 | mem.writeIntSliceLittle(u16, utf16le_as_bytes[0..], 0xdcdc); | |
| 661 | mem.writeIntSliceLittle(u16, utf16le_as_bytes[2..], 0xdcdc); | |
| 662 | const result = utf16leToUtf8Alloc(std.testing.allocator, &utf16le); | |
| 663 | try std.testing.expectError(error.UnexpectedSecondSurrogateHalf, result); | |
| 664 | } | |
| 656 | 665 | } |
| 657 | 666 | |
| 658 | 667 | pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![:0]u16 { |
| 659 | 668 | var result = std.ArrayList(u16).init(allocator); |
| 669 | errdefer result.deinit(); | |
| 660 | 670 | // optimistically guess that it will not require surrogate pairs |
| 661 | try result.ensureCapacity(utf8.len + 1); | |
| 671 | try result.ensureTotalCapacity(utf8.len + 1); | |
| 662 | 672 | |
| 663 | 673 | const view = try Utf8View.init(utf8); |
| 664 | 674 | var it = view.iterator(); |
| ... | ... | @@ -718,6 +728,10 @@ test "utf8ToUtf16Le" { |
| 718 | 728 | try testing.expectEqual(@as(usize, 2), length); |
| 719 | 729 | try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16le[0..])); |
| 720 | 730 | } |
| 731 | { | |
| 732 | const result = utf8ToUtf16Le(utf16le[0..], "\xf4\x90\x80\x80"); | |
| 733 | try testing.expectError(error.InvalidUtf8, result); | |
| 734 | } | |
| 721 | 735 | } |
| 722 | 736 | |
| 723 | 737 | test "utf8ToUtf16LeWithNull" { |
| ... | ... | @@ -733,6 +747,10 @@ test "utf8ToUtf16LeWithNull" { |
| 733 | 747 | try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16[0..])); |
| 734 | 748 | try testing.expect(utf16[2] == 0); |
| 735 | 749 | } |
| 750 | { | |
| 751 | const result = utf8ToUtf16LeWithNull(testing.allocator, "\xf4\x90\x80\x80"); | |
| 752 | try testing.expectError(error.InvalidUtf8, result); | |
| 753 | } | |
| 736 | 754 | } |
| 737 | 755 | |
| 738 | 756 | /// Converts a UTF-8 string literal into a UTF-16LE string literal. |
lib/std/x/os/net.zig+12-16| ... | ... | @@ -27,7 +27,7 @@ pub fn resolveScopeId(name: []const u8) !u32 { |
| 27 | 27 | return rc; |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | const fd = try os.socket(os.AF.UNIX, os.SOCK.DGRAM, 0); | |
| 30 | const fd = try os.socket(os.AF.INET, os.SOCK.DGRAM, 0); | |
| 31 | 31 | defer os.closeSocket(fd); |
| 32 | 32 | |
| 33 | 33 | var f: os.ifreq = undefined; |
| ... | ... | @@ -566,21 +566,17 @@ test "ipv6: parse & format" { |
| 566 | 566 | |
| 567 | 567 | test "ipv6: parse & format addresses with scope ids" { |
| 568 | 568 | if (!have_ifnamesize) return error.SkipZigTest; |
| 569 | ||
| 570 | const inputs = [_][]const u8{ | |
| 571 | "FF01::FB%lo", | |
| 572 | }; | |
| 573 | ||
| 574 | const outputs = [_][]const u8{ | |
| 575 | "ff01::fb%1", | |
| 569 | const iface = if (native_os.tag == .linux) | |
| 570 | "lo" | |
| 571 | else | |
| 572 | "lo0"; | |
| 573 | const input = "FF01::FB%" ++ iface; | |
| 574 | const output = "ff01::fb%1"; | |
| 575 | ||
| 576 | const parsed = IPv6.parse(input) catch |err| switch (err) { | |
| 577 | error.InterfaceNotFound => return, | |
| 578 | else => return err, | |
| 576 | 579 | }; |
| 577 | 580 | |
| 578 | for (inputs) |input, i| { | |
| 579 | const parsed = IPv6.parse(input) catch |err| switch (err) { | |
| 580 | error.InterfaceNotFound => continue, | |
| 581 | else => return err, | |
| 582 | }; | |
| 583 | ||
| 584 | try testing.expectFmt(outputs[i], "{}", .{parsed}); | |
| 585 | } | |
| 581 | try testing.expectFmt(output, "{}", .{parsed}); | |
| 586 | 582 | } |
lib/std/x/os/socket.zig+1-1| ... | ... | @@ -37,7 +37,7 @@ pub const Socket = struct { |
| 37 | 37 | |
| 38 | 38 | /// POSIX `sockaddr.storage`. The expected size and alignment is specified in IETF RFC 2553. |
| 39 | 39 | pub const Storage = extern struct { |
| 40 | pub const expected_size = 128; | |
| 40 | pub const expected_size = os.sockaddr.SS_MAXSIZE; | |
| 41 | 41 | pub const expected_alignment = 8; |
| 42 | 42 | |
| 43 | 43 | pub const padding_size = expected_size - |
lib/std/zig/Ast.zig+94-16| ... | ... | @@ -262,6 +262,9 @@ pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void { |
| 262 | 262 | token_tags[parse_error.token].symbol(), |
| 263 | 263 | }); |
| 264 | 264 | }, |
| 265 | .extra_addrspace_qualifier => { | |
| 266 | return stream.writeAll("extra addrspace qualifier"); | |
| 267 | }, | |
| 265 | 268 | .extra_align_qualifier => { |
| 266 | 269 | return stream.writeAll("extra align qualifier"); |
| 267 | 270 | }, |
| ... | ... | @@ -392,14 +395,18 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex { |
| 392 | 395 | .assign_mod, |
| 393 | 396 | .assign_add, |
| 394 | 397 | .assign_sub, |
| 395 | .assign_bit_shift_left, | |
| 396 | .assign_bit_shift_right, | |
| 398 | .assign_shl, | |
| 399 | .assign_shl_sat, | |
| 400 | .assign_shr, | |
| 397 | 401 | .assign_bit_and, |
| 398 | 402 | .assign_bit_xor, |
| 399 | 403 | .assign_bit_or, |
| 400 | 404 | .assign_mul_wrap, |
| 401 | 405 | .assign_add_wrap, |
| 402 | 406 | .assign_sub_wrap, |
| 407 | .assign_mul_sat, | |
| 408 | .assign_add_sat, | |
| 409 | .assign_sub_sat, | |
| 403 | 410 | .assign, |
| 404 | 411 | .merge_error_sets, |
| 405 | 412 | .mul, |
| ... | ... | @@ -407,13 +414,17 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex { |
| 407 | 414 | .mod, |
| 408 | 415 | .array_mult, |
| 409 | 416 | .mul_wrap, |
| 417 | .mul_sat, | |
| 410 | 418 | .add, |
| 411 | 419 | .sub, |
| 412 | 420 | .array_cat, |
| 413 | 421 | .add_wrap, |
| 414 | 422 | .sub_wrap, |
| 415 | .bit_shift_left, | |
| 416 | .bit_shift_right, | |
| 423 | .add_sat, | |
| 424 | .sub_sat, | |
| 425 | .shl, | |
| 426 | .shl_sat, | |
| 427 | .shr, | |
| 417 | 428 | .bit_and, |
| 418 | 429 | .bit_xor, |
| 419 | 430 | .bit_or, |
| ... | ... | @@ -648,14 +659,18 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex { |
| 648 | 659 | .assign_mod, |
| 649 | 660 | .assign_add, |
| 650 | 661 | .assign_sub, |
| 651 | .assign_bit_shift_left, | |
| 652 | .assign_bit_shift_right, | |
| 662 | .assign_shl, | |
| 663 | .assign_shl_sat, | |
| 664 | .assign_shr, | |
| 653 | 665 | .assign_bit_and, |
| 654 | 666 | .assign_bit_xor, |
| 655 | 667 | .assign_bit_or, |
| 656 | 668 | .assign_mul_wrap, |
| 657 | 669 | .assign_add_wrap, |
| 658 | 670 | .assign_sub_wrap, |
| 671 | .assign_mul_sat, | |
| 672 | .assign_add_sat, | |
| 673 | .assign_sub_sat, | |
| 659 | 674 | .assign, |
| 660 | 675 | .merge_error_sets, |
| 661 | 676 | .mul, |
| ... | ... | @@ -663,13 +678,17 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex { |
| 663 | 678 | .mod, |
| 664 | 679 | .array_mult, |
| 665 | 680 | .mul_wrap, |
| 681 | .mul_sat, | |
| 666 | 682 | .add, |
| 667 | 683 | .sub, |
| 668 | 684 | .array_cat, |
| 669 | 685 | .add_wrap, |
| 670 | 686 | .sub_wrap, |
| 671 | .bit_shift_left, | |
| 672 | .bit_shift_right, | |
| 687 | .add_sat, | |
| 688 | .sub_sat, | |
| 689 | .shl, | |
| 690 | .shl_sat, | |
| 691 | .shr, | |
| 673 | 692 | .bit_and, |
| 674 | 693 | .bit_xor, |
| 675 | 694 | .bit_or, |
| ... | ... | @@ -1021,7 +1040,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex { |
| 1021 | 1040 | }, |
| 1022 | 1041 | .fn_proto_one => { |
| 1023 | 1042 | const extra = tree.extraData(datas[n].lhs, Node.FnProtoOne); |
| 1024 | // linksection, callconv, align can appear in any order, so we | |
| 1043 | // addrspace, linksection, callconv, align can appear in any order, so we | |
| 1025 | 1044 | // find the last one here. |
| 1026 | 1045 | var max_node: Node.Index = datas[n].rhs; |
| 1027 | 1046 | var max_start = token_starts[main_tokens[max_node]]; |
| ... | ... | @@ -1034,6 +1053,14 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex { |
| 1034 | 1053 | max_offset = 1; // for the rparen |
| 1035 | 1054 | } |
| 1036 | 1055 | } |
| 1056 | if (extra.addrspace_expr != 0) { | |
| 1057 | const start = token_starts[main_tokens[extra.addrspace_expr]]; | |
| 1058 | if (start > max_start) { | |
| 1059 | max_node = extra.addrspace_expr; | |
| 1060 | max_start = start; | |
| 1061 | max_offset = 1; // for the rparen | |
| 1062 | } | |
| 1063 | } | |
| 1037 | 1064 | if (extra.section_expr != 0) { |
| 1038 | 1065 | const start = token_starts[main_tokens[extra.section_expr]]; |
| 1039 | 1066 | if (start > max_start) { |
| ... | ... | @@ -1055,7 +1082,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex { |
| 1055 | 1082 | }, |
| 1056 | 1083 | .fn_proto => { |
| 1057 | 1084 | const extra = tree.extraData(datas[n].lhs, Node.FnProto); |
| 1058 | // linksection, callconv, align can appear in any order, so we | |
| 1085 | // addrspace, linksection, callconv, align can appear in any order, so we | |
| 1059 | 1086 | // find the last one here. |
| 1060 | 1087 | var max_node: Node.Index = datas[n].rhs; |
| 1061 | 1088 | var max_start = token_starts[main_tokens[max_node]]; |
| ... | ... | @@ -1068,6 +1095,14 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex { |
| 1068 | 1095 | max_offset = 1; // for the rparen |
| 1069 | 1096 | } |
| 1070 | 1097 | } |
| 1098 | if (extra.addrspace_expr != 0) { | |
| 1099 | const start = token_starts[main_tokens[extra.addrspace_expr]]; | |
| 1100 | if (start > max_start) { | |
| 1101 | max_node = extra.addrspace_expr; | |
| 1102 | max_start = start; | |
| 1103 | max_offset = 1; // for the rparen | |
| 1104 | } | |
| 1105 | } | |
| 1071 | 1106 | if (extra.section_expr != 0) { |
| 1072 | 1107 | const start = token_starts[main_tokens[extra.section_expr]]; |
| 1073 | 1108 | if (start > max_start) { |
| ... | ... | @@ -1138,6 +1173,7 @@ pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl { |
| 1138 | 1173 | return tree.fullVarDecl(.{ |
| 1139 | 1174 | .type_node = extra.type_node, |
| 1140 | 1175 | .align_node = extra.align_node, |
| 1176 | .addrspace_node = extra.addrspace_node, | |
| 1141 | 1177 | .section_node = extra.section_node, |
| 1142 | 1178 | .init_node = data.rhs, |
| 1143 | 1179 | .mut_token = tree.nodes.items(.main_token)[node], |
| ... | ... | @@ -1151,6 +1187,7 @@ pub fn localVarDecl(tree: Tree, node: Node.Index) full.VarDecl { |
| 1151 | 1187 | return tree.fullVarDecl(.{ |
| 1152 | 1188 | .type_node = extra.type_node, |
| 1153 | 1189 | .align_node = extra.align_node, |
| 1190 | .addrspace_node = 0, | |
| 1154 | 1191 | .section_node = 0, |
| 1155 | 1192 | .init_node = data.rhs, |
| 1156 | 1193 | .mut_token = tree.nodes.items(.main_token)[node], |
| ... | ... | @@ -1163,6 +1200,7 @@ pub fn simpleVarDecl(tree: Tree, node: Node.Index) full.VarDecl { |
| 1163 | 1200 | return tree.fullVarDecl(.{ |
| 1164 | 1201 | .type_node = data.lhs, |
| 1165 | 1202 | .align_node = 0, |
| 1203 | .addrspace_node = 0, | |
| 1166 | 1204 | .section_node = 0, |
| 1167 | 1205 | .init_node = data.rhs, |
| 1168 | 1206 | .mut_token = tree.nodes.items(.main_token)[node], |
| ... | ... | @@ -1175,6 +1213,7 @@ pub fn alignedVarDecl(tree: Tree, node: Node.Index) full.VarDecl { |
| 1175 | 1213 | return tree.fullVarDecl(.{ |
| 1176 | 1214 | .type_node = 0, |
| 1177 | 1215 | .align_node = data.lhs, |
| 1216 | .addrspace_node = 0, | |
| 1178 | 1217 | .section_node = 0, |
| 1179 | 1218 | .init_node = data.rhs, |
| 1180 | 1219 | .mut_token = tree.nodes.items(.main_token)[node], |
| ... | ... | @@ -1249,6 +1288,7 @@ pub fn fnProtoSimple(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full. |
| 1249 | 1288 | .return_type = data.rhs, |
| 1250 | 1289 | .params = params, |
| 1251 | 1290 | .align_expr = 0, |
| 1291 | .addrspace_expr = 0, | |
| 1252 | 1292 | .section_expr = 0, |
| 1253 | 1293 | .callconv_expr = 0, |
| 1254 | 1294 | }); |
| ... | ... | @@ -1265,6 +1305,7 @@ pub fn fnProtoMulti(tree: Tree, node: Node.Index) full.FnProto { |
| 1265 | 1305 | .return_type = data.rhs, |
| 1266 | 1306 | .params = params, |
| 1267 | 1307 | .align_expr = 0, |
| 1308 | .addrspace_expr = 0, | |
| 1268 | 1309 | .section_expr = 0, |
| 1269 | 1310 | .callconv_expr = 0, |
| 1270 | 1311 | }); |
| ... | ... | @@ -1282,6 +1323,7 @@ pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnP |
| 1282 | 1323 | .return_type = data.rhs, |
| 1283 | 1324 | .params = params, |
| 1284 | 1325 | .align_expr = extra.align_expr, |
| 1326 | .addrspace_expr = extra.addrspace_expr, | |
| 1285 | 1327 | .section_expr = extra.section_expr, |
| 1286 | 1328 | .callconv_expr = extra.callconv_expr, |
| 1287 | 1329 | }); |
| ... | ... | @@ -1298,6 +1340,7 @@ pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto { |
| 1298 | 1340 | .return_type = data.rhs, |
| 1299 | 1341 | .params = params, |
| 1300 | 1342 | .align_expr = extra.align_expr, |
| 1343 | .addrspace_expr = extra.addrspace_expr, | |
| 1301 | 1344 | .section_expr = extra.section_expr, |
| 1302 | 1345 | .callconv_expr = extra.callconv_expr, |
| 1303 | 1346 | }); |
| ... | ... | @@ -1453,6 +1496,7 @@ pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType { |
| 1453 | 1496 | return tree.fullPtrType(.{ |
| 1454 | 1497 | .main_token = tree.nodes.items(.main_token)[node], |
| 1455 | 1498 | .align_node = data.lhs, |
| 1499 | .addrspace_node = 0, | |
| 1456 | 1500 | .sentinel = 0, |
| 1457 | 1501 | .bit_range_start = 0, |
| 1458 | 1502 | .bit_range_end = 0, |
| ... | ... | @@ -1466,6 +1510,7 @@ pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType { |
| 1466 | 1510 | return tree.fullPtrType(.{ |
| 1467 | 1511 | .main_token = tree.nodes.items(.main_token)[node], |
| 1468 | 1512 | .align_node = 0, |
| 1513 | .addrspace_node = 0, | |
| 1469 | 1514 | .sentinel = data.lhs, |
| 1470 | 1515 | .bit_range_start = 0, |
| 1471 | 1516 | .bit_range_end = 0, |
| ... | ... | @@ -1480,6 +1525,7 @@ pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType { |
| 1480 | 1525 | return tree.fullPtrType(.{ |
| 1481 | 1526 | .main_token = tree.nodes.items(.main_token)[node], |
| 1482 | 1527 | .align_node = extra.align_node, |
| 1528 | .addrspace_node = extra.addrspace_node, | |
| 1483 | 1529 | .sentinel = extra.sentinel, |
| 1484 | 1530 | .bit_range_start = 0, |
| 1485 | 1531 | .bit_range_end = 0, |
| ... | ... | @@ -1494,6 +1540,7 @@ pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType { |
| 1494 | 1540 | return tree.fullPtrType(.{ |
| 1495 | 1541 | .main_token = tree.nodes.items(.main_token)[node], |
| 1496 | 1542 | .align_node = extra.align_node, |
| 1543 | .addrspace_node = extra.addrspace_node, | |
| 1497 | 1544 | .sentinel = extra.sentinel, |
| 1498 | 1545 | .bit_range_start = extra.bit_range_start, |
| 1499 | 1546 | .bit_range_end = extra.bit_range_end, |
| ... | ... | @@ -2063,6 +2110,7 @@ pub const full = struct { |
| 2063 | 2110 | mut_token: TokenIndex, |
| 2064 | 2111 | type_node: Node.Index, |
| 2065 | 2112 | align_node: Node.Index, |
| 2113 | addrspace_node: Node.Index, | |
| 2066 | 2114 | section_node: Node.Index, |
| 2067 | 2115 | init_node: Node.Index, |
| 2068 | 2116 | }; |
| ... | ... | @@ -2130,6 +2178,7 @@ pub const full = struct { |
| 2130 | 2178 | return_type: Node.Index, |
| 2131 | 2179 | params: []const Node.Index, |
| 2132 | 2180 | align_expr: Node.Index, |
| 2181 | addrspace_expr: Node.Index, | |
| 2133 | 2182 | section_expr: Node.Index, |
| 2134 | 2183 | callconv_expr: Node.Index, |
| 2135 | 2184 | }; |
| ... | ... | @@ -2288,6 +2337,7 @@ pub const full = struct { |
| 2288 | 2337 | pub const Components = struct { |
| 2289 | 2338 | main_token: TokenIndex, |
| 2290 | 2339 | align_node: Node.Index, |
| 2340 | addrspace_node: Node.Index, | |
| 2291 | 2341 | sentinel: Node.Index, |
| 2292 | 2342 | bit_range_start: Node.Index, |
| 2293 | 2343 | bit_range_end: Node.Index, |
| ... | ... | @@ -2397,6 +2447,7 @@ pub const Error = struct { |
| 2397 | 2447 | expected_var_decl_or_fn, |
| 2398 | 2448 | expected_loop_payload, |
| 2399 | 2449 | expected_container, |
| 2450 | extra_addrspace_qualifier, | |
| 2400 | 2451 | extra_align_qualifier, |
| 2401 | 2452 | extra_allowzero_qualifier, |
| 2402 | 2453 | extra_const_qualifier, |
| ... | ... | @@ -2489,9 +2540,11 @@ pub const Node = struct { |
| 2489 | 2540 | /// `lhs -= rhs`. main_token is op. |
| 2490 | 2541 | assign_sub, |
| 2491 | 2542 | /// `lhs <<= rhs`. main_token is op. |
| 2492 | assign_bit_shift_left, | |
| 2543 | assign_shl, | |
| 2544 | /// `lhs <<|= rhs`. main_token is op. | |
| 2545 | assign_shl_sat, | |
| 2493 | 2546 | /// `lhs >>= rhs`. main_token is op. |
| 2494 | assign_bit_shift_right, | |
| 2547 | assign_shr, | |
| 2495 | 2548 | /// `lhs &= rhs`. main_token is op. |
| 2496 | 2549 | assign_bit_and, |
| 2497 | 2550 | /// `lhs ^= rhs`. main_token is op. |
| ... | ... | @@ -2504,6 +2557,12 @@ pub const Node = struct { |
| 2504 | 2557 | assign_add_wrap, |
| 2505 | 2558 | /// `lhs -%= rhs`. main_token is op. |
| 2506 | 2559 | assign_sub_wrap, |
| 2560 | /// `lhs *|= rhs`. main_token is op. | |
| 2561 | assign_mul_sat, | |
| 2562 | /// `lhs +|= rhs`. main_token is op. | |
| 2563 | assign_add_sat, | |
| 2564 | /// `lhs -|= rhs`. main_token is op. | |
| 2565 | assign_sub_sat, | |
| 2507 | 2566 | /// `lhs = rhs`. main_token is op. |
| 2508 | 2567 | assign, |
| 2509 | 2568 | /// `lhs || rhs`. main_token is the `||`. |
| ... | ... | @@ -2518,6 +2577,8 @@ pub const Node = struct { |
| 2518 | 2577 | array_mult, |
| 2519 | 2578 | /// `lhs *% rhs`. main_token is the `*%`. |
| 2520 | 2579 | mul_wrap, |
| 2580 | /// `lhs *| rhs`. main_token is the `*|`. | |
| 2581 | mul_sat, | |
| 2521 | 2582 | /// `lhs + rhs`. main_token is the `+`. |
| 2522 | 2583 | add, |
| 2523 | 2584 | /// `lhs - rhs`. main_token is the `-`. |
| ... | ... | @@ -2528,10 +2589,16 @@ pub const Node = struct { |
| 2528 | 2589 | add_wrap, |
| 2529 | 2590 | /// `lhs -% rhs`. main_token is the `-%`. |
| 2530 | 2591 | sub_wrap, |
| 2592 | /// `lhs +| rhs`. main_token is the `+|`. | |
| 2593 | add_sat, | |
| 2594 | /// `lhs -| rhs`. main_token is the `-|`. | |
| 2595 | sub_sat, | |
| 2531 | 2596 | /// `lhs << rhs`. main_token is the `<<`. |
| 2532 | bit_shift_left, | |
| 2597 | shl, | |
| 2598 | /// `lhs <<| rhs`. main_token is the `<<|`. | |
| 2599 | shl_sat, | |
| 2533 | 2600 | /// `lhs >> rhs`. main_token is the `>>`. |
| 2534 | bit_shift_right, | |
| 2601 | shr, | |
| 2535 | 2602 | /// `lhs & rhs`. main_token is the `&`. |
| 2536 | 2603 | bit_and, |
| 2537 | 2604 | /// `lhs ^ rhs`. main_token is the `^`. |
| ... | ... | @@ -2723,13 +2790,13 @@ pub const Node = struct { |
| 2723 | 2790 | /// main_token is the `fn` keyword. |
| 2724 | 2791 | /// extern function declarations use this tag. |
| 2725 | 2792 | fn_proto_multi, |
| 2726 | /// `fn(a: b) rhs linksection(e) callconv(f)`. `FnProtoOne[lhs]`. | |
| 2793 | /// `fn(a: b) rhs addrspace(e) linksection(f) callconv(g)`. `FnProtoOne[lhs]`. | |
| 2727 | 2794 | /// zero or one parameters. |
| 2728 | 2795 | /// anytype and ... parameters are omitted from the AST tree. |
| 2729 | 2796 | /// main_token is the `fn` keyword. |
| 2730 | 2797 | /// extern function declarations use this tag. |
| 2731 | 2798 | fn_proto_one, |
| 2732 | /// `fn(a: b, c: d) rhs linksection(e) callconv(f)`. `FnProto[lhs]`. | |
| 2799 | /// `fn(a: b, c: d) rhs addrspace(e) linksection(f) callconv(g)`. `FnProto[lhs]`. | |
| 2733 | 2800 | /// anytype and ... parameters are omitted from the AST tree. |
| 2734 | 2801 | /// main_token is the `fn` keyword. |
| 2735 | 2802 | /// extern function declarations use this tag. |
| ... | ... | @@ -2893,11 +2960,13 @@ pub const Node = struct { |
| 2893 | 2960 | pub const PtrType = struct { |
| 2894 | 2961 | sentinel: Index, |
| 2895 | 2962 | align_node: Index, |
| 2963 | addrspace_node: Index, | |
| 2896 | 2964 | }; |
| 2897 | 2965 | |
| 2898 | 2966 | pub const PtrTypeBitRange = struct { |
| 2899 | 2967 | sentinel: Index, |
| 2900 | 2968 | align_node: Index, |
| 2969 | addrspace_node: Index, | |
| 2901 | 2970 | bit_range_start: Index, |
| 2902 | 2971 | bit_range_end: Index, |
| 2903 | 2972 | }; |
| ... | ... | @@ -2920,8 +2989,13 @@ pub const Node = struct { |
| 2920 | 2989 | }; |
| 2921 | 2990 | |
| 2922 | 2991 | pub const GlobalVarDecl = struct { |
| 2992 | /// Populated if there is an explicit type ascription. | |
| 2923 | 2993 | type_node: Index, |
| 2994 | /// Populated if align(A) is present. | |
| 2924 | 2995 | align_node: Index, |
| 2996 | /// Populated if addrspace(A) is present. | |
| 2997 | addrspace_node: Index, | |
| 2998 | /// Populated if linksection(A) is present. | |
| 2925 | 2999 | section_node: Index, |
| 2926 | 3000 | }; |
| 2927 | 3001 | |
| ... | ... | @@ -2953,6 +3027,8 @@ pub const Node = struct { |
| 2953 | 3027 | param: Index, |
| 2954 | 3028 | /// Populated if align(A) is present. |
| 2955 | 3029 | align_expr: Index, |
| 3030 | /// Populated if addrspace(A) is present. | |
| 3031 | addrspace_expr: Index, | |
| 2956 | 3032 | /// Populated if linksection(A) is present. |
| 2957 | 3033 | section_expr: Index, |
| 2958 | 3034 | /// Populated if callconv(A) is present. |
| ... | ... | @@ -2964,6 +3040,8 @@ pub const Node = struct { |
| 2964 | 3040 | params_end: Index, |
| 2965 | 3041 | /// Populated if align(A) is present. |
| 2966 | 3042 | align_expr: Index, |
| 3043 | /// Populated if addrspace(A) is present. | |
| 3044 | addrspace_expr: Index, | |
| 2967 | 3045 | /// Populated if linksection(A) is present. |
| 2968 | 3046 | section_expr: Index, |
| 2969 | 3047 | /// Populated if callconv(A) is present. |
lib/std/zig/c_translation.zig+1| ... | ... | @@ -325,6 +325,7 @@ pub fn FlexibleArrayType(comptime SelfType: type, ElementType: type) type { |
| 325 | 325 | .is_const = ptr.is_const, |
| 326 | 326 | .is_volatile = ptr.is_volatile, |
| 327 | 327 | .alignment = @alignOf(ElementType), |
| 328 | .address_space = .generic, | |
| 328 | 329 | .child = ElementType, |
| 329 | 330 | .is_allowzero = true, |
| 330 | 331 | .sentinel = null, |
lib/std/zig/parse.zig+102-44| ... | ... | @@ -17,7 +17,7 @@ pub fn parse(gpa: *Allocator, source: [:0]const u8) Allocator.Error!Ast { |
| 17 | 17 | |
| 18 | 18 | // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count. |
| 19 | 19 | const estimated_token_count = source.len / 8; |
| 20 | try tokens.ensureCapacity(gpa, estimated_token_count); | |
| 20 | try tokens.ensureTotalCapacity(gpa, estimated_token_count); | |
| 21 | 21 | |
| 22 | 22 | var tokenizer = std.zig.Tokenizer.init(source); |
| 23 | 23 | while (true) { |
| ... | ... | @@ -48,7 +48,7 @@ pub fn parse(gpa: *Allocator, source: [:0]const u8) Allocator.Error!Ast { |
| 48 | 48 | // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes. |
| 49 | 49 | // Make sure at least 1 so we can use appendAssumeCapacity on the root node below. |
| 50 | 50 | const estimated_node_count = (tokens.len + 2) / 2; |
| 51 | try parser.nodes.ensureCapacity(gpa, estimated_node_count); | |
| 51 | try parser.nodes.ensureTotalCapacity(gpa, estimated_node_count); | |
| 52 | 52 | |
| 53 | 53 | // Root node must be index 0. |
| 54 | 54 | // Root <- skip ContainerMembers eof |
| ... | ... | @@ -138,7 +138,7 @@ const Parser = struct { |
| 138 | 138 | |
| 139 | 139 | fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index { |
| 140 | 140 | const fields = std.meta.fields(@TypeOf(extra)); |
| 141 | try p.extra_data.ensureCapacity(p.gpa, p.extra_data.items.len + fields.len); | |
| 141 | try p.extra_data.ensureUnusedCapacity(p.gpa, fields.len); | |
| 142 | 142 | const result = @intCast(u32, p.extra_data.items.len); |
| 143 | 143 | inline for (fields) |field| { |
| 144 | 144 | comptime assert(field.field_type == Node.Index); |
| ... | ... | @@ -629,7 +629,7 @@ const Parser = struct { |
| 629 | 629 | }; |
| 630 | 630 | } |
| 631 | 631 | |
| 632 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr | |
| 632 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr | |
| 633 | 633 | fn parseFnProto(p: *Parser) !Node.Index { |
| 634 | 634 | const fn_token = p.eatToken(.keyword_fn) orelse return null_node; |
| 635 | 635 | |
| ... | ... | @@ -639,6 +639,7 @@ const Parser = struct { |
| 639 | 639 | _ = p.eatToken(.identifier); |
| 640 | 640 | const params = try p.parseParamDeclList(); |
| 641 | 641 | const align_expr = try p.parseByteAlign(); |
| 642 | const addrspace_expr = try p.parseAddrSpace(); | |
| 642 | 643 | const section_expr = try p.parseLinkSection(); |
| 643 | 644 | const callconv_expr = try p.parseCallconv(); |
| 644 | 645 | _ = p.eatToken(.bang); |
| ... | ... | @@ -650,7 +651,7 @@ const Parser = struct { |
| 650 | 651 | try p.warn(.expected_return_type); |
| 651 | 652 | } |
| 652 | 653 | |
| 653 | if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) { | |
| 654 | if (align_expr == 0 and section_expr == 0 and callconv_expr == 0 and addrspace_expr == 0) { | |
| 654 | 655 | switch (params) { |
| 655 | 656 | .zero_or_one => |param| return p.setNode(fn_proto_index, .{ |
| 656 | 657 | .tag = .fn_proto_simple, |
| ... | ... | @@ -683,6 +684,7 @@ const Parser = struct { |
| 683 | 684 | .lhs = try p.addExtra(Node.FnProtoOne{ |
| 684 | 685 | .param = param, |
| 685 | 686 | .align_expr = align_expr, |
| 687 | .addrspace_expr = addrspace_expr, | |
| 686 | 688 | .section_expr = section_expr, |
| 687 | 689 | .callconv_expr = callconv_expr, |
| 688 | 690 | }), |
| ... | ... | @@ -698,6 +700,7 @@ const Parser = struct { |
| 698 | 700 | .params_start = span.start, |
| 699 | 701 | .params_end = span.end, |
| 700 | 702 | .align_expr = align_expr, |
| 703 | .addrspace_expr = addrspace_expr, | |
| 701 | 704 | .section_expr = section_expr, |
| 702 | 705 | .callconv_expr = callconv_expr, |
| 703 | 706 | }), |
| ... | ... | @@ -708,7 +711,7 @@ const Parser = struct { |
| 708 | 711 | } |
| 709 | 712 | } |
| 710 | 713 | |
| 711 | /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON | |
| 714 | /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON | |
| 712 | 715 | fn parseVarDecl(p: *Parser) !Node.Index { |
| 713 | 716 | const mut_token = p.eatToken(.keyword_const) orelse |
| 714 | 717 | p.eatToken(.keyword_var) orelse |
| ... | ... | @@ -717,9 +720,10 @@ const Parser = struct { |
| 717 | 720 | _ = try p.expectToken(.identifier); |
| 718 | 721 | const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr(); |
| 719 | 722 | const align_node = try p.parseByteAlign(); |
| 723 | const addrspace_node = try p.parseAddrSpace(); | |
| 720 | 724 | const section_node = try p.parseLinkSection(); |
| 721 | 725 | const init_node: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr(); |
| 722 | if (section_node == 0) { | |
| 726 | if (section_node == 0 and addrspace_node == 0) { | |
| 723 | 727 | if (align_node == 0) { |
| 724 | 728 | return p.addNode(.{ |
| 725 | 729 | .tag = .simple_var_decl, |
| ... | ... | @@ -759,6 +763,7 @@ const Parser = struct { |
| 759 | 763 | .lhs = try p.addExtra(Node.GlobalVarDecl{ |
| 760 | 764 | .type_node = type_node, |
| 761 | 765 | .align_node = align_node, |
| 766 | .addrspace_node = addrspace_node, | |
| 762 | 767 | .section_node = section_node, |
| 763 | 768 | }), |
| 764 | 769 | .rhs = init_node, |
| ... | ... | @@ -1263,14 +1268,18 @@ const Parser = struct { |
| 1263 | 1268 | .percent_equal => .assign_mod, |
| 1264 | 1269 | .plus_equal => .assign_add, |
| 1265 | 1270 | .minus_equal => .assign_sub, |
| 1266 | .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left, | |
| 1267 | .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right, | |
| 1271 | .angle_bracket_angle_bracket_left_equal => .assign_shl, | |
| 1272 | .angle_bracket_angle_bracket_left_pipe_equal => .assign_shl_sat, | |
| 1273 | .angle_bracket_angle_bracket_right_equal => .assign_shr, | |
| 1268 | 1274 | .ampersand_equal => .assign_bit_and, |
| 1269 | 1275 | .caret_equal => .assign_bit_xor, |
| 1270 | 1276 | .pipe_equal => .assign_bit_or, |
| 1271 | 1277 | .asterisk_percent_equal => .assign_mul_wrap, |
| 1272 | 1278 | .plus_percent_equal => .assign_add_wrap, |
| 1273 | 1279 | .minus_percent_equal => .assign_sub_wrap, |
| 1280 | .asterisk_pipe_equal => .assign_mul_sat, | |
| 1281 | .plus_pipe_equal => .assign_add_sat, | |
| 1282 | .minus_pipe_equal => .assign_sub_sat, | |
| 1274 | 1283 | .equal => .assign, |
| 1275 | 1284 | else => return expr, |
| 1276 | 1285 | }; |
| ... | ... | @@ -1337,14 +1346,17 @@ const Parser = struct { |
| 1337 | 1346 | .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" }, |
| 1338 | 1347 | .keyword_catch = .{ .prec = 40, .tag = .@"catch" }, |
| 1339 | 1348 | |
| 1340 | .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left }, | |
| 1341 | .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right }, | |
| 1349 | .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .shl }, | |
| 1350 | .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .shl_sat }, | |
| 1351 | .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .shr }, | |
| 1342 | 1352 | |
| 1343 | 1353 | .plus = .{ .prec = 60, .tag = .add }, |
| 1344 | 1354 | .minus = .{ .prec = 60, .tag = .sub }, |
| 1345 | 1355 | .plus_plus = .{ .prec = 60, .tag = .array_cat }, |
| 1346 | 1356 | .plus_percent = .{ .prec = 60, .tag = .add_wrap }, |
| 1347 | 1357 | .minus_percent = .{ .prec = 60, .tag = .sub_wrap }, |
| 1358 | .plus_pipe = .{ .prec = 60, .tag = .add_sat }, | |
| 1359 | .minus_pipe = .{ .prec = 60, .tag = .sub_sat }, | |
| 1348 | 1360 | |
| 1349 | 1361 | .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets }, |
| 1350 | 1362 | .asterisk = .{ .prec = 70, .tag = .mul }, |
| ... | ... | @@ -1352,6 +1364,7 @@ const Parser = struct { |
| 1352 | 1364 | .percent = .{ .prec = 70, .tag = .mod }, |
| 1353 | 1365 | .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult }, |
| 1354 | 1366 | .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap }, |
| 1367 | .asterisk_pipe = .{ .prec = 70, .tag = .mul_sat }, | |
| 1355 | 1368 | }); |
| 1356 | 1369 | |
| 1357 | 1370 | fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index { |
| ... | ... | @@ -1440,8 +1453,8 @@ const Parser = struct { |
| 1440 | 1453 | /// PrefixTypeOp |
| 1441 | 1454 | /// <- QUESTIONMARK |
| 1442 | 1455 | /// / KEYWORD_anyframe MINUSRARROW |
| 1443 | /// / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | |
| 1444 | /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | |
| 1456 | /// / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | |
| 1457 | /// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | |
| 1445 | 1458 | /// / ArrayTypeStart |
| 1446 | 1459 | /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET |
| 1447 | 1460 | /// PtrTypeStart |
| ... | ... | @@ -1474,29 +1487,43 @@ const Parser = struct { |
| 1474 | 1487 | const asterisk = p.nextToken(); |
| 1475 | 1488 | const mods = try p.parsePtrModifiers(); |
| 1476 | 1489 | const elem_type = try p.expectTypeExpr(); |
| 1477 | if (mods.bit_range_start == 0) { | |
| 1490 | if (mods.bit_range_start != 0) { | |
| 1478 | 1491 | return p.addNode(.{ |
| 1479 | .tag = .ptr_type_aligned, | |
| 1492 | .tag = .ptr_type_bit_range, | |
| 1480 | 1493 | .main_token = asterisk, |
| 1481 | 1494 | .data = .{ |
| 1482 | .lhs = mods.align_node, | |
| 1495 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | |
| 1496 | .sentinel = 0, | |
| 1497 | .align_node = mods.align_node, | |
| 1498 | .addrspace_node = mods.addrspace_node, | |
| 1499 | .bit_range_start = mods.bit_range_start, | |
| 1500 | .bit_range_end = mods.bit_range_end, | |
| 1501 | }), | |
| 1483 | 1502 | .rhs = elem_type, |
| 1484 | 1503 | }, |
| 1485 | 1504 | }); |
| 1486 | } else { | |
| 1505 | } else if (mods.addrspace_node != 0) { | |
| 1487 | 1506 | return p.addNode(.{ |
| 1488 | .tag = .ptr_type_bit_range, | |
| 1507 | .tag = .ptr_type, | |
| 1489 | 1508 | .main_token = asterisk, |
| 1490 | 1509 | .data = .{ |
| 1491 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | |
| 1510 | .lhs = try p.addExtra(Node.PtrType{ | |
| 1492 | 1511 | .sentinel = 0, |
| 1493 | 1512 | .align_node = mods.align_node, |
| 1494 | .bit_range_start = mods.bit_range_start, | |
| 1495 | .bit_range_end = mods.bit_range_end, | |
| 1513 | .addrspace_node = mods.addrspace_node, | |
| 1496 | 1514 | }), |
| 1497 | 1515 | .rhs = elem_type, |
| 1498 | 1516 | }, |
| 1499 | 1517 | }); |
| 1518 | } else { | |
| 1519 | return p.addNode(.{ | |
| 1520 | .tag = .ptr_type_aligned, | |
| 1521 | .main_token = asterisk, | |
| 1522 | .data = .{ | |
| 1523 | .lhs = mods.align_node, | |
| 1524 | .rhs = elem_type, | |
| 1525 | }, | |
| 1526 | }); | |
| 1500 | 1527 | } |
| 1501 | 1528 | }, |
| 1502 | 1529 | .asterisk_asterisk => { |
| ... | ... | @@ -1504,29 +1531,43 @@ const Parser = struct { |
| 1504 | 1531 | const mods = try p.parsePtrModifiers(); |
| 1505 | 1532 | const elem_type = try p.expectTypeExpr(); |
| 1506 | 1533 | const inner: Node.Index = inner: { |
| 1507 | if (mods.bit_range_start == 0) { | |
| 1534 | if (mods.bit_range_start != 0) { | |
| 1508 | 1535 | break :inner try p.addNode(.{ |
| 1509 | .tag = .ptr_type_aligned, | |
| 1536 | .tag = .ptr_type_bit_range, | |
| 1510 | 1537 | .main_token = asterisk, |
| 1511 | 1538 | .data = .{ |
| 1512 | .lhs = mods.align_node, | |
| 1539 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | |
| 1540 | .sentinel = 0, | |
| 1541 | .align_node = mods.align_node, | |
| 1542 | .addrspace_node = mods.addrspace_node, | |
| 1543 | .bit_range_start = mods.bit_range_start, | |
| 1544 | .bit_range_end = mods.bit_range_end, | |
| 1545 | }), | |
| 1513 | 1546 | .rhs = elem_type, |
| 1514 | 1547 | }, |
| 1515 | 1548 | }); |
| 1516 | } else { | |
| 1549 | } else if (mods.addrspace_node != 0) { | |
| 1517 | 1550 | break :inner try p.addNode(.{ |
| 1518 | .tag = .ptr_type_bit_range, | |
| 1551 | .tag = .ptr_type, | |
| 1519 | 1552 | .main_token = asterisk, |
| 1520 | 1553 | .data = .{ |
| 1521 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | |
| 1554 | .lhs = try p.addExtra(Node.PtrType{ | |
| 1522 | 1555 | .sentinel = 0, |
| 1523 | 1556 | .align_node = mods.align_node, |
| 1524 | .bit_range_start = mods.bit_range_start, | |
| 1525 | .bit_range_end = mods.bit_range_end, | |
| 1557 | .addrspace_node = mods.addrspace_node, | |
| 1526 | 1558 | }), |
| 1527 | 1559 | .rhs = elem_type, |
| 1528 | 1560 | }, |
| 1529 | 1561 | }); |
| 1562 | } else { | |
| 1563 | break :inner try p.addNode(.{ | |
| 1564 | .tag = .ptr_type_aligned, | |
| 1565 | .main_token = asterisk, | |
| 1566 | .data = .{ | |
| 1567 | .lhs = mods.align_node, | |
| 1568 | .rhs = elem_type, | |
| 1569 | }, | |
| 1570 | }); | |
| 1530 | 1571 | } |
| 1531 | 1572 | }; |
| 1532 | 1573 | return p.addNode(.{ |
| ... | ... | @@ -1543,24 +1584,19 @@ const Parser = struct { |
| 1543 | 1584 | _ = p.nextToken(); |
| 1544 | 1585 | const asterisk = p.nextToken(); |
| 1545 | 1586 | var sentinel: Node.Index = 0; |
| 1546 | prefix: { | |
| 1547 | if (p.eatToken(.identifier)) |ident| { | |
| 1548 | const token_slice = p.source[p.token_starts[ident]..][0..2]; | |
| 1549 | if (!std.mem.eql(u8, token_slice, "c]")) { | |
| 1550 | p.tok_i -= 1; | |
| 1551 | } else { | |
| 1552 | break :prefix; | |
| 1553 | } | |
| 1554 | } | |
| 1555 | if (p.eatToken(.colon)) |_| { | |
| 1556 | sentinel = try p.expectExpr(); | |
| 1587 | if (p.eatToken(.identifier)) |ident| { | |
| 1588 | const ident_slice = p.source[p.token_starts[ident]..p.token_starts[ident + 1]]; | |
| 1589 | if (!std.mem.eql(u8, std.mem.trimRight(u8, ident_slice, &std.ascii.spaces), "c")) { | |
| 1590 | p.tok_i -= 1; | |
| 1557 | 1591 | } |
| 1592 | } else if (p.eatToken(.colon)) |_| { | |
| 1593 | sentinel = try p.expectExpr(); | |
| 1558 | 1594 | } |
| 1559 | 1595 | _ = try p.expectToken(.r_bracket); |
| 1560 | 1596 | const mods = try p.parsePtrModifiers(); |
| 1561 | 1597 | const elem_type = try p.expectTypeExpr(); |
| 1562 | 1598 | if (mods.bit_range_start == 0) { |
| 1563 | if (sentinel == 0) { | |
| 1599 | if (sentinel == 0 and mods.addrspace_node == 0) { | |
| 1564 | 1600 | return p.addNode(.{ |
| 1565 | 1601 | .tag = .ptr_type_aligned, |
| 1566 | 1602 | .main_token = asterisk, |
| ... | ... | @@ -1569,7 +1605,7 @@ const Parser = struct { |
| 1569 | 1605 | .rhs = elem_type, |
| 1570 | 1606 | }, |
| 1571 | 1607 | }); |
| 1572 | } else if (mods.align_node == 0) { | |
| 1608 | } else if (mods.align_node == 0 and mods.addrspace_node == 0) { | |
| 1573 | 1609 | return p.addNode(.{ |
| 1574 | 1610 | .tag = .ptr_type_sentinel, |
| 1575 | 1611 | .main_token = asterisk, |
| ... | ... | @@ -1586,6 +1622,7 @@ const Parser = struct { |
| 1586 | 1622 | .lhs = try p.addExtra(Node.PtrType{ |
| 1587 | 1623 | .sentinel = sentinel, |
| 1588 | 1624 | .align_node = mods.align_node, |
| 1625 | .addrspace_node = mods.addrspace_node, | |
| 1589 | 1626 | }), |
| 1590 | 1627 | .rhs = elem_type, |
| 1591 | 1628 | }, |
| ... | ... | @@ -1599,6 +1636,7 @@ const Parser = struct { |
| 1599 | 1636 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ |
| 1600 | 1637 | .sentinel = sentinel, |
| 1601 | 1638 | .align_node = mods.align_node, |
| 1639 | .addrspace_node = mods.addrspace_node, | |
| 1602 | 1640 | .bit_range_start = mods.bit_range_start, |
| 1603 | 1641 | .bit_range_end = mods.bit_range_end, |
| 1604 | 1642 | }), |
| ... | ... | @@ -1624,7 +1662,7 @@ const Parser = struct { |
| 1624 | 1662 | .token = p.nodes.items(.main_token)[mods.bit_range_start], |
| 1625 | 1663 | }); |
| 1626 | 1664 | } |
| 1627 | if (sentinel == 0) { | |
| 1665 | if (sentinel == 0 and mods.addrspace_node == 0) { | |
| 1628 | 1666 | return p.addNode(.{ |
| 1629 | 1667 | .tag = .ptr_type_aligned, |
| 1630 | 1668 | .main_token = lbracket, |
| ... | ... | @@ -1633,7 +1671,7 @@ const Parser = struct { |
| 1633 | 1671 | .rhs = elem_type, |
| 1634 | 1672 | }, |
| 1635 | 1673 | }); |
| 1636 | } else if (mods.align_node == 0) { | |
| 1674 | } else if (mods.align_node == 0 and mods.addrspace_node == 0) { | |
| 1637 | 1675 | return p.addNode(.{ |
| 1638 | 1676 | .tag = .ptr_type_sentinel, |
| 1639 | 1677 | .main_token = lbracket, |
| ... | ... | @@ -1650,6 +1688,7 @@ const Parser = struct { |
| 1650 | 1688 | .lhs = try p.addExtra(Node.PtrType{ |
| 1651 | 1689 | .sentinel = sentinel, |
| 1652 | 1690 | .align_node = mods.align_node, |
| 1691 | .addrspace_node = mods.addrspace_node, | |
| 1653 | 1692 | }), |
| 1654 | 1693 | .rhs = elem_type, |
| 1655 | 1694 | }, |
| ... | ... | @@ -1661,6 +1700,7 @@ const Parser = struct { |
| 1661 | 1700 | .keyword_const, |
| 1662 | 1701 | .keyword_volatile, |
| 1663 | 1702 | .keyword_allowzero, |
| 1703 | .keyword_addrspace, | |
| 1664 | 1704 | => return p.fail(.ptr_mod_on_array_child_type), |
| 1665 | 1705 | else => {}, |
| 1666 | 1706 | } |
| ... | ... | @@ -2879,6 +2919,15 @@ const Parser = struct { |
| 2879 | 2919 | return expr_node; |
| 2880 | 2920 | } |
| 2881 | 2921 | |
| 2922 | /// AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN | |
| 2923 | fn parseAddrSpace(p: *Parser) !Node.Index { | |
| 2924 | _ = p.eatToken(.keyword_addrspace) orelse return null_node; | |
| 2925 | _ = try p.expectToken(.l_paren); | |
| 2926 | const expr_node = try p.expectExpr(); | |
| 2927 | _ = try p.expectToken(.r_paren); | |
| 2928 | return expr_node; | |
| 2929 | } | |
| 2930 | ||
| 2882 | 2931 | /// ParamDecl |
| 2883 | 2932 | /// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType |
| 2884 | 2933 | /// / DOT3 |
| ... | ... | @@ -3011,6 +3060,7 @@ const Parser = struct { |
| 3011 | 3060 | |
| 3012 | 3061 | const PtrModifiers = struct { |
| 3013 | 3062 | align_node: Node.Index, |
| 3063 | addrspace_node: Node.Index, | |
| 3014 | 3064 | bit_range_start: Node.Index, |
| 3015 | 3065 | bit_range_end: Node.Index, |
| 3016 | 3066 | }; |
| ... | ... | @@ -3018,12 +3068,14 @@ const Parser = struct { |
| 3018 | 3068 | fn parsePtrModifiers(p: *Parser) !PtrModifiers { |
| 3019 | 3069 | var result: PtrModifiers = .{ |
| 3020 | 3070 | .align_node = 0, |
| 3071 | .addrspace_node = 0, | |
| 3021 | 3072 | .bit_range_start = 0, |
| 3022 | 3073 | .bit_range_end = 0, |
| 3023 | 3074 | }; |
| 3024 | 3075 | var saw_const = false; |
| 3025 | 3076 | var saw_volatile = false; |
| 3026 | 3077 | var saw_allowzero = false; |
| 3078 | var saw_addrspace = false; | |
| 3027 | 3079 | while (true) { |
| 3028 | 3080 | switch (p.token_tags[p.tok_i]) { |
| 3029 | 3081 | .keyword_align => { |
| ... | ... | @@ -3063,6 +3115,12 @@ const Parser = struct { |
| 3063 | 3115 | p.tok_i += 1; |
| 3064 | 3116 | saw_allowzero = true; |
| 3065 | 3117 | }, |
| 3118 | .keyword_addrspace => { | |
| 3119 | if (saw_addrspace) { | |
| 3120 | try p.warn(.extra_addrspace_qualifier); | |
| 3121 | } | |
| 3122 | result.addrspace_node = try p.parseAddrSpace(); | |
| 3123 | }, | |
| 3066 | 3124 | else => return result, |
| 3067 | 3125 | } |
| 3068 | 3126 | } |
lib/std/zig/parser_test.zig+52-10| ... | ... | @@ -404,6 +404,10 @@ test "zig fmt: trailing comma in fn parameter list" { |
| 404 | 404 | \\pub fn f( |
| 405 | 405 | \\ a: i32, |
| 406 | 406 | \\ b: i32, |
| 407 | \\) addrspace(.generic) i32 {} | |
| 408 | \\pub fn f( | |
| 409 | \\ a: i32, | |
| 410 | \\ b: i32, | |
| 407 | 411 | \\) linksection(".text") i32 {} |
| 408 | 412 | \\pub fn f( |
| 409 | 413 | \\ a: i32, |
| ... | ... | @@ -553,8 +557,8 @@ test "zig fmt: sentinel-terminated slice type" { |
| 553 | 557 | test "zig fmt: pointer-to-one with modifiers" { |
| 554 | 558 | try testCanonical( |
| 555 | 559 | \\const x: *u32 = undefined; |
| 556 | \\const y: *allowzero align(8) const volatile u32 = undefined; | |
| 557 | \\const z: *allowzero align(8:4:2) const volatile u32 = undefined; | |
| 560 | \\const y: *allowzero align(8) addrspace(.generic) const volatile u32 = undefined; | |
| 561 | \\const z: *allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined; | |
| 558 | 562 | \\ |
| 559 | 563 | ); |
| 560 | 564 | } |
| ... | ... | @@ -562,8 +566,8 @@ test "zig fmt: pointer-to-one with modifiers" { |
| 562 | 566 | test "zig fmt: pointer-to-many with modifiers" { |
| 563 | 567 | try testCanonical( |
| 564 | 568 | \\const x: [*]u32 = undefined; |
| 565 | \\const y: [*]allowzero align(8) const volatile u32 = undefined; | |
| 566 | \\const z: [*]allowzero align(8:4:2) const volatile u32 = undefined; | |
| 569 | \\const y: [*]allowzero align(8) addrspace(.generic) const volatile u32 = undefined; | |
| 570 | \\const z: [*]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined; | |
| 567 | 571 | \\ |
| 568 | 572 | ); |
| 569 | 573 | } |
| ... | ... | @@ -571,8 +575,8 @@ test "zig fmt: pointer-to-many with modifiers" { |
| 571 | 575 | test "zig fmt: sentinel pointer with modifiers" { |
| 572 | 576 | try testCanonical( |
| 573 | 577 | \\const x: [*:42]u32 = undefined; |
| 574 | \\const y: [*:42]allowzero align(8) const volatile u32 = undefined; | |
| 575 | \\const y: [*:42]allowzero align(8:4:2) const volatile u32 = undefined; | |
| 578 | \\const y: [*:42]allowzero align(8) addrspace(.generic) const volatile u32 = undefined; | |
| 579 | \\const y: [*:42]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined; | |
| 576 | 580 | \\ |
| 577 | 581 | ); |
| 578 | 582 | } |
| ... | ... | @@ -580,8 +584,8 @@ test "zig fmt: sentinel pointer with modifiers" { |
| 580 | 584 | test "zig fmt: c pointer with modifiers" { |
| 581 | 585 | try testCanonical( |
| 582 | 586 | \\const x: [*c]u32 = undefined; |
| 583 | \\const y: [*c]allowzero align(8) const volatile u32 = undefined; | |
| 584 | \\const z: [*c]allowzero align(8:4:2) const volatile u32 = undefined; | |
| 587 | \\const y: [*c]allowzero align(8) addrspace(.generic) const volatile u32 = undefined; | |
| 588 | \\const z: [*c]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined; | |
| 585 | 589 | \\ |
| 586 | 590 | ); |
| 587 | 591 | } |
| ... | ... | @@ -589,7 +593,7 @@ test "zig fmt: c pointer with modifiers" { |
| 589 | 593 | test "zig fmt: slice with modifiers" { |
| 590 | 594 | try testCanonical( |
| 591 | 595 | \\const x: []u32 = undefined; |
| 592 | \\const y: []allowzero align(8) const volatile u32 = undefined; | |
| 596 | \\const y: []allowzero align(8) addrspace(.generic) const volatile u32 = undefined; | |
| 593 | 597 | \\ |
| 594 | 598 | ); |
| 595 | 599 | } |
| ... | ... | @@ -597,7 +601,7 @@ test "zig fmt: slice with modifiers" { |
| 597 | 601 | test "zig fmt: sentinel slice with modifiers" { |
| 598 | 602 | try testCanonical( |
| 599 | 603 | \\const x: [:42]u32 = undefined; |
| 600 | \\const y: [:42]allowzero align(8) const volatile u32 = undefined; | |
| 604 | \\const y: [:42]allowzero align(8) addrspace(.generic) const volatile u32 = undefined; | |
| 601 | 605 | \\ |
| 602 | 606 | ); |
| 603 | 607 | } |
| ... | ... | @@ -1129,6 +1133,16 @@ test "zig fmt: linksection" { |
| 1129 | 1133 | ); |
| 1130 | 1134 | } |
| 1131 | 1135 | |
| 1136 | test "zig fmt: addrspace" { | |
| 1137 | try testCanonical( | |
| 1138 | \\export var python_length: u64 align(1) addrspace(.generic); | |
| 1139 | \\export var python_color: Color addrspace(.generic) = .green; | |
| 1140 | \\export var python_legs: u0 align(8) addrspace(.generic) linksection(".python") = 0; | |
| 1141 | \\export fn python_hiss() align(8) addrspace(.generic) linksection(".python") void; | |
| 1142 | \\ | |
| 1143 | ); | |
| 1144 | } | |
| 1145 | ||
| 1132 | 1146 | test "zig fmt: correctly space struct fields with doc comments" { |
| 1133 | 1147 | try testTransform( |
| 1134 | 1148 | \\pub const S = struct { |
| ... | ... | @@ -4725,6 +4739,26 @@ test "zig fmt: assignment with inline for and inline while" { |
| 4725 | 4739 | ); |
| 4726 | 4740 | } |
| 4727 | 4741 | |
| 4742 | test "zig fmt: saturating arithmetic" { | |
| 4743 | try testCanonical( | |
| 4744 | \\test { | |
| 4745 | \\ const actual = switch (op) { | |
| 4746 | \\ .add => a +| b, | |
| 4747 | \\ .sub => a -| b, | |
| 4748 | \\ .mul => a *| b, | |
| 4749 | \\ .shl => a <<| b, | |
| 4750 | \\ }; | |
| 4751 | \\ switch (op) { | |
| 4752 | \\ .add => actual +|= b, | |
| 4753 | \\ .sub => actual -|= b, | |
| 4754 | \\ .mul => actual *|= b, | |
| 4755 | \\ .shl => actual <<|= b, | |
| 4756 | \\ } | |
| 4757 | \\} | |
| 4758 | \\ | |
| 4759 | ); | |
| 4760 | } | |
| 4761 | ||
| 4728 | 4762 | test "zig fmt: insert trailing comma if there are comments between switch values" { |
| 4729 | 4763 | try testTransform( |
| 4730 | 4764 | \\const a = switch (b) { |
| ... | ... | @@ -5225,6 +5259,14 @@ test "recovery: nonfinal varargs" { |
| 5225 | 5259 | }); |
| 5226 | 5260 | } |
| 5227 | 5261 | |
| 5262 | test "recovery: eof in c pointer" { | |
| 5263 | try testError( | |
| 5264 | \\const Ptr = [*c | |
| 5265 | , &[_]Error{ | |
| 5266 | .expected_token, | |
| 5267 | }); | |
| 5268 | } | |
| 5269 | ||
| 5228 | 5270 | const std = @import("std"); |
| 5229 | 5271 | const mem = std.mem; |
| 5230 | 5272 | const print = std.debug.print; |
lib/std/zig/render.zig+62-10| ... | ... | @@ -333,27 +333,33 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 333 | 333 | |
| 334 | 334 | .add, |
| 335 | 335 | .add_wrap, |
| 336 | .add_sat, | |
| 336 | 337 | .array_cat, |
| 337 | 338 | .array_mult, |
| 338 | 339 | .assign, |
| 339 | 340 | .assign_bit_and, |
| 340 | 341 | .assign_bit_or, |
| 341 | .assign_bit_shift_left, | |
| 342 | .assign_bit_shift_right, | |
| 342 | .assign_shl, | |
| 343 | .assign_shl_sat, | |
| 344 | .assign_shr, | |
| 343 | 345 | .assign_bit_xor, |
| 344 | 346 | .assign_div, |
| 345 | 347 | .assign_sub, |
| 346 | 348 | .assign_sub_wrap, |
| 349 | .assign_sub_sat, | |
| 347 | 350 | .assign_mod, |
| 348 | 351 | .assign_add, |
| 349 | 352 | .assign_add_wrap, |
| 353 | .assign_add_sat, | |
| 350 | 354 | .assign_mul, |
| 351 | 355 | .assign_mul_wrap, |
| 356 | .assign_mul_sat, | |
| 352 | 357 | .bang_equal, |
| 353 | 358 | .bit_and, |
| 354 | 359 | .bit_or, |
| 355 | .bit_shift_left, | |
| 356 | .bit_shift_right, | |
| 360 | .shl, | |
| 361 | .shl_sat, | |
| 362 | .shr, | |
| 357 | 363 | .bit_xor, |
| 358 | 364 | .bool_and, |
| 359 | 365 | .bool_or, |
| ... | ... | @@ -367,8 +373,10 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 367 | 373 | .mod, |
| 368 | 374 | .mul, |
| 369 | 375 | .mul_wrap, |
| 376 | .mul_sat, | |
| 370 | 377 | .sub, |
| 371 | 378 | .sub_wrap, |
| 379 | .sub_sat, | |
| 372 | 380 | .@"orelse", |
| 373 | 381 | => { |
| 374 | 382 | const infix = datas[node]; |
| ... | ... | @@ -797,6 +805,14 @@ fn renderPtrType( |
| 797 | 805 | } |
| 798 | 806 | } |
| 799 | 807 | |
| 808 | if (ptr_type.ast.addrspace_node != 0) { | |
| 809 | const addrspace_first = tree.firstToken(ptr_type.ast.addrspace_node); | |
| 810 | try renderToken(ais, tree, addrspace_first - 2, .none); // addrspace | |
| 811 | try renderToken(ais, tree, addrspace_first - 1, .none); // lparen | |
| 812 | try renderExpression(gpa, ais, tree, ptr_type.ast.addrspace_node, .none); | |
| 813 | try renderToken(ais, tree, tree.lastToken(ptr_type.ast.addrspace_node) + 1, .space); // rparen | |
| 814 | } | |
| 815 | ||
| 800 | 816 | if (ptr_type.const_token) |const_token| { |
| 801 | 817 | try renderToken(ais, tree, const_token, .space); |
| 802 | 818 | } |
| ... | ... | @@ -921,6 +937,7 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe |
| 921 | 937 | |
| 922 | 938 | const name_space = if (var_decl.ast.type_node == 0 and |
| 923 | 939 | (var_decl.ast.align_node != 0 or |
| 940 | var_decl.ast.addrspace_node != 0 or | |
| 924 | 941 | var_decl.ast.section_node != 0 or |
| 925 | 942 | var_decl.ast.init_node != 0)) |
| 926 | 943 | Space.space |
| ... | ... | @@ -930,8 +947,8 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe |
| 930 | 947 | |
| 931 | 948 | if (var_decl.ast.type_node != 0) { |
| 932 | 949 | try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // : |
| 933 | if (var_decl.ast.align_node != 0 or var_decl.ast.section_node != 0 or | |
| 934 | var_decl.ast.init_node != 0) | |
| 950 | if (var_decl.ast.align_node != 0 or var_decl.ast.addrspace_node != 0 or | |
| 951 | var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) | |
| 935 | 952 | { |
| 936 | 953 | try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space); |
| 937 | 954 | } else { |
| ... | ... | @@ -948,6 +965,23 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe |
| 948 | 965 | try renderToken(ais, tree, align_kw, Space.none); // align |
| 949 | 966 | try renderToken(ais, tree, lparen, Space.none); // ( |
| 950 | 967 | try renderExpression(gpa, ais, tree, var_decl.ast.align_node, Space.none); |
| 968 | if (var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or | |
| 969 | var_decl.ast.init_node != 0) | |
| 970 | { | |
| 971 | try renderToken(ais, tree, rparen, .space); // ) | |
| 972 | } else { | |
| 973 | try renderToken(ais, tree, rparen, .none); // ) | |
| 974 | return renderToken(ais, tree, rparen + 1, Space.newline); // ; | |
| 975 | } | |
| 976 | } | |
| 977 | ||
| 978 | if (var_decl.ast.addrspace_node != 0) { | |
| 979 | const lparen = tree.firstToken(var_decl.ast.addrspace_node) - 1; | |
| 980 | const addrspace_kw = lparen - 1; | |
| 981 | const rparen = tree.lastToken(var_decl.ast.addrspace_node) + 1; | |
| 982 | try renderToken(ais, tree, addrspace_kw, Space.none); // addrspace | |
| 983 | try renderToken(ais, tree, lparen, Space.none); // ( | |
| 984 | try renderExpression(gpa, ais, tree, var_decl.ast.addrspace_node, Space.none); | |
| 951 | 985 | if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) { |
| 952 | 986 | try renderToken(ais, tree, rparen, .space); // ) |
| 953 | 987 | } else { |
| ... | ... | @@ -1267,6 +1301,14 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro |
| 1267 | 1301 | smallest_start = start; |
| 1268 | 1302 | } |
| 1269 | 1303 | } |
| 1304 | if (fn_proto.ast.addrspace_expr != 0) { | |
| 1305 | const tok = tree.firstToken(fn_proto.ast.addrspace_expr) - 3; | |
| 1306 | const start = token_starts[tok]; | |
| 1307 | if (start < smallest_start) { | |
| 1308 | rparen = tok; | |
| 1309 | smallest_start = start; | |
| 1310 | } | |
| 1311 | } | |
| 1270 | 1312 | if (fn_proto.ast.section_expr != 0) { |
| 1271 | 1313 | const tok = tree.firstToken(fn_proto.ast.section_expr) - 3; |
| 1272 | 1314 | const start = token_starts[tok]; |
| ... | ... | @@ -1407,6 +1449,16 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro |
| 1407 | 1449 | try renderToken(ais, tree, align_rparen, .space); // ) |
| 1408 | 1450 | } |
| 1409 | 1451 | |
| 1452 | if (fn_proto.ast.addrspace_expr != 0) { | |
| 1453 | const align_lparen = tree.firstToken(fn_proto.ast.addrspace_expr) - 1; | |
| 1454 | const align_rparen = tree.lastToken(fn_proto.ast.addrspace_expr) + 1; | |
| 1455 | ||
| 1456 | try renderToken(ais, tree, align_lparen - 1, .none); // addrspace | |
| 1457 | try renderToken(ais, tree, align_lparen, .none); // ( | |
| 1458 | try renderExpression(gpa, ais, tree, fn_proto.ast.addrspace_expr, .none); | |
| 1459 | try renderToken(ais, tree, align_rparen, .space); // ) | |
| 1460 | } | |
| 1461 | ||
| 1410 | 1462 | if (fn_proto.ast.section_expr != 0) { |
| 1411 | 1463 | const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1; |
| 1412 | 1464 | const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1; |
| ... | ... | @@ -2476,8 +2528,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool { |
| 2476 | 2528 | .assign, |
| 2477 | 2529 | .assign_bit_and, |
| 2478 | 2530 | .assign_bit_or, |
| 2479 | .assign_bit_shift_left, | |
| 2480 | .assign_bit_shift_right, | |
| 2531 | .assign_shl, | |
| 2532 | .assign_shr, | |
| 2481 | 2533 | .assign_bit_xor, |
| 2482 | 2534 | .assign_div, |
| 2483 | 2535 | .assign_sub, |
| ... | ... | @@ -2490,8 +2542,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool { |
| 2490 | 2542 | .bang_equal, |
| 2491 | 2543 | .bit_and, |
| 2492 | 2544 | .bit_or, |
| 2493 | .bit_shift_left, | |
| 2494 | .bit_shift_right, | |
| 2545 | .shl, | |
| 2546 | .shr, | |
| 2495 | 2547 | .bit_xor, |
| 2496 | 2548 | .bool_and, |
| 2497 | 2549 | .bool_or, |
lib/std/zig/string_literal.zig+1-1| ... | ... | @@ -29,7 +29,7 @@ pub fn parseAppend(buf: *std.ArrayList(u8), bytes: []const u8) error{OutOfMemory |
| 29 | 29 | const slice = bytes[1..]; |
| 30 | 30 | |
| 31 | 31 | const prev_len = buf.items.len; |
| 32 | try buf.ensureCapacity(prev_len + slice.len - 1); | |
| 32 | try buf.ensureUnusedCapacity(slice.len - 1); | |
| 33 | 33 | errdefer buf.shrinkRetainingCapacity(prev_len); |
| 34 | 34 | |
| 35 | 35 | const State = enum { |
lib/std/zig/system.zig+23| ... | ... | @@ -101,6 +101,17 @@ pub const NativePaths = struct { |
| 101 | 101 | return self; |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | if (comptime native_target.os.tag == .solaris) { | |
| 105 | try self.addLibDir("/usr/lib/64"); | |
| 106 | try self.addLibDir("/usr/local/lib/64"); | |
| 107 | try self.addLibDir("/lib/64"); | |
| 108 | ||
| 109 | try self.addIncludeDir("/usr/include"); | |
| 110 | try self.addIncludeDir("/usr/local/include"); | |
| 111 | ||
| 112 | return self; | |
| 113 | } | |
| 114 | ||
| 104 | 115 | if (native_target.os.tag != .windows) { |
| 105 | 116 | const triple = try native_target.linuxTriple(allocator); |
| 106 | 117 | const qual = native_target.cpu.arch.ptrBitWidth(); |
| ... | ... | @@ -243,6 +254,18 @@ pub const NativeTargetInfo = struct { |
| 243 | 254 | error.InvalidVersion => {}, |
| 244 | 255 | } |
| 245 | 256 | }, |
| 257 | .solaris => { | |
| 258 | const uts = std.os.uname(); | |
| 259 | const release = mem.spanZ(&uts.release); | |
| 260 | if (std.builtin.Version.parse(release)) |ver| { | |
| 261 | os.version_range.semver.min = ver; | |
| 262 | os.version_range.semver.max = ver; | |
| 263 | } else |err| switch (err) { | |
| 264 | error.Overflow => {}, | |
| 265 | error.InvalidCharacter => {}, | |
| 266 | error.InvalidVersion => {}, | |
| 267 | } | |
| 268 | }, | |
| 246 | 269 | .windows => { |
| 247 | 270 | const detected_version = windows.detectRuntimeVersion(); |
| 248 | 271 | os.version_range.windows.min = detected_version; |
lib/std/zig/tokenizer.zig+115-1| ... | ... | @@ -11,6 +11,7 @@ pub const Token = struct { |
| 11 | 11 | }; |
| 12 | 12 | |
| 13 | 13 | pub const keywords = std.ComptimeStringMap(Tag, .{ |
| 14 | .{ "addrspace", .keyword_addrspace }, | |
| 14 | 15 | .{ "align", .keyword_align }, |
| 15 | 16 | .{ "allowzero", .keyword_allowzero }, |
| 16 | 17 | .{ "and", .keyword_and }, |
| ... | ... | @@ -102,15 +103,21 @@ pub const Token = struct { |
| 102 | 103 | plus_equal, |
| 103 | 104 | plus_percent, |
| 104 | 105 | plus_percent_equal, |
| 106 | plus_pipe, | |
| 107 | plus_pipe_equal, | |
| 105 | 108 | minus, |
| 106 | 109 | minus_equal, |
| 107 | 110 | minus_percent, |
| 108 | 111 | minus_percent_equal, |
| 112 | minus_pipe, | |
| 113 | minus_pipe_equal, | |
| 109 | 114 | asterisk, |
| 110 | 115 | asterisk_equal, |
| 111 | 116 | asterisk_asterisk, |
| 112 | 117 | asterisk_percent, |
| 113 | 118 | asterisk_percent_equal, |
| 119 | asterisk_pipe, | |
| 120 | asterisk_pipe_equal, | |
| 114 | 121 | arrow, |
| 115 | 122 | colon, |
| 116 | 123 | slash, |
| ... | ... | @@ -123,6 +130,8 @@ pub const Token = struct { |
| 123 | 130 | angle_bracket_left_equal, |
| 124 | 131 | angle_bracket_angle_bracket_left, |
| 125 | 132 | angle_bracket_angle_bracket_left_equal, |
| 133 | angle_bracket_angle_bracket_left_pipe, | |
| 134 | angle_bracket_angle_bracket_left_pipe_equal, | |
| 126 | 135 | angle_bracket_right, |
| 127 | 136 | angle_bracket_right_equal, |
| 128 | 137 | angle_bracket_angle_bracket_right, |
| ... | ... | @@ -132,6 +141,7 @@ pub const Token = struct { |
| 132 | 141 | float_literal, |
| 133 | 142 | doc_comment, |
| 134 | 143 | container_doc_comment, |
| 144 | keyword_addrspace, | |
| 135 | 145 | keyword_align, |
| 136 | 146 | keyword_allowzero, |
| 137 | 147 | keyword_and, |
| ... | ... | @@ -225,15 +235,21 @@ pub const Token = struct { |
| 225 | 235 | .plus_equal => "+=", |
| 226 | 236 | .plus_percent => "+%", |
| 227 | 237 | .plus_percent_equal => "+%=", |
| 238 | .plus_pipe => "+|", | |
| 239 | .plus_pipe_equal => "+|=", | |
| 228 | 240 | .minus => "-", |
| 229 | 241 | .minus_equal => "-=", |
| 230 | 242 | .minus_percent => "-%", |
| 231 | 243 | .minus_percent_equal => "-%=", |
| 244 | .minus_pipe => "-|", | |
| 245 | .minus_pipe_equal => "-|=", | |
| 232 | 246 | .asterisk => "*", |
| 233 | 247 | .asterisk_equal => "*=", |
| 234 | 248 | .asterisk_asterisk => "**", |
| 235 | 249 | .asterisk_percent => "*%", |
| 236 | 250 | .asterisk_percent_equal => "*%=", |
| 251 | .asterisk_pipe => "*|", | |
| 252 | .asterisk_pipe_equal => "*|=", | |
| 237 | 253 | .arrow => "->", |
| 238 | 254 | .colon => ":", |
| 239 | 255 | .slash => "/", |
| ... | ... | @@ -246,11 +262,14 @@ pub const Token = struct { |
| 246 | 262 | .angle_bracket_left_equal => "<=", |
| 247 | 263 | .angle_bracket_angle_bracket_left => "<<", |
| 248 | 264 | .angle_bracket_angle_bracket_left_equal => "<<=", |
| 265 | .angle_bracket_angle_bracket_left_pipe => "<<|", | |
| 266 | .angle_bracket_angle_bracket_left_pipe_equal => "<<|=", | |
| 249 | 267 | .angle_bracket_right => ">", |
| 250 | 268 | .angle_bracket_right_equal => ">=", |
| 251 | 269 | .angle_bracket_angle_bracket_right => ">>", |
| 252 | 270 | .angle_bracket_angle_bracket_right_equal => ">>=", |
| 253 | 271 | .tilde => "~", |
| 272 | .keyword_addrspace => "addrspace", | |
| 254 | 273 | .keyword_align => "align", |
| 255 | 274 | .keyword_allowzero => "allowzero", |
| 256 | 275 | .keyword_and => "and", |
| ... | ... | @@ -349,8 +368,10 @@ pub const Tokenizer = struct { |
| 349 | 368 | pipe, |
| 350 | 369 | minus, |
| 351 | 370 | minus_percent, |
| 371 | minus_pipe, | |
| 352 | 372 | asterisk, |
| 353 | 373 | asterisk_percent, |
| 374 | asterisk_pipe, | |
| 354 | 375 | slash, |
| 355 | 376 | line_comment_start, |
| 356 | 377 | line_comment, |
| ... | ... | @@ -379,8 +400,10 @@ pub const Tokenizer = struct { |
| 379 | 400 | percent, |
| 380 | 401 | plus, |
| 381 | 402 | plus_percent, |
| 403 | plus_pipe, | |
| 382 | 404 | angle_bracket_left, |
| 383 | 405 | angle_bracket_angle_bracket_left, |
| 406 | angle_bracket_angle_bracket_left_pipe, | |
| 384 | 407 | angle_bracket_right, |
| 385 | 408 | angle_bracket_angle_bracket_right, |
| 386 | 409 | period, |
| ... | ... | @@ -581,6 +604,9 @@ pub const Tokenizer = struct { |
| 581 | 604 | '%' => { |
| 582 | 605 | state = .asterisk_percent; |
| 583 | 606 | }, |
| 607 | '|' => { | |
| 608 | state = .asterisk_pipe; | |
| 609 | }, | |
| 584 | 610 | else => { |
| 585 | 611 | result.tag = .asterisk; |
| 586 | 612 | break; |
| ... | ... | @@ -599,6 +625,18 @@ pub const Tokenizer = struct { |
| 599 | 625 | }, |
| 600 | 626 | }, |
| 601 | 627 | |
| 628 | .asterisk_pipe => switch (c) { | |
| 629 | '=' => { | |
| 630 | result.tag = .asterisk_pipe_equal; | |
| 631 | self.index += 1; | |
| 632 | break; | |
| 633 | }, | |
| 634 | else => { | |
| 635 | result.tag = .asterisk_pipe; | |
| 636 | break; | |
| 637 | }, | |
| 638 | }, | |
| 639 | ||
| 602 | 640 | .percent => switch (c) { |
| 603 | 641 | '=' => { |
| 604 | 642 | result.tag = .percent_equal; |
| ... | ... | @@ -625,6 +663,9 @@ pub const Tokenizer = struct { |
| 625 | 663 | '%' => { |
| 626 | 664 | state = .plus_percent; |
| 627 | 665 | }, |
| 666 | '|' => { | |
| 667 | state = .plus_pipe; | |
| 668 | }, | |
| 628 | 669 | else => { |
| 629 | 670 | result.tag = .plus; |
| 630 | 671 | break; |
| ... | ... | @@ -643,6 +684,18 @@ pub const Tokenizer = struct { |
| 643 | 684 | }, |
| 644 | 685 | }, |
| 645 | 686 | |
| 687 | .plus_pipe => switch (c) { | |
| 688 | '=' => { | |
| 689 | result.tag = .plus_pipe_equal; | |
| 690 | self.index += 1; | |
| 691 | break; | |
| 692 | }, | |
| 693 | else => { | |
| 694 | result.tag = .plus_pipe; | |
| 695 | break; | |
| 696 | }, | |
| 697 | }, | |
| 698 | ||
| 646 | 699 | .caret => switch (c) { |
| 647 | 700 | '=' => { |
| 648 | 701 | result.tag = .caret_equal; |
| ... | ... | @@ -700,7 +753,7 @@ pub const Tokenizer = struct { |
| 700 | 753 | }, |
| 701 | 754 | |
| 702 | 755 | .string_literal_backslash => switch (c) { |
| 703 | '\n' => { | |
| 756 | 0, '\n' => { | |
| 704 | 757 | result.tag = .invalid; |
| 705 | 758 | break; |
| 706 | 759 | }, |
| ... | ... | @@ -769,6 +822,10 @@ pub const Tokenizer = struct { |
| 769 | 822 | }, |
| 770 | 823 | |
| 771 | 824 | .char_literal_unicode_escape_saw_u => switch (c) { |
| 825 | 0 => { | |
| 826 | result.tag = .invalid; | |
| 827 | break; | |
| 828 | }, | |
| 772 | 829 | '{' => { |
| 773 | 830 | state = .char_literal_unicode_escape; |
| 774 | 831 | }, |
| ... | ... | @@ -779,6 +836,10 @@ pub const Tokenizer = struct { |
| 779 | 836 | }, |
| 780 | 837 | |
| 781 | 838 | .char_literal_unicode_escape => switch (c) { |
| 839 | 0 => { | |
| 840 | result.tag = .invalid; | |
| 841 | break; | |
| 842 | }, | |
| 782 | 843 | '0'...'9', 'a'...'f', 'A'...'F' => {}, |
| 783 | 844 | '}' => { |
| 784 | 845 | state = .char_literal_end; // too many/few digits handled later |
| ... | ... | @@ -892,6 +953,9 @@ pub const Tokenizer = struct { |
| 892 | 953 | '%' => { |
| 893 | 954 | state = .minus_percent; |
| 894 | 955 | }, |
| 956 | '|' => { | |
| 957 | state = .minus_pipe; | |
| 958 | }, | |
| 895 | 959 | else => { |
| 896 | 960 | result.tag = .minus; |
| 897 | 961 | break; |
| ... | ... | @@ -909,6 +973,17 @@ pub const Tokenizer = struct { |
| 909 | 973 | break; |
| 910 | 974 | }, |
| 911 | 975 | }, |
| 976 | .minus_pipe => switch (c) { | |
| 977 | '=' => { | |
| 978 | result.tag = .minus_pipe_equal; | |
| 979 | self.index += 1; | |
| 980 | break; | |
| 981 | }, | |
| 982 | else => { | |
| 983 | result.tag = .minus_pipe; | |
| 984 | break; | |
| 985 | }, | |
| 986 | }, | |
| 912 | 987 | |
| 913 | 988 | .angle_bracket_left => switch (c) { |
| 914 | 989 | '<' => { |
| ... | ... | @@ -931,12 +1006,27 @@ pub const Tokenizer = struct { |
| 931 | 1006 | self.index += 1; |
| 932 | 1007 | break; |
| 933 | 1008 | }, |
| 1009 | '|' => { | |
| 1010 | state = .angle_bracket_angle_bracket_left_pipe; | |
| 1011 | }, | |
| 934 | 1012 | else => { |
| 935 | 1013 | result.tag = .angle_bracket_angle_bracket_left; |
| 936 | 1014 | break; |
| 937 | 1015 | }, |
| 938 | 1016 | }, |
| 939 | 1017 | |
| 1018 | .angle_bracket_angle_bracket_left_pipe => switch (c) { | |
| 1019 | '=' => { | |
| 1020 | result.tag = .angle_bracket_angle_bracket_left_pipe_equal; | |
| 1021 | self.index += 1; | |
| 1022 | break; | |
| 1023 | }, | |
| 1024 | else => { | |
| 1025 | result.tag = .angle_bracket_angle_bracket_left_pipe; | |
| 1026 | break; | |
| 1027 | }, | |
| 1028 | }, | |
| 1029 | ||
| 940 | 1030 | .angle_bracket_right => switch (c) { |
| 941 | 1031 | '>' => { |
| 942 | 1032 | state = .angle_bracket_angle_bracket_right; |
| ... | ... | @@ -1919,6 +2009,30 @@ test "tokenizer - invalid builtin identifiers" { |
| 1919 | 2009 | try testTokenize("@0()", &.{ .invalid, .integer_literal, .l_paren, .r_paren }); |
| 1920 | 2010 | } |
| 1921 | 2011 | |
| 2012 | test "tokenizer - invalid token with unfinished escape right before eof" { | |
| 2013 | try testTokenize("\"\\", &.{.invalid}); | |
| 2014 | try testTokenize("'\\", &.{.invalid}); | |
| 2015 | try testTokenize("'\\u", &.{.invalid}); | |
| 2016 | } | |
| 2017 | ||
| 2018 | test "tokenizer - saturating" { | |
| 2019 | try testTokenize("<<", &.{.angle_bracket_angle_bracket_left}); | |
| 2020 | try testTokenize("<<|", &.{.angle_bracket_angle_bracket_left_pipe}); | |
| 2021 | try testTokenize("<<|=", &.{.angle_bracket_angle_bracket_left_pipe_equal}); | |
| 2022 | ||
| 2023 | try testTokenize("*", &.{.asterisk}); | |
| 2024 | try testTokenize("*|", &.{.asterisk_pipe}); | |
| 2025 | try testTokenize("*|=", &.{.asterisk_pipe_equal}); | |
| 2026 | ||
| 2027 | try testTokenize("+", &.{.plus}); | |
| 2028 | try testTokenize("+|", &.{.plus_pipe}); | |
| 2029 | try testTokenize("+|=", &.{.plus_pipe_equal}); | |
| 2030 | ||
| 2031 | try testTokenize("-", &.{.minus}); | |
| 2032 | try testTokenize("-|", &.{.minus_pipe}); | |
| 2033 | try testTokenize("-|=", &.{.minus_pipe_equal}); | |
| 2034 | } | |
| 2035 | ||
| 1922 | 2036 | fn testTokenize(source: [:0]const u8, expected_tokens: []const Token.Tag) !void { |
| 1923 | 2037 | var tokenizer = Tokenizer.init(source); |
| 1924 | 2038 | for (expected_tokens) |expected_token_id| { |
src/Air.zig+150-8| ... | ... | @@ -44,6 +44,11 @@ pub const Inst = struct { |
| 44 | 44 | /// is the same as both operands. |
| 45 | 45 | /// Uses the `bin_op` field. |
| 46 | 46 | addwrap, |
| 47 | /// Saturating integer addition. | |
| 48 | /// Both operands are guaranteed to be the same type, and the result type | |
| 49 | /// is the same as both operands. | |
| 50 | /// Uses the `bin_op` field. | |
| 51 | add_sat, | |
| 47 | 52 | /// Float or integer subtraction. For integers, wrapping is undefined behavior. |
| 48 | 53 | /// Both operands are guaranteed to be the same type, and the result type |
| 49 | 54 | /// is the same as both operands. |
| ... | ... | @@ -54,6 +59,11 @@ pub const Inst = struct { |
| 54 | 59 | /// is the same as both operands. |
| 55 | 60 | /// Uses the `bin_op` field. |
| 56 | 61 | subwrap, |
| 62 | /// Saturating integer subtraction. | |
| 63 | /// Both operands are guaranteed to be the same type, and the result type | |
| 64 | /// is the same as both operands. | |
| 65 | /// Uses the `bin_op` field. | |
| 66 | sub_sat, | |
| 57 | 67 | /// Float or integer multiplication. For integers, wrapping is undefined behavior. |
| 58 | 68 | /// Both operands are guaranteed to be the same type, and the result type |
| 59 | 69 | /// is the same as both operands. |
| ... | ... | @@ -64,15 +74,26 @@ pub const Inst = struct { |
| 64 | 74 | /// is the same as both operands. |
| 65 | 75 | /// Uses the `bin_op` field. |
| 66 | 76 | mulwrap, |
| 77 | /// Saturating integer multiplication. | |
| 78 | /// Both operands are guaranteed to be the same type, and the result type | |
| 79 | /// is the same as both operands. | |
| 80 | /// Uses the `bin_op` field. | |
| 81 | mul_sat, | |
| 67 | 82 | /// Integer or float division. For integers, wrapping is undefined behavior. |
| 68 | 83 | /// Both operands are guaranteed to be the same type, and the result type |
| 69 | 84 | /// is the same as both operands. |
| 70 | 85 | /// Uses the `bin_op` field. |
| 71 | 86 | div, |
| 72 | /// Integer or float remainder. | |
| 73 | /// Both operands are guaranteed to be the same type, and the result type is the same as both operands. | |
| 87 | /// Integer or float remainder division. | |
| 88 | /// Both operands are guaranteed to be the same type, and the result type | |
| 89 | /// is the same as both operands. | |
| 74 | 90 | /// Uses the `bin_op` field. |
| 75 | 91 | rem, |
| 92 | /// Integer or float modulus division. | |
| 93 | /// Both operands are guaranteed to be the same type, and the result type | |
| 94 | /// is the same as both operands. | |
| 95 | /// Uses the `bin_op` field. | |
| 96 | mod, | |
| 76 | 97 | /// Add an offset to a pointer, returning a new pointer. |
| 77 | 98 | /// The offset is in element type units, not bytes. |
| 78 | 99 | /// Wrapping is undefined behavior. |
| ... | ... | @@ -104,6 +125,14 @@ pub const Inst = struct { |
| 104 | 125 | /// Shift left. `<<` |
| 105 | 126 | /// Uses the `bin_op` field. |
| 106 | 127 | shl, |
| 128 | /// Shift left; For unsigned integers, the shift produces a poison value if it shifts | |
| 129 | /// out any non-zero bits. For signed integers, the shift produces a poison value if | |
| 130 | /// it shifts out any bits that disagree with the resultant sign bit. | |
| 131 | /// Uses the `bin_op` field. | |
| 132 | shl_exact, | |
| 133 | /// Saturating integer shift left. `<<|` | |
| 134 | /// Uses the `bin_op` field. | |
| 135 | shl_sat, | |
| 107 | 136 | /// Bitwise XOR. `^` |
| 108 | 137 | /// Uses the `bin_op` field. |
| 109 | 138 | xor, |
| ... | ... | @@ -131,6 +160,15 @@ pub const Inst = struct { |
| 131 | 160 | /// Result type is the return type of the function being called. |
| 132 | 161 | /// Uses the `pl_op` field with the `Call` payload. operand is the callee. |
| 133 | 162 | call, |
| 163 | /// Count leading zeroes of an integer according to its representation in twos complement. | |
| 164 | /// Result type will always be an unsigned integer big enough to fit the answer. | |
| 165 | /// Uses the `ty_op` field. | |
| 166 | clz, | |
| 167 | /// Count trailing zeroes of an integer according to its representation in twos complement. | |
| 168 | /// Result type will always be an unsigned integer big enough to fit the answer. | |
| 169 | /// Uses the `ty_op` field. | |
| 170 | ctz, | |
| 171 | ||
| 134 | 172 | /// `<`. Result type is always bool. |
| 135 | 173 | /// Uses the `bin_op` field. |
| 136 | 174 | cmp_lt, |
| ... | ... | @@ -149,6 +187,7 @@ pub const Inst = struct { |
| 149 | 187 | /// `!=`. Result type is always bool. |
| 150 | 188 | /// Uses the `bin_op` field. |
| 151 | 189 | cmp_neq, |
| 190 | ||
| 152 | 191 | /// Conditional branch. |
| 153 | 192 | /// Result type is always noreturn; no instructions in a block follow this one. |
| 154 | 193 | /// Uses the `pl_op` field. Operand is the condition. Payload is `CondBr`. |
| ... | ... | @@ -225,9 +264,12 @@ pub const Inst = struct { |
| 225 | 264 | /// Indicates the program counter will never get to this instruction. |
| 226 | 265 | /// Result type is always noreturn; no instructions in a block follow this one. |
| 227 | 266 | unreach, |
| 228 | /// Convert from one float type to another. | |
| 267 | /// Convert from a float type to a smaller one. | |
| 229 | 268 | /// Uses the `ty_op` field. |
| 230 | floatcast, | |
| 269 | fptrunc, | |
| 270 | /// Convert from a float type to a wider one. | |
| 271 | /// Uses the `ty_op` field. | |
| 272 | fpext, | |
| 231 | 273 | /// Returns an integer with a different type than the operand. The new type may have |
| 232 | 274 | /// fewer, the same, or more bits than the operand type. However, the instruction |
| 233 | 275 | /// guarantees that the same integer value fits in both types. |
| ... | ... | @@ -265,19 +307,29 @@ pub const Inst = struct { |
| 265 | 307 | /// wrap from E to E!T |
| 266 | 308 | /// Uses the `ty_op` field. |
| 267 | 309 | wrap_errunion_err, |
| 268 | /// Given a pointer to a struct and a field index, returns a pointer to the field. | |
| 310 | /// Given a pointer to a struct or union and a field index, returns a pointer to the field. | |
| 269 | 311 | /// Uses the `ty_pl` field, payload is `StructField`. |
| 312 | /// TODO rename to `agg_field_ptr`. | |
| 270 | 313 | struct_field_ptr, |
| 271 | /// Given a pointer to a struct, returns a pointer to the field. | |
| 314 | /// Given a pointer to a struct or union, returns a pointer to the field. | |
| 272 | 315 | /// The field index is the number at the end of the name. |
| 273 | 316 | /// Uses `ty_op` field. |
| 317 | /// TODO rename to `agg_field_ptr_index_X` | |
| 274 | 318 | struct_field_ptr_index_0, |
| 275 | 319 | struct_field_ptr_index_1, |
| 276 | 320 | struct_field_ptr_index_2, |
| 277 | 321 | struct_field_ptr_index_3, |
| 278 | /// Given a byval struct and a field index, returns the field byval. | |
| 322 | /// Given a byval struct or union and a field index, returns the field byval. | |
| 279 | 323 | /// Uses the `ty_pl` field, payload is `StructField`. |
| 324 | /// TODO rename to `agg_field_val` | |
| 280 | 325 | struct_field_val, |
| 326 | /// Given a pointer to a tagged union, set its tag to the provided value. | |
| 327 | /// Result type is always void. | |
| 328 | /// Uses the `bin_op` field. LHS is union pointer, RHS is new tag value. | |
| 329 | set_union_tag, | |
| 330 | /// Given a tagged union value, get its tag value. | |
| 331 | /// Uses the `ty_op` field. | |
| 332 | get_union_tag, | |
| 281 | 333 | /// Given a slice value, return the length. |
| 282 | 334 | /// Result type is always usize. |
| 283 | 335 | /// Uses the `ty_op` field. |
| ... | ... | @@ -309,10 +361,52 @@ pub const Inst = struct { |
| 309 | 361 | /// Given a pointer to an array, return a slice. |
| 310 | 362 | /// Uses the `ty_op` field. |
| 311 | 363 | array_to_slice, |
| 364 | /// Given a float operand, return the integer with the closest mathematical meaning. | |
| 365 | /// Uses the `ty_op` field. | |
| 366 | float_to_int, | |
| 367 | /// Given an integer operand, return the float with the closest mathematical meaning. | |
| 368 | /// Uses the `ty_op` field. | |
| 369 | int_to_float, | |
| 370 | ||
| 371 | /// Given dest ptr, value, and len, set all elements at dest to value. | |
| 372 | /// Result type is always void. | |
| 373 | /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the | |
| 374 | /// value, `rhs` is the length. | |
| 375 | /// The element type may be any type, not just u8. | |
| 376 | memset, | |
| 377 | /// Given dest ptr, src ptr, and len, copy len elements from src to dest. | |
| 378 | /// Result type is always void. | |
| 379 | /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the | |
| 380 | /// src ptr, `rhs` is the length. | |
| 381 | /// The element type may be any type, not just u8. | |
| 382 | memcpy, | |
| 383 | ||
| 312 | 384 | /// Uses the `ty_pl` field with payload `Cmpxchg`. |
| 313 | 385 | cmpxchg_weak, |
| 314 | 386 | /// Uses the `ty_pl` field with payload `Cmpxchg`. |
| 315 | 387 | cmpxchg_strong, |
| 388 | /// Lowers to a memory fence instruction. | |
| 389 | /// Result type is always void. | |
| 390 | /// Uses the `fence` field. | |
| 391 | fence, | |
| 392 | /// Atomically load from a pointer. | |
| 393 | /// Result type is the element type of the pointer. | |
| 394 | /// Uses the `atomic_load` field. | |
| 395 | atomic_load, | |
| 396 | /// Atomically store through a pointer. | |
| 397 | /// Result type is always `void`. | |
| 398 | /// Uses the `bin_op` field. LHS is pointer, RHS is element. | |
| 399 | atomic_store_unordered, | |
| 400 | /// Same as `atomic_store_unordered` but with `AtomicOrder.Monotonic`. | |
| 401 | atomic_store_monotonic, | |
| 402 | /// Same as `atomic_store_unordered` but with `AtomicOrder.Release`. | |
| 403 | atomic_store_release, | |
| 404 | /// Same as `atomic_store_unordered` but with `AtomicOrder.SeqCst`. | |
| 405 | atomic_store_seq_cst, | |
| 406 | /// Atomically read-modify-write via a pointer. | |
| 407 | /// Result type is the element type of the pointer. | |
| 408 | /// Uses the `pl_op` field with payload `AtomicRmw`. Operand is `ptr`. | |
| 409 | atomic_rmw, | |
| 316 | 410 | |
| 317 | 411 | pub fn fromCmpOp(op: std.math.CompareOperator) Tag { |
| 318 | 412 | return switch (op) { |
| ... | ... | @@ -380,6 +474,11 @@ pub const Inst = struct { |
| 380 | 474 | line: u32, |
| 381 | 475 | column: u32, |
| 382 | 476 | }, |
| 477 | fence: std.builtin.AtomicOrder, | |
| 478 | atomic_load: struct { | |
| 479 | ptr: Ref, | |
| 480 | order: std.builtin.AtomicOrder, | |
| 481 | }, | |
| 383 | 482 | |
| 384 | 483 | // Make sure we don't accidentally add a field to make this union |
| 385 | 484 | // bigger than expected. Note that in Debug builds, Zig is allowed |
| ... | ... | @@ -464,6 +563,21 @@ pub const Cmpxchg = struct { |
| 464 | 563 | } |
| 465 | 564 | }; |
| 466 | 565 | |
| 566 | pub const AtomicRmw = struct { | |
| 567 | operand: Inst.Ref, | |
| 568 | /// 0b00000000000000000000000000000XXX - ordering | |
| 569 | /// 0b0000000000000000000000000XXXX000 - op | |
| 570 | flags: u32, | |
| 571 | ||
| 572 | pub fn ordering(self: AtomicRmw) std.builtin.AtomicOrder { | |
| 573 | return @intToEnum(std.builtin.AtomicOrder, @truncate(u3, self.flags)); | |
| 574 | } | |
| 575 | ||
| 576 | pub fn op(self: AtomicRmw) std.builtin.AtomicRmwOp { | |
| 577 | return @intToEnum(std.builtin.AtomicRmwOp, @truncate(u4, self.flags >> 3)); | |
| 578 | } | |
| 579 | }; | |
| 580 | ||
| 467 | 581 | pub fn getMainBody(air: Air) []const Air.Inst.Index { |
| 468 | 582 | const body_index = air.extra[@enumToInt(ExtraIndex.main_block)]; |
| 469 | 583 | const extra = air.extraData(Block, body_index); |
| ... | ... | @@ -485,12 +599,16 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 485 | 599 | |
| 486 | 600 | .add, |
| 487 | 601 | .addwrap, |
| 602 | .add_sat, | |
| 488 | 603 | .sub, |
| 489 | 604 | .subwrap, |
| 605 | .sub_sat, | |
| 490 | 606 | .mul, |
| 491 | 607 | .mulwrap, |
| 608 | .mul_sat, | |
| 492 | 609 | .div, |
| 493 | 610 | .rem, |
| 611 | .mod, | |
| 494 | 612 | .bit_and, |
| 495 | 613 | .bit_or, |
| 496 | 614 | .xor, |
| ... | ... | @@ -498,6 +616,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 498 | 616 | .ptr_sub, |
| 499 | 617 | .shr, |
| 500 | 618 | .shl, |
| 619 | .shl_exact, | |
| 620 | .shl_sat, | |
| 501 | 621 | => return air.typeOf(datas[inst].bin_op.lhs), |
| 502 | 622 | |
| 503 | 623 | .cmp_lt, |
| ... | ... | @@ -535,7 +655,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 535 | 655 | .not, |
| 536 | 656 | .bitcast, |
| 537 | 657 | .load, |
| 538 | .floatcast, | |
| 658 | .fpext, | |
| 659 | .fptrunc, | |
| 539 | 660 | .intcast, |
| 540 | 661 | .trunc, |
| 541 | 662 | .optional_payload, |
| ... | ... | @@ -553,6 +674,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 553 | 674 | .struct_field_ptr_index_2, |
| 554 | 675 | .struct_field_ptr_index_3, |
| 555 | 676 | .array_to_slice, |
| 677 | .float_to_int, | |
| 678 | .int_to_float, | |
| 679 | .get_union_tag, | |
| 680 | .clz, | |
| 681 | .ctz, | |
| 556 | 682 | => return air.getRefType(datas[inst].ty_op.ty), |
| 557 | 683 | |
| 558 | 684 | .loop, |
| ... | ... | @@ -566,6 +692,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 566 | 692 | .breakpoint, |
| 567 | 693 | .dbg_stmt, |
| 568 | 694 | .store, |
| 695 | .fence, | |
| 696 | .atomic_store_unordered, | |
| 697 | .atomic_store_monotonic, | |
| 698 | .atomic_store_release, | |
| 699 | .atomic_store_seq_cst, | |
| 700 | .memset, | |
| 701 | .memcpy, | |
| 702 | .set_union_tag, | |
| 569 | 703 | => return Type.initTag(.void), |
| 570 | 704 | |
| 571 | 705 | .ptrtoint, |
| ... | ... | @@ -588,6 +722,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 588 | 722 | const inner_ptr_ty = outer_ptr_ty.elemType(); |
| 589 | 723 | return inner_ptr_ty.elemType(); |
| 590 | 724 | }, |
| 725 | .atomic_load => { | |
| 726 | const ptr_ty = air.typeOf(datas[inst].atomic_load.ptr); | |
| 727 | return ptr_ty.elemType(); | |
| 728 | }, | |
| 729 | .atomic_rmw => { | |
| 730 | const ptr_ty = air.typeOf(datas[inst].pl_op.operand); | |
| 731 | return ptr_ty.elemType(); | |
| 732 | }, | |
| 591 | 733 | } |
| 592 | 734 | } |
| 593 | 735 |
src/AstGen.zig+691-261| ... | ... | @@ -56,6 +56,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 { |
| 56 | 56 | u32 => @field(extra, field.name), |
| 57 | 57 | Zir.Inst.Ref => @enumToInt(@field(extra, field.name)), |
| 58 | 58 | i32 => @bitCast(u32, @field(extra, field.name)), |
| 59 | Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)), | |
| 59 | 60 | else => @compileError("bad field type"), |
| 60 | 61 | }); |
| 61 | 62 | } |
| ... | ... | @@ -124,7 +125,7 @@ pub fn generate(gpa: *Allocator, tree: Ast) Allocator.Error!Zir { |
| 124 | 125 | container_decl, |
| 125 | 126 | .Auto, |
| 126 | 127 | )) |struct_decl_ref| { |
| 127 | astgen.extra.items[@enumToInt(Zir.ExtraIndex.main_struct)] = @enumToInt(struct_decl_ref); | |
| 128 | assert(refToIndex(struct_decl_ref).? == 0); | |
| 128 | 129 | } else |err| switch (err) { |
| 129 | 130 | error.OutOfMemory => return error.OutOfMemory, |
| 130 | 131 | error.AnalysisFail => {}, // Handled via compile_errors below. |
| ... | ... | @@ -197,7 +198,7 @@ pub const ResultLoc = union(enum) { |
| 197 | 198 | none_or_ref, |
| 198 | 199 | /// The expression will be coerced into this type, but it will be evaluated as an rvalue. |
| 199 | 200 | ty: Zir.Inst.Ref, |
| 200 | /// Same as `ty` but it is guaranteed that Sema will additionall perform the coercion, | |
| 201 | /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion, | |
| 201 | 202 | /// so no `as` instruction needs to be emitted. |
| 202 | 203 | coerced_ty: Zir.Inst.Ref, |
| 203 | 204 | /// The expression must store its result into this typed pointer. The result instruction |
| ... | ... | @@ -316,29 +317,37 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins |
| 316 | 317 | .assign, |
| 317 | 318 | .assign_bit_and, |
| 318 | 319 | .assign_bit_or, |
| 319 | .assign_bit_shift_left, | |
| 320 | .assign_bit_shift_right, | |
| 320 | .assign_shl, | |
| 321 | .assign_shl_sat, | |
| 322 | .assign_shr, | |
| 321 | 323 | .assign_bit_xor, |
| 322 | 324 | .assign_div, |
| 323 | 325 | .assign_sub, |
| 324 | 326 | .assign_sub_wrap, |
| 327 | .assign_sub_sat, | |
| 325 | 328 | .assign_mod, |
| 326 | 329 | .assign_add, |
| 327 | 330 | .assign_add_wrap, |
| 331 | .assign_add_sat, | |
| 328 | 332 | .assign_mul, |
| 329 | 333 | .assign_mul_wrap, |
| 334 | .assign_mul_sat, | |
| 330 | 335 | .add, |
| 331 | 336 | .add_wrap, |
| 337 | .add_sat, | |
| 332 | 338 | .sub, |
| 333 | 339 | .sub_wrap, |
| 340 | .sub_sat, | |
| 334 | 341 | .mul, |
| 335 | 342 | .mul_wrap, |
| 343 | .mul_sat, | |
| 336 | 344 | .div, |
| 337 | 345 | .mod, |
| 338 | 346 | .bit_and, |
| 339 | 347 | .bit_or, |
| 340 | .bit_shift_left, | |
| 341 | .bit_shift_right, | |
| 348 | .shl, | |
| 349 | .shl_sat, | |
| 350 | .shr, | |
| 342 | 351 | .bit_xor, |
| 343 | 352 | .bang_equal, |
| 344 | 353 | .equal_equal, |
| ... | ... | @@ -479,7 +488,7 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins |
| 479 | 488 | return expr(gz, scope, .ref, node); |
| 480 | 489 | } |
| 481 | 490 | |
| 482 | /// Turn Zig AST into untyped ZIR istructions. | |
| 491 | /// Turn Zig AST into untyped ZIR instructions. | |
| 483 | 492 | /// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the |
| 484 | 493 | /// result instruction can be used to inspect whether it is isNoReturn() but that is it, |
| 485 | 494 | /// it must otherwise not be used. |
| ... | ... | @@ -521,11 +530,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 521 | 530 | return rvalue(gz, rl, .void_value, node); |
| 522 | 531 | }, |
| 523 | 532 | |
| 524 | .assign_bit_shift_left => { | |
| 533 | .assign_shl => { | |
| 525 | 534 | try assignShift(gz, scope, node, .shl); |
| 526 | 535 | return rvalue(gz, rl, .void_value, node); |
| 527 | 536 | }, |
| 528 | .assign_bit_shift_right => { | |
| 537 | .assign_shl_sat => { | |
| 538 | try assignShiftSat(gz, scope, node); | |
| 539 | return rvalue(gz, rl, .void_value, node); | |
| 540 | }, | |
| 541 | .assign_shr => { | |
| 529 | 542 | try assignShift(gz, scope, node, .shr); |
| 530 | 543 | return rvalue(gz, rl, .void_value, node); |
| 531 | 544 | }, |
| ... | ... | @@ -554,6 +567,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 554 | 567 | try assignOp(gz, scope, node, .subwrap); |
| 555 | 568 | return rvalue(gz, rl, .void_value, node); |
| 556 | 569 | }, |
| 570 | .assign_sub_sat => { | |
| 571 | try assignOp(gz, scope, node, .sub_sat); | |
| 572 | return rvalue(gz, rl, .void_value, node); | |
| 573 | }, | |
| 557 | 574 | .assign_mod => { |
| 558 | 575 | try assignOp(gz, scope, node, .mod_rem); |
| 559 | 576 | return rvalue(gz, rl, .void_value, node); |
| ... | ... | @@ -566,6 +583,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 566 | 583 | try assignOp(gz, scope, node, .addwrap); |
| 567 | 584 | return rvalue(gz, rl, .void_value, node); |
| 568 | 585 | }, |
| 586 | .assign_add_sat => { | |
| 587 | try assignOp(gz, scope, node, .add_sat); | |
| 588 | return rvalue(gz, rl, .void_value, node); | |
| 589 | }, | |
| 569 | 590 | .assign_mul => { |
| 570 | 591 | try assignOp(gz, scope, node, .mul); |
| 571 | 592 | return rvalue(gz, rl, .void_value, node); |
| ... | ... | @@ -574,19 +595,28 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 574 | 595 | try assignOp(gz, scope, node, .mulwrap); |
| 575 | 596 | return rvalue(gz, rl, .void_value, node); |
| 576 | 597 | }, |
| 598 | .assign_mul_sat => { | |
| 599 | try assignOp(gz, scope, node, .mul_sat); | |
| 600 | return rvalue(gz, rl, .void_value, node); | |
| 601 | }, | |
| 577 | 602 | |
| 578 | 603 | // zig fmt: off |
| 579 | .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl), | |
| 580 | .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr), | |
| 604 | .shl => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl), | |
| 605 | .shr => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr), | |
| 581 | 606 | |
| 582 | 607 | .add => return simpleBinOp(gz, scope, rl, node, .add), |
| 583 | 608 | .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap), |
| 609 | .add_sat => return simpleBinOp(gz, scope, rl, node, .add_sat), | |
| 584 | 610 | .sub => return simpleBinOp(gz, scope, rl, node, .sub), |
| 585 | 611 | .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap), |
| 612 | .sub_sat => return simpleBinOp(gz, scope, rl, node, .sub_sat), | |
| 586 | 613 | .mul => return simpleBinOp(gz, scope, rl, node, .mul), |
| 587 | 614 | .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap), |
| 615 | .mul_sat => return simpleBinOp(gz, scope, rl, node, .mul_sat), | |
| 588 | 616 | .div => return simpleBinOp(gz, scope, rl, node, .div), |
| 589 | 617 | .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem), |
| 618 | .shl_sat => return simpleBinOp(gz, scope, rl, node, .shl_sat), | |
| 619 | ||
| 590 | 620 | .bit_and => { |
| 591 | 621 | const current_ampersand_token = main_tokens[node]; |
| 592 | 622 | if (token_tags[current_ampersand_token + 1] == .ampersand) { |
| ... | ... | @@ -1116,6 +1146,11 @@ fn fnProtoExpr( |
| 1116 | 1146 | const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { |
| 1117 | 1147 | break :inst try expr(gz, scope, align_rl, fn_proto.ast.align_expr); |
| 1118 | 1148 | }; |
| 1149 | ||
| 1150 | if (fn_proto.ast.addrspace_expr != 0) { | |
| 1151 | return astgen.failNode(fn_proto.ast.addrspace_expr, "addrspace not allowed on function prototypes", .{}); | |
| 1152 | } | |
| 1153 | ||
| 1119 | 1154 | if (fn_proto.ast.section_expr != 0) { |
| 1120 | 1155 | return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{}); |
| 1121 | 1156 | } |
| ... | ... | @@ -1352,7 +1387,14 @@ fn structInitExpr( |
| 1352 | 1387 | const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) { |
| 1353 | 1388 | .array_type => tree.arrayType(struct_init.ast.type_expr), |
| 1354 | 1389 | .array_type_sentinel => tree.arrayTypeSentinel(struct_init.ast.type_expr), |
| 1355 | else => break :array, | |
| 1390 | else => { | |
| 1391 | if (struct_init.ast.fields.len == 0) { | |
| 1392 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); | |
| 1393 | const result = try gz.addUnNode(.struct_init_empty, ty_inst, node); | |
| 1394 | return rvalue(gz, rl, result, node); | |
| 1395 | } | |
| 1396 | break :array; | |
| 1397 | }, | |
| 1356 | 1398 | }; |
| 1357 | 1399 | const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and |
| 1358 | 1400 | // This intentionally does not support `@"_"` syntax. |
| ... | ... | @@ -1414,8 +1456,8 @@ fn structInitExpr( |
| 1414 | 1456 | const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init); |
| 1415 | 1457 | return rvalue(gz, rl, result, node); |
| 1416 | 1458 | }, |
| 1417 | .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst), | |
| 1418 | .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, node, struct_init, block_gz.rl_ptr), | |
| 1459 | .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst), | |
| 1460 | .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr), | |
| 1419 | 1461 | } |
| 1420 | 1462 | } |
| 1421 | 1463 | |
| ... | ... | @@ -1454,6 +1496,26 @@ fn structInitExprRlNone( |
| 1454 | 1496 | } |
| 1455 | 1497 | |
| 1456 | 1498 | fn structInitExprRlPtr( |
| 1499 | gz: *GenZir, | |
| 1500 | scope: *Scope, | |
| 1501 | rl: ResultLoc, | |
| 1502 | node: Ast.Node.Index, | |
| 1503 | struct_init: Ast.full.StructInit, | |
| 1504 | result_ptr: Zir.Inst.Ref, | |
| 1505 | ) InnerError!Zir.Inst.Ref { | |
| 1506 | if (struct_init.ast.type_expr == 0) { | |
| 1507 | return structInitExprRlPtrInner(gz, scope, node, struct_init, result_ptr); | |
| 1508 | } | |
| 1509 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); | |
| 1510 | ||
| 1511 | var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr); | |
| 1512 | defer as_scope.instructions.deinit(gz.astgen.gpa); | |
| 1513 | ||
| 1514 | const result = try structInitExprRlPtrInner(&as_scope, scope, node, struct_init, as_scope.rl_ptr); | |
| 1515 | return as_scope.finishCoercion(gz, rl, node, result, ty_inst); | |
| 1516 | } | |
| 1517 | ||
| 1518 | fn structInitExprRlPtrInner( | |
| 1457 | 1519 | gz: *GenZir, |
| 1458 | 1520 | scope: *Scope, |
| 1459 | 1521 | node: Ast.Node.Index, |
| ... | ... | @@ -1467,9 +1529,6 @@ fn structInitExprRlPtr( |
| 1467 | 1529 | const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len); |
| 1468 | 1530 | defer gpa.free(field_ptr_list); |
| 1469 | 1531 | |
| 1470 | if (struct_init.ast.type_expr != 0) | |
| 1471 | _ = try typeExpr(gz, scope, struct_init.ast.type_expr); | |
| 1472 | ||
| 1473 | 1532 | for (struct_init.ast.fields) |field_init, i| { |
| 1474 | 1533 | const name_token = tree.firstToken(field_init) - 2; |
| 1475 | 1534 | const str_index = try astgen.identAsString(name_token); |
| ... | ... | @@ -1484,7 +1543,7 @@ fn structInitExprRlPtr( |
| 1484 | 1543 | .body_len = @intCast(u32, field_ptr_list.len), |
| 1485 | 1544 | }); |
| 1486 | 1545 | try astgen.extra.appendSlice(gpa, field_ptr_list); |
| 1487 | return .void_value; | |
| 1546 | return Zir.Inst.Ref.void_value; | |
| 1488 | 1547 | } |
| 1489 | 1548 | |
| 1490 | 1549 | fn structInitExprRlTy( |
| ... | ... | @@ -1868,8 +1927,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod |
| 1868 | 1927 | |
| 1869 | 1928 | .assign => try assign(gz, scope, statement), |
| 1870 | 1929 | |
| 1871 | .assign_bit_shift_left => try assignShift(gz, scope, statement, .shl), | |
| 1872 | .assign_bit_shift_right => try assignShift(gz, scope, statement, .shr), | |
| 1930 | .assign_shl => try assignShift(gz, scope, statement, .shl), | |
| 1931 | .assign_shr => try assignShift(gz, scope, statement, .shr), | |
| 1873 | 1932 | |
| 1874 | 1933 | .assign_bit_and => try assignOp(gz, scope, statement, .bit_and), |
| 1875 | 1934 | .assign_bit_or => try assignOp(gz, scope, statement, .bit_or), |
| ... | ... | @@ -1905,17 +1964,21 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 1905 | 1964 | // in the above while loop. |
| 1906 | 1965 | const zir_tags = gz.astgen.instructions.items(.tag); |
| 1907 | 1966 | switch (zir_tags[inst]) { |
| 1908 | // For some instructions, swap in a slightly different ZIR tag | |
| 1967 | // For some instructions, modify the zir data | |
| 1909 | 1968 | // so we can avoid a separate ensure_result_used instruction. |
| 1910 | .call_chkused => unreachable, | |
| 1911 | 1969 | .call => { |
| 1912 | zir_tags[inst] = .call_chkused; | |
| 1970 | const extra_index = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index; | |
| 1971 | const slot = &gz.astgen.extra.items[extra_index]; | |
| 1972 | var flags = @bitCast(Zir.Inst.Call.Flags, slot.*); | |
| 1973 | flags.ensure_result_used = true; | |
| 1974 | slot.* = @bitCast(u32, flags); | |
| 1913 | 1975 | break :b true; |
| 1914 | 1976 | }, |
| 1915 | 1977 | |
| 1916 | 1978 | // ZIR instructions that might be a type other than `noreturn` or `void`. |
| 1917 | 1979 | .add, |
| 1918 | 1980 | .addwrap, |
| 1981 | .add_sat, | |
| 1919 | 1982 | .param, |
| 1920 | 1983 | .param_comptime, |
| 1921 | 1984 | .param_anytype, |
| ... | ... | @@ -1947,9 +2010,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 1947 | 2010 | .bool_br_and, |
| 1948 | 2011 | .bool_br_or, |
| 1949 | 2012 | .bool_not, |
| 1950 | .call_compile_time, | |
| 1951 | .call_nosuspend, | |
| 1952 | .call_async, | |
| 1953 | 2013 | .cmp_lt, |
| 1954 | 2014 | .cmp_lte, |
| 1955 | 2015 | .cmp_eq, |
| ... | ... | @@ -1967,8 +2027,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 1967 | 2027 | .elem_val_node, |
| 1968 | 2028 | .field_ptr, |
| 1969 | 2029 | .field_val, |
| 2030 | .field_call_bind, | |
| 1970 | 2031 | .field_ptr_named, |
| 1971 | 2032 | .field_val_named, |
| 2033 | .field_call_bind_named, | |
| 1972 | 2034 | .func, |
| 1973 | 2035 | .func_inferred, |
| 1974 | 2036 | .int, |
| ... | ... | @@ -1983,13 +2045,15 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 1983 | 2045 | .mod_rem, |
| 1984 | 2046 | .mul, |
| 1985 | 2047 | .mulwrap, |
| 1986 | .param_type, | |
| 2048 | .mul_sat, | |
| 1987 | 2049 | .ref, |
| 1988 | 2050 | .shl, |
| 2051 | .shl_sat, | |
| 1989 | 2052 | .shr, |
| 1990 | 2053 | .str, |
| 1991 | 2054 | .sub, |
| 1992 | 2055 | .subwrap, |
| 2056 | .sub_sat, | |
| 1993 | 2057 | .negate, |
| 1994 | 2058 | .negate_wrap, |
| 1995 | 2059 | .typeof, |
| ... | ... | @@ -2049,9 +2113,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 2049 | 2113 | .union_init_ptr, |
| 2050 | 2114 | .field_type, |
| 2051 | 2115 | .field_type_ref, |
| 2052 | .opaque_decl, | |
| 2053 | .opaque_decl_anon, | |
| 2054 | .opaque_decl_func, | |
| 2055 | 2116 | .error_set_decl, |
| 2056 | 2117 | .error_set_decl_anon, |
| 2057 | 2118 | .error_set_decl_func, |
| ... | ... | @@ -2118,14 +2179,11 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 2118 | 2179 | .select, |
| 2119 | 2180 | .atomic_load, |
| 2120 | 2181 | .atomic_rmw, |
| 2121 | .atomic_store, | |
| 2122 | 2182 | .mul_add, |
| 2123 | 2183 | .builtin_call, |
| 2124 | 2184 | .field_ptr_type, |
| 2125 | 2185 | .field_parent_ptr, |
| 2126 | 2186 | .maximum, |
| 2127 | .memcpy, | |
| 2128 | .memset, | |
| 2129 | 2187 | .minimum, |
| 2130 | 2188 | .builtin_async_call, |
| 2131 | 2189 | .c_import, |
| ... | ... | @@ -2134,6 +2192,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 2134 | 2192 | .await_nosuspend, |
| 2135 | 2193 | .ret_err_value_code, |
| 2136 | 2194 | .extended, |
| 2195 | .closure_get, | |
| 2137 | 2196 | => break :b false, |
| 2138 | 2197 | |
| 2139 | 2198 | // ZIR instructions that are always `noreturn`. |
| ... | ... | @@ -2162,8 +2221,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 2162 | 2221 | .ensure_result_used, |
| 2163 | 2222 | .ensure_result_non_error, |
| 2164 | 2223 | .@"export", |
| 2224 | .export_value, | |
| 2165 | 2225 | .set_eval_branch_quota, |
| 2166 | 2226 | .ensure_err_payload_void, |
| 2227 | .atomic_store, | |
| 2167 | 2228 | .store, |
| 2168 | 2229 | .store_node, |
| 2169 | 2230 | .store_to_block_ptr, |
| ... | ... | @@ -2175,6 +2236,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 2175 | 2236 | .set_cold, |
| 2176 | 2237 | .set_float_mode, |
| 2177 | 2238 | .set_runtime_safety, |
| 2239 | .closure_capture, | |
| 2240 | .memcpy, | |
| 2241 | .memset, | |
| 2178 | 2242 | => break :b true, |
| 2179 | 2243 | } |
| 2180 | 2244 | } else switch (maybe_unused_result) { |
| ... | ... | @@ -2371,6 +2435,7 @@ fn varDecl( |
| 2371 | 2435 | const gpa = astgen.gpa; |
| 2372 | 2436 | const tree = astgen.tree; |
| 2373 | 2437 | const token_tags = tree.tokens.items(.tag); |
| 2438 | const main_tokens = tree.nodes.items(.main_token); | |
| 2374 | 2439 | |
| 2375 | 2440 | const name_token = var_decl.ast.mut_token + 1; |
| 2376 | 2441 | const ident_name_raw = tree.tokenSlice(name_token); |
| ... | ... | @@ -2385,6 +2450,14 @@ fn varDecl( |
| 2385 | 2450 | return astgen.failNode(node, "variables must be initialized", .{}); |
| 2386 | 2451 | } |
| 2387 | 2452 | |
| 2453 | if (var_decl.ast.addrspace_node != 0) { | |
| 2454 | return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw}); | |
| 2455 | } | |
| 2456 | ||
| 2457 | if (var_decl.ast.section_node != 0) { | |
| 2458 | return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw}); | |
| 2459 | } | |
| 2460 | ||
| 2388 | 2461 | const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0) |
| 2389 | 2462 | try expr(gz, scope, align_rl, var_decl.ast.align_node) |
| 2390 | 2463 | else |
| ... | ... | @@ -2494,7 +2567,11 @@ fn varDecl( |
| 2494 | 2567 | for (init_scope.instructions.items) |src_inst| { |
| 2495 | 2568 | if (zir_tags[src_inst] == .store_to_block_ptr) { |
| 2496 | 2569 | if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) { |
| 2497 | zir_tags[src_inst] = .store_to_inferred_ptr; | |
| 2570 | if (var_decl.ast.type_node != 0) { | |
| 2571 | zir_tags[src_inst] = .store; | |
| 2572 | } else { | |
| 2573 | zir_tags[src_inst] = .store_to_inferred_ptr; | |
| 2574 | } | |
| 2498 | 2575 | } |
| 2499 | 2576 | } |
| 2500 | 2577 | parent_zir.appendAssumeCapacity(src_inst); |
| ... | ... | @@ -2668,6 +2745,24 @@ fn assignShift( |
| 2668 | 2745 | _ = try gz.addBin(.store, lhs_ptr, result); |
| 2669 | 2746 | } |
| 2670 | 2747 | |
| 2748 | fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void { | |
| 2749 | try emitDbgNode(gz, infix_node); | |
| 2750 | const astgen = gz.astgen; | |
| 2751 | const tree = astgen.tree; | |
| 2752 | const node_datas = tree.nodes.items(.data); | |
| 2753 | ||
| 2754 | const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs); | |
| 2755 | const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node); | |
| 2756 | // Saturating shift-left allows any integer type for both the LHS and RHS. | |
| 2757 | const rhs = try expr(gz, scope, .none, node_datas[infix_node].rhs); | |
| 2758 | ||
| 2759 | const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{ | |
| 2760 | .lhs = lhs, | |
| 2761 | .rhs = rhs, | |
| 2762 | }); | |
| 2763 | _ = try gz.addBin(.store, lhs_ptr, result); | |
| 2764 | } | |
| 2765 | ||
| 2671 | 2766 | fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { |
| 2672 | 2767 | const astgen = gz.astgen; |
| 2673 | 2768 | const tree = astgen.tree; |
| ... | ... | @@ -2714,6 +2809,7 @@ fn ptrType( |
| 2714 | 2809 | const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type); |
| 2715 | 2810 | |
| 2716 | 2811 | const simple = ptr_info.ast.align_node == 0 and |
| 2812 | ptr_info.ast.addrspace_node == 0 and | |
| 2717 | 2813 | ptr_info.ast.sentinel == 0 and |
| 2718 | 2814 | ptr_info.ast.bit_range_start == 0; |
| 2719 | 2815 | |
| ... | ... | @@ -2732,6 +2828,7 @@ fn ptrType( |
| 2732 | 2828 | |
| 2733 | 2829 | var sentinel_ref: Zir.Inst.Ref = .none; |
| 2734 | 2830 | var align_ref: Zir.Inst.Ref = .none; |
| 2831 | var addrspace_ref: Zir.Inst.Ref = .none; | |
| 2735 | 2832 | var bit_start_ref: Zir.Inst.Ref = .none; |
| 2736 | 2833 | var bit_end_ref: Zir.Inst.Ref = .none; |
| 2737 | 2834 | var trailing_count: u32 = 0; |
| ... | ... | @@ -2744,6 +2841,10 @@ fn ptrType( |
| 2744 | 2841 | align_ref = try expr(gz, scope, align_rl, ptr_info.ast.align_node); |
| 2745 | 2842 | trailing_count += 1; |
| 2746 | 2843 | } |
| 2844 | if (ptr_info.ast.addrspace_node != 0) { | |
| 2845 | addrspace_ref = try expr(gz, scope, .{ .ty = .address_space_type }, ptr_info.ast.addrspace_node); | |
| 2846 | trailing_count += 1; | |
| 2847 | } | |
| 2747 | 2848 | if (ptr_info.ast.bit_range_start != 0) { |
| 2748 | 2849 | assert(ptr_info.ast.bit_range_end != 0); |
| 2749 | 2850 | bit_start_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_start); |
| ... | ... | @@ -2764,6 +2865,9 @@ fn ptrType( |
| 2764 | 2865 | if (align_ref != .none) { |
| 2765 | 2866 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref)); |
| 2766 | 2867 | } |
| 2868 | if (addrspace_ref != .none) { | |
| 2869 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(addrspace_ref)); | |
| 2870 | } | |
| 2767 | 2871 | if (bit_start_ref != .none) { |
| 2768 | 2872 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref)); |
| 2769 | 2873 | gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref)); |
| ... | ... | @@ -2779,6 +2883,7 @@ fn ptrType( |
| 2779 | 2883 | .is_volatile = ptr_info.volatile_token != null, |
| 2780 | 2884 | .has_sentinel = sentinel_ref != .none, |
| 2781 | 2885 | .has_align = align_ref != .none, |
| 2886 | .has_addrspace = addrspace_ref != .none, | |
| 2782 | 2887 | .has_bit_range = bit_start_ref != .none, |
| 2783 | 2888 | }, |
| 2784 | 2889 | .size = ptr_info.size, |
| ... | ... | @@ -2847,7 +2952,7 @@ const WipDecls = struct { |
| 2847 | 2952 | is_pub: bool, |
| 2848 | 2953 | is_export: bool, |
| 2849 | 2954 | has_align: bool, |
| 2850 | has_section: bool, | |
| 2955 | has_section_or_addrspace: bool, | |
| 2851 | 2956 | ) Allocator.Error!void { |
| 2852 | 2957 | if (wip_decls.decl_index % fields_per_u32 == 0 and wip_decls.decl_index != 0) { |
| 2853 | 2958 | try wip_decls.bit_bag.append(gpa, wip_decls.cur_bit_bag); |
| ... | ... | @@ -2857,7 +2962,7 @@ const WipDecls = struct { |
| 2857 | 2962 | (@as(u32, @boolToInt(is_pub)) << 28) | |
| 2858 | 2963 | (@as(u32, @boolToInt(is_export)) << 29) | |
| 2859 | 2964 | (@as(u32, @boolToInt(has_align)) << 30) | |
| 2860 | (@as(u32, @boolToInt(has_section)) << 31); | |
| 2965 | (@as(u32, @boolToInt(has_section_or_addrspace)) << 31); | |
| 2861 | 2966 | wip_decls.decl_index += 1; |
| 2862 | 2967 | } |
| 2863 | 2968 | |
| ... | ... | @@ -2922,7 +3027,8 @@ fn fnDecl( |
| 2922 | 3027 | const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false; |
| 2923 | 3028 | break :blk token_tags[maybe_inline_token] == .keyword_inline; |
| 2924 | 3029 | }; |
| 2925 | try wip_decls.next(gpa, is_pub, is_export, fn_proto.ast.align_expr != 0, fn_proto.ast.section_expr != 0); | |
| 3030 | const has_section_or_addrspace = fn_proto.ast.section_expr != 0 or fn_proto.ast.addrspace_expr != 0; | |
| 3031 | try wip_decls.next(gpa, is_pub, is_export, fn_proto.ast.align_expr != 0, has_section_or_addrspace); | |
| 2926 | 3032 | |
| 2927 | 3033 | var params_scope = &fn_gz.base; |
| 2928 | 3034 | const is_var_args = is_var_args: { |
| ... | ... | @@ -3011,6 +3117,9 @@ fn fnDecl( |
| 3011 | 3117 | const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { |
| 3012 | 3118 | break :inst try expr(&decl_gz, params_scope, align_rl, fn_proto.ast.align_expr); |
| 3013 | 3119 | }; |
| 3120 | const addrspace_inst: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: { | |
| 3121 | break :inst try expr(&decl_gz, params_scope, .{ .ty = .address_space_type }, fn_proto.ast.addrspace_expr); | |
| 3122 | }; | |
| 3014 | 3123 | const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: { |
| 3015 | 3124 | break :inst try comptimeExpr(&decl_gz, params_scope, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr); |
| 3016 | 3125 | }; |
| ... | ... | @@ -3112,7 +3221,7 @@ fn fnDecl( |
| 3112 | 3221 | _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst); |
| 3113 | 3222 | try decl_gz.setBlockBody(block_inst); |
| 3114 | 3223 | |
| 3115 | try wip_decls.payload.ensureUnusedCapacity(gpa, 9); | |
| 3224 | try wip_decls.payload.ensureUnusedCapacity(gpa, 10); | |
| 3116 | 3225 | { |
| 3117 | 3226 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); |
| 3118 | 3227 | const casted = @bitCast([4]u32, contents_hash); |
| ... | ... | @@ -3127,8 +3236,10 @@ fn fnDecl( |
| 3127 | 3236 | if (align_inst != .none) { |
| 3128 | 3237 | wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst)); |
| 3129 | 3238 | } |
| 3130 | if (section_inst != .none) { | |
| 3239 | ||
| 3240 | if (has_section_or_addrspace) { | |
| 3131 | 3241 | wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst)); |
| 3242 | wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst)); | |
| 3132 | 3243 | } |
| 3133 | 3244 | } |
| 3134 | 3245 | |
| ... | ... | @@ -3175,10 +3286,14 @@ fn globalVarDecl( |
| 3175 | 3286 | const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: { |
| 3176 | 3287 | break :inst try expr(&block_scope, &block_scope.base, align_rl, var_decl.ast.align_node); |
| 3177 | 3288 | }; |
| 3289 | const addrspace_inst: Zir.Inst.Ref = if (var_decl.ast.addrspace_node == 0) .none else inst: { | |
| 3290 | break :inst try expr(&block_scope, &block_scope.base, .{ .ty = .address_space_type }, var_decl.ast.addrspace_node); | |
| 3291 | }; | |
| 3178 | 3292 | const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: { |
| 3179 | 3293 | break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .ty = .const_slice_u8_type }, var_decl.ast.section_node); |
| 3180 | 3294 | }; |
| 3181 | try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, section_inst != .none); | |
| 3295 | const has_section_or_addrspace = section_inst != .none or addrspace_inst != .none; | |
| 3296 | try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, has_section_or_addrspace); | |
| 3182 | 3297 | |
| 3183 | 3298 | const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: { |
| 3184 | 3299 | if (!is_mutable) { |
| ... | ... | @@ -3256,7 +3371,7 @@ fn globalVarDecl( |
| 3256 | 3371 | _ = try block_scope.addBreak(.break_inline, block_inst, var_inst); |
| 3257 | 3372 | try block_scope.setBlockBody(block_inst); |
| 3258 | 3373 | |
| 3259 | try wip_decls.payload.ensureUnusedCapacity(gpa, 9); | |
| 3374 | try wip_decls.payload.ensureUnusedCapacity(gpa, 10); | |
| 3260 | 3375 | { |
| 3261 | 3376 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(node)); |
| 3262 | 3377 | const casted = @bitCast([4]u32, contents_hash); |
| ... | ... | @@ -3271,8 +3386,9 @@ fn globalVarDecl( |
| 3271 | 3386 | if (align_inst != .none) { |
| 3272 | 3387 | wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst)); |
| 3273 | 3388 | } |
| 3274 | if (section_inst != .none) { | |
| 3389 | if (has_section_or_addrspace) { | |
| 3275 | 3390 | wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst)); |
| 3391 | wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst)); | |
| 3276 | 3392 | } |
| 3277 | 3393 | } |
| 3278 | 3394 | |
| ... | ... | @@ -3474,8 +3590,9 @@ fn structDeclInner( |
| 3474 | 3590 | container_decl: Ast.full.ContainerDecl, |
| 3475 | 3591 | layout: std.builtin.TypeInfo.ContainerLayout, |
| 3476 | 3592 | ) InnerError!Zir.Inst.Ref { |
| 3593 | const decl_inst = try gz.reserveInstructionIndex(); | |
| 3594 | ||
| 3477 | 3595 | if (container_decl.ast.members.len == 0) { |
| 3478 | const decl_inst = try gz.reserveInstructionIndex(); | |
| 3479 | 3596 | try gz.setStruct(decl_inst, .{ |
| 3480 | 3597 | .src_node = node, |
| 3481 | 3598 | .layout = layout, |
| ... | ... | @@ -3493,11 +3610,19 @@ fn structDeclInner( |
| 3493 | 3610 | const node_tags = tree.nodes.items(.tag); |
| 3494 | 3611 | const node_datas = tree.nodes.items(.data); |
| 3495 | 3612 | |
| 3613 | var namespace: Scope.Namespace = .{ | |
| 3614 | .parent = scope, | |
| 3615 | .node = node, | |
| 3616 | .inst = decl_inst, | |
| 3617 | .declaring_gz = gz, | |
| 3618 | }; | |
| 3619 | defer namespace.deinit(gpa); | |
| 3620 | ||
| 3496 | 3621 | // The struct_decl instruction introduces a scope in which the decls of the struct |
| 3497 | 3622 | // are in scope, so that field types, alignments, and default value expressions |
| 3498 | 3623 | // can refer to decls within the struct itself. |
| 3499 | 3624 | var block_scope: GenZir = .{ |
| 3500 | .parent = scope, | |
| 3625 | .parent = &namespace.base, | |
| 3501 | 3626 | .decl_node_index = node, |
| 3502 | 3627 | .decl_line = gz.calcLine(node), |
| 3503 | 3628 | .astgen = astgen, |
| ... | ... | @@ -3506,16 +3631,13 @@ fn structDeclInner( |
| 3506 | 3631 | }; |
| 3507 | 3632 | defer block_scope.instructions.deinit(gpa); |
| 3508 | 3633 | |
| 3509 | var namespace: Scope.Namespace = .{ .parent = scope, .node = node }; | |
| 3510 | defer namespace.decls.deinit(gpa); | |
| 3511 | ||
| 3512 | 3634 | try astgen.scanDecls(&namespace, container_decl.ast.members); |
| 3513 | 3635 | |
| 3514 | 3636 | var wip_decls: WipDecls = .{}; |
| 3515 | 3637 | defer wip_decls.deinit(gpa); |
| 3516 | 3638 | |
| 3517 | 3639 | // We don't know which members are fields until we iterate, so cannot do |
| 3518 | // an accurate ensureCapacity yet. | |
| 3640 | // an accurate ensureTotalCapacity yet. | |
| 3519 | 3641 | var fields_data = ArrayListUnmanaged(u32){}; |
| 3520 | 3642 | defer fields_data.deinit(gpa); |
| 3521 | 3643 | |
| ... | ... | @@ -3713,7 +3835,6 @@ fn structDeclInner( |
| 3713 | 3835 | } |
| 3714 | 3836 | } |
| 3715 | 3837 | |
| 3716 | const decl_inst = try gz.reserveInstructionIndex(); | |
| 3717 | 3838 | if (block_scope.instructions.items.len != 0) { |
| 3718 | 3839 | _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); |
| 3719 | 3840 | } |
| ... | ... | @@ -3727,11 +3848,18 @@ fn structDeclInner( |
| 3727 | 3848 | .known_has_bits = known_has_bits, |
| 3728 | 3849 | }); |
| 3729 | 3850 | |
| 3730 | try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len + | |
| 3731 | @boolToInt(field_index != 0) + fields_data.items.len + | |
| 3851 | // zig fmt: off | |
| 3852 | try astgen.extra.ensureUnusedCapacity(gpa, | |
| 3853 | bit_bag.items.len + | |
| 3854 | @boolToInt(wip_decls.decl_index != 0) + | |
| 3855 | wip_decls.payload.items.len + | |
| 3732 | 3856 | block_scope.instructions.items.len + |
| 3733 | wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) + | |
| 3734 | wip_decls.payload.items.len); | |
| 3857 | wip_decls.bit_bag.items.len + | |
| 3858 | @boolToInt(field_index != 0) + | |
| 3859 | fields_data.items.len | |
| 3860 | ); | |
| 3861 | // zig fmt: on | |
| 3862 | ||
| 3735 | 3863 | astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. |
| 3736 | 3864 | if (wip_decls.decl_index != 0) { |
| 3737 | 3865 | astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); |
| ... | ... | @@ -3758,17 +3886,27 @@ fn unionDeclInner( |
| 3758 | 3886 | arg_node: Ast.Node.Index, |
| 3759 | 3887 | have_auto_enum: bool, |
| 3760 | 3888 | ) InnerError!Zir.Inst.Ref { |
| 3889 | const decl_inst = try gz.reserveInstructionIndex(); | |
| 3890 | ||
| 3761 | 3891 | const astgen = gz.astgen; |
| 3762 | 3892 | const gpa = astgen.gpa; |
| 3763 | 3893 | const tree = astgen.tree; |
| 3764 | 3894 | const node_tags = tree.nodes.items(.tag); |
| 3765 | 3895 | const node_datas = tree.nodes.items(.data); |
| 3766 | 3896 | |
| 3897 | var namespace: Scope.Namespace = .{ | |
| 3898 | .parent = scope, | |
| 3899 | .node = node, | |
| 3900 | .inst = decl_inst, | |
| 3901 | .declaring_gz = gz, | |
| 3902 | }; | |
| 3903 | defer namespace.deinit(gpa); | |
| 3904 | ||
| 3767 | 3905 | // The union_decl instruction introduces a scope in which the decls of the union |
| 3768 | 3906 | // are in scope, so that field types, alignments, and default value expressions |
| 3769 | 3907 | // can refer to decls within the union itself. |
| 3770 | 3908 | var block_scope: GenZir = .{ |
| 3771 | .parent = scope, | |
| 3909 | .parent = &namespace.base, | |
| 3772 | 3910 | .decl_node_index = node, |
| 3773 | 3911 | .decl_line = gz.calcLine(node), |
| 3774 | 3912 | .astgen = astgen, |
| ... | ... | @@ -3777,13 +3915,10 @@ fn unionDeclInner( |
| 3777 | 3915 | }; |
| 3778 | 3916 | defer block_scope.instructions.deinit(gpa); |
| 3779 | 3917 | |
| 3780 | var namespace: Scope.Namespace = .{ .parent = scope, .node = node }; | |
| 3781 | defer namespace.decls.deinit(gpa); | |
| 3782 | ||
| 3783 | 3918 | try astgen.scanDecls(&namespace, members); |
| 3784 | 3919 | |
| 3785 | 3920 | const arg_inst: Zir.Inst.Ref = if (arg_node != 0) |
| 3786 | try typeExpr(gz, &namespace.base, arg_node) | |
| 3921 | try typeExpr(&block_scope, &namespace.base, arg_node) | |
| 3787 | 3922 | else |
| 3788 | 3923 | .none; |
| 3789 | 3924 | |
| ... | ... | @@ -3791,7 +3926,7 @@ fn unionDeclInner( |
| 3791 | 3926 | defer wip_decls.deinit(gpa); |
| 3792 | 3927 | |
| 3793 | 3928 | // We don't know which members are fields until we iterate, so cannot do |
| 3794 | // an accurate ensureCapacity yet. | |
| 3929 | // an accurate ensureTotalCapacity yet. | |
| 3795 | 3930 | var fields_data = ArrayListUnmanaged(u32){}; |
| 3796 | 3931 | defer fields_data.deinit(gpa); |
| 3797 | 3932 | |
| ... | ... | @@ -3996,7 +4131,6 @@ fn unionDeclInner( |
| 3996 | 4131 | } |
| 3997 | 4132 | } |
| 3998 | 4133 | |
| 3999 | const decl_inst = try gz.reserveInstructionIndex(); | |
| 4000 | 4134 | if (block_scope.instructions.items.len != 0) { |
| 4001 | 4135 | _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); |
| 4002 | 4136 | } |
| ... | ... | @@ -4011,11 +4145,18 @@ fn unionDeclInner( |
| 4011 | 4145 | .auto_enum_tag = have_auto_enum, |
| 4012 | 4146 | }); |
| 4013 | 4147 | |
| 4014 | try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len + | |
| 4015 | 1 + fields_data.items.len + | |
| 4148 | // zig fmt: off | |
| 4149 | try astgen.extra.ensureUnusedCapacity(gpa, | |
| 4150 | bit_bag.items.len + | |
| 4151 | @boolToInt(wip_decls.decl_index != 0) + | |
| 4152 | wip_decls.payload.items.len + | |
| 4016 | 4153 | block_scope.instructions.items.len + |
| 4017 | wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) + | |
| 4018 | wip_decls.payload.items.len); | |
| 4154 | wip_decls.bit_bag.items.len + | |
| 4155 | 1 + // cur_bit_bag | |
| 4156 | fields_data.items.len | |
| 4157 | ); | |
| 4158 | // zig fmt: on | |
| 4159 | ||
| 4019 | 4160 | astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. |
| 4020 | 4161 | if (wip_decls.decl_index != 0) { |
| 4021 | 4162 | astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); |
| ... | ... | @@ -4178,10 +4319,20 @@ fn containerDecl( |
| 4178 | 4319 | // how structs are handled above. |
| 4179 | 4320 | const nonexhaustive = counts.nonexhaustive_node != 0; |
| 4180 | 4321 | |
| 4322 | const decl_inst = try gz.reserveInstructionIndex(); | |
| 4323 | ||
| 4324 | var namespace: Scope.Namespace = .{ | |
| 4325 | .parent = scope, | |
| 4326 | .node = node, | |
| 4327 | .inst = decl_inst, | |
| 4328 | .declaring_gz = gz, | |
| 4329 | }; | |
| 4330 | defer namespace.deinit(gpa); | |
| 4331 | ||
| 4181 | 4332 | // The enum_decl instruction introduces a scope in which the decls of the enum |
| 4182 | 4333 | // are in scope, so that tag values can refer to decls within the enum itself. |
| 4183 | 4334 | var block_scope: GenZir = .{ |
| 4184 | .parent = scope, | |
| 4335 | .parent = &namespace.base, | |
| 4185 | 4336 | .decl_node_index = node, |
| 4186 | 4337 | .decl_line = gz.calcLine(node), |
| 4187 | 4338 | .astgen = astgen, |
| ... | ... | @@ -4190,13 +4341,10 @@ fn containerDecl( |
| 4190 | 4341 | }; |
| 4191 | 4342 | defer block_scope.instructions.deinit(gpa); |
| 4192 | 4343 | |
| 4193 | var namespace: Scope.Namespace = .{ .parent = scope, .node = node }; | |
| 4194 | defer namespace.decls.deinit(gpa); | |
| 4195 | ||
| 4196 | 4344 | try astgen.scanDecls(&namespace, container_decl.ast.members); |
| 4197 | 4345 | |
| 4198 | 4346 | const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0) |
| 4199 | try comptimeExpr(gz, &namespace.base, .{ .ty = .type_type }, container_decl.ast.arg) | |
| 4347 | try comptimeExpr(&block_scope, &namespace.base, .{ .ty = .type_type }, container_decl.ast.arg) | |
| 4200 | 4348 | else |
| 4201 | 4349 | .none; |
| 4202 | 4350 | |
| ... | ... | @@ -4391,7 +4539,6 @@ fn containerDecl( |
| 4391 | 4539 | } |
| 4392 | 4540 | } |
| 4393 | 4541 | |
| 4394 | const decl_inst = try gz.reserveInstructionIndex(); | |
| 4395 | 4542 | if (block_scope.instructions.items.len != 0) { |
| 4396 | 4543 | _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); |
| 4397 | 4544 | } |
| ... | ... | @@ -4405,11 +4552,18 @@ fn containerDecl( |
| 4405 | 4552 | .decls_len = @intCast(u32, wip_decls.decl_index), |
| 4406 | 4553 | }); |
| 4407 | 4554 | |
| 4408 | try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len + | |
| 4409 | 1 + fields_data.items.len + | |
| 4555 | // zig fmt: off | |
| 4556 | try astgen.extra.ensureUnusedCapacity(gpa, | |
| 4557 | bit_bag.items.len + | |
| 4558 | @boolToInt(wip_decls.decl_index != 0) + | |
| 4559 | wip_decls.payload.items.len + | |
| 4410 | 4560 | block_scope.instructions.items.len + |
| 4411 | wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) + | |
| 4412 | wip_decls.payload.items.len); | |
| 4561 | wip_decls.bit_bag.items.len + | |
| 4562 | 1 + // cur_bit_bag | |
| 4563 | fields_data.items.len | |
| 4564 | ); | |
| 4565 | // zig fmt: on | |
| 4566 | ||
| 4413 | 4567 | astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. |
| 4414 | 4568 | if (wip_decls.decl_index != 0) { |
| 4415 | 4569 | astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); |
| ... | ... | @@ -4426,8 +4580,15 @@ fn containerDecl( |
| 4426 | 4580 | .keyword_opaque => { |
| 4427 | 4581 | assert(container_decl.ast.arg == 0); |
| 4428 | 4582 | |
| 4429 | var namespace: Scope.Namespace = .{ .parent = scope, .node = node }; | |
| 4430 | defer namespace.decls.deinit(gpa); | |
| 4583 | const decl_inst = try gz.reserveInstructionIndex(); | |
| 4584 | ||
| 4585 | var namespace: Scope.Namespace = .{ | |
| 4586 | .parent = scope, | |
| 4587 | .node = node, | |
| 4588 | .inst = decl_inst, | |
| 4589 | .declaring_gz = gz, | |
| 4590 | }; | |
| 4591 | defer namespace.deinit(gpa); | |
| 4431 | 4592 | |
| 4432 | 4593 | try astgen.scanDecls(&namespace, container_decl.ast.members); |
| 4433 | 4594 | |
| ... | ... | @@ -4565,21 +4726,20 @@ fn containerDecl( |
| 4565 | 4726 | wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field); |
| 4566 | 4727 | } |
| 4567 | 4728 | } |
| 4568 | const tag: Zir.Inst.Tag = switch (gz.anon_name_strategy) { | |
| 4569 | .parent => .opaque_decl, | |
| 4570 | .anon => .opaque_decl_anon, | |
| 4571 | .func => .opaque_decl_func, | |
| 4572 | }; | |
| 4573 | const decl_inst = try gz.addBlock(tag, node); | |
| 4574 | try gz.instructions.append(gpa, decl_inst); | |
| 4575 | 4729 | |
| 4576 | try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).Struct.fields.len + | |
| 4577 | wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) + | |
| 4578 | wip_decls.payload.items.len); | |
| 4579 | const zir_datas = astgen.instructions.items(.data); | |
| 4580 | zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{ | |
| 4730 | try gz.setOpaque(decl_inst, .{ | |
| 4731 | .src_node = node, | |
| 4581 | 4732 | .decls_len = @intCast(u32, wip_decls.decl_index), |
| 4582 | 4733 | }); |
| 4734 | ||
| 4735 | // zig fmt: off | |
| 4736 | try astgen.extra.ensureUnusedCapacity(gpa, | |
| 4737 | wip_decls.bit_bag.items.len + | |
| 4738 | @boolToInt(wip_decls.decl_index != 0) + | |
| 4739 | wip_decls.payload.items.len | |
| 4740 | ); | |
| 4741 | // zig fmt: on | |
| 4742 | ||
| 4583 | 4743 | astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. |
| 4584 | 4744 | if (wip_decls.decl_index != 0) { |
| 4585 | 4745 | astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); |
| ... | ... | @@ -4865,6 +5025,21 @@ fn fieldAccess( |
| 4865 | 5025 | scope: *Scope, |
| 4866 | 5026 | rl: ResultLoc, |
| 4867 | 5027 | node: Ast.Node.Index, |
| 5028 | ) InnerError!Zir.Inst.Ref { | |
| 5029 | if (rl == .ref) { | |
| 5030 | return addFieldAccess(.field_ptr, gz, scope, .ref, node); | |
| 5031 | } else { | |
| 5032 | const access = try addFieldAccess(.field_val, gz, scope, .none_or_ref, node); | |
| 5033 | return rvalue(gz, rl, access, node); | |
| 5034 | } | |
| 5035 | } | |
| 5036 | ||
| 5037 | fn addFieldAccess( | |
| 5038 | tag: Zir.Inst.Tag, | |
| 5039 | gz: *GenZir, | |
| 5040 | scope: *Scope, | |
| 5041 | lhs_rl: ResultLoc, | |
| 5042 | node: Ast.Node.Index, | |
| 4868 | 5043 | ) InnerError!Zir.Inst.Ref { |
| 4869 | 5044 | const astgen = gz.astgen; |
| 4870 | 5045 | const tree = astgen.tree; |
| ... | ... | @@ -4875,16 +5050,11 @@ fn fieldAccess( |
| 4875 | 5050 | const dot_token = main_tokens[node]; |
| 4876 | 5051 | const field_ident = dot_token + 1; |
| 4877 | 5052 | const str_index = try astgen.identAsString(field_ident); |
| 4878 | switch (rl) { | |
| 4879 | .ref => return gz.addPlNode(.field_ptr, node, Zir.Inst.Field{ | |
| 4880 | .lhs = try expr(gz, scope, .ref, object_node), | |
| 4881 | .field_name_start = str_index, | |
| 4882 | }), | |
| 4883 | else => return rvalue(gz, rl, try gz.addPlNode(.field_val, node, Zir.Inst.Field{ | |
| 4884 | .lhs = try expr(gz, scope, .none_or_ref, object_node), | |
| 4885 | .field_name_start = str_index, | |
| 4886 | }), node), | |
| 4887 | } | |
| 5053 | ||
| 5054 | return gz.addPlNode(tag, node, Zir.Inst.Field{ | |
| 5055 | .lhs = try expr(gz, scope, lhs_rl, object_node), | |
| 5056 | .field_name_start = str_index, | |
| 5057 | }); | |
| 4888 | 5058 | } |
| 4889 | 5059 | |
| 4890 | 5060 | fn arrayAccess( |
| ... | ... | @@ -6320,6 +6490,7 @@ fn identifier( |
| 6320 | 6490 | |
| 6321 | 6491 | const astgen = gz.astgen; |
| 6322 | 6492 | const tree = astgen.tree; |
| 6493 | const gpa = astgen.gpa; | |
| 6323 | 6494 | const main_tokens = tree.nodes.items(.main_token); |
| 6324 | 6495 | |
| 6325 | 6496 | const ident_token = main_tokens[ident]; |
| ... | ... | @@ -6366,16 +6537,28 @@ fn identifier( |
| 6366 | 6537 | const name_str_index = try astgen.identAsString(ident_token); |
| 6367 | 6538 | var s = scope; |
| 6368 | 6539 | var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already |
| 6369 | var hit_namespace: Ast.Node.Index = 0; | |
| 6540 | var num_namespaces_out: u32 = 0; | |
| 6541 | var capturing_namespace: ?*Scope.Namespace = null; | |
| 6370 | 6542 | while (true) switch (s.tag) { |
| 6371 | 6543 | .local_val => { |
| 6372 | 6544 | const local_val = s.cast(Scope.LocalVal).?; |
| 6373 | 6545 | |
| 6374 | 6546 | if (local_val.name == name_str_index) { |
| 6375 | local_val.used = true; | |
| 6376 | 6547 | // Locals cannot shadow anything, so we do not need to look for ambiguous |
| 6377 | 6548 | // references in this case. |
| 6378 | return rvalue(gz, rl, local_val.inst, ident); | |
| 6549 | local_val.used = true; | |
| 6550 | ||
| 6551 | const value_inst = try tunnelThroughClosure( | |
| 6552 | gz, | |
| 6553 | ident, | |
| 6554 | num_namespaces_out, | |
| 6555 | capturing_namespace, | |
| 6556 | local_val.inst, | |
| 6557 | local_val.token_src, | |
| 6558 | gpa, | |
| 6559 | ); | |
| 6560 | ||
| 6561 | return rvalue(gz, rl, value_inst, ident); | |
| 6379 | 6562 | } |
| 6380 | 6563 | s = local_val.parent; |
| 6381 | 6564 | }, |
| ... | ... | @@ -6383,16 +6566,29 @@ fn identifier( |
| 6383 | 6566 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 6384 | 6567 | if (local_ptr.name == name_str_index) { |
| 6385 | 6568 | local_ptr.used = true; |
| 6386 | if (hit_namespace != 0 and !local_ptr.maybe_comptime) { | |
| 6569 | ||
| 6570 | // Can't close over a runtime variable | |
| 6571 | if (num_namespaces_out != 0 and !local_ptr.maybe_comptime) { | |
| 6387 | 6572 | return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{ |
| 6388 | 6573 | try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}), |
| 6389 | try astgen.errNoteNode(hit_namespace, "crosses namespace boundary here", .{}), | |
| 6574 | try astgen.errNoteNode(capturing_namespace.?.node, "crosses namespace boundary here", .{}), | |
| 6390 | 6575 | }); |
| 6391 | 6576 | } |
| 6577 | ||
| 6578 | const ptr_inst = try tunnelThroughClosure( | |
| 6579 | gz, | |
| 6580 | ident, | |
| 6581 | num_namespaces_out, | |
| 6582 | capturing_namespace, | |
| 6583 | local_ptr.ptr, | |
| 6584 | local_ptr.token_src, | |
| 6585 | gpa, | |
| 6586 | ); | |
| 6587 | ||
| 6392 | 6588 | switch (rl) { |
| 6393 | .ref, .none_or_ref => return local_ptr.ptr, | |
| 6589 | .ref, .none_or_ref => return ptr_inst, | |
| 6394 | 6590 | else => { |
| 6395 | const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident); | |
| 6591 | const loaded = try gz.addUnNode(.load, ptr_inst, ident); | |
| 6396 | 6592 | return rvalue(gz, rl, loaded, ident); |
| 6397 | 6593 | }, |
| 6398 | 6594 | } |
| ... | ... | @@ -6413,7 +6609,8 @@ fn identifier( |
| 6413 | 6609 | // We found a match but must continue looking for ambiguous references to decls. |
| 6414 | 6610 | found_already = i; |
| 6415 | 6611 | } |
| 6416 | hit_namespace = ns.node; | |
| 6612 | num_namespaces_out += 1; | |
| 6613 | capturing_namespace = ns; | |
| 6417 | 6614 | s = ns.parent; |
| 6418 | 6615 | }, |
| 6419 | 6616 | .top => break, |
| ... | ... | @@ -6433,6 +6630,37 @@ fn identifier( |
| 6433 | 6630 | } |
| 6434 | 6631 | } |
| 6435 | 6632 | |
| 6633 | /// Adds a capture to a namespace, if needed. | |
| 6634 | /// Returns the index of the closure_capture instruction. | |
| 6635 | fn tunnelThroughClosure( | |
| 6636 | gz: *GenZir, | |
| 6637 | inner_ref_node: Ast.Node.Index, | |
| 6638 | num_tunnels: u32, | |
| 6639 | ns: ?*Scope.Namespace, | |
| 6640 | value: Zir.Inst.Ref, | |
| 6641 | token: Ast.TokenIndex, | |
| 6642 | gpa: *Allocator, | |
| 6643 | ) !Zir.Inst.Ref { | |
| 6644 | // For trivial values, we don't need a tunnel. | |
| 6645 | // Just return the ref. | |
| 6646 | if (num_tunnels == 0 or refToIndex(value) == null) { | |
| 6647 | return value; | |
| 6648 | } | |
| 6649 | ||
| 6650 | // Otherwise we need a tunnel. Check if this namespace | |
| 6651 | // already has one for this value. | |
| 6652 | const gop = try ns.?.captures.getOrPut(gpa, refToIndex(value).?); | |
| 6653 | if (!gop.found_existing) { | |
| 6654 | // Make a new capture for this value | |
| 6655 | const capture_ref = try ns.?.declaring_gz.?.addUnTok(.closure_capture, value, token); | |
| 6656 | gop.value_ptr.* = refToIndex(capture_ref).?; | |
| 6657 | } | |
| 6658 | ||
| 6659 | // Add an instruction to get the value from the closure into | |
| 6660 | // our current context | |
| 6661 | return try gz.addInstNode(.closure_get, gop.value_ptr.*, inner_ref_node); | |
| 6662 | } | |
| 6663 | ||
| 6436 | 6664 | fn stringLiteral( |
| 6437 | 6665 | gz: *GenZir, |
| 6438 | 6666 | rl: ResultLoc, |
| ... | ... | @@ -6866,35 +7094,13 @@ fn asRlPtr( |
| 6866 | 7094 | operand_node: Ast.Node.Index, |
| 6867 | 7095 | dest_type: Zir.Inst.Ref, |
| 6868 | 7096 | ) InnerError!Zir.Inst.Ref { |
| 6869 | // Detect whether this expr() call goes into rvalue() to store the result into the | |
| 6870 | // result location. If it does, elide the coerce_result_ptr instruction | |
| 6871 | // as well as the store instruction, instead passing the result as an rvalue. | |
| 6872 | 7097 | const astgen = parent_gz.astgen; |
| 6873 | 7098 | |
| 6874 | var as_scope = parent_gz.makeSubBlock(scope); | |
| 7099 | var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr); | |
| 6875 | 7100 | defer as_scope.instructions.deinit(astgen.gpa); |
| 6876 | 7101 | |
| 6877 | as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); | |
| 6878 | 7102 | const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node); |
| 6879 | const parent_zir = &parent_gz.instructions; | |
| 6880 | if (as_scope.rvalue_rl_count == 1) { | |
| 6881 | // Busted! This expression didn't actually need a pointer. | |
| 6882 | const zir_tags = astgen.instructions.items(.tag); | |
| 6883 | const zir_datas = astgen.instructions.items(.data); | |
| 6884 | try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len); | |
| 6885 | for (as_scope.instructions.items) |src_inst| { | |
| 6886 | if (indexToRef(src_inst) == as_scope.rl_ptr) continue; | |
| 6887 | if (zir_tags[src_inst] == .store_to_block_ptr) { | |
| 6888 | if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; | |
| 6889 | } | |
| 6890 | parent_zir.appendAssumeCapacity(src_inst); | |
| 6891 | } | |
| 6892 | const casted_result = try parent_gz.addBin(.as, dest_type, result); | |
| 6893 | return rvalue(parent_gz, rl, casted_result, operand_node); | |
| 6894 | } else { | |
| 6895 | try parent_zir.appendSlice(astgen.gpa, as_scope.instructions.items); | |
| 6896 | return result; | |
| 6897 | } | |
| 7103 | return as_scope.finishCoercion(parent_gz, rl, operand_node, result, dest_type); | |
| 6898 | 7104 | } |
| 6899 | 7105 | |
| 6900 | 7106 | fn bitCast( |
| ... | ... | @@ -7030,16 +7236,15 @@ fn builtinCall( |
| 7030 | 7236 | return rvalue(gz, rl, result, node); |
| 7031 | 7237 | }, |
| 7032 | 7238 | .field => { |
| 7033 | const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); | |
| 7034 | 7239 | if (rl == .ref) { |
| 7035 | 7240 | return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ |
| 7036 | 7241 | .lhs = try expr(gz, scope, .ref, params[0]), |
| 7037 | .field_name = field_name, | |
| 7242 | .field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]), | |
| 7038 | 7243 | }); |
| 7039 | 7244 | } |
| 7040 | 7245 | const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{ |
| 7041 | 7246 | .lhs = try expr(gz, scope, .none, params[0]), |
| 7042 | .field_name = field_name, | |
| 7247 | .field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]), | |
| 7043 | 7248 | }); |
| 7044 | 7249 | return rvalue(gz, rl, result, node); |
| 7045 | 7250 | }, |
| ... | ... | @@ -7047,7 +7252,7 @@ fn builtinCall( |
| 7047 | 7252 | .bit_cast => return bitCast( gz, scope, rl, node, params[0], params[1]), |
| 7048 | 7253 | .TypeOf => return typeOf( gz, scope, rl, node, params), |
| 7049 | 7254 | .union_init => return unionInit(gz, scope, rl, node, params), |
| 7050 | .c_import => return cImport( gz, scope, rl, node, params[0]), | |
| 7255 | .c_import => return cImport( gz, scope, node, params[0]), | |
| 7051 | 7256 | |
| 7052 | 7257 | .@"export" => { |
| 7053 | 7258 | const node_tags = tree.nodes.items(.tag); |
| ... | ... | @@ -7060,32 +7265,55 @@ fn builtinCall( |
| 7060 | 7265 | .identifier => { |
| 7061 | 7266 | const ident_token = main_tokens[params[0]]; |
| 7062 | 7267 | decl_name = try astgen.identAsString(ident_token); |
| 7063 | { | |
| 7064 | var s = scope; | |
| 7065 | while (true) switch (s.tag) { | |
| 7066 | .local_val => { | |
| 7067 | const local_val = s.cast(Scope.LocalVal).?; | |
| 7068 | if (local_val.name == decl_name) { | |
| 7069 | local_val.used = true; | |
| 7070 | break; | |
| 7071 | } | |
| 7072 | s = local_val.parent; | |
| 7073 | }, | |
| 7074 | .local_ptr => { | |
| 7075 | const local_ptr = s.cast(Scope.LocalPtr).?; | |
| 7076 | if (local_ptr.name == decl_name) { | |
| 7077 | if (!local_ptr.maybe_comptime) | |
| 7078 | return astgen.failNode(params[0], "unable to export runtime-known value", .{}); | |
| 7079 | local_ptr.used = true; | |
| 7080 | break; | |
| 7268 | ||
| 7269 | var s = scope; | |
| 7270 | var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already | |
| 7271 | while (true) switch (s.tag) { | |
| 7272 | .local_val => { | |
| 7273 | const local_val = s.cast(Scope.LocalVal).?; | |
| 7274 | if (local_val.name == decl_name) { | |
| 7275 | local_val.used = true; | |
| 7276 | _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{ | |
| 7277 | .operand = local_val.inst, | |
| 7278 | .options = try comptimeExpr(gz, scope, .{ .coerced_ty = .export_options_type }, params[1]), | |
| 7279 | }); | |
| 7280 | return rvalue(gz, rl, .void_value, node); | |
| 7281 | } | |
| 7282 | s = local_val.parent; | |
| 7283 | }, | |
| 7284 | .local_ptr => { | |
| 7285 | const local_ptr = s.cast(Scope.LocalPtr).?; | |
| 7286 | if (local_ptr.name == decl_name) { | |
| 7287 | if (!local_ptr.maybe_comptime) | |
| 7288 | return astgen.failNode(params[0], "unable to export runtime-known value", .{}); | |
| 7289 | local_ptr.used = true; | |
| 7290 | const loaded = try gz.addUnNode(.load, local_ptr.ptr, node); | |
| 7291 | _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{ | |
| 7292 | .operand = loaded, | |
| 7293 | .options = try comptimeExpr(gz, scope, .{ .coerced_ty = .export_options_type }, params[1]), | |
| 7294 | }); | |
| 7295 | return rvalue(gz, rl, .void_value, node); | |
| 7296 | } | |
| 7297 | s = local_ptr.parent; | |
| 7298 | }, | |
| 7299 | .gen_zir => s = s.cast(GenZir).?.parent, | |
| 7300 | .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, | |
| 7301 | .namespace => { | |
| 7302 | const ns = s.cast(Scope.Namespace).?; | |
| 7303 | if (ns.decls.get(decl_name)) |i| { | |
| 7304 | if (found_already) |f| { | |
| 7305 | return astgen.failNodeNotes(node, "ambiguous reference", .{}, &.{ | |
| 7306 | try astgen.errNoteNode(f, "declared here", .{}), | |
| 7307 | try astgen.errNoteNode(i, "also declared here", .{}), | |
| 7308 | }); | |
| 7081 | 7309 | } |
| 7082 | s = local_ptr.parent; | |
| 7083 | }, | |
| 7084 | .gen_zir => s = s.cast(GenZir).?.parent, | |
| 7085 | .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, | |
| 7086 | .namespace, .top => break, | |
| 7087 | }; | |
| 7088 | } | |
| 7310 | // We found a match but must continue looking for ambiguous references to decls. | |
| 7311 | found_already = i; | |
| 7312 | } | |
| 7313 | s = ns.parent; | |
| 7314 | }, | |
| 7315 | .top => break, | |
| 7316 | }; | |
| 7089 | 7317 | }, |
| 7090 | 7318 | .field_access => { |
| 7091 | 7319 | const namespace_node = node_datas[params[0]].lhs; |
| ... | ... | @@ -7116,9 +7344,13 @@ fn builtinCall( |
| 7116 | 7344 | }); |
| 7117 | 7345 | return rvalue(gz, rl, result, node); |
| 7118 | 7346 | }, |
| 7347 | .fence => { | |
| 7348 | const order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[0]); | |
| 7349 | const result = try gz.addUnNode(.fence, order, node); | |
| 7350 | return rvalue(gz, rl, result, node); | |
| 7351 | }, | |
| 7119 | 7352 | |
| 7120 | 7353 | .breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint), |
| 7121 | .fence => return simpleNoOpVoid(gz, rl, node, .fence), | |
| 7122 | 7354 | |
| 7123 | 7355 | .This => return rvalue(gz, rl, try gz.addNodeExtended(.this, node), node), |
| 7124 | 7356 | .return_address => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr, node), node), |
| ... | ... | @@ -7230,6 +7462,7 @@ fn builtinCall( |
| 7230 | 7462 | return rvalue(gz, rl, result, node); |
| 7231 | 7463 | }, |
| 7232 | 7464 | .c_define => { |
| 7465 | if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{}); | |
| 7233 | 7466 | const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[0]); |
| 7234 | 7467 | const value = try comptimeExpr(gz, scope, .none, params[1]); |
| 7235 | 7468 | const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{ |
| ... | ... | @@ -7305,13 +7538,9 @@ fn builtinCall( |
| 7305 | 7538 | return rvalue(gz, rl, result, node); |
| 7306 | 7539 | }, |
| 7307 | 7540 | |
| 7308 | .add_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .add_with_saturation), | |
| 7309 | .sub_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .sub_with_saturation), | |
| 7310 | .mul_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .mul_with_saturation), | |
| 7311 | .shl_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .shl_with_saturation), | |
| 7312 | ||
| 7313 | 7541 | .atomic_load => { |
| 7314 | 7542 | const int_type = try typeExpr(gz, scope, params[0]); |
| 7543 | // TODO allow this pointer type to be volatile | |
| 7315 | 7544 | const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ |
| 7316 | 7545 | .ptr_type_simple = .{ |
| 7317 | 7546 | .is_allowzero = false, |
| ... | ... | @@ -7321,16 +7550,17 @@ fn builtinCall( |
| 7321 | 7550 | .elem_type = int_type, |
| 7322 | 7551 | }, |
| 7323 | 7552 | } }); |
| 7324 | const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[1]); | |
| 7325 | const ordering = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[2]); | |
| 7326 | 7553 | const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.Bin{ |
| 7327 | .lhs = ptr, | |
| 7328 | .rhs = ordering, | |
| 7554 | // zig fmt: off | |
| 7555 | .lhs = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), | |
| 7556 | .rhs = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]), | |
| 7557 | // zig fmt: on | |
| 7329 | 7558 | }); |
| 7330 | 7559 | return rvalue(gz, rl, result, node); |
| 7331 | 7560 | }, |
| 7332 | 7561 | .atomic_rmw => { |
| 7333 | 7562 | const int_type = try typeExpr(gz, scope, params[0]); |
| 7563 | // TODO allow this pointer type to be volatile | |
| 7334 | 7564 | const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ |
| 7335 | 7565 | .ptr_type_simple = .{ |
| 7336 | 7566 | .is_allowzero = false, |
| ... | ... | @@ -7340,20 +7570,19 @@ fn builtinCall( |
| 7340 | 7570 | .elem_type = int_type, |
| 7341 | 7571 | }, |
| 7342 | 7572 | } }); |
| 7343 | const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[1]); | |
| 7344 | const operation = try expr(gz, scope, .{ .ty = .atomic_rmw_op_type }, params[2]); | |
| 7345 | const operand = try expr(gz, scope, .{ .ty = int_type }, params[3]); | |
| 7346 | const ordering = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[4]); | |
| 7347 | 7573 | const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{ |
| 7348 | .ptr = ptr, | |
| 7349 | .operation = operation, | |
| 7350 | .operand = operand, | |
| 7351 | .ordering = ordering, | |
| 7574 | // zig fmt: off | |
| 7575 | .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), | |
| 7576 | .operation = try expr(gz, scope, .{ .coerced_ty = .atomic_rmw_op_type }, params[2]), | |
| 7577 | .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]), | |
| 7578 | .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]), | |
| 7579 | // zig fmt: on | |
| 7352 | 7580 | }); |
| 7353 | 7581 | return rvalue(gz, rl, result, node); |
| 7354 | 7582 | }, |
| 7355 | 7583 | .atomic_store => { |
| 7356 | 7584 | const int_type = try typeExpr(gz, scope, params[0]); |
| 7585 | // TODO allow this pointer type to be volatile | |
| 7357 | 7586 | const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ |
| 7358 | 7587 | .ptr_type_simple = .{ |
| 7359 | 7588 | .is_allowzero = false, |
| ... | ... | @@ -7363,13 +7592,12 @@ fn builtinCall( |
| 7363 | 7592 | .elem_type = int_type, |
| 7364 | 7593 | }, |
| 7365 | 7594 | } }); |
| 7366 | const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[1]); | |
| 7367 | const operand = try expr(gz, scope, .{ .ty = int_type }, params[2]); | |
| 7368 | const ordering = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[3]); | |
| 7369 | 7595 | const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ |
| 7370 | .ptr = ptr, | |
| 7371 | .operand = operand, | |
| 7372 | .ordering = ordering, | |
| 7596 | // zig fmt: off | |
| 7597 | .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), | |
| 7598 | .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]), | |
| 7599 | .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[3]), | |
| 7600 | // zig fmt: on | |
| 7373 | 7601 | }); |
| 7374 | 7602 | return rvalue(gz, rl, result, node); |
| 7375 | 7603 | }, |
| ... | ... | @@ -7387,7 +7615,7 @@ fn builtinCall( |
| 7387 | 7615 | }, |
| 7388 | 7616 | .call => { |
| 7389 | 7617 | const options = try comptimeExpr(gz, scope, .{ .ty = .call_options_type }, params[0]); |
| 7390 | const callee = try expr(gz, scope, .none, params[1]); | |
| 7618 | const callee = try calleeExpr(gz, scope, params[1]); | |
| 7391 | 7619 | const args = try expr(gz, scope, .none, params[2]); |
| 7392 | 7620 | const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{ |
| 7393 | 7621 | .options = options, |
| ... | ... | @@ -7409,17 +7637,17 @@ fn builtinCall( |
| 7409 | 7637 | }, |
| 7410 | 7638 | .memcpy => { |
| 7411 | 7639 | const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{ |
| 7412 | .dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]), | |
| 7413 | .source = try expr(gz, scope, .{ .ty = .manyptr_const_u8_type }, params[1]), | |
| 7414 | .byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]), | |
| 7640 | .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]), | |
| 7641 | .source = try expr(gz, scope, .{ .coerced_ty = .manyptr_const_u8_type }, params[1]), | |
| 7642 | .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]), | |
| 7415 | 7643 | }); |
| 7416 | 7644 | return rvalue(gz, rl, result, node); |
| 7417 | 7645 | }, |
| 7418 | 7646 | .memset => { |
| 7419 | 7647 | const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{ |
| 7420 | .dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]), | |
| 7421 | .byte = try expr(gz, scope, .{ .ty = .u8_type }, params[1]), | |
| 7422 | .byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]), | |
| 7648 | .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]), | |
| 7649 | .byte = try expr(gz, scope, .{ .coerced_ty = .u8_type }, params[1]), | |
| 7650 | .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]), | |
| 7423 | 7651 | }); |
| 7424 | 7652 | return rvalue(gz, rl, result, node); |
| 7425 | 7653 | }, |
| ... | ... | @@ -7452,12 +7680,11 @@ fn builtinCall( |
| 7452 | 7680 | }, |
| 7453 | 7681 | .Vector => { |
| 7454 | 7682 | const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{ |
| 7455 | .lhs = try comptimeExpr(gz, scope, .{.ty = .u32_type}, params[0]), | |
| 7683 | .lhs = try comptimeExpr(gz, scope, .{ .ty = .u32_type }, params[0]), | |
| 7456 | 7684 | .rhs = try typeExpr(gz, scope, params[1]), |
| 7457 | 7685 | }); |
| 7458 | 7686 | return rvalue(gz, rl, result, node); |
| 7459 | 7687 | }, |
| 7460 | ||
| 7461 | 7688 | } |
| 7462 | 7689 | // zig fmt: on |
| 7463 | 7690 | } |
| ... | ... | @@ -7603,6 +7830,8 @@ fn simpleCBuiltin( |
| 7603 | 7830 | operand_node: Ast.Node.Index, |
| 7604 | 7831 | tag: Zir.Inst.Extended, |
| 7605 | 7832 | ) InnerError!Zir.Inst.Ref { |
| 7833 | const name: []const u8 = if (tag == .c_undef) "C undef" else "C include"; | |
| 7834 | if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name}); | |
| 7606 | 7835 | const operand = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, operand_node); |
| 7607 | 7836 | _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{ |
| 7608 | 7837 | .node = gz.nodeIndexToRelative(node), |
| ... | ... | @@ -7651,7 +7880,6 @@ fn shiftOp( |
| 7651 | 7880 | fn cImport( |
| 7652 | 7881 | gz: *GenZir, |
| 7653 | 7882 | scope: *Scope, |
| 7654 | rl: ResultLoc, | |
| 7655 | 7883 | node: Ast.Node.Index, |
| 7656 | 7884 | body_node: Ast.Node.Index, |
| 7657 | 7885 | ) InnerError!Zir.Inst.Ref { |
| ... | ... | @@ -7660,6 +7888,7 @@ fn cImport( |
| 7660 | 7888 | |
| 7661 | 7889 | var block_scope = gz.makeSubBlock(scope); |
| 7662 | 7890 | block_scope.force_comptime = true; |
| 7891 | block_scope.c_import = true; | |
| 7663 | 7892 | defer block_scope.instructions.deinit(gpa); |
| 7664 | 7893 | |
| 7665 | 7894 | const block_inst = try gz.addBlock(.c_import, node); |
| ... | ... | @@ -7670,7 +7899,7 @@ fn cImport( |
| 7670 | 7899 | try block_scope.setBlockBody(block_inst); |
| 7671 | 7900 | try gz.instructions.append(gpa, block_inst); |
| 7672 | 7901 | |
| 7673 | return rvalue(gz, rl, .void_value, node); | |
| 7902 | return indexToRef(block_inst); | |
| 7674 | 7903 | } |
| 7675 | 7904 | |
| 7676 | 7905 | fn overflowArithmetic( |
| ... | ... | @@ -7703,24 +7932,6 @@ fn overflowArithmetic( |
| 7703 | 7932 | return rvalue(gz, rl, result, node); |
| 7704 | 7933 | } |
| 7705 | 7934 | |
| 7706 | fn saturatingArithmetic( | |
| 7707 | gz: *GenZir, | |
| 7708 | scope: *Scope, | |
| 7709 | rl: ResultLoc, | |
| 7710 | node: Ast.Node.Index, | |
| 7711 | params: []const Ast.Node.Index, | |
| 7712 | tag: Zir.Inst.Extended, | |
| 7713 | ) InnerError!Zir.Inst.Ref { | |
| 7714 | const lhs = try expr(gz, scope, .none, params[0]); | |
| 7715 | const rhs = try expr(gz, scope, .none, params[1]); | |
| 7716 | const result = try gz.addExtendedPayload(tag, Zir.Inst.SaturatingArithmetic{ | |
| 7717 | .node = gz.nodeIndexToRelative(node), | |
| 7718 | .lhs = lhs, | |
| 7719 | .rhs = rhs, | |
| 7720 | }); | |
| 7721 | return rvalue(gz, rl, result, node); | |
| 7722 | } | |
| 7723 | ||
| 7724 | 7935 | fn callExpr( |
| 7725 | 7936 | gz: *GenZir, |
| 7726 | 7937 | scope: *Scope, |
| ... | ... | @@ -7729,20 +7940,16 @@ fn callExpr( |
| 7729 | 7940 | call: Ast.full.Call, |
| 7730 | 7941 | ) InnerError!Zir.Inst.Ref { |
| 7731 | 7942 | const astgen = gz.astgen; |
| 7732 | const lhs = try expr(gz, scope, .none, call.ast.fn_expr); | |
| 7943 | ||
| 7944 | const callee = try calleeExpr(gz, scope, call.ast.fn_expr); | |
| 7733 | 7945 | |
| 7734 | 7946 | const args = try astgen.gpa.alloc(Zir.Inst.Ref, call.ast.params.len); |
| 7735 | 7947 | defer astgen.gpa.free(args); |
| 7736 | 7948 | |
| 7737 | 7949 | for (call.ast.params) |param_node, i| { |
| 7738 | const param_type = try gz.add(.{ | |
| 7739 | .tag = .param_type, | |
| 7740 | .data = .{ .param_type = .{ | |
| 7741 | .callee = lhs, | |
| 7742 | .param_index = @intCast(u32, i), | |
| 7743 | } }, | |
| 7744 | }); | |
| 7745 | args[i] = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node); | |
| 7950 | // Parameters are always temporary values, they have no | |
| 7951 | // meaningful result location. Sema will coerce them. | |
| 7952 | args[i] = try expr(gz, scope, .none, param_node); | |
| 7746 | 7953 | } |
| 7747 | 7954 | |
| 7748 | 7955 | const modifier: std.builtin.CallOptions.Modifier = blk: { |
| ... | ... | @@ -7757,20 +7964,72 @@ fn callExpr( |
| 7757 | 7964 | } |
| 7758 | 7965 | break :blk .auto; |
| 7759 | 7966 | }; |
| 7760 | const result: Zir.Inst.Ref = res: { | |
| 7761 | const tag: Zir.Inst.Tag = switch (modifier) { | |
| 7762 | .auto => .call, | |
| 7763 | .async_kw => .call_async, | |
| 7764 | .never_tail => unreachable, | |
| 7765 | .never_inline => unreachable, | |
| 7766 | .no_async => .call_nosuspend, | |
| 7767 | .always_tail => unreachable, | |
| 7768 | .always_inline => unreachable, | |
| 7769 | .compile_time => .call_compile_time, | |
| 7770 | }; | |
| 7771 | break :res try gz.addCall(tag, lhs, args, node); | |
| 7772 | }; | |
| 7773 | return rvalue(gz, rl, result, node); // TODO function call with result location | |
| 7967 | const call_inst = try gz.addCall(modifier, callee, args, node); | |
| 7968 | return rvalue(gz, rl, call_inst, node); // TODO function call with result location | |
| 7969 | } | |
| 7970 | ||
| 7971 | /// calleeExpr generates the function part of a call expression (f in f(x)), or the | |
| 7972 | /// callee argument to the @call() builtin. If the lhs is a field access or the | |
| 7973 | /// @field() builtin, we need to generate a special field_call_bind instruction | |
| 7974 | /// instead of the normal field_val or field_ptr. If this is a inst.func() call, | |
| 7975 | /// this instruction will capture the value of the first argument before evaluating | |
| 7976 | /// the other arguments. We need to use .ref here to guarantee we will be able to | |
| 7977 | /// promote an lvalue to an address if the first parameter requires it. This | |
| 7978 | /// unfortunately also means we need to take a reference to any types on the lhs. | |
| 7979 | fn calleeExpr( | |
| 7980 | gz: *GenZir, | |
| 7981 | scope: *Scope, | |
| 7982 | node: Ast.Node.Index, | |
| 7983 | ) InnerError!Zir.Inst.Ref { | |
| 7984 | const astgen = gz.astgen; | |
| 7985 | const tree = astgen.tree; | |
| 7986 | ||
| 7987 | const tag = tree.nodes.items(.tag)[node]; | |
| 7988 | switch (tag) { | |
| 7989 | .field_access => return addFieldAccess(.field_call_bind, gz, scope, .ref, node), | |
| 7990 | ||
| 7991 | .builtin_call_two, | |
| 7992 | .builtin_call_two_comma, | |
| 7993 | .builtin_call, | |
| 7994 | .builtin_call_comma, | |
| 7995 | => { | |
| 7996 | const node_datas = tree.nodes.items(.data); | |
| 7997 | const main_tokens = tree.nodes.items(.main_token); | |
| 7998 | const builtin_token = main_tokens[node]; | |
| 7999 | const builtin_name = tree.tokenSlice(builtin_token); | |
| 8000 | ||
| 8001 | var inline_params: [2]Ast.Node.Index = undefined; | |
| 8002 | var params: []Ast.Node.Index = switch (tag) { | |
| 8003 | .builtin_call, | |
| 8004 | .builtin_call_comma, | |
| 8005 | => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs], | |
| 8006 | ||
| 8007 | .builtin_call_two, | |
| 8008 | .builtin_call_two_comma, | |
| 8009 | => blk: { | |
| 8010 | inline_params = .{ node_datas[node].lhs, node_datas[node].rhs }; | |
| 8011 | const len: usize = if (inline_params[0] == 0) @as(usize, 0) else if (inline_params[1] == 0) @as(usize, 1) else @as(usize, 2); | |
| 8012 | break :blk inline_params[0..len]; | |
| 8013 | }, | |
| 8014 | ||
| 8015 | else => unreachable, | |
| 8016 | }; | |
| 8017 | ||
| 8018 | // If anything is wrong, fall back to builtinCall. | |
| 8019 | // It will emit any necessary compile errors and notes. | |
| 8020 | if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) { | |
| 8021 | const lhs = try expr(gz, scope, .ref, params[0]); | |
| 8022 | const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); | |
| 8023 | return gz.addPlNode(.field_call_bind_named, node, Zir.Inst.FieldNamed{ | |
| 8024 | .lhs = lhs, | |
| 8025 | .field_name = field_name, | |
| 8026 | }); | |
| 8027 | } | |
| 8028 | ||
| 8029 | return builtinCall(gz, scope, .none, node, params); | |
| 8030 | }, | |
| 8031 | else => return expr(gz, scope, .none, node), | |
| 8032 | } | |
| 7774 | 8033 | } |
| 7775 | 8034 | |
| 7776 | 8035 | pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{ |
| ... | ... | @@ -7892,27 +8151,33 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool |
| 7892 | 8151 | .asm_simple, |
| 7893 | 8152 | .add, |
| 7894 | 8153 | .add_wrap, |
| 8154 | .add_sat, | |
| 7895 | 8155 | .array_cat, |
| 7896 | 8156 | .array_mult, |
| 7897 | 8157 | .assign, |
| 7898 | 8158 | .assign_bit_and, |
| 7899 | 8159 | .assign_bit_or, |
| 7900 | .assign_bit_shift_left, | |
| 7901 | .assign_bit_shift_right, | |
| 8160 | .assign_shl, | |
| 8161 | .assign_shl_sat, | |
| 8162 | .assign_shr, | |
| 7902 | 8163 | .assign_bit_xor, |
| 7903 | 8164 | .assign_div, |
| 7904 | 8165 | .assign_sub, |
| 7905 | 8166 | .assign_sub_wrap, |
| 8167 | .assign_sub_sat, | |
| 7906 | 8168 | .assign_mod, |
| 7907 | 8169 | .assign_add, |
| 7908 | 8170 | .assign_add_wrap, |
| 8171 | .assign_add_sat, | |
| 7909 | 8172 | .assign_mul, |
| 7910 | 8173 | .assign_mul_wrap, |
| 8174 | .assign_mul_sat, | |
| 7911 | 8175 | .bang_equal, |
| 7912 | 8176 | .bit_and, |
| 7913 | 8177 | .bit_or, |
| 7914 | .bit_shift_left, | |
| 7915 | .bit_shift_right, | |
| 8178 | .shl, | |
| 8179 | .shl_sat, | |
| 8180 | .shr, | |
| 7916 | 8181 | .bit_xor, |
| 7917 | 8182 | .bool_and, |
| 7918 | 8183 | .bool_or, |
| ... | ... | @@ -7927,10 +8192,12 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool |
| 7927 | 8192 | .mod, |
| 7928 | 8193 | .mul, |
| 7929 | 8194 | .mul_wrap, |
| 8195 | .mul_sat, | |
| 7930 | 8196 | .switch_range, |
| 7931 | 8197 | .field_access, |
| 7932 | 8198 | .sub, |
| 7933 | 8199 | .sub_wrap, |
| 8200 | .sub_sat, | |
| 7934 | 8201 | .slice, |
| 7935 | 8202 | .slice_open, |
| 7936 | 8203 | .slice_sentinel, |
| ... | ... | @@ -8008,17 +8275,31 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool |
| 8008 | 8275 | } |
| 8009 | 8276 | }, |
| 8010 | 8277 | |
| 8011 | .builtin_call, | |
| 8012 | .builtin_call_comma, | |
| 8013 | .builtin_call_two, | |
| 8014 | .builtin_call_two_comma, | |
| 8015 | => { | |
| 8278 | .builtin_call_two, .builtin_call_two_comma => { | |
| 8279 | const builtin_token = main_tokens[node]; | |
| 8280 | const builtin_name = tree.tokenSlice(builtin_token); | |
| 8281 | // If the builtin is an invalid name, we don't cause an error here; instead | |
| 8282 | // let it pass, and the error will be "invalid builtin function" later. | |
| 8283 | const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false; | |
| 8284 | switch (builtin_info.needs_mem_loc) { | |
| 8285 | .never => return false, | |
| 8286 | .always => return true, | |
| 8287 | .forward1 => node = node_datas[node].rhs, | |
| 8288 | } | |
| 8289 | }, | |
| 8290 | ||
| 8291 | .builtin_call, .builtin_call_comma => { | |
| 8292 | const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; | |
| 8016 | 8293 | const builtin_token = main_tokens[node]; |
| 8017 | 8294 | const builtin_name = tree.tokenSlice(builtin_token); |
| 8018 | 8295 | // If the builtin is an invalid name, we don't cause an error here; instead |
| 8019 | 8296 | // let it pass, and the error will be "invalid builtin function" later. |
| 8020 | 8297 | const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false; |
| 8021 | return builtin_info.needs_mem_loc; | |
| 8298 | switch (builtin_info.needs_mem_loc) { | |
| 8299 | .never => return false, | |
| 8300 | .always => return true, | |
| 8301 | .forward1 => node = params[1], | |
| 8302 | } | |
| 8022 | 8303 | }, |
| 8023 | 8304 | } |
| 8024 | 8305 | } |
| ... | ... | @@ -8125,27 +8406,33 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never |
| 8125 | 8406 | .tagged_union_enum_tag_trailing, |
| 8126 | 8407 | .add, |
| 8127 | 8408 | .add_wrap, |
| 8409 | .add_sat, | |
| 8128 | 8410 | .array_cat, |
| 8129 | 8411 | .array_mult, |
| 8130 | 8412 | .assign, |
| 8131 | 8413 | .assign_bit_and, |
| 8132 | 8414 | .assign_bit_or, |
| 8133 | .assign_bit_shift_left, | |
| 8134 | .assign_bit_shift_right, | |
| 8415 | .assign_shl, | |
| 8416 | .assign_shl_sat, | |
| 8417 | .assign_shr, | |
| 8135 | 8418 | .assign_bit_xor, |
| 8136 | 8419 | .assign_div, |
| 8137 | 8420 | .assign_sub, |
| 8138 | 8421 | .assign_sub_wrap, |
| 8422 | .assign_sub_sat, | |
| 8139 | 8423 | .assign_mod, |
| 8140 | 8424 | .assign_add, |
| 8141 | 8425 | .assign_add_wrap, |
| 8426 | .assign_add_sat, | |
| 8142 | 8427 | .assign_mul, |
| 8143 | 8428 | .assign_mul_wrap, |
| 8429 | .assign_mul_sat, | |
| 8144 | 8430 | .bang_equal, |
| 8145 | 8431 | .bit_and, |
| 8146 | 8432 | .bit_or, |
| 8147 | .bit_shift_left, | |
| 8148 | .bit_shift_right, | |
| 8433 | .shl, | |
| 8434 | .shl_sat, | |
| 8435 | .shr, | |
| 8149 | 8436 | .bit_xor, |
| 8150 | 8437 | .bool_and, |
| 8151 | 8438 | .bool_or, |
| ... | ... | @@ -8160,9 +8447,11 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never |
| 8160 | 8447 | .mod, |
| 8161 | 8448 | .mul, |
| 8162 | 8449 | .mul_wrap, |
| 8450 | .mul_sat, | |
| 8163 | 8451 | .switch_range, |
| 8164 | 8452 | .sub, |
| 8165 | 8453 | .sub_wrap, |
| 8454 | .sub_sat, | |
| 8166 | 8455 | .slice, |
| 8167 | 8456 | .slice_open, |
| 8168 | 8457 | .slice_sentinel, |
| ... | ... | @@ -8297,27 +8586,33 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool { |
| 8297 | 8586 | .asm_simple, |
| 8298 | 8587 | .add, |
| 8299 | 8588 | .add_wrap, |
| 8589 | .add_sat, | |
| 8300 | 8590 | .array_cat, |
| 8301 | 8591 | .array_mult, |
| 8302 | 8592 | .assign, |
| 8303 | 8593 | .assign_bit_and, |
| 8304 | 8594 | .assign_bit_or, |
| 8305 | .assign_bit_shift_left, | |
| 8306 | .assign_bit_shift_right, | |
| 8595 | .assign_shl, | |
| 8596 | .assign_shl_sat, | |
| 8597 | .assign_shr, | |
| 8307 | 8598 | .assign_bit_xor, |
| 8308 | 8599 | .assign_div, |
| 8309 | 8600 | .assign_sub, |
| 8310 | 8601 | .assign_sub_wrap, |
| 8602 | .assign_sub_sat, | |
| 8311 | 8603 | .assign_mod, |
| 8312 | 8604 | .assign_add, |
| 8313 | 8605 | .assign_add_wrap, |
| 8606 | .assign_add_sat, | |
| 8314 | 8607 | .assign_mul, |
| 8315 | 8608 | .assign_mul_wrap, |
| 8609 | .assign_mul_sat, | |
| 8316 | 8610 | .bang_equal, |
| 8317 | 8611 | .bit_and, |
| 8318 | 8612 | .bit_or, |
| 8319 | .bit_shift_left, | |
| 8320 | .bit_shift_right, | |
| 8613 | .shl, | |
| 8614 | .shl_sat, | |
| 8615 | .shr, | |
| 8321 | 8616 | .bit_xor, |
| 8322 | 8617 | .bool_and, |
| 8323 | 8618 | .bool_or, |
| ... | ... | @@ -8332,10 +8627,12 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool { |
| 8332 | 8627 | .mod, |
| 8333 | 8628 | .mul, |
| 8334 | 8629 | .mul_wrap, |
| 8630 | .mul_sat, | |
| 8335 | 8631 | .switch_range, |
| 8336 | 8632 | .field_access, |
| 8337 | 8633 | .sub, |
| 8338 | 8634 | .sub_wrap, |
| 8635 | .sub_sat, | |
| 8339 | 8636 | .slice, |
| 8340 | 8637 | .slice_open, |
| 8341 | 8638 | .slice_sentinel, |
| ... | ... | @@ -8894,6 +9191,17 @@ const Scope = struct { |
| 8894 | 9191 | return @fieldParentPtr(T, "base", base); |
| 8895 | 9192 | } |
| 8896 | 9193 | |
| 9194 | fn parent(base: *Scope) ?*Scope { | |
| 9195 | return switch (base.tag) { | |
| 9196 | .gen_zir => base.cast(GenZir).?.parent, | |
| 9197 | .local_val => base.cast(LocalVal).?.parent, | |
| 9198 | .local_ptr => base.cast(LocalPtr).?.parent, | |
| 9199 | .defer_normal, .defer_error => base.cast(Defer).?.parent, | |
| 9200 | .namespace => base.cast(Namespace).?.parent, | |
| 9201 | .top => null, | |
| 9202 | }; | |
| 9203 | } | |
| 9204 | ||
| 8897 | 9205 | const Tag = enum { |
| 8898 | 9206 | gen_zir, |
| 8899 | 9207 | local_val, |
| ... | ... | @@ -8919,7 +9227,7 @@ const Scope = struct { |
| 8919 | 9227 | const LocalVal = struct { |
| 8920 | 9228 | const base_tag: Tag = .local_val; |
| 8921 | 9229 | base: Scope = Scope{ .tag = base_tag }, |
| 8922 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`. | |
| 9230 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. | |
| 8923 | 9231 | parent: *Scope, |
| 8924 | 9232 | gen_zir: *GenZir, |
| 8925 | 9233 | inst: Zir.Inst.Ref, |
| ... | ... | @@ -8938,7 +9246,7 @@ const Scope = struct { |
| 8938 | 9246 | const LocalPtr = struct { |
| 8939 | 9247 | const base_tag: Tag = .local_ptr; |
| 8940 | 9248 | base: Scope = Scope{ .tag = base_tag }, |
| 8941 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`. | |
| 9249 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. | |
| 8942 | 9250 | parent: *Scope, |
| 8943 | 9251 | gen_zir: *GenZir, |
| 8944 | 9252 | ptr: Zir.Inst.Ref, |
| ... | ... | @@ -8956,7 +9264,7 @@ const Scope = struct { |
| 8956 | 9264 | |
| 8957 | 9265 | const Defer = struct { |
| 8958 | 9266 | base: Scope, |
| 8959 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`. | |
| 9267 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. | |
| 8960 | 9268 | parent: *Scope, |
| 8961 | 9269 | defer_node: Ast.Node.Index, |
| 8962 | 9270 | }; |
| ... | ... | @@ -8967,11 +9275,27 @@ const Scope = struct { |
| 8967 | 9275 | const base_tag: Tag = .namespace; |
| 8968 | 9276 | base: Scope = Scope{ .tag = base_tag }, |
| 8969 | 9277 | |
| 9278 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. | |
| 8970 | 9279 | parent: *Scope, |
| 8971 | 9280 | /// Maps string table index to the source location of declaration, |
| 8972 | 9281 | /// for the purposes of reporting name shadowing compile errors. |
| 8973 | 9282 | decls: std.AutoHashMapUnmanaged(u32, Ast.Node.Index) = .{}, |
| 8974 | 9283 | node: Ast.Node.Index, |
| 9284 | inst: Zir.Inst.Index, | |
| 9285 | ||
| 9286 | /// The astgen scope containing this namespace. | |
| 9287 | /// Only valid during astgen. | |
| 9288 | declaring_gz: ?*GenZir, | |
| 9289 | ||
| 9290 | /// Map from the raw captured value to the instruction | |
| 9291 | /// ref of the capture for decls in this namespace | |
| 9292 | captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}, | |
| 9293 | ||
| 9294 | pub fn deinit(self: *Namespace, gpa: *Allocator) void { | |
| 9295 | self.decls.deinit(gpa); | |
| 9296 | self.captures.deinit(gpa); | |
| 9297 | self.* = undefined; | |
| 9298 | } | |
| 8975 | 9299 | }; |
| 8976 | 9300 | |
| 8977 | 9301 | const Top = struct { |
| ... | ... | @@ -8987,12 +9311,14 @@ const GenZir = struct { |
| 8987 | 9311 | base: Scope = Scope{ .tag = base_tag }, |
| 8988 | 9312 | force_comptime: bool, |
| 8989 | 9313 | in_defer: bool, |
| 9314 | c_import: bool = false, | |
| 8990 | 9315 | /// How decls created in this scope should be named. |
| 8991 | 9316 | anon_name_strategy: Zir.Inst.NameStrategy = .anon, |
| 8992 | 9317 | /// The containing decl AST node. |
| 8993 | 9318 | decl_node_index: Ast.Node.Index, |
| 8994 | 9319 | /// The containing decl line index, absolute. |
| 8995 | 9320 | decl_line: u32, |
| 9321 | /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. | |
| 8996 | 9322 | parent: *Scope, |
| 8997 | 9323 | /// All `GenZir` scopes for the same ZIR share this. |
| 8998 | 9324 | astgen: *AstGen, |
| ... | ... | @@ -9028,10 +9354,17 @@ const GenZir = struct { |
| 9028 | 9354 | suspend_node: Ast.Node.Index = 0, |
| 9029 | 9355 | nosuspend_node: Ast.Node.Index = 0, |
| 9030 | 9356 | |
| 9357 | /// Namespace members are lazy. When executing a decl within a namespace, | |
| 9358 | /// any references to external instructions need to be treated specially. | |
| 9359 | /// This list tracks those references. See also .closure_capture and .closure_get. | |
| 9360 | /// Keys are the raw instruction index, values are the closure_capture instruction. | |
| 9361 | captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}, | |
| 9362 | ||
| 9031 | 9363 | fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir { |
| 9032 | 9364 | return .{ |
| 9033 | 9365 | .force_comptime = gz.force_comptime, |
| 9034 | 9366 | .in_defer = gz.in_defer, |
| 9367 | .c_import = gz.c_import, | |
| 9035 | 9368 | .decl_node_index = gz.decl_node_index, |
| 9036 | 9369 | .decl_line = gz.decl_line, |
| 9037 | 9370 | .parent = scope, |
| ... | ... | @@ -9041,6 +9374,52 @@ const GenZir = struct { |
| 9041 | 9374 | }; |
| 9042 | 9375 | } |
| 9043 | 9376 | |
| 9377 | fn makeCoercionScope( | |
| 9378 | parent_gz: *GenZir, | |
| 9379 | scope: *Scope, | |
| 9380 | dest_type: Zir.Inst.Ref, | |
| 9381 | result_ptr: Zir.Inst.Ref, | |
| 9382 | ) !GenZir { | |
| 9383 | // Detect whether this expr() call goes into rvalue() to store the result into the | |
| 9384 | // result location. If it does, elide the coerce_result_ptr instruction | |
| 9385 | // as well as the store instruction, instead passing the result as an rvalue. | |
| 9386 | var as_scope = parent_gz.makeSubBlock(scope); | |
| 9387 | errdefer as_scope.instructions.deinit(parent_gz.astgen.gpa); | |
| 9388 | as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); | |
| 9389 | ||
| 9390 | return as_scope; | |
| 9391 | } | |
| 9392 | ||
| 9393 | fn finishCoercion( | |
| 9394 | as_scope: *GenZir, | |
| 9395 | parent_gz: *GenZir, | |
| 9396 | rl: ResultLoc, | |
| 9397 | src_node: Ast.Node.Index, | |
| 9398 | result: Zir.Inst.Ref, | |
| 9399 | dest_type: Zir.Inst.Ref, | |
| 9400 | ) !Zir.Inst.Ref { | |
| 9401 | const astgen = as_scope.astgen; | |
| 9402 | const parent_zir = &parent_gz.instructions; | |
| 9403 | if (as_scope.rvalue_rl_count == 1) { | |
| 9404 | // Busted! This expression didn't actually need a pointer. | |
| 9405 | const zir_tags = astgen.instructions.items(.tag); | |
| 9406 | const zir_datas = astgen.instructions.items(.data); | |
| 9407 | try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len); | |
| 9408 | for (as_scope.instructions.items) |src_inst| { | |
| 9409 | if (indexToRef(src_inst) == as_scope.rl_ptr) continue; | |
| 9410 | if (zir_tags[src_inst] == .store_to_block_ptr) { | |
| 9411 | if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; | |
| 9412 | } | |
| 9413 | parent_zir.appendAssumeCapacity(src_inst); | |
| 9414 | } | |
| 9415 | const casted_result = try parent_gz.addBin(.as, dest_type, result); | |
| 9416 | return rvalue(parent_gz, rl, casted_result, src_node); | |
| 9417 | } else { | |
| 9418 | try parent_zir.appendSlice(astgen.gpa, as_scope.instructions.items); | |
| 9419 | return result; | |
| 9420 | } | |
| 9421 | } | |
| 9422 | ||
| 9044 | 9423 | const Label = struct { |
| 9045 | 9424 | token: Ast.TokenIndex, |
| 9046 | 9425 | block_inst: Zir.Inst.Index, |
| ... | ... | @@ -9357,7 +9736,7 @@ const GenZir = struct { |
| 9357 | 9736 | |
| 9358 | 9737 | fn addCall( |
| 9359 | 9738 | gz: *GenZir, |
| 9360 | tag: Zir.Inst.Tag, | |
| 9739 | modifier: std.builtin.CallOptions.Modifier, | |
| 9361 | 9740 | callee: Zir.Inst.Ref, |
| 9362 | 9741 | args: []const Zir.Inst.Ref, |
| 9363 | 9742 | /// Absolute node index. This function does the conversion to offset from Decl. |
| ... | ... | @@ -9366,20 +9745,24 @@ const GenZir = struct { |
| 9366 | 9745 | assert(callee != .none); |
| 9367 | 9746 | assert(src_node != 0); |
| 9368 | 9747 | const gpa = gz.astgen.gpa; |
| 9748 | const Call = Zir.Inst.Call; | |
| 9369 | 9749 | try gz.instructions.ensureUnusedCapacity(gpa, 1); |
| 9370 | 9750 | try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); |
| 9371 | try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Call).Struct.fields.len + | |
| 9751 | try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Call).Struct.fields.len + | |
| 9372 | 9752 | args.len); |
| 9373 | 9753 | |
| 9374 | const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Call{ | |
| 9754 | const payload_index = gz.astgen.addExtraAssumeCapacity(Call{ | |
| 9375 | 9755 | .callee = callee, |
| 9376 | .args_len = @intCast(u32, args.len), | |
| 9756 | .flags = .{ | |
| 9757 | .packed_modifier = @intCast(Call.Flags.PackedModifier, @enumToInt(modifier)), | |
| 9758 | .args_len = @intCast(Call.Flags.PackedArgsLen, args.len), | |
| 9759 | }, | |
| 9377 | 9760 | }); |
| 9378 | 9761 | gz.astgen.appendRefsAssumeCapacity(args); |
| 9379 | 9762 | |
| 9380 | 9763 | const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); |
| 9381 | 9764 | gz.astgen.instructions.appendAssumeCapacity(.{ |
| 9382 | .tag = tag, | |
| 9765 | .tag = .call, | |
| 9383 | 9766 | .data = .{ .pl_node = .{ |
| 9384 | 9767 | .src_node = gz.nodeIndexToRelative(src_node), |
| 9385 | 9768 | .payload_index = payload_index, |
| ... | ... | @@ -9695,6 +10078,22 @@ const GenZir = struct { |
| 9695 | 10078 | }); |
| 9696 | 10079 | } |
| 9697 | 10080 | |
| 10081 | fn addInstNode( | |
| 10082 | gz: *GenZir, | |
| 10083 | tag: Zir.Inst.Tag, | |
| 10084 | inst: Zir.Inst.Index, | |
| 10085 | /// Absolute node index. This function does the conversion to offset from Decl. | |
| 10086 | src_node: Ast.Node.Index, | |
| 10087 | ) !Zir.Inst.Ref { | |
| 10088 | return gz.add(.{ | |
| 10089 | .tag = tag, | |
| 10090 | .data = .{ .inst_node = .{ | |
| 10091 | .inst = inst, | |
| 10092 | .src_node = gz.nodeIndexToRelative(src_node), | |
| 10093 | } }, | |
| 10094 | }); | |
| 10095 | } | |
| 10096 | ||
| 9698 | 10097 | fn addNodeExtended( |
| 9699 | 10098 | gz: *GenZir, |
| 9700 | 10099 | opcode: Zir.Inst.Extended, |
| ... | ... | @@ -9996,6 +10395,37 @@ const GenZir = struct { |
| 9996 | 10395 | }); |
| 9997 | 10396 | } |
| 9998 | 10397 | |
| 10398 | fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct { | |
| 10399 | src_node: Ast.Node.Index, | |
| 10400 | decls_len: u32, | |
| 10401 | }) !void { | |
| 10402 | const astgen = gz.astgen; | |
| 10403 | const gpa = astgen.gpa; | |
| 10404 | ||
| 10405 | try astgen.extra.ensureUnusedCapacity(gpa, 2); | |
| 10406 | const payload_index = @intCast(u32, astgen.extra.items.len); | |
| 10407 | ||
| 10408 | if (args.src_node != 0) { | |
| 10409 | const node_offset = gz.nodeIndexToRelative(args.src_node); | |
| 10410 | astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset)); | |
| 10411 | } | |
| 10412 | if (args.decls_len != 0) { | |
| 10413 | astgen.extra.appendAssumeCapacity(args.decls_len); | |
| 10414 | } | |
| 10415 | astgen.instructions.set(inst, .{ | |
| 10416 | .tag = .extended, | |
| 10417 | .data = .{ .extended = .{ | |
| 10418 | .opcode = .opaque_decl, | |
| 10419 | .small = @bitCast(u16, Zir.Inst.OpaqueDecl.Small{ | |
| 10420 | .has_src_node = args.src_node != 0, | |
| 10421 | .has_decls_len = args.decls_len != 0, | |
| 10422 | .name_strategy = gz.anon_name_strategy, | |
| 10423 | }), | |
| 10424 | .operand = payload_index, | |
| 10425 | } }, | |
| 10426 | }); | |
| 10427 | } | |
| 10428 | ||
| 9999 | 10429 | fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref { |
| 10000 | 10430 | return indexToRef(try gz.addAsIndex(inst)); |
| 10001 | 10431 | } |
src/BuiltinFn.zig+17-41| ... | ... | @@ -2,7 +2,6 @@ const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub const Tag = enum { |
| 4 | 4 | add_with_overflow, |
| 5 | add_with_saturation, | |
| 6 | 5 | align_cast, |
| 7 | 6 | align_of, |
| 8 | 7 | as, |
| ... | ... | @@ -66,7 +65,6 @@ pub const Tag = enum { |
| 66 | 65 | wasm_memory_grow, |
| 67 | 66 | mod, |
| 68 | 67 | mul_with_overflow, |
| 69 | mul_with_saturation, | |
| 70 | 68 | panic, |
| 71 | 69 | pop_count, |
| 72 | 70 | ptr_cast, |
| ... | ... | @@ -81,12 +79,10 @@ pub const Tag = enum { |
| 81 | 79 | set_runtime_safety, |
| 82 | 80 | shl_exact, |
| 83 | 81 | shl_with_overflow, |
| 84 | shl_with_saturation, | |
| 85 | 82 | shr_exact, |
| 86 | 83 | shuffle, |
| 87 | 84 | size_of, |
| 88 | 85 | splat, |
| 89 | sub_with_saturation, | |
| 90 | 86 | reduce, |
| 91 | 87 | src, |
| 92 | 88 | sqrt, |
| ... | ... | @@ -114,10 +110,19 @@ pub const Tag = enum { |
| 114 | 110 | Vector, |
| 115 | 111 | }; |
| 116 | 112 | |
| 113 | pub const MemLocRequirement = enum { | |
| 114 | /// The builtin never needs a memory location. | |
| 115 | never, | |
| 116 | /// The builtin always needs a memory location. | |
| 117 | always, | |
| 118 | /// The builtin forwards the question to argument at index 1. | |
| 119 | forward1, | |
| 120 | }; | |
| 121 | ||
| 117 | 122 | tag: Tag, |
| 118 | 123 | |
| 119 | /// `true` if the builtin call can take advantage of a result location pointer. | |
| 120 | needs_mem_loc: bool = false, | |
| 124 | /// Info about the builtin call's ability to take advantage of a result location pointer. | |
| 125 | needs_mem_loc: MemLocRequirement = .never, | |
| 121 | 126 | /// `true` if the builtin call can be the left-hand side of an expression (assigned to). |
| 122 | 127 | allows_lvalue: bool = false, |
| 123 | 128 | /// The number of parameters to this builtin function. `null` means variable number |
| ... | ... | @@ -152,7 +157,7 @@ pub const list = list: { |
| 152 | 157 | "@as", |
| 153 | 158 | .{ |
| 154 | 159 | .tag = .as, |
| 155 | .needs_mem_loc = true, | |
| 160 | .needs_mem_loc = .forward1, | |
| 156 | 161 | .param_count = 2, |
| 157 | 162 | }, |
| 158 | 163 | }, |
| ... | ... | @@ -188,7 +193,7 @@ pub const list = list: { |
| 188 | 193 | "@bitCast", |
| 189 | 194 | .{ |
| 190 | 195 | .tag = .bit_cast, |
| 191 | .needs_mem_loc = true, | |
| 196 | .needs_mem_loc = .forward1, | |
| 192 | 197 | .param_count = 2, |
| 193 | 198 | }, |
| 194 | 199 | }, |
| ... | ... | @@ -252,7 +257,7 @@ pub const list = list: { |
| 252 | 257 | "@call", |
| 253 | 258 | .{ |
| 254 | 259 | .tag = .call, |
| 255 | .needs_mem_loc = true, | |
| 260 | .needs_mem_loc = .always, | |
| 256 | 261 | .param_count = 3, |
| 257 | 262 | }, |
| 258 | 263 | }, |
| ... | ... | @@ -414,7 +419,7 @@ pub const list = list: { |
| 414 | 419 | "@field", |
| 415 | 420 | .{ |
| 416 | 421 | .tag = .field, |
| 417 | .needs_mem_loc = true, | |
| 422 | .needs_mem_loc = .always, | |
| 418 | 423 | .param_count = 2, |
| 419 | 424 | .allows_lvalue = true, |
| 420 | 425 | }, |
| ... | ... | @@ -531,34 +536,6 @@ pub const list = list: { |
| 531 | 536 | .param_count = 2, |
| 532 | 537 | }, |
| 533 | 538 | }, |
| 534 | .{ | |
| 535 | "@addWithSaturation", | |
| 536 | .{ | |
| 537 | .tag = .add_with_saturation, | |
| 538 | .param_count = 2, | |
| 539 | }, | |
| 540 | }, | |
| 541 | .{ | |
| 542 | "@subWithSaturation", | |
| 543 | .{ | |
| 544 | .tag = .sub_with_saturation, | |
| 545 | .param_count = 2, | |
| 546 | }, | |
| 547 | }, | |
| 548 | .{ | |
| 549 | "@mulWithSaturation", | |
| 550 | .{ | |
| 551 | .tag = .mul_with_saturation, | |
| 552 | .param_count = 2, | |
| 553 | }, | |
| 554 | }, | |
| 555 | .{ | |
| 556 | "@shlWithSaturation", | |
| 557 | .{ | |
| 558 | .tag = .shl_with_saturation, | |
| 559 | .param_count = 2, | |
| 560 | }, | |
| 561 | }, | |
| 562 | 539 | .{ |
| 563 | 540 | "@memcpy", |
| 564 | 541 | .{ |
| ... | ... | @@ -731,7 +708,6 @@ pub const list = list: { |
| 731 | 708 | "@splat", |
| 732 | 709 | .{ |
| 733 | 710 | .tag = .splat, |
| 734 | .needs_mem_loc = true, | |
| 735 | 711 | .param_count = 2, |
| 736 | 712 | }, |
| 737 | 713 | }, |
| ... | ... | @@ -746,7 +722,7 @@ pub const list = list: { |
| 746 | 722 | "@src", |
| 747 | 723 | .{ |
| 748 | 724 | .tag = .src, |
| 749 | .needs_mem_loc = true, | |
| 725 | .needs_mem_loc = .always, | |
| 750 | 726 | .param_count = 0, |
| 751 | 727 | }, |
| 752 | 728 | }, |
| ... | ... | @@ -901,7 +877,7 @@ pub const list = list: { |
| 901 | 877 | "@unionInit", |
| 902 | 878 | .{ |
| 903 | 879 | .tag = .union_init, |
| 904 | .needs_mem_loc = true, | |
| 880 | .needs_mem_loc = .always, | |
| 905 | 881 | .param_count = 3, |
| 906 | 882 | }, |
| 907 | 883 | }, |
src/Compilation.zig+134-60| ... | ... | @@ -812,7 +812,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 812 | 812 | |
| 813 | 813 | const needs_c_symbols = !options.skip_linker_dependencies and is_exe_or_dyn_lib; |
| 814 | 814 | |
| 815 | // WASI-only. Resolve the optinal exec-model option, defaults to command. | |
| 815 | // WASI-only. Resolve the optional exec-model option, defaults to command. | |
| 816 | 816 | const wasi_exec_model = if (options.target.os.tag != .wasi) undefined else options.wasi_exec_model orelse .command; |
| 817 | 817 | |
| 818 | 818 | const comp: *Compilation = comp: { |
| ... | ... | @@ -849,10 +849,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 849 | 849 | if (options.use_llvm) |explicit| |
| 850 | 850 | break :blk explicit; |
| 851 | 851 | |
| 852 | // If we have no zig code to compile, no need for LLVM. | |
| 853 | if (options.main_pkg == null) | |
| 854 | break :blk false; | |
| 855 | ||
| 856 | 852 | // If we are outputting .c code we must use Zig backend. |
| 857 | 853 | if (ofmt == .c) |
| 858 | 854 | break :blk false; |
| ... | ... | @@ -861,6 +857,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 861 | 857 | if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) |
| 862 | 858 | break :blk true; |
| 863 | 859 | |
| 860 | // If we have no zig code to compile, no need for LLVM. | |
| 861 | if (options.main_pkg == null) | |
| 862 | break :blk false; | |
| 863 | ||
| 864 | 864 | // The stage1 compiler depends on the stage1 C++ LLVM backend |
| 865 | 865 | // to compile zig code. |
| 866 | 866 | if (use_stage1) |
| ... | ... | @@ -876,9 +876,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 876 | 876 | if (options.use_llvm == true) { |
| 877 | 877 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 878 | 878 | } |
| 879 | if (options.machine_code_model != .default) { | |
| 880 | return error.MachineCodeModelNotSupportedWithoutLlvm; | |
| 881 | } | |
| 882 | 879 | if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) { |
| 883 | 880 | return error.EmittingLlvmModuleRequiresUsingLlvmBackend; |
| 884 | 881 | } |
| ... | ... | @@ -1100,7 +1097,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 1100 | 1097 | |
| 1101 | 1098 | if (feature.llvm_name) |llvm_name| { |
| 1102 | 1099 | const plus_or_minus = "-+"[@boolToInt(is_enabled)]; |
| 1103 | try buf.ensureCapacity(buf.items.len + 2 + llvm_name.len); | |
| 1100 | try buf.ensureUnusedCapacity(2 + llvm_name.len); | |
| 1104 | 1101 | buf.appendAssumeCapacity(plus_or_minus); |
| 1105 | 1102 | buf.appendSliceAssumeCapacity(llvm_name); |
| 1106 | 1103 | buf.appendSliceAssumeCapacity(","); |
| ... | ... | @@ -1180,6 +1177,8 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 1180 | 1177 | } |
| 1181 | 1178 | hash.add(valgrind); |
| 1182 | 1179 | hash.add(single_threaded); |
| 1180 | hash.add(use_stage1); | |
| 1181 | hash.add(use_llvm); | |
| 1183 | 1182 | hash.add(dll_export_fns); |
| 1184 | 1183 | hash.add(options.is_test); |
| 1185 | 1184 | hash.add(options.skip_linker_dependencies); |
| ... | ... | @@ -1350,7 +1349,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 1350 | 1349 | |
| 1351 | 1350 | var system_libs: std.StringArrayHashMapUnmanaged(void) = .{}; |
| 1352 | 1351 | errdefer system_libs.deinit(gpa); |
| 1353 | try system_libs.ensureCapacity(gpa, options.system_libs.len); | |
| 1352 | try system_libs.ensureTotalCapacity(gpa, options.system_libs.len); | |
| 1354 | 1353 | for (options.system_libs) |lib_name| { |
| 1355 | 1354 | system_libs.putAssumeCapacity(lib_name, {}); |
| 1356 | 1355 | } |
| ... | ... | @@ -1486,7 +1485,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 1486 | 1485 | errdefer comp.astgen_wait_group.deinit(); |
| 1487 | 1486 | |
| 1488 | 1487 | // Add a `CObject` for each `c_source_files`. |
| 1489 | try comp.c_object_table.ensureCapacity(gpa, options.c_source_files.len); | |
| 1488 | try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len); | |
| 1490 | 1489 | for (options.c_source_files) |c_source_file| { |
| 1491 | 1490 | const c_object = try gpa.create(CObject); |
| 1492 | 1491 | errdefer gpa.destroy(c_object); |
| ... | ... | @@ -1577,25 +1576,30 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 1577 | 1576 | // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler |
| 1578 | 1577 | // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for |
| 1579 | 1578 | // stage1 but not stage2. |
| 1580 | if (comp.bin_file.options.use_stage1) { | |
| 1581 | if (comp.bin_file.options.include_compiler_rt) { | |
| 1582 | if (is_exe_or_dyn_lib) { | |
| 1583 | try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} }); | |
| 1584 | } else if (options.output_mode != .Obj) { | |
| 1585 | // If build-obj with -fcompiler-rt is requested, that is handled specially | |
| 1586 | // elsewhere. In this case we are making a static library, so we ask | |
| 1587 | // for a compiler-rt object to put in it. | |
| 1588 | try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} }); | |
| 1589 | } | |
| 1579 | const capable_of_building_compiler_rt = comp.bin_file.options.use_stage1 or | |
| 1580 | comp.bin_file.options.use_llvm; | |
| 1581 | const capable_of_building_zig_libc = comp.bin_file.options.use_stage1 or | |
| 1582 | comp.bin_file.options.use_llvm; | |
| 1583 | const capable_of_building_ssp = comp.bin_file.options.use_stage1; | |
| 1584 | ||
| 1585 | if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) { | |
| 1586 | if (is_exe_or_dyn_lib) { | |
| 1587 | try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} }); | |
| 1588 | } else if (options.output_mode != .Obj) { | |
| 1589 | // If build-obj with -fcompiler-rt is requested, that is handled specially | |
| 1590 | // elsewhere. In this case we are making a static library, so we ask | |
| 1591 | // for a compiler-rt object to put in it. | |
| 1592 | try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} }); | |
| 1590 | 1593 | } |
| 1591 | if (needs_c_symbols) { | |
| 1592 | // MinGW provides no libssp, use our own implementation. | |
| 1593 | if (comp.getTarget().isMinGW()) { | |
| 1594 | try comp.work_queue.writeItem(.{ .libssp = {} }); | |
| 1595 | } | |
| 1596 | if (!comp.bin_file.options.link_libc) { | |
| 1597 | try comp.work_queue.writeItem(.{ .zig_libc = {} }); | |
| 1598 | } | |
| 1594 | } | |
| 1595 | if (needs_c_symbols) { | |
| 1596 | // MinGW provides no libssp, use our own implementation. | |
| 1597 | if (comp.getTarget().isMinGW() and capable_of_building_ssp) { | |
| 1598 | try comp.work_queue.writeItem(.{ .libssp = {} }); | |
| 1599 | } | |
| 1600 | ||
| 1601 | if (!comp.bin_file.options.link_libc and capable_of_building_zig_libc) { | |
| 1602 | try comp.work_queue.writeItem(.{ .zig_libc = {} }); | |
| 1599 | 1603 | } |
| 1600 | 1604 | } |
| 1601 | 1605 | } |
| ... | ... | @@ -1647,6 +1651,9 @@ pub fn destroy(self: *Compilation) void { |
| 1647 | 1651 | if (self.compiler_rt_static_lib) |*crt_file| { |
| 1648 | 1652 | crt_file.deinit(gpa); |
| 1649 | 1653 | } |
| 1654 | if (self.compiler_rt_obj) |*crt_file| { | |
| 1655 | crt_file.deinit(gpa); | |
| 1656 | } | |
| 1650 | 1657 | if (self.libssp_static_lib) |*crt_file| { |
| 1651 | 1658 | crt_file.deinit(gpa); |
| 1652 | 1659 | } |
| ... | ... | @@ -1793,6 +1800,10 @@ pub fn update(self: *Compilation) !void { |
| 1793 | 1800 | } |
| 1794 | 1801 | } |
| 1795 | 1802 | |
| 1803 | // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and | |
| 1804 | // -femit-asm to handle, in the case of C objects. | |
| 1805 | try self.emitOthers(); | |
| 1806 | ||
| 1796 | 1807 | // If there are any errors, we anticipate the source files being loaded |
| 1797 | 1808 | // to report error messages. Otherwise we unload all source files to save memory. |
| 1798 | 1809 | // The ZIR needs to stay loaded in memory because (1) Decl objects contain references |
| ... | ... | @@ -1808,6 +1819,37 @@ pub fn update(self: *Compilation) !void { |
| 1808 | 1819 | } |
| 1809 | 1820 | } |
| 1810 | 1821 | |
| 1822 | fn emitOthers(comp: *Compilation) !void { | |
| 1823 | if (comp.bin_file.options.output_mode != .Obj or comp.bin_file.options.module != null or | |
| 1824 | comp.c_object_table.count() == 0) | |
| 1825 | { | |
| 1826 | return; | |
| 1827 | } | |
| 1828 | const obj_path = comp.c_object_table.keys()[0].status.success.object_path; | |
| 1829 | const cwd = std.fs.cwd(); | |
| 1830 | const ext = std.fs.path.extension(obj_path); | |
| 1831 | const basename = obj_path[0 .. obj_path.len - ext.len]; | |
| 1832 | // This obj path always ends with the object file extension, but if we change the | |
| 1833 | // extension to .ll, .bc, or .s, then it will be the path to those things. | |
| 1834 | const outs = [_]struct { | |
| 1835 | emit: ?EmitLoc, | |
| 1836 | ext: []const u8, | |
| 1837 | }{ | |
| 1838 | .{ .emit = comp.emit_asm, .ext = ".s" }, | |
| 1839 | .{ .emit = comp.emit_llvm_ir, .ext = ".ll" }, | |
| 1840 | .{ .emit = comp.emit_llvm_bc, .ext = ".bc" }, | |
| 1841 | }; | |
| 1842 | for (outs) |out| { | |
| 1843 | if (out.emit) |loc| { | |
| 1844 | if (loc.directory) |directory| { | |
| 1845 | const src_path = try std.fmt.allocPrint(comp.gpa, "{s}{s}", .{ basename, out.ext }); | |
| 1846 | defer comp.gpa.free(src_path); | |
| 1847 | try cwd.copyFile(src_path, directory.handle, loc.basename, .{}); | |
| 1848 | } | |
| 1849 | } | |
| 1850 | } | |
| 1851 | } | |
| 1852 | ||
| 1811 | 1853 | /// Having the file open for writing is problematic as far as executing the |
| 1812 | 1854 | /// binary is concerned. This will remove the write flag, or close the file, |
| 1813 | 1855 | /// or whatever is needed so that it can be executed. |
| ... | ... | @@ -2113,7 +2155,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 2113 | 2155 | const module = self.bin_file.options.module.?; |
| 2114 | 2156 | const decl = func.owner_decl; |
| 2115 | 2157 | |
| 2116 | var air = module.analyzeFnBody(decl, func) catch |err| switch (err) { | |
| 2158 | var tmp_arena = std.heap.ArenaAllocator.init(gpa); | |
| 2159 | defer tmp_arena.deinit(); | |
| 2160 | const sema_arena = &tmp_arena.allocator; | |
| 2161 | ||
| 2162 | var air = module.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) { | |
| 2117 | 2163 | error.AnalysisFail => { |
| 2118 | 2164 | assert(func.state != .in_progress); |
| 2119 | 2165 | continue; |
| ... | ... | @@ -2175,16 +2221,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 2175 | 2221 | const decl_emit_h = decl.getEmitH(module); |
| 2176 | 2222 | const fwd_decl = &decl_emit_h.fwd_decl; |
| 2177 | 2223 | fwd_decl.shrinkRetainingCapacity(0); |
| 2224 | var typedefs_arena = std.heap.ArenaAllocator.init(gpa); | |
| 2225 | defer typedefs_arena.deinit(); | |
| 2178 | 2226 | |
| 2179 | 2227 | var dg: c_codegen.DeclGen = .{ |
| 2228 | .gpa = gpa, | |
| 2180 | 2229 | .module = module, |
| 2181 | 2230 | .error_msg = null, |
| 2182 | 2231 | .decl = decl, |
| 2183 | 2232 | .fwd_decl = fwd_decl.toManaged(gpa), |
| 2184 | // we don't want to emit optionals and error unions to headers since they have no ABI | |
| 2185 | .typedefs = undefined, | |
| 2233 | .typedefs = c_codegen.TypedefMap.init(gpa), | |
| 2234 | .typedefs_arena = &typedefs_arena.allocator, | |
| 2186 | 2235 | }; |
| 2187 | 2236 | defer dg.fwd_decl.deinit(); |
| 2237 | defer dg.typedefs.deinit(); | |
| 2188 | 2238 | |
| 2189 | 2239 | c_codegen.genHeader(&dg) catch |err| switch (err) { |
| 2190 | 2240 | error.AnalysisFail => { |
| ... | ... | @@ -2612,7 +2662,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { |
| 2612 | 2662 | |
| 2613 | 2663 | const dep_basename = std.fs.path.basename(out_dep_path); |
| 2614 | 2664 | try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); |
| 2615 | try comp.stage1_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); | |
| 2665 | if (build_options.is_stage1 and comp.bin_file.options.use_stage1) try comp.stage1_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); | |
| 2616 | 2666 | |
| 2617 | 2667 | const digest = man.final(); |
| 2618 | 2668 | const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); |
| ... | ... | @@ -2721,16 +2771,12 @@ fn reportRetryableAstGenError( |
| 2721 | 2771 | try Module.ErrorMsg.create( |
| 2722 | 2772 | gpa, |
| 2723 | 2773 | src_loc, |
| 2724 | "unable to load '{'}" ++ std.fs.path.sep_str ++ "{'}': {s}", | |
| 2725 | .{ | |
| 2726 | std.zig.fmtEscapes(dir_path), | |
| 2727 | std.zig.fmtEscapes(file.sub_file_path), | |
| 2728 | @errorName(err), | |
| 2729 | }, | |
| 2774 | "unable to load '{s}" ++ std.fs.path.sep_str ++ "{s}': {s}", | |
| 2775 | .{ dir_path, file.sub_file_path, @errorName(err) }, | |
| 2730 | 2776 | ) |
| 2731 | 2777 | else |
| 2732 | try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{'}': {s}", .{ | |
| 2733 | std.zig.fmtEscapes(file.sub_file_path), @errorName(err), | |
| 2778 | try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{s}': {s}", .{ | |
| 2779 | file.sub_file_path, @errorName(err), | |
| 2734 | 2780 | }); |
| 2735 | 2781 | errdefer err_msg.destroy(gpa); |
| 2736 | 2782 | |
| ... | ... | @@ -2766,6 +2812,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 2766 | 2812 | defer man.deinit(); |
| 2767 | 2813 | |
| 2768 | 2814 | man.hash.add(comp.clang_preprocessor_mode); |
| 2815 | man.hash.addOptionalEmitLoc(comp.emit_asm); | |
| 2816 | man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); | |
| 2817 | man.hash.addOptionalEmitLoc(comp.emit_llvm_bc); | |
| 2769 | 2818 | |
| 2770 | 2819 | try man.hashCSource(c_object.src); |
| 2771 | 2820 | |
| ... | ... | @@ -2789,16 +2838,29 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 2789 | 2838 | comp.bin_file.options.root_name |
| 2790 | 2839 | else |
| 2791 | 2840 | c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len]; |
| 2792 | const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ | |
| 2793 | o_basename_noext, | |
| 2794 | comp.bin_file.options.object_format.fileExt(comp.bin_file.options.target.cpu.arch), | |
| 2795 | }); | |
| 2796 | 2841 | |
| 2842 | const o_ext = comp.bin_file.options.object_format.fileExt(comp.bin_file.options.target.cpu.arch); | |
| 2797 | 2843 | const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: { |
| 2798 | 2844 | var argv = std.ArrayList([]const u8).init(comp.gpa); |
| 2799 | 2845 | defer argv.deinit(); |
| 2800 | 2846 | |
| 2801 | // We can't know the digest until we do the C compiler invocation, so we need a temporary filename. | |
| 2847 | // In case we are doing passthrough mode, we need to detect -S and -emit-llvm. | |
| 2848 | const out_ext = e: { | |
| 2849 | if (!comp.clang_passthrough_mode) | |
| 2850 | break :e o_ext; | |
| 2851 | if (comp.emit_asm != null) | |
| 2852 | break :e ".s"; | |
| 2853 | if (comp.emit_llvm_ir != null) | |
| 2854 | break :e ".ll"; | |
| 2855 | if (comp.emit_llvm_bc != null) | |
| 2856 | break :e ".bc"; | |
| 2857 | ||
| 2858 | break :e o_ext; | |
| 2859 | }; | |
| 2860 | const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext }); | |
| 2861 | ||
| 2862 | // We can't know the digest until we do the C compiler invocation, | |
| 2863 | // so we need a temporary filename. | |
| 2802 | 2864 | const out_obj_path = try comp.tmpFilePath(arena, o_basename); |
| 2803 | 2865 | var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); |
| 2804 | 2866 | defer zig_cache_tmp_dir.close(); |
| ... | ... | @@ -2812,15 +2874,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 2812 | 2874 | try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path}); |
| 2813 | 2875 | try comp.addCCArgs(arena, &argv, ext, out_dep_path); |
| 2814 | 2876 | |
| 2815 | try argv.ensureCapacity(argv.items.len + 3); | |
| 2877 | try argv.ensureUnusedCapacity(6 + c_object.src.extra_flags.len); | |
| 2816 | 2878 | switch (comp.clang_preprocessor_mode) { |
| 2817 | 2879 | .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }), |
| 2818 | 2880 | .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }), |
| 2819 | 2881 | .stdout => argv.appendAssumeCapacity("-E"), |
| 2820 | 2882 | } |
| 2821 | ||
| 2822 | try argv.append(c_object.src.src_path); | |
| 2823 | try argv.appendSlice(c_object.src.extra_flags); | |
| 2883 | if (comp.clang_passthrough_mode) { | |
| 2884 | if (comp.emit_asm != null) { | |
| 2885 | argv.appendAssumeCapacity("-S"); | |
| 2886 | } else if (comp.emit_llvm_ir != null) { | |
| 2887 | argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" }); | |
| 2888 | } else if (comp.emit_llvm_bc != null) { | |
| 2889 | argv.appendAssumeCapacity("-emit-llvm"); | |
| 2890 | } | |
| 2891 | } | |
| 2892 | argv.appendAssumeCapacity(c_object.src.src_path); | |
| 2893 | argv.appendSliceAssumeCapacity(c_object.src.extra_flags); | |
| 2824 | 2894 | |
| 2825 | 2895 | if (comp.verbose_cc) { |
| 2826 | 2896 | dump_argv(argv.items); |
| ... | ... | @@ -2840,8 +2910,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 2840 | 2910 | switch (term) { |
| 2841 | 2911 | .Exited => |code| { |
| 2842 | 2912 | if (code != 0) { |
| 2843 | // TODO https://github.com/ziglang/zig/issues/6342 | |
| 2844 | std.process.exit(1); | |
| 2913 | std.process.exit(code); | |
| 2845 | 2914 | } |
| 2846 | 2915 | if (comp.clang_preprocessor_mode == .stdout) |
| 2847 | 2916 | std.process.exit(0); |
| ... | ... | @@ -2857,9 +2926,6 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 2857 | 2926 | |
| 2858 | 2927 | const stderr_reader = child.stderr.?.reader(); |
| 2859 | 2928 | |
| 2860 | // TODO https://github.com/ziglang/zig/issues/6343 | |
| 2861 | // Please uncomment and use stdout once this issue is fixed | |
| 2862 | // const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32)); | |
| 2863 | 2929 | const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); |
| 2864 | 2930 | |
| 2865 | 2931 | const term = child.wait() catch |err| { |
| ... | ... | @@ -2909,6 +2975,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 2909 | 2975 | break :blk digest; |
| 2910 | 2976 | }; |
| 2911 | 2977 | |
| 2978 | const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext }); | |
| 2979 | ||
| 2912 | 2980 | c_object.status = .{ |
| 2913 | 2981 | .success = .{ |
| 2914 | 2982 | .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ |
| ... | ... | @@ -3032,7 +3100,7 @@ pub fn addCCArgs( |
| 3032 | 3100 | |
| 3033 | 3101 | // It would be really nice if there was a more compact way to communicate this info to Clang. |
| 3034 | 3102 | const all_features_list = target.cpu.arch.allFeaturesList(); |
| 3035 | try argv.ensureCapacity(argv.items.len + all_features_list.len * 4); | |
| 3103 | try argv.ensureUnusedCapacity(all_features_list.len * 4); | |
| 3036 | 3104 | for (all_features_list) |feature, index_usize| { |
| 3037 | 3105 | const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize); |
| 3038 | 3106 | const is_enabled = target.cpu.features.isEnabled(index); |
| ... | ... | @@ -3533,7 +3601,7 @@ fn detectLibCIncludeDirs( |
| 3533 | 3601 | |
| 3534 | 3602 | fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs { |
| 3535 | 3603 | var list = std.ArrayList([]const u8).init(arena); |
| 3536 | try list.ensureCapacity(4); | |
| 3604 | try list.ensureTotalCapacity(4); | |
| 3537 | 3605 | |
| 3538 | 3606 | list.appendAssumeCapacity(lci.include_dir.?); |
| 3539 | 3607 | |
| ... | ... | @@ -3640,7 +3708,7 @@ fn setMiscFailure( |
| 3640 | 3708 | comptime format: []const u8, |
| 3641 | 3709 | args: anytype, |
| 3642 | 3710 | ) Allocator.Error!void { |
| 3643 | try comp.misc_failures.ensureCapacity(comp.gpa, comp.misc_failures.count() + 1); | |
| 3711 | try comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1); | |
| 3644 | 3712 | const msg = try std.fmt.allocPrint(comp.gpa, format, args); |
| 3645 | 3713 | comp.misc_failures.putAssumeCapacityNoClobber(tag, .{ .msg = msg }); |
| 3646 | 3714 | } |
| ... | ... | @@ -3662,6 +3730,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc |
| 3662 | 3730 | const target = comp.getTarget(); |
| 3663 | 3731 | const generic_arch_name = target.cpu.arch.genericName(); |
| 3664 | 3732 | const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; |
| 3733 | const stage2_x86_cx16 = target.cpu.arch == .x86_64 and | |
| 3734 | std.Target.x86.featureSetHas(target.cpu.features, .cx16); | |
| 3665 | 3735 | |
| 3666 | 3736 | @setEvalBranchQuota(4000); |
| 3667 | 3737 | try buffer.writer().print( |
| ... | ... | @@ -3673,6 +3743,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc |
| 3673 | 3743 | \\pub const zig_is_stage2 = {}; |
| 3674 | 3744 | \\/// Temporary until self-hosted supports the `cpu.arch` value. |
| 3675 | 3745 | \\pub const stage2_arch: std.Target.Cpu.Arch = .{}; |
| 3746 | \\/// Temporary until self-hosted can call `std.Target.x86.featureSetHas` at comptime. | |
| 3747 | \\pub const stage2_x86_cx16 = {}; | |
| 3676 | 3748 | \\ |
| 3677 | 3749 | \\pub const output_mode = std.builtin.OutputMode.{}; |
| 3678 | 3750 | \\pub const link_mode = std.builtin.LinkMode.{}; |
| ... | ... | @@ -3688,6 +3760,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc |
| 3688 | 3760 | build_options.version, |
| 3689 | 3761 | !use_stage1, |
| 3690 | 3762 | std.zig.fmtId(@tagName(target.cpu.arch)), |
| 3763 | stage2_x86_cx16, | |
| 3691 | 3764 | std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)), |
| 3692 | 3765 | std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)), |
| 3693 | 3766 | comp.bin_file.options.is_test, |
| ... | ... | @@ -3912,6 +3985,7 @@ fn buildOutputFromZig( |
| 3912 | 3985 | }, |
| 3913 | 3986 | .root_src_path = src_basename, |
| 3914 | 3987 | }; |
| 3988 | defer main_pkg.deinitTable(comp.gpa); | |
| 3915 | 3989 | const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; |
| 3916 | 3990 | const target = comp.getTarget(); |
| 3917 | 3991 | const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{ |
| ... | ... | @@ -3970,7 +4044,7 @@ fn buildOutputFromZig( |
| 3970 | 4044 | defer if (!keep_errors) errors.deinit(sub_compilation.gpa); |
| 3971 | 4045 | |
| 3972 | 4046 | if (errors.list.len != 0) { |
| 3973 | try comp.misc_failures.ensureCapacity(comp.gpa, comp.misc_failures.count() + 1); | |
| 4047 | try comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1); | |
| 3974 | 4048 | comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{ |
| 3975 | 4049 | .msg = try std.fmt.allocPrint(comp.gpa, "sub-compilation of {s} failed", .{ |
| 3976 | 4050 | @tagName(misc_task_tag), |
| ... | ... | @@ -4402,7 +4476,7 @@ pub fn build_crt_file( |
| 4402 | 4476 | |
| 4403 | 4477 | try sub_compilation.updateSubCompilation(); |
| 4404 | 4478 | |
| 4405 | try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1); | |
| 4479 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); | |
| 4406 | 4480 | |
| 4407 | 4481 | comp.crt_files.putAssumeCapacityNoClobber(basename, .{ |
| 4408 | 4482 | .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ |
src/Liveness.zig+35-3| ... | ... | @@ -30,7 +30,7 @@ tomb_bits: []usize, |
| 30 | 30 | /// The main tomb bits are still used and the extra ones are starting with the lsb of the |
| 31 | 31 | /// value here. |
| 32 | 32 | special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32), |
| 33 | /// Auxilliary data. The way this data is interpreted is determined contextually. | |
| 33 | /// Auxiliary data. The way this data is interpreted is determined contextually. | |
| 34 | 34 | extra: []const u32, |
| 35 | 35 | |
| 36 | 36 | /// Trailing is the set of instructions whose lifetimes end at the start of the then branch, |
| ... | ... | @@ -226,12 +226,16 @@ fn analyzeInst( |
| 226 | 226 | switch (inst_tags[inst]) { |
| 227 | 227 | .add, |
| 228 | 228 | .addwrap, |
| 229 | .add_sat, | |
| 229 | 230 | .sub, |
| 230 | 231 | .subwrap, |
| 232 | .sub_sat, | |
| 231 | 233 | .mul, |
| 232 | 234 | .mulwrap, |
| 235 | .mul_sat, | |
| 233 | 236 | .div, |
| 234 | 237 | .rem, |
| 238 | .mod, | |
| 235 | 239 | .ptr_add, |
| 236 | 240 | .ptr_sub, |
| 237 | 241 | .bit_and, |
| ... | ... | @@ -251,7 +255,14 @@ fn analyzeInst( |
| 251 | 255 | .ptr_elem_val, |
| 252 | 256 | .ptr_ptr_elem_val, |
| 253 | 257 | .shl, |
| 258 | .shl_exact, | |
| 259 | .shl_sat, | |
| 254 | 260 | .shr, |
| 261 | .atomic_store_unordered, | |
| 262 | .atomic_store_monotonic, | |
| 263 | .atomic_store_release, | |
| 264 | .atomic_store_seq_cst, | |
| 265 | .set_union_tag, | |
| 255 | 266 | => { |
| 256 | 267 | const o = inst_datas[inst].bin_op; |
| 257 | 268 | return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none }); |
| ... | ... | @@ -264,12 +275,14 @@ fn analyzeInst( |
| 264 | 275 | .breakpoint, |
| 265 | 276 | .dbg_stmt, |
| 266 | 277 | .unreach, |
| 278 | .fence, | |
| 267 | 279 | => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }), |
| 268 | 280 | |
| 269 | 281 | .not, |
| 270 | 282 | .bitcast, |
| 271 | 283 | .load, |
| 272 | .floatcast, | |
| 284 | .fpext, | |
| 285 | .fptrunc, | |
| 273 | 286 | .intcast, |
| 274 | 287 | .trunc, |
| 275 | 288 | .optional_payload, |
| ... | ... | @@ -288,6 +301,11 @@ fn analyzeInst( |
| 288 | 301 | .struct_field_ptr_index_2, |
| 289 | 302 | .struct_field_ptr_index_3, |
| 290 | 303 | .array_to_slice, |
| 304 | .float_to_int, | |
| 305 | .int_to_float, | |
| 306 | .get_union_tag, | |
| 307 | .clz, | |
| 308 | .ctz, | |
| 291 | 309 | => { |
| 292 | 310 | const o = inst_datas[inst].ty_op; |
| 293 | 311 | return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none }); |
| ... | ... | @@ -344,6 +362,20 @@ fn analyzeInst( |
| 344 | 362 | const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data; |
| 345 | 363 | return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value }); |
| 346 | 364 | }, |
| 365 | .atomic_load => { | |
| 366 | const ptr = inst_datas[inst].atomic_load.ptr; | |
| 367 | return trackOperands(a, new_set, inst, main_tomb, .{ ptr, .none, .none }); | |
| 368 | }, | |
| 369 | .atomic_rmw => { | |
| 370 | const pl_op = inst_datas[inst].pl_op; | |
| 371 | const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data; | |
| 372 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none }); | |
| 373 | }, | |
| 374 | .memset, .memcpy => { | |
| 375 | const pl_op = inst_datas[inst].pl_op; | |
| 376 | const extra = a.air.extraData(Air.Bin, pl_op.payload).data; | |
| 377 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); | |
| 378 | }, | |
| 347 | 379 | .br => { |
| 348 | 380 | const br = inst_datas[inst].br; |
| 349 | 381 | return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none }); |
| ... | ... | @@ -440,7 +472,7 @@ fn analyzeInst( |
| 440 | 472 | } |
| 441 | 473 | // Now we have to correctly populate new_set. |
| 442 | 474 | if (new_set) |ns| { |
| 443 | try ns.ensureCapacity(gpa, @intCast(u32, ns.count() + then_table.count() + else_table.count())); | |
| 475 | try ns.ensureUnusedCapacity(gpa, @intCast(u32, then_table.count() + else_table.count())); | |
| 444 | 476 | var it = then_table.keyIterator(); |
| 445 | 477 | while (it.next()) |key| { |
| 446 | 478 | _ = ns.putAssumeCapacity(key.*, {}); |
src/Module.zig+368-165| ... | ... | @@ -64,11 +64,16 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, |
| 64 | 64 | /// The set of all the generic function instantiations. This is used so that when a generic |
| 65 | 65 | /// function is called twice with the same comptime parameter arguments, both calls dispatch |
| 66 | 66 | /// to the same function. |
| 67 | /// TODO: remove functions from this set when they are destroyed. | |
| 67 | 68 | monomorphed_funcs: MonomorphedFuncsSet = .{}, |
| 68 | ||
| 69 | 69 | /// The set of all comptime function calls that have been cached so that future calls |
| 70 | 70 | /// with the same parameters will get the same return value. |
| 71 | 71 | memoized_calls: MemoizedCallSet = .{}, |
| 72 | /// Contains the values from `@setAlignStack`. A sparse table is used here | |
| 73 | /// instead of a field of `Fn` because usage of `@setAlignStack` is rare, while | |
| 74 | /// functions are many. | |
| 75 | /// TODO: remove functions from this set when they are destroyed. | |
| 76 | align_stack_fns: std.AutoHashMapUnmanaged(*const Fn, SetAlignStack) = .{}, | |
| 72 | 77 | |
| 73 | 78 | /// We optimize memory usage for a compilation with no compile errors by storing the |
| 74 | 79 | /// error messages and mapping outside of `Decl`. |
| ... | ... | @@ -215,6 +220,13 @@ pub const MemoizedCall = struct { |
| 215 | 220 | } |
| 216 | 221 | }; |
| 217 | 222 | |
| 223 | pub const SetAlignStack = struct { | |
| 224 | alignment: u32, | |
| 225 | /// TODO: This needs to store a non-lazy source location for the case of an inline function | |
| 226 | /// which does `@setAlignStack` (applying it to the caller). | |
| 227 | src: LazySrcLoc, | |
| 228 | }; | |
| 229 | ||
| 218 | 230 | /// A `Module` has zero or one of these depending on whether `-femit-h` is enabled. |
| 219 | 231 | pub const GlobalEmitH = struct { |
| 220 | 232 | /// Where to put the output. |
| ... | ... | @@ -236,6 +248,9 @@ pub const Export = struct { |
| 236 | 248 | link: link.File.Export, |
| 237 | 249 | /// The Decl that performs the export. Note that this is *not* the Decl being exported. |
| 238 | 250 | owner_decl: *Decl, |
| 251 | /// The Decl containing the export statement. Inline function calls | |
| 252 | /// may cause this to be different from the owner_decl. | |
| 253 | src_decl: *Decl, | |
| 239 | 254 | /// The Decl being exported. Note this is *not* the Decl performing the export. |
| 240 | 255 | exported_decl: *Decl, |
| 241 | 256 | status: enum { |
| ... | ... | @@ -249,8 +264,8 @@ pub const Export = struct { |
| 249 | 264 | |
| 250 | 265 | pub fn getSrcLoc(exp: Export) SrcLoc { |
| 251 | 266 | return .{ |
| 252 | .file_scope = exp.owner_decl.namespace.file_scope, | |
| 253 | .parent_decl_node = exp.owner_decl.src_node, | |
| 267 | .file_scope = exp.src_decl.namespace.file_scope, | |
| 268 | .parent_decl_node = exp.src_decl.src_node, | |
| 254 | 269 | .lazy = exp.src, |
| 255 | 270 | }; |
| 256 | 271 | } |
| ... | ... | @@ -263,6 +278,56 @@ pub const DeclPlusEmitH = struct { |
| 263 | 278 | emit_h: EmitH, |
| 264 | 279 | }; |
| 265 | 280 | |
| 281 | pub const CaptureScope = struct { | |
| 282 | parent: ?*CaptureScope, | |
| 283 | ||
| 284 | /// Values from this decl's evaluation that will be closed over in | |
| 285 | /// child decls. Values stored in the value_arena of the linked decl. | |
| 286 | /// During sema, this map is backed by the gpa. Once sema completes, | |
| 287 | /// it is reallocated using the value_arena. | |
| 288 | captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, TypedValue) = .{}, | |
| 289 | }; | |
| 290 | ||
| 291 | pub const WipCaptureScope = struct { | |
| 292 | scope: *CaptureScope, | |
| 293 | finalized: bool, | |
| 294 | gpa: *Allocator, | |
| 295 | perm_arena: *Allocator, | |
| 296 | ||
| 297 | pub fn init(gpa: *Allocator, perm_arena: *Allocator, parent: ?*CaptureScope) !@This() { | |
| 298 | const scope = try perm_arena.create(CaptureScope); | |
| 299 | scope.* = .{ .parent = parent }; | |
| 300 | return @This(){ | |
| 301 | .scope = scope, | |
| 302 | .finalized = false, | |
| 303 | .gpa = gpa, | |
| 304 | .perm_arena = perm_arena, | |
| 305 | }; | |
| 306 | } | |
| 307 | ||
| 308 | pub fn finalize(noalias self: *@This()) !void { | |
| 309 | assert(!self.finalized); | |
| 310 | // use a temp to avoid unintentional aliasing due to RLS | |
| 311 | const tmp = try self.scope.captures.clone(self.perm_arena); | |
| 312 | self.scope.captures = tmp; | |
| 313 | self.finalized = true; | |
| 314 | } | |
| 315 | ||
| 316 | pub fn reset(noalias self: *@This(), parent: ?*CaptureScope) !void { | |
| 317 | if (!self.finalized) try self.finalize(); | |
| 318 | self.scope = try self.perm_arena.create(CaptureScope); | |
| 319 | self.scope.* = .{ .parent = parent }; | |
| 320 | self.finalized = false; | |
| 321 | } | |
| 322 | ||
| 323 | pub fn deinit(noalias self: *@This()) void { | |
| 324 | if (!self.finalized) { | |
| 325 | self.scope.captures.deinit(self.gpa); | |
| 326 | } | |
| 327 | self.* = undefined; | |
| 328 | } | |
| 329 | }; | |
| 330 | ||
| 266 | 331 | pub const Decl = struct { |
| 267 | 332 | /// Allocated with Module's allocator; outlives the ZIR code. |
| 268 | 333 | name: [*:0]const u8, |
| ... | ... | @@ -276,7 +341,9 @@ pub const Decl = struct { |
| 276 | 341 | align_val: Value, |
| 277 | 342 | /// Populated when `has_tv`. |
| 278 | 343 | linksection_val: Value, |
| 279 | /// The memory for ty, val, align_val, linksection_val. | |
| 344 | /// Populated when `has_tv`. | |
| 345 | @"addrspace": std.builtin.AddressSpace, | |
| 346 | /// The memory for ty, val, align_val, linksection_val, and captures. | |
| 280 | 347 | /// If this is `null` then there is no memory management needed. |
| 281 | 348 | value_arena: ?*std.heap.ArenaAllocator.State = null, |
| 282 | 349 | /// The direct parent namespace of the Decl. |
| ... | ... | @@ -285,6 +352,11 @@ pub const Decl = struct { |
| 285 | 352 | /// the namespace of the struct, since there is no parent. |
| 286 | 353 | namespace: *Scope.Namespace, |
| 287 | 354 | |
| 355 | /// The scope which lexically contains this decl. A decl must depend | |
| 356 | /// on its lexical parent, in order to ensure that this pointer is valid. | |
| 357 | /// This scope is allocated out of the arena of the parent decl. | |
| 358 | src_scope: ?*CaptureScope, | |
| 359 | ||
| 288 | 360 | /// An integer that can be checked against the corresponding incrementing |
| 289 | 361 | /// generation field of Module. This is used to determine whether `complete` status |
| 290 | 362 | /// represents pre- or post- re-analysis. |
| ... | ... | @@ -339,7 +411,7 @@ pub const Decl = struct { |
| 339 | 411 | /// to require re-analysis. |
| 340 | 412 | outdated, |
| 341 | 413 | }, |
| 342 | /// Whether `typed_value`, `align_val`, and `linksection_val` are populated. | |
| 414 | /// Whether `typed_value`, `align_val`, `linksection_val` and `addrspace` are populated. | |
| 343 | 415 | has_tv: bool, |
| 344 | 416 | /// If `true` it means the `Decl` is the resource owner of the type/value associated |
| 345 | 417 | /// with it. That means when `Decl` is destroyed, the cleanup code should additionally |
| ... | ... | @@ -354,8 +426,8 @@ pub const Decl = struct { |
| 354 | 426 | is_exported: bool, |
| 355 | 427 | /// Whether the ZIR code provides an align instruction. |
| 356 | 428 | has_align: bool, |
| 357 | /// Whether the ZIR code provides a linksection instruction. | |
| 358 | has_linksection: bool, | |
| 429 | /// Whether the ZIR code provides a linksection and address space instruction. | |
| 430 | has_linksection_or_addrspace: bool, | |
| 359 | 431 | /// Flag used by garbage collection to mark and sweep. |
| 360 | 432 | /// Decls which correspond to an AST node always have this field set to `true`. |
| 361 | 433 | /// Anonymous Decls are initialized with this field set to `false` and then it |
| ... | ... | @@ -477,14 +549,22 @@ pub const Decl = struct { |
| 477 | 549 | if (!decl.has_align) return .none; |
| 478 | 550 | assert(decl.zir_decl_index != 0); |
| 479 | 551 | const zir = decl.namespace.file_scope.zir; |
| 480 | return @intToEnum(Zir.Inst.Ref, zir.extra[decl.zir_decl_index + 6]); | |
| 552 | return @intToEnum(Zir.Inst.Ref, zir.extra[decl.zir_decl_index + 7]); | |
| 481 | 553 | } |
| 482 | 554 | |
| 483 | 555 | pub fn zirLinksectionRef(decl: Decl) Zir.Inst.Ref { |
| 484 | if (!decl.has_linksection) return .none; | |
| 556 | if (!decl.has_linksection_or_addrspace) return .none; | |
| 557 | assert(decl.zir_decl_index != 0); | |
| 558 | const zir = decl.namespace.file_scope.zir; | |
| 559 | const extra_index = decl.zir_decl_index + 7 + @boolToInt(decl.has_align); | |
| 560 | return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | |
| 561 | } | |
| 562 | ||
| 563 | pub fn zirAddrspaceRef(decl: Decl) Zir.Inst.Ref { | |
| 564 | if (!decl.has_linksection_or_addrspace) return .none; | |
| 485 | 565 | assert(decl.zir_decl_index != 0); |
| 486 | 566 | const zir = decl.namespace.file_scope.zir; |
| 487 | const extra_index = decl.zir_decl_index + 6 + @boolToInt(decl.has_align); | |
| 567 | const extra_index = decl.zir_decl_index + 7 + @boolToInt(decl.has_align) + 1; | |
| 488 | 568 | return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); |
| 489 | 569 | } |
| 490 | 570 | |
| ... | ... | @@ -542,11 +622,11 @@ pub const Decl = struct { |
| 542 | 622 | return decl.namespace.renderFullyQualifiedName(unqualified_name, writer); |
| 543 | 623 | } |
| 544 | 624 | |
| 545 | pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![]u8 { | |
| 625 | pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![:0]u8 { | |
| 546 | 626 | var buffer = std.ArrayList(u8).init(gpa); |
| 547 | 627 | defer buffer.deinit(); |
| 548 | 628 | try decl.renderFullyQualifiedName(buffer.writer()); |
| 549 | return buffer.toOwnedSlice(); | |
| 629 | return buffer.toOwnedSliceSentinel(0); | |
| 550 | 630 | } |
| 551 | 631 | |
| 552 | 632 | pub fn typedValue(decl: Decl) error{AnalysisFail}!TypedValue { |
| ... | ... | @@ -588,7 +668,7 @@ pub const Decl = struct { |
| 588 | 668 | |
| 589 | 669 | /// If the Decl has a value and it is a function, return it, |
| 590 | 670 | /// otherwise null. |
| 591 | pub fn getFunction(decl: *Decl) ?*Fn { | |
| 671 | pub fn getFunction(decl: *const Decl) ?*Fn { | |
| 592 | 672 | if (!decl.owns_tv) return null; |
| 593 | 673 | const func = (decl.val.castTag(.function) orelse return null).data; |
| 594 | 674 | assert(func.owner_decl == decl); |
| ... | ... | @@ -733,7 +813,7 @@ pub const Struct = struct { |
| 733 | 813 | is_comptime: bool, |
| 734 | 814 | }; |
| 735 | 815 | |
| 736 | pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 { | |
| 816 | pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![:0]u8 { | |
| 737 | 817 | return s.owner_decl.getFullyQualifiedName(gpa); |
| 738 | 818 | } |
| 739 | 819 | |
| ... | ... | @@ -779,6 +859,36 @@ pub const EnumSimple = struct { |
| 779 | 859 | } |
| 780 | 860 | }; |
| 781 | 861 | |
| 862 | /// Represents the data that an enum declaration provides, when there are no | |
| 863 | /// declarations. However an integer tag type is provided, and the enum tag values | |
| 864 | /// are explicitly provided. | |
| 865 | pub const EnumNumbered = struct { | |
| 866 | /// The Decl that corresponds to the enum itself. | |
| 867 | owner_decl: *Decl, | |
| 868 | /// An integer type which is used for the numerical value of the enum. | |
| 869 | /// Whether zig chooses this type or the user specifies it, it is stored here. | |
| 870 | tag_ty: Type, | |
| 871 | /// Set of field names in declaration order. | |
| 872 | fields: NameMap, | |
| 873 | /// Maps integer tag value to field index. | |
| 874 | /// Entries are in declaration order, same as `fields`. | |
| 875 | /// If this hash map is empty, it means the enum tags are auto-numbered. | |
| 876 | values: ValueMap, | |
| 877 | /// Offset from `owner_decl`, points to the enum decl AST node. | |
| 878 | node_offset: i32, | |
| 879 | ||
| 880 | pub const NameMap = EnumFull.NameMap; | |
| 881 | pub const ValueMap = EnumFull.ValueMap; | |
| 882 | ||
| 883 | pub fn srcLoc(self: EnumNumbered) SrcLoc { | |
| 884 | return .{ | |
| 885 | .file_scope = self.owner_decl.getFileScope(), | |
| 886 | .parent_decl_node = self.owner_decl.src_node, | |
| 887 | .lazy = .{ .node_offset = self.node_offset }, | |
| 888 | }; | |
| 889 | } | |
| 890 | }; | |
| 891 | ||
| 782 | 892 | /// Represents the data that an enum declaration provides, when there is |
| 783 | 893 | /// at least one tag value explicitly specified, or at least one declaration. |
| 784 | 894 | pub const EnumFull = struct { |
| ... | ... | @@ -788,16 +898,17 @@ pub const EnumFull = struct { |
| 788 | 898 | /// Whether zig chooses this type or the user specifies it, it is stored here. |
| 789 | 899 | tag_ty: Type, |
| 790 | 900 | /// Set of field names in declaration order. |
| 791 | fields: std.StringArrayHashMapUnmanaged(void), | |
| 901 | fields: NameMap, | |
| 792 | 902 | /// Maps integer tag value to field index. |
| 793 | 903 | /// Entries are in declaration order, same as `fields`. |
| 794 | 904 | /// If this hash map is empty, it means the enum tags are auto-numbered. |
| 795 | 905 | values: ValueMap, |
| 796 | /// Represents the declarations inside this struct. | |
| 906 | /// Represents the declarations inside this enum. | |
| 797 | 907 | namespace: Scope.Namespace, |
| 798 | 908 | /// Offset from `owner_decl`, points to the enum decl AST node. |
| 799 | 909 | node_offset: i32, |
| 800 | 910 | |
| 911 | pub const NameMap = std.StringArrayHashMapUnmanaged(void); | |
| 801 | 912 | pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false); |
| 802 | 913 | |
| 803 | 914 | pub fn srcLoc(self: EnumFull) SrcLoc { |
| ... | ... | @@ -853,6 +964,44 @@ pub const Union = struct { |
| 853 | 964 | .lazy = .{ .node_offset = self.node_offset }, |
| 854 | 965 | }; |
| 855 | 966 | } |
| 967 | ||
| 968 | pub fn haveFieldTypes(u: Union) bool { | |
| 969 | return switch (u.status) { | |
| 970 | .none, | |
| 971 | .field_types_wip, | |
| 972 | => false, | |
| 973 | .have_field_types, | |
| 974 | .layout_wip, | |
| 975 | .have_layout, | |
| 976 | => true, | |
| 977 | }; | |
| 978 | } | |
| 979 | ||
| 980 | pub fn onlyTagHasCodegenBits(u: Union) bool { | |
| 981 | assert(u.haveFieldTypes()); | |
| 982 | for (u.fields.values()) |field| { | |
| 983 | if (field.ty.hasCodeGenBits()) return false; | |
| 984 | } | |
| 985 | return true; | |
| 986 | } | |
| 987 | ||
| 988 | pub fn mostAlignedField(u: Union, target: Target) u32 { | |
| 989 | assert(u.haveFieldTypes()); | |
| 990 | var most_alignment: u64 = 0; | |
| 991 | var most_index: usize = undefined; | |
| 992 | for (u.fields.values()) |field, i| { | |
| 993 | if (!field.ty.hasCodeGenBits()) continue; | |
| 994 | const field_align = if (field.abi_align.tag() == .abi_align_default) | |
| 995 | field.ty.abiAlignment(target) | |
| 996 | else | |
| 997 | field.abi_align.toUnsignedInt(); | |
| 998 | if (field_align > most_alignment) { | |
| 999 | most_alignment = field_align; | |
| 1000 | most_index = i; | |
| 1001 | } | |
| 1002 | } | |
| 1003 | return @intCast(u32, most_index); | |
| 1004 | } | |
| 856 | 1005 | }; |
| 857 | 1006 | |
| 858 | 1007 | /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. |
| ... | ... | @@ -881,6 +1030,7 @@ pub const Fn = struct { |
| 881 | 1030 | |
| 882 | 1031 | state: Analysis, |
| 883 | 1032 | is_cold: bool = false, |
| 1033 | is_noinline: bool = false, | |
| 884 | 1034 | |
| 885 | 1035 | pub const Analysis = enum { |
| 886 | 1036 | queued, |
| ... | ... | @@ -936,19 +1086,21 @@ pub const Scope = struct { |
| 936 | 1086 | return @fieldParentPtr(T, "base", base); |
| 937 | 1087 | } |
| 938 | 1088 | |
| 939 | pub fn ownerDecl(scope: *Scope) ?*Decl { | |
| 1089 | /// Get the decl which contains this decl, for the purposes of source reporting | |
| 1090 | pub fn srcDecl(scope: *Scope) ?*Decl { | |
| 940 | 1091 | return switch (scope.tag) { |
| 941 | .block => scope.cast(Block).?.sema.owner_decl, | |
| 1092 | .block => scope.cast(Block).?.src_decl, | |
| 942 | 1093 | .file => null, |
| 943 | .namespace => null, | |
| 1094 | .namespace => scope.cast(Namespace).?.getDecl(), | |
| 944 | 1095 | }; |
| 945 | 1096 | } |
| 946 | 1097 | |
| 947 | pub fn srcDecl(scope: *Scope) ?*Decl { | |
| 1098 | /// Get the scope which contains this decl, for resolving closure_get instructions. | |
| 1099 | pub fn srcScope(scope: *Scope) ?*CaptureScope { | |
| 948 | 1100 | return switch (scope.tag) { |
| 949 | .block => scope.cast(Block).?.src_decl, | |
| 1101 | .block => scope.cast(Block).?.wip_capture_scope, | |
| 950 | 1102 | .file => null, |
| 951 | .namespace => scope.cast(Namespace).?.getDecl(), | |
| 1103 | .namespace => scope.cast(Namespace).?.getDecl().src_scope, | |
| 952 | 1104 | }; |
| 953 | 1105 | } |
| 954 | 1106 | |
| ... | ... | @@ -1288,6 +1440,9 @@ pub const Scope = struct { |
| 1288 | 1440 | instructions: ArrayListUnmanaged(Air.Inst.Index), |
| 1289 | 1441 | // `param` instructions are collected here to be used by the `func` instruction. |
| 1290 | 1442 | params: std.ArrayListUnmanaged(Param) = .{}, |
| 1443 | ||
| 1444 | wip_capture_scope: *CaptureScope, | |
| 1445 | ||
| 1291 | 1446 | label: ?*Label = null, |
| 1292 | 1447 | inlining: ?*Inlining, |
| 1293 | 1448 | /// If runtime_index is not 0 then one of these is guaranteed to be non null. |
| ... | ... | @@ -1302,6 +1457,8 @@ pub const Scope = struct { |
| 1302 | 1457 | /// when null, it is determined by build mode, changed by @setRuntimeSafety |
| 1303 | 1458 | want_safety: ?bool = null, |
| 1304 | 1459 | |
| 1460 | c_import_buf: ?*std.ArrayList(u8) = null, | |
| 1461 | ||
| 1305 | 1462 | const Param = struct { |
| 1306 | 1463 | /// `noreturn` means `anytype`. |
| 1307 | 1464 | ty: Type, |
| ... | ... | @@ -1347,6 +1504,7 @@ pub const Scope = struct { |
| 1347 | 1504 | .sema = parent.sema, |
| 1348 | 1505 | .src_decl = parent.src_decl, |
| 1349 | 1506 | .instructions = .{}, |
| 1507 | .wip_capture_scope = parent.wip_capture_scope, | |
| 1350 | 1508 | .label = null, |
| 1351 | 1509 | .inlining = parent.inlining, |
| 1352 | 1510 | .is_comptime = parent.is_comptime, |
| ... | ... | @@ -1354,6 +1512,7 @@ pub const Scope = struct { |
| 1354 | 1512 | .runtime_loop = parent.runtime_loop, |
| 1355 | 1513 | .runtime_index = parent.runtime_index, |
| 1356 | 1514 | .want_safety = parent.want_safety, |
| 1515 | .c_import_buf = parent.c_import_buf, | |
| 1357 | 1516 | }; |
| 1358 | 1517 | } |
| 1359 | 1518 | |
| ... | ... | @@ -1453,6 +1612,40 @@ pub const Scope = struct { |
| 1453 | 1612 | }); |
| 1454 | 1613 | } |
| 1455 | 1614 | |
| 1615 | pub fn addStructFieldPtr( | |
| 1616 | block: *Block, | |
| 1617 | struct_ptr: Air.Inst.Ref, | |
| 1618 | field_index: u32, | |
| 1619 | ptr_field_ty: Type, | |
| 1620 | ) !Air.Inst.Ref { | |
| 1621 | const ty = try block.sema.addType(ptr_field_ty); | |
| 1622 | const tag: Air.Inst.Tag = switch (field_index) { | |
| 1623 | 0 => .struct_field_ptr_index_0, | |
| 1624 | 1 => .struct_field_ptr_index_1, | |
| 1625 | 2 => .struct_field_ptr_index_2, | |
| 1626 | 3 => .struct_field_ptr_index_3, | |
| 1627 | else => { | |
| 1628 | return block.addInst(.{ | |
| 1629 | .tag = .struct_field_ptr, | |
| 1630 | .data = .{ .ty_pl = .{ | |
| 1631 | .ty = ty, | |
| 1632 | .payload = try block.sema.addExtra(Air.StructField{ | |
| 1633 | .struct_operand = struct_ptr, | |
| 1634 | .field_index = @intCast(u32, field_index), | |
| 1635 | }), | |
| 1636 | } }, | |
| 1637 | }); | |
| 1638 | }, | |
| 1639 | }; | |
| 1640 | return block.addInst(.{ | |
| 1641 | .tag = tag, | |
| 1642 | .data = .{ .ty_op = .{ | |
| 1643 | .ty = ty, | |
| 1644 | .operand = struct_ptr, | |
| 1645 | } }, | |
| 1646 | }); | |
| 1647 | } | |
| 1648 | ||
| 1456 | 1649 | pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { |
| 1457 | 1650 | return Air.indexToRef(try block.addInstAsIndex(inst)); |
| 1458 | 1651 | } |
| ... | ... | @@ -2092,39 +2285,39 @@ pub const LazySrcLoc = union(enum) { |
| 2092 | 2285 | node_offset_bin_op: i32, |
| 2093 | 2286 | /// The source location points to the LHS of a binary expression, found |
| 2094 | 2287 | /// by taking this AST node index offset from the containing Decl AST node, |
| 2095 | /// which points to a binary expression AST node. Next, nagivate to the LHS. | |
| 2288 | /// which points to a binary expression AST node. Next, navigate to the LHS. | |
| 2096 | 2289 | /// The Decl is determined contextually. |
| 2097 | 2290 | node_offset_bin_lhs: i32, |
| 2098 | 2291 | /// The source location points to the RHS of a binary expression, found |
| 2099 | 2292 | /// by taking this AST node index offset from the containing Decl AST node, |
| 2100 | /// which points to a binary expression AST node. Next, nagivate to the RHS. | |
| 2293 | /// which points to a binary expression AST node. Next, navigate to the RHS. | |
| 2101 | 2294 | /// The Decl is determined contextually. |
| 2102 | 2295 | node_offset_bin_rhs: i32, |
| 2103 | 2296 | /// The source location points to the operand of a switch expression, found |
| 2104 | 2297 | /// by taking this AST node index offset from the containing Decl AST node, |
| 2105 | /// which points to a switch expression AST node. Next, nagivate to the operand. | |
| 2298 | /// which points to a switch expression AST node. Next, navigate to the operand. | |
| 2106 | 2299 | /// The Decl is determined contextually. |
| 2107 | 2300 | node_offset_switch_operand: i32, |
| 2108 | 2301 | /// The source location points to the else/`_` prong of a switch expression, found |
| 2109 | 2302 | /// by taking this AST node index offset from the containing Decl AST node, |
| 2110 | /// which points to a switch expression AST node. Next, nagivate to the else/`_` prong. | |
| 2303 | /// which points to a switch expression AST node. Next, navigate to the else/`_` prong. | |
| 2111 | 2304 | /// The Decl is determined contextually. |
| 2112 | 2305 | node_offset_switch_special_prong: i32, |
| 2113 | 2306 | /// The source location points to all the ranges of a switch expression, found |
| 2114 | 2307 | /// by taking this AST node index offset from the containing Decl AST node, |
| 2115 | /// which points to a switch expression AST node. Next, nagivate to any of the | |
| 2308 | /// which points to a switch expression AST node. Next, navigate to any of the | |
| 2116 | 2309 | /// range nodes. The error applies to all of them. |
| 2117 | 2310 | /// The Decl is determined contextually. |
| 2118 | 2311 | node_offset_switch_range: i32, |
| 2119 | 2312 | /// The source location points to the calling convention of a function type |
| 2120 | 2313 | /// expression, found by taking this AST node index offset from the containing |
| 2121 | /// Decl AST node, which points to a function type AST node. Next, nagivate to | |
| 2314 | /// Decl AST node, which points to a function type AST node. Next, navigate to | |
| 2122 | 2315 | /// the calling convention node. |
| 2123 | 2316 | /// The Decl is determined contextually. |
| 2124 | 2317 | node_offset_fn_type_cc: i32, |
| 2125 | 2318 | /// The source location points to the return type of a function type |
| 2126 | 2319 | /// expression, found by taking this AST node index offset from the containing |
| 2127 | /// Decl AST node, which points to a function type AST node. Next, nagivate to | |
| 2320 | /// Decl AST node, which points to a function type AST node. Next, navigate to | |
| 2128 | 2321 | /// the return type node. |
| 2129 | 2322 | /// The Decl is determined contextually. |
| 2130 | 2323 | node_offset_fn_type_ret_ty: i32, |
| ... | ... | @@ -2347,6 +2540,7 @@ pub fn deinit(mod: *Module) void { |
| 2347 | 2540 | |
| 2348 | 2541 | mod.error_name_list.deinit(gpa); |
| 2349 | 2542 | mod.test_functions.deinit(gpa); |
| 2543 | mod.align_stack_fns.deinit(gpa); | |
| 2350 | 2544 | mod.monomorphed_funcs.deinit(gpa); |
| 2351 | 2545 | |
| 2352 | 2546 | { |
| ... | ... | @@ -2362,6 +2556,7 @@ pub fn deinit(mod: *Module) void { |
| 2362 | 2556 | fn freeExportList(gpa: *Allocator, export_list: []*Export) void { |
| 2363 | 2557 | for (export_list) |exp| { |
| 2364 | 2558 | gpa.free(exp.options.name); |
| 2559 | if (exp.options.section) |s| gpa.free(s); | |
| 2365 | 2560 | gpa.destroy(exp); |
| 2366 | 2561 | } |
| 2367 | 2562 | gpa.free(export_list); |
| ... | ... | @@ -2372,7 +2567,7 @@ const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8; |
| 2372 | 2567 | // We need a better language feature for initializing a union with |
| 2373 | 2568 | // a runtime known tag. |
| 2374 | 2569 | const Stage1DataLayout = extern struct { |
| 2375 | data: [8]u8 align(8), | |
| 2570 | data: [8]u8 align(@alignOf(Zir.Inst.Data)), | |
| 2376 | 2571 | safety_tag: u8, |
| 2377 | 2572 | }; |
| 2378 | 2573 | comptime { |
| ... | ... | @@ -2873,12 +3068,10 @@ pub fn mapOldZirToNew( |
| 2873 | 3068 | var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{}; |
| 2874 | 3069 | defer match_stack.deinit(gpa); |
| 2875 | 3070 | |
| 2876 | const old_main_struct_inst = old_zir.getMainStruct(); | |
| 2877 | const new_main_struct_inst = new_zir.getMainStruct(); | |
| 2878 | ||
| 3071 | // Main struct inst is always the same | |
| 2879 | 3072 | try match_stack.append(gpa, .{ |
| 2880 | .old_inst = old_main_struct_inst, | |
| 2881 | .new_inst = new_main_struct_inst, | |
| 3073 | .old_inst = Zir.main_struct_inst, | |
| 3074 | .new_inst = Zir.main_struct_inst, | |
| 2882 | 3075 | }); |
| 2883 | 3076 | |
| 2884 | 3077 | var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa); |
| ... | ... | @@ -3036,6 +3229,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void { |
| 3036 | 3229 | const struct_obj = try new_decl_arena.allocator.create(Module.Struct); |
| 3037 | 3230 | const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj); |
| 3038 | 3231 | const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty); |
| 3232 | const ty_ty = comptime Type.initTag(.type); | |
| 3039 | 3233 | struct_obj.* = .{ |
| 3040 | 3234 | .owner_decl = undefined, // set below |
| 3041 | 3235 | .fields = .{}, |
| ... | ... | @@ -3050,7 +3244,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void { |
| 3050 | 3244 | .file_scope = file, |
| 3051 | 3245 | }, |
| 3052 | 3246 | }; |
| 3053 | const new_decl = try mod.allocateNewDecl(&struct_obj.namespace, 0); | |
| 3247 | const new_decl = try mod.allocateNewDecl(&struct_obj.namespace, 0, null); | |
| 3054 | 3248 | file.root_decl = new_decl; |
| 3055 | 3249 | struct_obj.owner_decl = new_decl; |
| 3056 | 3250 | new_decl.src_line = 0; |
| ... | ... | @@ -3058,8 +3252,8 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void { |
| 3058 | 3252 | new_decl.is_pub = true; |
| 3059 | 3253 | new_decl.is_exported = false; |
| 3060 | 3254 | new_decl.has_align = false; |
| 3061 | new_decl.has_linksection = false; | |
| 3062 | new_decl.ty = struct_ty; | |
| 3255 | new_decl.has_linksection_or_addrspace = false; | |
| 3256 | new_decl.ty = ty_ty; | |
| 3063 | 3257 | new_decl.val = struct_val; |
| 3064 | 3258 | new_decl.has_tv = true; |
| 3065 | 3259 | new_decl.owns_tv = true; |
| ... | ... | @@ -3069,7 +3263,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void { |
| 3069 | 3263 | |
| 3070 | 3264 | if (file.status == .success_zir) { |
| 3071 | 3265 | assert(file.zir_loaded); |
| 3072 | const main_struct_inst = file.zir.getMainStruct(); | |
| 3266 | const main_struct_inst = Zir.main_struct_inst; | |
| 3073 | 3267 | struct_obj.zir_index = main_struct_inst; |
| 3074 | 3268 | |
| 3075 | 3269 | var sema_arena = std.heap.ArenaAllocator.init(gpa); |
| ... | ... | @@ -3079,6 +3273,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void { |
| 3079 | 3273 | .mod = mod, |
| 3080 | 3274 | .gpa = gpa, |
| 3081 | 3275 | .arena = &sema_arena.allocator, |
| 3276 | .perm_arena = &new_decl_arena.allocator, | |
| 3082 | 3277 | .code = file.zir, |
| 3083 | 3278 | .owner_decl = new_decl, |
| 3084 | 3279 | .namespace = &struct_obj.namespace, |
| ... | ... | @@ -3087,10 +3282,15 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void { |
| 3087 | 3282 | .owner_func = null, |
| 3088 | 3283 | }; |
| 3089 | 3284 | defer sema.deinit(); |
| 3285 | ||
| 3286 | var wip_captures = try WipCaptureScope.init(gpa, &new_decl_arena.allocator, null); | |
| 3287 | defer wip_captures.deinit(); | |
| 3288 | ||
| 3090 | 3289 | var block_scope: Scope.Block = .{ |
| 3091 | 3290 | .parent = null, |
| 3092 | 3291 | .sema = &sema, |
| 3093 | 3292 | .src_decl = new_decl, |
| 3293 | .wip_capture_scope = wip_captures.scope, | |
| 3094 | 3294 | .instructions = .{}, |
| 3095 | 3295 | .inlining = null, |
| 3096 | 3296 | .is_comptime = true, |
| ... | ... | @@ -3098,6 +3298,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void { |
| 3098 | 3298 | defer block_scope.instructions.deinit(gpa); |
| 3099 | 3299 | |
| 3100 | 3300 | if (sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj)) |_| { |
| 3301 | try wip_captures.finalize(); | |
| 3101 | 3302 | new_decl.analysis = .complete; |
| 3102 | 3303 | } else |err| switch (err) { |
| 3103 | 3304 | error.OutOfMemory => return error.OutOfMemory, |
| ... | ... | @@ -3127,6 +3328,10 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3127 | 3328 | |
| 3128 | 3329 | decl.analysis = .in_progress; |
| 3129 | 3330 | |
| 3331 | // We need the memory for the Type to go into the arena for the Decl | |
| 3332 | var decl_arena = std.heap.ArenaAllocator.init(gpa); | |
| 3333 | errdefer decl_arena.deinit(); | |
| 3334 | ||
| 3130 | 3335 | var analysis_arena = std.heap.ArenaAllocator.init(gpa); |
| 3131 | 3336 | defer analysis_arena.deinit(); |
| 3132 | 3337 | |
| ... | ... | @@ -3134,6 +3339,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3134 | 3339 | .mod = mod, |
| 3135 | 3340 | .gpa = gpa, |
| 3136 | 3341 | .arena = &analysis_arena.allocator, |
| 3342 | .perm_arena = &decl_arena.allocator, | |
| 3137 | 3343 | .code = zir, |
| 3138 | 3344 | .owner_decl = decl, |
| 3139 | 3345 | .namespace = decl.namespace, |
| ... | ... | @@ -3145,7 +3351,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3145 | 3351 | |
| 3146 | 3352 | if (decl.isRoot()) { |
| 3147 | 3353 | log.debug("semaDecl root {*} ({s})", .{ decl, decl.name }); |
| 3148 | const main_struct_inst = zir.getMainStruct(); | |
| 3354 | const main_struct_inst = Zir.main_struct_inst; | |
| 3149 | 3355 | const struct_obj = decl.getStruct().?; |
| 3150 | 3356 | // This might not have gotten set in `semaFile` if the first time had |
| 3151 | 3357 | // a ZIR failure, so we set it here in case. |
| ... | ... | @@ -3157,10 +3363,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3157 | 3363 | } |
| 3158 | 3364 | log.debug("semaDecl {*} ({s})", .{ decl, decl.name }); |
| 3159 | 3365 | |
| 3366 | var wip_captures = try WipCaptureScope.init(gpa, &decl_arena.allocator, decl.src_scope); | |
| 3367 | defer wip_captures.deinit(); | |
| 3368 | ||
| 3160 | 3369 | var block_scope: Scope.Block = .{ |
| 3161 | 3370 | .parent = null, |
| 3162 | 3371 | .sema = &sema, |
| 3163 | 3372 | .src_decl = decl, |
| 3373 | .wip_capture_scope = wip_captures.scope, | |
| 3164 | 3374 | .instructions = .{}, |
| 3165 | 3375 | .inlining = null, |
| 3166 | 3376 | .is_comptime = true, |
| ... | ... | @@ -3175,6 +3385,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3175 | 3385 | const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index); |
| 3176 | 3386 | const body = zir.extra[extra.end..][0..extra.data.body_len]; |
| 3177 | 3387 | const break_index = try sema.analyzeBody(&block_scope, body); |
| 3388 | try wip_captures.finalize(); | |
| 3178 | 3389 | const result_ref = zir_datas[break_index].@"break".operand; |
| 3179 | 3390 | const src: LazySrcLoc = .{ .node_offset = 0 }; |
| 3180 | 3391 | const decl_tv = try sema.resolveInstValue(&block_scope, src, result_ref); |
| ... | ... | @@ -3188,14 +3399,29 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3188 | 3399 | if (linksection_ref == .none) break :blk Value.initTag(.null_value); |
| 3189 | 3400 | break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val; |
| 3190 | 3401 | }; |
| 3402 | const address_space = blk: { | |
| 3403 | const addrspace_ctx: Sema.AddressSpaceContext = switch (decl_tv.val.tag()) { | |
| 3404 | .function, .extern_fn => .function, | |
| 3405 | .variable => .variable, | |
| 3406 | else => .constant, | |
| 3407 | }; | |
| 3408 | ||
| 3409 | break :blk switch (decl.zirAddrspaceRef()) { | |
| 3410 | .none => switch (addrspace_ctx) { | |
| 3411 | .function => target_util.defaultAddressSpace(sema.mod.getTarget(), .function), | |
| 3412 | .variable => target_util.defaultAddressSpace(sema.mod.getTarget(), .global_mutable), | |
| 3413 | .constant => target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant), | |
| 3414 | else => unreachable, | |
| 3415 | }, | |
| 3416 | else => |addrspace_ref| try sema.analyzeAddrspace(&block_scope, src, addrspace_ref, addrspace_ctx), | |
| 3417 | }; | |
| 3418 | }; | |
| 3419 | ||
| 3191 | 3420 | // Note this resolves the type of the Decl, not the value; if this Decl |
| 3192 | 3421 | // is a struct, for example, this resolves `type` (which needs no resolution), |
| 3193 | 3422 | // not the struct itself. |
| 3194 | 3423 | try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty); |
| 3195 | 3424 | |
| 3196 | // We need the memory for the Type to go into the arena for the Decl | |
| 3197 | var decl_arena = std.heap.ArenaAllocator.init(gpa); | |
| 3198 | errdefer decl_arena.deinit(); | |
| 3199 | 3425 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 3200 | 3426 | |
| 3201 | 3427 | if (decl.is_usingnamespace) { |
| ... | ... | @@ -3244,6 +3470,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3244 | 3470 | decl.val = try decl_tv.val.copy(&decl_arena.allocator); |
| 3245 | 3471 | decl.align_val = try align_val.copy(&decl_arena.allocator); |
| 3246 | 3472 | decl.linksection_val = try linksection_val.copy(&decl_arena.allocator); |
| 3473 | decl.@"addrspace" = address_space; | |
| 3247 | 3474 | decl.has_tv = true; |
| 3248 | 3475 | decl.owns_tv = owns_tv; |
| 3249 | 3476 | decl_arena_state.* = decl_arena.state; |
| ... | ... | @@ -3271,7 +3498,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3271 | 3498 | return mod.fail(&block_scope.base, export_src, "export of inline function", .{}); |
| 3272 | 3499 | } |
| 3273 | 3500 | // The scope needs to have the decl in it. |
| 3274 | try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl); | |
| 3501 | const options: std.builtin.ExportOptions = .{ .name = mem.spanZ(decl.name) }; | |
| 3502 | try mod.analyzeExport(&block_scope, export_src, options, decl); | |
| 3275 | 3503 | } |
| 3276 | 3504 | return type_changed or is_inline != prev_is_inline; |
| 3277 | 3505 | } |
| ... | ... | @@ -3305,6 +3533,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3305 | 3533 | decl.val = try decl_tv.val.copy(&decl_arena.allocator); |
| 3306 | 3534 | decl.align_val = try align_val.copy(&decl_arena.allocator); |
| 3307 | 3535 | decl.linksection_val = try linksection_val.copy(&decl_arena.allocator); |
| 3536 | decl.@"addrspace" = address_space; | |
| 3308 | 3537 | decl.has_tv = true; |
| 3309 | 3538 | decl_arena_state.* = decl_arena.state; |
| 3310 | 3539 | decl.value_arena = decl_arena_state; |
| ... | ... | @@ -3329,7 +3558,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3329 | 3558 | if (decl.is_exported) { |
| 3330 | 3559 | const export_src = src; // TODO point to the export token |
| 3331 | 3560 | // The scope needs to have the decl in it. |
| 3332 | try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl); | |
| 3561 | const options: std.builtin.ExportOptions = .{ .name = mem.spanZ(decl.name) }; | |
| 3562 | try mod.analyzeExport(&block_scope, export_src, options, decl); | |
| 3333 | 3563 | } |
| 3334 | 3564 | |
| 3335 | 3565 | return type_changed; |
| ... | ... | @@ -3490,7 +3720,7 @@ pub fn scanNamespace( |
| 3490 | 3720 | const zir = namespace.file_scope.zir; |
| 3491 | 3721 | |
| 3492 | 3722 | try mod.comp.work_queue.ensureUnusedCapacity(decls_len); |
| 3493 | try namespace.decls.ensureCapacity(gpa, decls_len); | |
| 3723 | try namespace.decls.ensureTotalCapacity(gpa, decls_len); | |
| 3494 | 3724 | |
| 3495 | 3725 | const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; |
| 3496 | 3726 | var extra_index = extra_start + bit_bags_count; |
| ... | ... | @@ -3512,8 +3742,8 @@ pub fn scanNamespace( |
| 3512 | 3742 | |
| 3513 | 3743 | const decl_sub_index = extra_index; |
| 3514 | 3744 | extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1) |
| 3515 | extra_index += @truncate(u1, flags >> 2); | |
| 3516 | extra_index += @truncate(u1, flags >> 3); | |
| 3745 | extra_index += @truncate(u1, flags >> 2); // Align | |
| 3746 | extra_index += @as(u2, @truncate(u1, flags >> 3)) * 2; // Link section or address space, consists of 2 Refs | |
| 3517 | 3747 | |
| 3518 | 3748 | try scanDecl(&scan_decl_iter, decl_sub_index, flags); |
| 3519 | 3749 | } |
| ... | ... | @@ -3539,10 +3769,10 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi |
| 3539 | 3769 | const zir = namespace.file_scope.zir; |
| 3540 | 3770 | |
| 3541 | 3771 | // zig fmt: off |
| 3542 | const is_pub = (flags & 0b0001) != 0; | |
| 3543 | const export_bit = (flags & 0b0010) != 0; | |
| 3544 | const has_align = (flags & 0b0100) != 0; | |
| 3545 | const has_linksection = (flags & 0b1000) != 0; | |
| 3772 | const is_pub = (flags & 0b0001) != 0; | |
| 3773 | const export_bit = (flags & 0b0010) != 0; | |
| 3774 | const has_align = (flags & 0b0100) != 0; | |
| 3775 | const has_linksection_or_addrspace = (flags & 0b1000) != 0; | |
| 3546 | 3776 | // zig fmt: on |
| 3547 | 3777 | |
| 3548 | 3778 | const line = iter.parent_decl.relativeToLine(zir.extra[decl_sub_index + 4]); |
| ... | ... | @@ -3588,7 +3818,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi |
| 3588 | 3818 | // We create a Decl for it regardless of analysis status. |
| 3589 | 3819 | const gop = try namespace.decls.getOrPut(gpa, decl_name); |
| 3590 | 3820 | if (!gop.found_existing) { |
| 3591 | const new_decl = try mod.allocateNewDecl(namespace, decl_node); | |
| 3821 | const new_decl = try mod.allocateNewDecl(namespace, decl_node, iter.parent_decl.src_scope); | |
| 3592 | 3822 | if (is_usingnamespace) { |
| 3593 | 3823 | namespace.usingnamespace_set.putAssumeCapacity(new_decl, is_pub); |
| 3594 | 3824 | } |
| ... | ... | @@ -3625,7 +3855,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi |
| 3625 | 3855 | new_decl.is_exported = is_exported; |
| 3626 | 3856 | new_decl.is_usingnamespace = is_usingnamespace; |
| 3627 | 3857 | new_decl.has_align = has_align; |
| 3628 | new_decl.has_linksection = has_linksection; | |
| 3858 | new_decl.has_linksection_or_addrspace = has_linksection_or_addrspace; | |
| 3629 | 3859 | new_decl.zir_decl_index = @intCast(u32, decl_sub_index); |
| 3630 | 3860 | new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive. |
| 3631 | 3861 | return; |
| ... | ... | @@ -3642,7 +3872,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi |
| 3642 | 3872 | decl.is_exported = is_exported; |
| 3643 | 3873 | decl.is_usingnamespace = is_usingnamespace; |
| 3644 | 3874 | decl.has_align = has_align; |
| 3645 | decl.has_linksection = has_linksection; | |
| 3875 | decl.has_linksection_or_addrspace = has_linksection_or_addrspace; | |
| 3646 | 3876 | decl.zir_decl_index = @intCast(u32, decl_sub_index); |
| 3647 | 3877 | if (decl.getFunction()) |_| { |
| 3648 | 3878 | switch (mod.comp.bin_file.tag) { |
| ... | ... | @@ -3660,7 +3890,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi |
| 3660 | 3890 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); |
| 3661 | 3891 | }, |
| 3662 | 3892 | .plan9 => { |
| 3663 | // TODO implement for plan9 | |
| 3893 | // TODO Look into detecting when this would be unnecessary by storing enough state | |
| 3894 | // in `Decl` to notice that the line number did not change. | |
| 3895 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); | |
| 3664 | 3896 | }, |
| 3665 | 3897 | .c, .wasm, .spirv => {}, |
| 3666 | 3898 | } |
| ... | ... | @@ -3706,13 +3938,6 @@ pub fn clearDecl( |
| 3706 | 3938 | dep.removeDependency(decl); |
| 3707 | 3939 | if (outdated_decls) |map| { |
| 3708 | 3940 | map.putAssumeCapacity(dep, {}); |
| 3709 | } else if (std.debug.runtime_safety) { | |
| 3710 | // If `outdated_decls` is `null`, it means we're being called from | |
| 3711 | // `Compilation` after `performAllTheWork` and we cannot queue up any | |
| 3712 | // more work. `dep` must necessarily be another Decl that is no longer | |
| 3713 | // being referenced, and will be in the `deletion_set`. Otherwise, | |
| 3714 | // something has gone wrong. | |
| 3715 | assert(mod.deletion_set.contains(dep)); | |
| 3716 | 3941 | } |
| 3717 | 3942 | } |
| 3718 | 3943 | decl.dependants.clearRetainingCapacity(); |
| ... | ... | @@ -3740,7 +3965,7 @@ pub fn clearDecl( |
| 3740 | 3965 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, |
| 3741 | 3966 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, |
| 3742 | 3967 | .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, |
| 3743 | .c => .{ .c = link.File.C.DeclBlock.empty }, | |
| 3968 | .c => .{ .c = {} }, | |
| 3744 | 3969 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, |
| 3745 | 3970 | .spirv => .{ .spirv = {} }, |
| 3746 | 3971 | }; |
| ... | ... | @@ -3749,7 +3974,7 @@ pub fn clearDecl( |
| 3749 | 3974 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, |
| 3750 | 3975 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, |
| 3751 | 3976 | .plan9 => .{ .plan9 = {} }, |
| 3752 | .c => .{ .c = link.File.C.FnBlock.empty }, | |
| 3977 | .c => .{ .c = {} }, | |
| 3753 | 3978 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, |
| 3754 | 3979 | .spirv => .{ .spirv = .{} }, |
| 3755 | 3980 | }; |
| ... | ... | @@ -3779,10 +4004,13 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void { |
| 3779 | 4004 | // about the Decl in the first place. |
| 3780 | 4005 | // Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we |
| 3781 | 4006 | // must call `freeDecl` in the linker backend now. |
| 3782 | if (decl.has_tv) { | |
| 3783 | if (decl.ty.hasCodeGenBits()) { | |
| 3784 | mod.comp.bin_file.freeDecl(decl); | |
| 3785 | } | |
| 4007 | switch (mod.comp.bin_file.tag) { | |
| 4008 | .c => {}, // this linker backend has already migrated to the new API | |
| 4009 | else => if (decl.has_tv) { | |
| 4010 | if (decl.ty.hasCodeGenBits()) { | |
| 4011 | mod.comp.bin_file.freeDecl(decl); | |
| 4012 | } | |
| 4013 | }, | |
| 3786 | 4014 | } |
| 3787 | 4015 | |
| 3788 | 4016 | const dependants = decl.dependants.keys(); |
| ... | ... | @@ -3800,7 +4028,7 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void { |
| 3800 | 4028 | |
| 3801 | 4029 | pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void { |
| 3802 | 4030 | log.debug("deleteAnonDecl {*} ({s})", .{ decl, decl.name }); |
| 3803 | const scope_decl = scope.ownerDecl().?; | |
| 4031 | const scope_decl = scope.srcDecl().?; | |
| 3804 | 4032 | assert(scope_decl.namespace.anon_decls.swapRemove(decl)); |
| 3805 | 4033 | decl.destroy(mod); |
| 3806 | 4034 | } |
| ... | ... | @@ -3844,22 +4072,21 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void { |
| 3844 | 4072 | mod.gpa.free(kv.value); |
| 3845 | 4073 | } |
| 3846 | 4074 | |
| 3847 | pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { | |
| 4075 | pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: *Allocator) SemaError!Air { | |
| 3848 | 4076 | const tracy = trace(@src()); |
| 3849 | 4077 | defer tracy.end(); |
| 3850 | 4078 | |
| 3851 | 4079 | const gpa = mod.gpa; |
| 3852 | 4080 | |
| 3853 | // Use the Decl's arena for function memory. | |
| 3854 | var arena = decl.value_arena.?.promote(gpa); | |
| 3855 | defer decl.value_arena.?.* = arena.state; | |
| 3856 | ||
| 3857 | const fn_ty = decl.ty; | |
| 4081 | // Use the Decl's arena for captured values. | |
| 4082 | var decl_arena = decl.value_arena.?.promote(gpa); | |
| 4083 | defer decl.value_arena.?.* = decl_arena.state; | |
| 3858 | 4084 | |
| 3859 | 4085 | var sema: Sema = .{ |
| 3860 | 4086 | .mod = mod, |
| 3861 | 4087 | .gpa = gpa, |
| 3862 | .arena = &arena.allocator, | |
| 4088 | .arena = arena, | |
| 4089 | .perm_arena = &decl_arena.allocator, | |
| 3863 | 4090 | .code = decl.namespace.file_scope.zir, |
| 3864 | 4091 | .owner_decl = decl, |
| 3865 | 4092 | .namespace = decl.namespace, |
| ... | ... | @@ -3874,10 +4101,14 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { |
| 3874 | 4101 | try sema.air_extra.ensureTotalCapacity(gpa, reserved_count); |
| 3875 | 4102 | sema.air_extra.items.len += reserved_count; |
| 3876 | 4103 | |
| 4104 | var wip_captures = try WipCaptureScope.init(gpa, &decl_arena.allocator, decl.src_scope); | |
| 4105 | defer wip_captures.deinit(); | |
| 4106 | ||
| 3877 | 4107 | var inner_block: Scope.Block = .{ |
| 3878 | 4108 | .parent = null, |
| 3879 | 4109 | .sema = &sema, |
| 3880 | 4110 | .src_decl = decl, |
| 4111 | .wip_capture_scope = wip_captures.scope, | |
| 3881 | 4112 | .instructions = .{}, |
| 3882 | 4113 | .inlining = null, |
| 3883 | 4114 | .is_comptime = false, |
| ... | ... | @@ -3893,6 +4124,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { |
| 3893 | 4124 | // This could be a generic function instantiation, however, in which case we need to |
| 3894 | 4125 | // map the comptime parameters to constant values and only emit arg AIR instructions |
| 3895 | 4126 | // for the runtime ones. |
| 4127 | const fn_ty = decl.ty; | |
| 3896 | 4128 | const runtime_params_len = @intCast(u32, fn_ty.fnParamLen()); |
| 3897 | 4129 | try inner_block.instructions.ensureTotalCapacity(gpa, runtime_params_len); |
| 3898 | 4130 | try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len * 2); // * 2 for the `addType` |
| ... | ... | @@ -3952,6 +4184,8 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { |
| 3952 | 4184 | else => |e| return e, |
| 3953 | 4185 | }; |
| 3954 | 4186 | |
| 4187 | try wip_captures.finalize(); | |
| 4188 | ||
| 3955 | 4189 | // Copy the block into place and mark that as the main block. |
| 3956 | 4190 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + |
| 3957 | 4191 | inner_block.instructions.items.len); |
| ... | ... | @@ -3977,6 +4211,12 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { |
| 3977 | 4211 | if (mod.failed_decls.fetchSwapRemove(decl)) |kv| { |
| 3978 | 4212 | kv.value.destroy(mod.gpa); |
| 3979 | 4213 | } |
| 4214 | if (decl.has_tv and decl.owns_tv) { | |
| 4215 | if (decl.val.castTag(.function)) |payload| { | |
| 4216 | const func = payload.data; | |
| 4217 | _ = mod.align_stack_fns.remove(func); | |
| 4218 | } | |
| 4219 | } | |
| 3980 | 4220 | if (mod.emit_h) |emit_h| { |
| 3981 | 4221 | if (emit_h.failed_decls.fetchSwapRemove(decl)) |kv| { |
| 3982 | 4222 | kv.value.destroy(mod.gpa); |
| ... | ... | @@ -3986,7 +4226,7 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { |
| 3986 | 4226 | decl.analysis = .outdated; |
| 3987 | 4227 | } |
| 3988 | 4228 | |
| 3989 | pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.Node.Index) !*Decl { | |
| 4229 | pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.Node.Index, src_scope: ?*CaptureScope) !*Decl { | |
| 3990 | 4230 | // If we have emit-h then we must allocate a bigger structure to store the emit-h state. |
| 3991 | 4231 | const new_decl: *Decl = if (mod.emit_h != null) blk: { |
| 3992 | 4232 | const parent_struct = try mod.gpa.create(DeclPlusEmitH); |
| ... | ... | @@ -4008,15 +4248,17 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast. |
| 4008 | 4248 | .val = undefined, |
| 4009 | 4249 | .align_val = undefined, |
| 4010 | 4250 | .linksection_val = undefined, |
| 4251 | .@"addrspace" = undefined, | |
| 4011 | 4252 | .analysis = .unreferenced, |
| 4012 | 4253 | .deletion_flag = false, |
| 4013 | 4254 | .zir_decl_index = 0, |
| 4255 | .src_scope = src_scope, | |
| 4014 | 4256 | .link = switch (mod.comp.bin_file.tag) { |
| 4015 | 4257 | .coff => .{ .coff = link.File.Coff.TextBlock.empty }, |
| 4016 | 4258 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, |
| 4017 | 4259 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, |
| 4018 | 4260 | .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, |
| 4019 | .c => .{ .c = link.File.C.DeclBlock.empty }, | |
| 4261 | .c => .{ .c = {} }, | |
| 4020 | 4262 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, |
| 4021 | 4263 | .spirv => .{ .spirv = {} }, |
| 4022 | 4264 | }, |
| ... | ... | @@ -4025,18 +4267,19 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast. |
| 4025 | 4267 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, |
| 4026 | 4268 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, |
| 4027 | 4269 | .plan9 => .{ .plan9 = {} }, |
| 4028 | .c => .{ .c = link.File.C.FnBlock.empty }, | |
| 4270 | .c => .{ .c = {} }, | |
| 4029 | 4271 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, |
| 4030 | 4272 | .spirv => .{ .spirv = .{} }, |
| 4031 | 4273 | }, |
| 4032 | 4274 | .generation = 0, |
| 4033 | 4275 | .is_pub = false, |
| 4034 | 4276 | .is_exported = false, |
| 4035 | .has_linksection = false, | |
| 4277 | .has_linksection_or_addrspace = false, | |
| 4036 | 4278 | .has_align = false, |
| 4037 | 4279 | .alive = false, |
| 4038 | 4280 | .is_usingnamespace = false, |
| 4039 | 4281 | }; |
| 4282 | ||
| 4040 | 4283 | return new_decl; |
| 4041 | 4284 | } |
| 4042 | 4285 | |
| ... | ... | @@ -4051,7 +4294,7 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged |
| 4051 | 4294 | } |
| 4052 | 4295 | |
| 4053 | 4296 | errdefer assert(mod.global_error_set.remove(name)); |
| 4054 | try mod.error_name_list.ensureCapacity(mod.gpa, mod.error_name_list.items.len + 1); | |
| 4297 | try mod.error_name_list.ensureUnusedCapacity(mod.gpa, 1); | |
| 4055 | 4298 | gop.key_ptr.* = try mod.gpa.dupe(u8, name); |
| 4056 | 4299 | gop.value_ptr.* = @intCast(ErrorInt, mod.error_name_list.items.len); |
| 4057 | 4300 | mod.error_name_list.appendAssumeCapacity(gop.key_ptr.*); |
| ... | ... | @@ -4063,34 +4306,44 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged |
| 4063 | 4306 | |
| 4064 | 4307 | pub fn analyzeExport( |
| 4065 | 4308 | mod: *Module, |
| 4066 | scope: *Scope, | |
| 4309 | block: *Scope.Block, | |
| 4067 | 4310 | src: LazySrcLoc, |
| 4068 | borrowed_symbol_name: []const u8, | |
| 4311 | borrowed_options: std.builtin.ExportOptions, | |
| 4069 | 4312 | exported_decl: *Decl, |
| 4070 | 4313 | ) !void { |
| 4071 | 4314 | try mod.ensureDeclAnalyzed(exported_decl); |
| 4072 | 4315 | switch (exported_decl.ty.zigTypeTag()) { |
| 4073 | 4316 | .Fn => {}, |
| 4074 | else => return mod.fail(scope, src, "unable to export type '{}'", .{exported_decl.ty}), | |
| 4317 | else => return mod.fail(&block.base, src, "unable to export type '{}'", .{exported_decl.ty}), | |
| 4075 | 4318 | } |
| 4076 | 4319 | |
| 4077 | try mod.decl_exports.ensureUnusedCapacity(mod.gpa, 1); | |
| 4078 | try mod.export_owners.ensureUnusedCapacity(mod.gpa, 1); | |
| 4320 | const gpa = mod.gpa; | |
| 4321 | ||
| 4322 | try mod.decl_exports.ensureUnusedCapacity(gpa, 1); | |
| 4323 | try mod.export_owners.ensureUnusedCapacity(gpa, 1); | |
| 4324 | ||
| 4325 | const new_export = try gpa.create(Export); | |
| 4326 | errdefer gpa.destroy(new_export); | |
| 4079 | 4327 | |
| 4080 | const new_export = try mod.gpa.create(Export); | |
| 4081 | errdefer mod.gpa.destroy(new_export); | |
| 4328 | const symbol_name = try gpa.dupe(u8, borrowed_options.name); | |
| 4329 | errdefer gpa.free(symbol_name); | |
| 4082 | 4330 | |
| 4083 | const symbol_name = try mod.gpa.dupe(u8, borrowed_symbol_name); | |
| 4084 | errdefer mod.gpa.free(symbol_name); | |
| 4331 | const section: ?[]const u8 = if (borrowed_options.section) |s| try gpa.dupe(u8, s) else null; | |
| 4332 | errdefer if (section) |s| gpa.free(s); | |
| 4085 | 4333 | |
| 4086 | const owner_decl = scope.ownerDecl().?; | |
| 4334 | const src_decl = block.src_decl; | |
| 4335 | const owner_decl = block.sema.owner_decl; | |
| 4087 | 4336 | |
| 4088 | 4337 | log.debug("exporting Decl '{s}' as symbol '{s}' from Decl '{s}'", .{ |
| 4089 | exported_decl.name, borrowed_symbol_name, owner_decl.name, | |
| 4338 | exported_decl.name, symbol_name, owner_decl.name, | |
| 4090 | 4339 | }); |
| 4091 | 4340 | |
| 4092 | 4341 | new_export.* = .{ |
| 4093 | .options = .{ .name = symbol_name }, | |
| 4342 | .options = .{ | |
| 4343 | .name = symbol_name, | |
| 4344 | .linkage = borrowed_options.linkage, | |
| 4345 | .section = section, | |
| 4346 | }, | |
| 4094 | 4347 | .src = src, |
| 4095 | 4348 | .link = switch (mod.comp.bin_file.tag) { |
| 4096 | 4349 | .coff => .{ .coff = {} }, |
| ... | ... | @@ -4102,6 +4355,7 @@ pub fn analyzeExport( |
| 4102 | 4355 | .spirv => .{ .spirv = {} }, |
| 4103 | 4356 | }, |
| 4104 | 4357 | .owner_decl = owner_decl, |
| 4358 | .src_decl = src_decl, | |
| 4105 | 4359 | .exported_decl = exported_decl, |
| 4106 | 4360 | .status = .in_progress, |
| 4107 | 4361 | }; |
| ... | ... | @@ -4111,18 +4365,18 @@ pub fn analyzeExport( |
| 4111 | 4365 | if (!eo_gop.found_existing) { |
| 4112 | 4366 | eo_gop.value_ptr.* = &[0]*Export{}; |
| 4113 | 4367 | } |
| 4114 | eo_gop.value_ptr.* = try mod.gpa.realloc(eo_gop.value_ptr.*, eo_gop.value_ptr.len + 1); | |
| 4368 | eo_gop.value_ptr.* = try gpa.realloc(eo_gop.value_ptr.*, eo_gop.value_ptr.len + 1); | |
| 4115 | 4369 | eo_gop.value_ptr.*[eo_gop.value_ptr.len - 1] = new_export; |
| 4116 | errdefer eo_gop.value_ptr.* = mod.gpa.shrink(eo_gop.value_ptr.*, eo_gop.value_ptr.len - 1); | |
| 4370 | errdefer eo_gop.value_ptr.* = gpa.shrink(eo_gop.value_ptr.*, eo_gop.value_ptr.len - 1); | |
| 4117 | 4371 | |
| 4118 | 4372 | // Add to exported_decl table. |
| 4119 | 4373 | const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl); |
| 4120 | 4374 | if (!de_gop.found_existing) { |
| 4121 | 4375 | de_gop.value_ptr.* = &[0]*Export{}; |
| 4122 | 4376 | } |
| 4123 | de_gop.value_ptr.* = try mod.gpa.realloc(de_gop.value_ptr.*, de_gop.value_ptr.len + 1); | |
| 4377 | de_gop.value_ptr.* = try gpa.realloc(de_gop.value_ptr.*, de_gop.value_ptr.len + 1); | |
| 4124 | 4378 | de_gop.value_ptr.*[de_gop.value_ptr.len - 1] = new_export; |
| 4125 | errdefer de_gop.value_ptr.* = mod.gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1); | |
| 4379 | errdefer de_gop.value_ptr.* = gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1); | |
| 4126 | 4380 | } |
| 4127 | 4381 | |
| 4128 | 4382 | /// Takes ownership of `name` even if it returns an error. |
| ... | ... | @@ -4132,39 +4386,43 @@ pub fn createAnonymousDeclNamed( |
| 4132 | 4386 | typed_value: TypedValue, |
| 4133 | 4387 | name: [:0]u8, |
| 4134 | 4388 | ) !*Decl { |
| 4135 | return mod.createAnonymousDeclFromDeclNamed(scope.ownerDecl().?, typed_value, name); | |
| 4389 | return mod.createAnonymousDeclFromDeclNamed(scope.srcDecl().?, scope.srcScope(), typed_value, name); | |
| 4136 | 4390 | } |
| 4137 | 4391 | |
| 4138 | 4392 | pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl { |
| 4139 | return mod.createAnonymousDeclFromDecl(scope.ownerDecl().?, typed_value); | |
| 4393 | return mod.createAnonymousDeclFromDecl(scope.srcDecl().?, scope.srcScope(), typed_value); | |
| 4140 | 4394 | } |
| 4141 | 4395 | |
| 4142 | pub fn createAnonymousDeclFromDecl(mod: *Module, owner_decl: *Decl, tv: TypedValue) !*Decl { | |
| 4396 | pub fn createAnonymousDeclFromDecl(mod: *Module, src_decl: *Decl, src_scope: ?*CaptureScope, tv: TypedValue) !*Decl { | |
| 4143 | 4397 | const name_index = mod.getNextAnonNameIndex(); |
| 4144 | 4398 | const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ |
| 4145 | owner_decl.name, name_index, | |
| 4399 | src_decl.name, name_index, | |
| 4146 | 4400 | }); |
| 4147 | return mod.createAnonymousDeclFromDeclNamed(owner_decl, tv, name); | |
| 4401 | return mod.createAnonymousDeclFromDeclNamed(src_decl, src_scope, tv, name); | |
| 4148 | 4402 | } |
| 4149 | 4403 | |
| 4150 | 4404 | /// Takes ownership of `name` even if it returns an error. |
| 4151 | 4405 | pub fn createAnonymousDeclFromDeclNamed( |
| 4152 | 4406 | mod: *Module, |
| 4153 | owner_decl: *Decl, | |
| 4407 | src_decl: *Decl, | |
| 4408 | src_scope: ?*CaptureScope, | |
| 4154 | 4409 | typed_value: TypedValue, |
| 4155 | 4410 | name: [:0]u8, |
| 4156 | 4411 | ) !*Decl { |
| 4157 | 4412 | errdefer mod.gpa.free(name); |
| 4158 | 4413 | |
| 4159 | const namespace = owner_decl.namespace; | |
| 4414 | const namespace = src_decl.namespace; | |
| 4160 | 4415 | try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1); |
| 4161 | 4416 | |
| 4162 | const new_decl = try mod.allocateNewDecl(namespace, owner_decl.src_node); | |
| 4417 | const new_decl = try mod.allocateNewDecl(namespace, src_decl.src_node, src_scope); | |
| 4163 | 4418 | |
| 4164 | 4419 | new_decl.name = name; |
| 4165 | new_decl.src_line = owner_decl.src_line; | |
| 4420 | new_decl.src_line = src_decl.src_line; | |
| 4166 | 4421 | new_decl.ty = typed_value.ty; |
| 4167 | 4422 | new_decl.val = typed_value.val; |
| 4423 | new_decl.align_val = Value.initTag(.null_value); | |
| 4424 | new_decl.linksection_val = Value.initTag(.null_value); | |
| 4425 | new_decl.@"addrspace" = .generic; // default global addrspace | |
| 4168 | 4426 | new_decl.has_tv = true; |
| 4169 | 4427 | new_decl.analysis = .complete; |
| 4170 | 4428 | new_decl.generation = mod.generation; |
| ... | ... | @@ -4305,61 +4563,6 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) Co |
| 4305 | 4563 | return error.AnalysisFail; |
| 4306 | 4564 | } |
| 4307 | 4565 | |
| 4308 | pub fn simplePtrType( | |
| 4309 | arena: *Allocator, | |
| 4310 | elem_ty: Type, | |
| 4311 | mutable: bool, | |
| 4312 | size: std.builtin.TypeInfo.Pointer.Size, | |
| 4313 | ) Allocator.Error!Type { | |
| 4314 | if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) { | |
| 4315 | return Type.initTag(.const_slice_u8); | |
| 4316 | } | |
| 4317 | // TODO stage1 type inference bug | |
| 4318 | const T = Type.Tag; | |
| 4319 | ||
| 4320 | const type_payload = try arena.create(Type.Payload.ElemType); | |
| 4321 | type_payload.* = .{ | |
| 4322 | .base = .{ | |
| 4323 | .tag = switch (size) { | |
| 4324 | .One => if (mutable) T.single_mut_pointer else T.single_const_pointer, | |
| 4325 | .Many => if (mutable) T.many_mut_pointer else T.many_const_pointer, | |
| 4326 | .C => if (mutable) T.c_mut_pointer else T.c_const_pointer, | |
| 4327 | .Slice => if (mutable) T.mut_slice else T.const_slice, | |
| 4328 | }, | |
| 4329 | }, | |
| 4330 | .data = elem_ty, | |
| 4331 | }; | |
| 4332 | return Type.initPayload(&type_payload.base); | |
| 4333 | } | |
| 4334 | ||
| 4335 | pub fn ptrType( | |
| 4336 | arena: *Allocator, | |
| 4337 | elem_ty: Type, | |
| 4338 | sentinel: ?Value, | |
| 4339 | @"align": u32, | |
| 4340 | bit_offset: u16, | |
| 4341 | host_size: u16, | |
| 4342 | mutable: bool, | |
| 4343 | @"allowzero": bool, | |
| 4344 | @"volatile": bool, | |
| 4345 | size: std.builtin.TypeInfo.Pointer.Size, | |
| 4346 | ) Allocator.Error!Type { | |
| 4347 | assert(host_size == 0 or bit_offset < host_size * 8); | |
| 4348 | ||
| 4349 | // TODO check if type can be represented by simplePtrType | |
| 4350 | return Type.Tag.pointer.create(arena, .{ | |
| 4351 | .pointee_type = elem_ty, | |
| 4352 | .sentinel = sentinel, | |
| 4353 | .@"align" = @"align", | |
| 4354 | .bit_offset = bit_offset, | |
| 4355 | .host_size = host_size, | |
| 4356 | .@"allowzero" = @"allowzero", | |
| 4357 | .mutable = mutable, | |
| 4358 | .@"volatile" = @"volatile", | |
| 4359 | .size = size, | |
| 4360 | }); | |
| 4361 | } | |
| 4362 | ||
| 4363 | 4566 | pub fn optionalType(arena: *Allocator, child_type: Type) Allocator.Error!Type { |
| 4364 | 4567 | switch (child_type.tag()) { |
| 4365 | 4568 | .single_const_pointer => return Type.Tag.optional_single_const_pointer.create( |
| ... | ... | @@ -4689,7 +4892,7 @@ pub fn populateTestFunctions(mod: *Module) !void { |
| 4689 | 4892 | const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file; |
| 4690 | 4893 | const builtin_namespace = builtin_file.root_decl.?.namespace; |
| 4691 | 4894 | const decl = builtin_namespace.decls.get("test_functions").?; |
| 4692 | var buf: Type.Payload.ElemType = undefined; | |
| 4895 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 4693 | 4896 | const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType(); |
| 4694 | 4897 | |
| 4695 | 4898 | const array_decl = d: { |
| ... | ... | @@ -4700,7 +4903,7 @@ pub fn populateTestFunctions(mod: *Module) !void { |
| 4700 | 4903 | const arena = &new_decl_arena.allocator; |
| 4701 | 4904 | |
| 4702 | 4905 | const test_fn_vals = try arena.alloc(Value, mod.test_functions.count()); |
| 4703 | const array_decl = try mod.createAnonymousDeclFromDecl(decl, .{ | |
| 4906 | const array_decl = try mod.createAnonymousDeclFromDecl(decl, null, .{ | |
| 4704 | 4907 | .ty = try Type.Tag.array.create(arena, .{ |
| 4705 | 4908 | .len = test_fn_vals.len, |
| 4706 | 4909 | .elem_type = try tmp_test_fn_ty.copy(arena), |
| ... | ... | @@ -4713,7 +4916,7 @@ pub fn populateTestFunctions(mod: *Module) !void { |
| 4713 | 4916 | var name_decl_arena = std.heap.ArenaAllocator.init(gpa); |
| 4714 | 4917 | errdefer name_decl_arena.deinit(); |
| 4715 | 4918 | const bytes = try name_decl_arena.allocator.dupe(u8, test_name_slice); |
| 4716 | const test_name_decl = try mod.createAnonymousDeclFromDecl(array_decl, .{ | |
| 4919 | const test_name_decl = try mod.createAnonymousDeclFromDecl(array_decl, null, .{ | |
| 4717 | 4920 | .ty = try Type.Tag.array_u8.create(&name_decl_arena.allocator, bytes.len), |
| 4718 | 4921 | .val = try Value.Tag.bytes.create(&name_decl_arena.allocator, bytes), |
| 4719 | 4922 | }); |
src/Package.zig+10-7| ... | ... | @@ -99,19 +99,22 @@ pub fn destroy(pkg: *Package, gpa: *Allocator) void { |
| 99 | 99 | } |
| 100 | 100 | } |
| 101 | 101 | |
| 102 | { | |
| 103 | var it = pkg.table.keyIterator(); | |
| 104 | while (it.next()) |key| { | |
| 105 | gpa.free(key.*); | |
| 106 | } | |
| 102 | pkg.deinitTable(gpa); | |
| 103 | gpa.destroy(pkg); | |
| 104 | } | |
| 105 | ||
| 106 | /// Only frees memory associated with the table. | |
| 107 | pub fn deinitTable(pkg: *Package, gpa: *Allocator) void { | |
| 108 | var it = pkg.table.keyIterator(); | |
| 109 | while (it.next()) |key| { | |
| 110 | gpa.free(key.*); | |
| 107 | 111 | } |
| 108 | 112 | |
| 109 | 113 | pkg.table.deinit(gpa); |
| 110 | gpa.destroy(pkg); | |
| 111 | 114 | } |
| 112 | 115 | |
| 113 | 116 | pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package) !void { |
| 114 | try pkg.table.ensureCapacity(gpa, pkg.table.count() + 1); | |
| 117 | try pkg.table.ensureUnusedCapacity(gpa, 1); | |
| 115 | 118 | const name_dupe = try mem.dupe(gpa, u8, name); |
| 116 | 119 | pkg.table.putAssumeCapacityNoClobber(name_dupe, package); |
| 117 | 120 | } |
src/Sema.zig+2886-736| ... | ... | @@ -8,8 +8,12 @@ |
| 8 | 8 | mod: *Module, |
| 9 | 9 | /// Alias to `mod.gpa`. |
| 10 | 10 | gpa: *Allocator, |
| 11 | /// Points to the arena allocator of the Decl. | |
| 11 | /// Points to the temporary arena allocator of the Sema. | |
| 12 | /// This arena will be cleared when the sema is destroyed. | |
| 12 | 13 | arena: *Allocator, |
| 14 | /// Points to the arena allocator for the owner_decl. | |
| 15 | /// This arena will persist until the decl is invalidated. | |
| 16 | perm_arena: *Allocator, | |
| 13 | 17 | code: Zir, |
| 14 | 18 | air_instructions: std.MultiArrayList(Air.Inst) = .{}, |
| 15 | 19 | air_extra: std.ArrayListUnmanaged(u32) = .{}, |
| ... | ... | @@ -80,9 +84,13 @@ const Scope = Module.Scope; |
| 80 | 84 | const CompileError = Module.CompileError; |
| 81 | 85 | const SemaError = Module.SemaError; |
| 82 | 86 | const Decl = Module.Decl; |
| 87 | const CaptureScope = Module.CaptureScope; | |
| 88 | const WipCaptureScope = Module.WipCaptureScope; | |
| 83 | 89 | const LazySrcLoc = Module.LazySrcLoc; |
| 84 | 90 | const RangeSet = @import("RangeSet.zig"); |
| 85 | 91 | const target_util = @import("target.zig"); |
| 92 | const Package = @import("Package.zig"); | |
| 93 | const crash_report = @import("crash_report.zig"); | |
| 86 | 94 | |
| 87 | 95 | pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); |
| 88 | 96 | |
| ... | ... | @@ -128,15 +136,34 @@ pub fn analyzeBody( |
| 128 | 136 | ) CompileError!Zir.Inst.Index { |
| 129 | 137 | // No tracy calls here, to avoid interfering with the tail call mechanism. |
| 130 | 138 | |
| 139 | const parent_capture_scope = block.wip_capture_scope; | |
| 140 | ||
| 141 | var wip_captures = WipCaptureScope{ | |
| 142 | .finalized = true, | |
| 143 | .scope = parent_capture_scope, | |
| 144 | .perm_arena = sema.perm_arena, | |
| 145 | .gpa = sema.gpa, | |
| 146 | }; | |
| 147 | defer if (wip_captures.scope != parent_capture_scope) { | |
| 148 | wip_captures.deinit(); | |
| 149 | }; | |
| 150 | ||
| 131 | 151 | const map = &block.sema.inst_map; |
| 132 | 152 | const tags = block.sema.code.instructions.items(.tag); |
| 133 | 153 | const datas = block.sema.code.instructions.items(.data); |
| 134 | 154 | |
| 155 | var orig_captures: usize = parent_capture_scope.captures.count(); | |
| 156 | ||
| 157 | var crash_info = crash_report.prepAnalyzeBody(sema, block, body); | |
| 158 | crash_info.push(); | |
| 159 | defer crash_info.pop(); | |
| 160 | ||
| 135 | 161 | // We use a while(true) loop here to avoid a redundant way of breaking out of |
| 136 | 162 | // the loop. The only way to break out of the loop is with a `noreturn` |
| 137 | 163 | // instruction. |
| 138 | 164 | var i: usize = 0; |
| 139 | while (true) { | |
| 165 | const result = while (true) { | |
| 166 | crash_info.setBodyIndex(i); | |
| 140 | 167 | const inst = body[i]; |
| 141 | 168 | const air_inst: Air.Inst.Ref = switch (tags[inst]) { |
| 142 | 169 | // zig fmt: off |
| ... | ... | @@ -164,11 +191,8 @@ pub fn analyzeBody( |
| 164 | 191 | .bool_br_and => try sema.zirBoolBr(block, inst, false), |
| 165 | 192 | .bool_br_or => try sema.zirBoolBr(block, inst, true), |
| 166 | 193 | .c_import => try sema.zirCImport(block, inst), |
| 167 | .call => try sema.zirCall(block, inst, .auto, false), | |
| 168 | .call_chkused => try sema.zirCall(block, inst, .auto, true), | |
| 169 | .call_compile_time => try sema.zirCall(block, inst, .compile_time, false), | |
| 170 | .call_nosuspend => try sema.zirCall(block, inst, .no_async, false), | |
| 171 | .call_async => try sema.zirCall(block, inst, .async_kw, false), | |
| 194 | .call => try sema.zirCall(block, inst), | |
| 195 | .closure_get => try sema.zirClosureGet(block, inst), | |
| 172 | 196 | .cmp_lt => try sema.zirCmp(block, inst, .lt), |
| 173 | 197 | .cmp_lte => try sema.zirCmp(block, inst, .lte), |
| 174 | 198 | .cmp_eq => try sema.zirCmpEq(block, inst, .eq, .cmp_eq), |
| ... | ... | @@ -201,6 +225,8 @@ pub fn analyzeBody( |
| 201 | 225 | .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), |
| 202 | 226 | .field_val => try sema.zirFieldVal(block, inst), |
| 203 | 227 | .field_val_named => try sema.zirFieldValNamed(block, inst), |
| 228 | .field_call_bind => try sema.zirFieldCallBind(block, inst), | |
| 229 | .field_call_bind_named => try sema.zirFieldCallBindNamed(block, inst), | |
| 204 | 230 | .func => try sema.zirFunc(block, inst, false), |
| 205 | 231 | .func_inferred => try sema.zirFunc(block, inst, true), |
| 206 | 232 | .import => try sema.zirImport(block, inst), |
| ... | ... | @@ -222,12 +248,10 @@ pub fn analyzeBody( |
| 222 | 248 | .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), |
| 223 | 249 | .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false), |
| 224 | 250 | .optional_type => try sema.zirOptionalType(block, inst), |
| 225 | .param_type => try sema.zirParamType(block, inst), | |
| 226 | 251 | .ptr_type => try sema.zirPtrType(block, inst), |
| 227 | 252 | .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), |
| 228 | 253 | .ref => try sema.zirRef(block, inst), |
| 229 | 254 | .ret_err_value_code => try sema.zirRetErrValueCode(block, inst), |
| 230 | .shl => try sema.zirShl(block, inst), | |
| 231 | 255 | .shr => try sema.zirShr(block, inst), |
| 232 | 256 | .slice_end => try sema.zirSliceEnd(block, inst), |
| 233 | 257 | .slice_sentinel => try sema.zirSliceSentinel(block, inst), |
| ... | ... | @@ -300,9 +324,6 @@ pub fn analyzeBody( |
| 300 | 324 | .div_exact => try sema.zirDivExact(block, inst), |
| 301 | 325 | .div_floor => try sema.zirDivFloor(block, inst), |
| 302 | 326 | .div_trunc => try sema.zirDivTrunc(block, inst), |
| 303 | .mod => try sema.zirMod(block, inst), | |
| 304 | .rem => try sema.zirRem(block, inst), | |
| 305 | .shl_exact => try sema.zirShlExact(block, inst), | |
| 306 | 327 | .shr_exact => try sema.zirShrExact(block, inst), |
| 307 | 328 | .bit_offset_of => try sema.zirBitOffsetOf(block, inst), |
| 308 | 329 | .offset_of => try sema.zirOffsetOf(block, inst), |
| ... | ... | @@ -314,14 +335,11 @@ pub fn analyzeBody( |
| 314 | 335 | .select => try sema.zirSelect(block, inst), |
| 315 | 336 | .atomic_load => try sema.zirAtomicLoad(block, inst), |
| 316 | 337 | .atomic_rmw => try sema.zirAtomicRmw(block, inst), |
| 317 | .atomic_store => try sema.zirAtomicStore(block, inst), | |
| 318 | 338 | .mul_add => try sema.zirMulAdd(block, inst), |
| 319 | 339 | .builtin_call => try sema.zirBuiltinCall(block, inst), |
| 320 | 340 | .field_ptr_type => try sema.zirFieldPtrType(block, inst), |
| 321 | 341 | .field_parent_ptr => try sema.zirFieldParentPtr(block, inst), |
| 322 | 342 | .maximum => try sema.zirMaximum(block, inst), |
| 323 | .memcpy => try sema.zirMemcpy(block, inst), | |
| 324 | .memset => try sema.zirMemset(block, inst), | |
| 325 | 343 | .minimum => try sema.zirMinimum(block, inst), |
| 326 | 344 | .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst), |
| 327 | 345 | .@"resume" => try sema.zirResume(block, inst), |
| ... | ... | @@ -343,32 +361,38 @@ pub fn analyzeBody( |
| 343 | 361 | .trunc => try sema.zirUnaryMath(block, inst), |
| 344 | 362 | .round => try sema.zirUnaryMath(block, inst), |
| 345 | 363 | |
| 346 | .opaque_decl => try sema.zirOpaqueDecl(block, inst, .parent), | |
| 347 | .opaque_decl_anon => try sema.zirOpaqueDecl(block, inst, .anon), | |
| 348 | .opaque_decl_func => try sema.zirOpaqueDecl(block, inst, .func), | |
| 349 | 364 | .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent), |
| 350 | 365 | .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), |
| 351 | 366 | .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func), |
| 352 | 367 | |
| 353 | .add => try sema.zirArithmetic(block, inst), | |
| 354 | .addwrap => try sema.zirArithmetic(block, inst), | |
| 355 | .div => try sema.zirArithmetic(block, inst), | |
| 356 | .mod_rem => try sema.zirArithmetic(block, inst), | |
| 357 | .mul => try sema.zirArithmetic(block, inst), | |
| 358 | .mulwrap => try sema.zirArithmetic(block, inst), | |
| 359 | .sub => try sema.zirArithmetic(block, inst), | |
| 360 | .subwrap => try sema.zirArithmetic(block, inst), | |
| 368 | .add => try sema.zirArithmetic(block, inst, .add), | |
| 369 | .addwrap => try sema.zirArithmetic(block, inst, .addwrap), | |
| 370 | .add_sat => try sema.zirArithmetic(block, inst, .add_sat), | |
| 371 | .div => try sema.zirArithmetic(block, inst, .div), | |
| 372 | .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem), | |
| 373 | .mod => try sema.zirArithmetic(block, inst, .mod), | |
| 374 | .rem => try sema.zirArithmetic(block, inst, .rem), | |
| 375 | .mul => try sema.zirArithmetic(block, inst, .mul), | |
| 376 | .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap), | |
| 377 | .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat), | |
| 378 | .sub => try sema.zirArithmetic(block, inst, .sub), | |
| 379 | .subwrap => try sema.zirArithmetic(block, inst, .subwrap), | |
| 380 | .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat), | |
| 381 | ||
| 382 | .shl => try sema.zirShl(block, inst, .shl), | |
| 383 | .shl_exact => try sema.zirShl(block, inst, .shl_exact), | |
| 384 | .shl_sat => try sema.zirShl(block, inst, .shl_sat), | |
| 361 | 385 | |
| 362 | 386 | // Instructions that we know to *always* be noreturn based solely on their tag. |
| 363 | 387 | // These functions match the return type of analyzeBody so that we can |
| 364 | 388 | // tail call them here. |
| 365 | .compile_error => return sema.zirCompileError(block, inst), | |
| 366 | .ret_coerce => return sema.zirRetCoerce(block, inst), | |
| 367 | .ret_node => return sema.zirRetNode(block, inst), | |
| 368 | .ret_load => return sema.zirRetLoad(block, inst), | |
| 369 | .ret_err_value => return sema.zirRetErrValue(block, inst), | |
| 370 | .@"unreachable" => return sema.zirUnreachable(block, inst), | |
| 371 | .panic => return sema.zirPanic(block, inst), | |
| 389 | .compile_error => break sema.zirCompileError(block, inst), | |
| 390 | .ret_coerce => break sema.zirRetCoerce(block, inst), | |
| 391 | .ret_node => break sema.zirRetNode(block, inst), | |
| 392 | .ret_load => break sema.zirRetLoad(block, inst), | |
| 393 | .ret_err_value => break sema.zirRetErrValue(block, inst), | |
| 394 | .@"unreachable" => break sema.zirUnreachable(block, inst), | |
| 395 | .panic => break sema.zirPanic(block, inst), | |
| 372 | 396 | // zig fmt: on |
| 373 | 397 | |
| 374 | 398 | // Instructions that we know can *never* be noreturn based solely on |
| ... | ... | @@ -377,7 +401,9 @@ pub fn analyzeBody( |
| 377 | 401 | // We also know that they cannot be referenced later, so we avoid |
| 378 | 402 | // putting them into the map. |
| 379 | 403 | .breakpoint => { |
| 380 | try sema.zirBreakpoint(block, inst); | |
| 404 | if (!block.is_comptime) { | |
| 405 | _ = try block.addNoOp(.breakpoint); | |
| 406 | } | |
| 381 | 407 | i += 1; |
| 382 | 408 | continue; |
| 383 | 409 | }, |
| ... | ... | @@ -411,6 +437,11 @@ pub fn analyzeBody( |
| 411 | 437 | i += 1; |
| 412 | 438 | continue; |
| 413 | 439 | }, |
| 440 | .atomic_store => { | |
| 441 | try sema.zirAtomicStore(block, inst); | |
| 442 | i += 1; | |
| 443 | continue; | |
| 444 | }, | |
| 414 | 445 | .store => { |
| 415 | 446 | try sema.zirStore(block, inst); |
| 416 | 447 | i += 1; |
| ... | ... | @@ -451,6 +482,11 @@ pub fn analyzeBody( |
| 451 | 482 | i += 1; |
| 452 | 483 | continue; |
| 453 | 484 | }, |
| 485 | .export_value => { | |
| 486 | try sema.zirExportValue(block, inst); | |
| 487 | i += 1; | |
| 488 | continue; | |
| 489 | }, | |
| 454 | 490 | .set_align_stack => { |
| 455 | 491 | try sema.zirSetAlignStack(block, inst); |
| 456 | 492 | i += 1; |
| ... | ... | @@ -491,34 +527,59 @@ pub fn analyzeBody( |
| 491 | 527 | i += 1; |
| 492 | 528 | continue; |
| 493 | 529 | }, |
| 530 | .closure_capture => { | |
| 531 | try sema.zirClosureCapture(block, inst); | |
| 532 | i += 1; | |
| 533 | continue; | |
| 534 | }, | |
| 535 | .memcpy => { | |
| 536 | try sema.zirMemcpy(block, inst); | |
| 537 | i += 1; | |
| 538 | continue; | |
| 539 | }, | |
| 540 | .memset => { | |
| 541 | try sema.zirMemset(block, inst); | |
| 542 | i += 1; | |
| 543 | continue; | |
| 544 | }, | |
| 494 | 545 | |
| 495 | 546 | // Special case instructions to handle comptime control flow. |
| 496 | 547 | .@"break" => { |
| 497 | 548 | if (block.is_comptime) { |
| 498 | return inst; // same as break_inline | |
| 549 | break inst; // same as break_inline | |
| 499 | 550 | } else { |
| 500 | return sema.zirBreak(block, inst); | |
| 551 | break sema.zirBreak(block, inst); | |
| 501 | 552 | } |
| 502 | 553 | }, |
| 503 | .break_inline => return inst, | |
| 554 | .break_inline => break inst, | |
| 504 | 555 | .repeat => { |
| 505 | 556 | if (block.is_comptime) { |
| 506 | 557 | // Send comptime control flow back to the beginning of this block. |
| 507 | 558 | const src: LazySrcLoc = .{ .node_offset = datas[inst].node }; |
| 508 | 559 | try sema.emitBackwardBranch(block, src); |
| 560 | if (wip_captures.scope.captures.count() != orig_captures) { | |
| 561 | try wip_captures.reset(parent_capture_scope); | |
| 562 | block.wip_capture_scope = wip_captures.scope; | |
| 563 | orig_captures = 0; | |
| 564 | } | |
| 509 | 565 | i = 0; |
| 510 | 566 | continue; |
| 511 | 567 | } else { |
| 512 | 568 | const src_node = sema.code.instructions.items(.data)[inst].node; |
| 513 | 569 | const src: LazySrcLoc = .{ .node_offset = src_node }; |
| 514 | 570 | try sema.requireRuntimeBlock(block, src); |
| 515 | return always_noreturn; | |
| 571 | break always_noreturn; | |
| 516 | 572 | } |
| 517 | 573 | }, |
| 518 | 574 | .repeat_inline => { |
| 519 | 575 | // Send comptime control flow back to the beginning of this block. |
| 520 | 576 | const src: LazySrcLoc = .{ .node_offset = datas[inst].node }; |
| 521 | 577 | try sema.emitBackwardBranch(block, src); |
| 578 | if (wip_captures.scope.captures.count() != orig_captures) { | |
| 579 | try wip_captures.reset(parent_capture_scope); | |
| 580 | block.wip_capture_scope = wip_captures.scope; | |
| 581 | orig_captures = 0; | |
| 582 | } | |
| 522 | 583 | i = 0; |
| 523 | 584 | continue; |
| 524 | 585 | }, |
| ... | ... | @@ -533,7 +594,7 @@ pub fn analyzeBody( |
| 533 | 594 | if (inst == break_data.block_inst) { |
| 534 | 595 | break :blk sema.resolveInst(break_data.operand); |
| 535 | 596 | } else { |
| 536 | return break_inst; | |
| 597 | break break_inst; | |
| 537 | 598 | } |
| 538 | 599 | }, |
| 539 | 600 | .block => blk: { |
| ... | ... | @@ -547,7 +608,7 @@ pub fn analyzeBody( |
| 547 | 608 | if (inst == break_data.block_inst) { |
| 548 | 609 | break :blk sema.resolveInst(break_data.operand); |
| 549 | 610 | } else { |
| 550 | return break_inst; | |
| 611 | break break_inst; | |
| 551 | 612 | } |
| 552 | 613 | }, |
| 553 | 614 | .block_inline => blk: { |
| ... | ... | @@ -560,11 +621,11 @@ pub fn analyzeBody( |
| 560 | 621 | if (inst == break_data.block_inst) { |
| 561 | 622 | break :blk sema.resolveInst(break_data.operand); |
| 562 | 623 | } else { |
| 563 | return break_inst; | |
| 624 | break break_inst; | |
| 564 | 625 | } |
| 565 | 626 | }, |
| 566 | 627 | .condbr => blk: { |
| 567 | if (!block.is_comptime) return sema.zirCondbr(block, inst); | |
| 628 | if (!block.is_comptime) break sema.zirCondbr(block, inst); | |
| 568 | 629 | // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220 |
| 569 | 630 | const inst_data = datas[inst].pl_node; |
| 570 | 631 | const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; |
| ... | ... | @@ -578,7 +639,7 @@ pub fn analyzeBody( |
| 578 | 639 | if (inst == break_data.block_inst) { |
| 579 | 640 | break :blk sema.resolveInst(break_data.operand); |
| 580 | 641 | } else { |
| 581 | return break_inst; | |
| 642 | break break_inst; | |
| 582 | 643 | } |
| 583 | 644 | }, |
| 584 | 645 | .condbr_inline => blk: { |
| ... | ... | @@ -594,15 +655,22 @@ pub fn analyzeBody( |
| 594 | 655 | if (inst == break_data.block_inst) { |
| 595 | 656 | break :blk sema.resolveInst(break_data.operand); |
| 596 | 657 | } else { |
| 597 | return break_inst; | |
| 658 | break break_inst; | |
| 598 | 659 | } |
| 599 | 660 | }, |
| 600 | 661 | }; |
| 601 | 662 | if (sema.typeOf(air_inst).isNoReturn()) |
| 602 | return always_noreturn; | |
| 663 | break always_noreturn; | |
| 603 | 664 | try map.put(sema.gpa, inst, air_inst); |
| 604 | 665 | i += 1; |
| 666 | } else unreachable; | |
| 667 | ||
| 668 | if (!wip_captures.finalized) { | |
| 669 | try wip_captures.finalize(); | |
| 670 | block.wip_capture_scope = parent_capture_scope; | |
| 605 | 671 | } |
| 672 | ||
| 673 | return result; | |
| 606 | 674 | } |
| 607 | 675 | |
| 608 | 676 | fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -614,6 +682,7 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 614 | 682 | .struct_decl => return sema.zirStructDecl( block, extended, inst), |
| 615 | 683 | .enum_decl => return sema.zirEnumDecl( block, extended), |
| 616 | 684 | .union_decl => return sema.zirUnionDecl( block, extended, inst), |
| 685 | .opaque_decl => return sema.zirOpaqueDecl( block, extended, inst), | |
| 617 | 686 | .ret_ptr => return sema.zirRetPtr( block, extended), |
| 618 | 687 | .ret_type => return sema.zirRetType( block, extended), |
| 619 | 688 | .this => return sema.zirThis( block, extended), |
| ... | ... | @@ -636,10 +705,6 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 636 | 705 | .c_define => return sema.zirCDefine( block, extended), |
| 637 | 706 | .wasm_memory_size => return sema.zirWasmMemorySize( block, extended), |
| 638 | 707 | .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended), |
| 639 | .add_with_saturation=> return sema.zirSatArithmetic( block, extended), | |
| 640 | .sub_with_saturation=> return sema.zirSatArithmetic( block, extended), | |
| 641 | .mul_with_saturation=> return sema.zirSatArithmetic( block, extended), | |
| 642 | .shl_with_saturation=> return sema.zirSatArithmetic( block, extended), | |
| 643 | 708 | // zig fmt: on |
| 644 | 709 | } |
| 645 | 710 | } |
| ... | ... | @@ -828,9 +893,18 @@ fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) Compile |
| 828 | 893 | return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{}); |
| 829 | 894 | } |
| 830 | 895 | |
| 896 | fn failWithDivideByZero(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) CompileError { | |
| 897 | return sema.mod.fail(&block.base, src, "division by zero here causes undefined behavior", .{}); | |
| 898 | } | |
| 899 | ||
| 900 | fn failWithModRemNegative(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError { | |
| 901 | return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty }); | |
| 902 | } | |
| 903 | ||
| 831 | 904 | /// Appropriate to call when the coercion has already been done by result |
| 832 | 905 | /// location semantics. Asserts the value fits in the provided `Int` type. |
| 833 | 906 | /// Only supports `Int` types 64 bits or less. |
| 907 | /// TODO don't ever call this since we're migrating towards ResultLoc.coerced_ty. | |
| 834 | 908 | fn resolveAlreadyCoercedInt( |
| 835 | 909 | sema: *Sema, |
| 836 | 910 | block: *Scope.Block, |
| ... | ... | @@ -847,6 +921,23 @@ fn resolveAlreadyCoercedInt( |
| 847 | 921 | } |
| 848 | 922 | } |
| 849 | 923 | |
| 924 | fn resolveAlign( | |
| 925 | sema: *Sema, | |
| 926 | block: *Scope.Block, | |
| 927 | src: LazySrcLoc, | |
| 928 | zir_ref: Zir.Inst.Ref, | |
| 929 | ) !u16 { | |
| 930 | const alignment_big = try sema.resolveInt(block, src, zir_ref, Type.initTag(.u16)); | |
| 931 | const alignment = @intCast(u16, alignment_big); // We coerce to u16 in the prev line. | |
| 932 | if (alignment == 0) return sema.mod.fail(&block.base, src, "alignment must be >= 1", .{}); | |
| 933 | if (!std.math.isPowerOfTwo(alignment)) { | |
| 934 | return sema.mod.fail(&block.base, src, "alignment value {d} is not a power of two", .{ | |
| 935 | alignment, | |
| 936 | }); | |
| 937 | } | |
| 938 | return alignment; | |
| 939 | } | |
| 940 | ||
| 850 | 941 | fn resolveInt( |
| 851 | 942 | sema: *Sema, |
| 852 | 943 | block: *Scope.Block, |
| ... | ... | @@ -900,10 +991,38 @@ fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) C |
| 900 | 991 | } |
| 901 | 992 | |
| 902 | 993 | fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 903 | _ = inst; | |
| 904 | 994 | const tracy = trace(@src()); |
| 905 | 995 | defer tracy.end(); |
| 906 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirCoerceResultPtr", .{}); | |
| 996 | ||
| 997 | const src: LazySrcLoc = sema.src; | |
| 998 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | |
| 999 | const pointee_ty = try sema.resolveType(block, src, bin_inst.lhs); | |
| 1000 | const ptr = sema.resolveInst(bin_inst.rhs); | |
| 1001 | ||
| 1002 | // Create a runtime bitcast instruction with exactly the type the pointer wants. | |
| 1003 | const ptr_ty = try Type.ptr(sema.arena, .{ | |
| 1004 | .pointee_type = pointee_ty, | |
| 1005 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 1006 | }); | |
| 1007 | try sema.requireRuntimeBlock(block, src); | |
| 1008 | const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr); | |
| 1009 | ||
| 1010 | if (Air.refToIndex(ptr)) |ptr_inst| { | |
| 1011 | if (sema.air_instructions.items(.tag)[ptr_inst] == .constant) { | |
| 1012 | const air_datas = sema.air_instructions.items(.data); | |
| 1013 | const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload]; | |
| 1014 | if (ptr_val.castTag(.inferred_alloc)) |inferred_alloc| { | |
| 1015 | // Add the stored instruction to the set we will use to resolve peer types | |
| 1016 | // for the inferred allocation. | |
| 1017 | // This instruction will not make it to codegen; it is only to participate | |
| 1018 | // in the `stored_inst_list` of the `inferred_alloc`. | |
| 1019 | const operand = try block.addTyOp(.bitcast, pointee_ty, .void_value); | |
| 1020 | try inferred_alloc.data.stored_inst_list.append(sema.arena, operand); | |
| 1021 | } | |
| 1022 | } | |
| 1023 | } | |
| 1024 | ||
| 1025 | return bitcasted_ptr; | |
| 907 | 1026 | } |
| 908 | 1027 | |
| 909 | 1028 | pub fn analyzeStructDecl( |
| ... | ... | @@ -981,7 +1100,6 @@ fn zirStructDecl( |
| 981 | 1100 | } |
| 982 | 1101 | |
| 983 | 1102 | fn createTypeName(sema: *Sema, block: *Scope.Block, name_strategy: Zir.Inst.NameStrategy) ![:0]u8 { |
| 984 | _ = block; | |
| 985 | 1103 | switch (name_strategy) { |
| 986 | 1104 | .anon => { |
| 987 | 1105 | // It would be neat to have "struct:line:column" but this name has |
| ... | ... | @@ -990,14 +1108,14 @@ fn createTypeName(sema: *Sema, block: *Scope.Block, name_strategy: Zir.Inst.Name |
| 990 | 1108 | // semantically analyzed. |
| 991 | 1109 | const name_index = sema.mod.getNextAnonNameIndex(); |
| 992 | 1110 | return std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{ |
| 993 | sema.owner_decl.name, name_index, | |
| 1111 | block.src_decl.name, name_index, | |
| 994 | 1112 | }); |
| 995 | 1113 | }, |
| 996 | .parent => return sema.gpa.dupeZ(u8, mem.spanZ(sema.owner_decl.name)), | |
| 1114 | .parent => return sema.gpa.dupeZ(u8, mem.spanZ(block.src_decl.name)), | |
| 997 | 1115 | .func => { |
| 998 | 1116 | const name_index = sema.mod.getNextAnonNameIndex(); |
| 999 | 1117 | const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{ |
| 1000 | sema.owner_decl.name, name_index, | |
| 1118 | block.src_decl.name, name_index, | |
| 1001 | 1119 | }); |
| 1002 | 1120 | log.warn("TODO: handle NameStrategy.func correctly instead of using anon name '{s}'", .{ |
| 1003 | 1121 | name, |
| ... | ... | @@ -1053,17 +1171,6 @@ fn zirEnumDecl( |
| 1053 | 1171 | var new_decl_arena = std.heap.ArenaAllocator.init(gpa); |
| 1054 | 1172 | errdefer new_decl_arena.deinit(); |
| 1055 | 1173 | |
| 1056 | const tag_ty = blk: { | |
| 1057 | if (tag_type_ref != .none) { | |
| 1058 | // TODO better source location | |
| 1059 | // TODO (needs AstGen fix too) move this eval to the block so it gets allocated | |
| 1060 | // in the new decl arena. | |
| 1061 | break :blk try sema.resolveType(block, src, tag_type_ref); | |
| 1062 | } | |
| 1063 | const bits = std.math.log2_int_ceil(usize, fields_len); | |
| 1064 | break :blk try Type.Tag.int_unsigned.create(&new_decl_arena.allocator, bits); | |
| 1065 | }; | |
| 1066 | ||
| 1067 | 1174 | const enum_obj = try new_decl_arena.allocator.create(Module.EnumFull); |
| 1068 | 1175 | const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumFull); |
| 1069 | 1176 | enum_ty_payload.* = .{ |
| ... | ... | @@ -1082,7 +1189,7 @@ fn zirEnumDecl( |
| 1082 | 1189 | |
| 1083 | 1190 | enum_obj.* = .{ |
| 1084 | 1191 | .owner_decl = new_decl, |
| 1085 | .tag_ty = tag_ty, | |
| 1192 | .tag_ty = Type.initTag(.@"null"), | |
| 1086 | 1193 | .fields = .{}, |
| 1087 | 1194 | .values = .{}, |
| 1088 | 1195 | .node_offset = src.node_offset, |
| ... | ... | @@ -1110,16 +1217,6 @@ fn zirEnumDecl( |
| 1110 | 1217 | const body_end = extra_index; |
| 1111 | 1218 | extra_index += bit_bags_count; |
| 1112 | 1219 | |
| 1113 | try enum_obj.fields.ensureCapacity(&new_decl_arena.allocator, fields_len); | |
| 1114 | const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { | |
| 1115 | if (bag != 0) break true; | |
| 1116 | } else false; | |
| 1117 | if (any_values) { | |
| 1118 | try enum_obj.values.ensureTotalCapacityContext(&new_decl_arena.allocator, fields_len, .{ | |
| 1119 | .ty = tag_ty, | |
| 1120 | }); | |
| 1121 | } | |
| 1122 | ||
| 1123 | 1220 | { |
| 1124 | 1221 | // We create a block for the field type instructions because they |
| 1125 | 1222 | // may need to reference Decls from inside the enum namespace. |
| ... | ... | @@ -1142,10 +1239,14 @@ fn zirEnumDecl( |
| 1142 | 1239 | sema.func = null; |
| 1143 | 1240 | defer sema.func = prev_func; |
| 1144 | 1241 | |
| 1242 | var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope); | |
| 1243 | defer wip_captures.deinit(); | |
| 1244 | ||
| 1145 | 1245 | var enum_block: Scope.Block = .{ |
| 1146 | 1246 | .parent = null, |
| 1147 | 1247 | .sema = sema, |
| 1148 | 1248 | .src_decl = new_decl, |
| 1249 | .wip_capture_scope = wip_captures.scope, | |
| 1149 | 1250 | .instructions = .{}, |
| 1150 | 1251 | .inlining = null, |
| 1151 | 1252 | .is_comptime = true, |
| ... | ... | @@ -1155,7 +1256,30 @@ fn zirEnumDecl( |
| 1155 | 1256 | if (body.len != 0) { |
| 1156 | 1257 | _ = try sema.analyzeBody(&enum_block, body); |
| 1157 | 1258 | } |
| 1259 | ||
| 1260 | try wip_captures.finalize(); | |
| 1261 | ||
| 1262 | const tag_ty = blk: { | |
| 1263 | if (tag_type_ref != .none) { | |
| 1264 | // TODO better source location | |
| 1265 | break :blk try sema.resolveType(block, src, tag_type_ref); | |
| 1266 | } | |
| 1267 | const bits = std.math.log2_int_ceil(usize, fields_len); | |
| 1268 | break :blk try Type.Tag.int_unsigned.create(&new_decl_arena.allocator, bits); | |
| 1269 | }; | |
| 1270 | enum_obj.tag_ty = tag_ty; | |
| 1271 | } | |
| 1272 | ||
| 1273 | try enum_obj.fields.ensureTotalCapacity(&new_decl_arena.allocator, fields_len); | |
| 1274 | const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { | |
| 1275 | if (bag != 0) break true; | |
| 1276 | } else false; | |
| 1277 | if (any_values) { | |
| 1278 | try enum_obj.values.ensureTotalCapacityContext(&new_decl_arena.allocator, fields_len, .{ | |
| 1279 | .ty = enum_obj.tag_ty, | |
| 1280 | }); | |
| 1158 | 1281 | } |
| 1282 | ||
| 1159 | 1283 | var bit_bag_index: usize = body_end; |
| 1160 | 1284 | var cur_bit_bag: u32 = undefined; |
| 1161 | 1285 | var field_i: u32 = 0; |
| ... | ... | @@ -1194,10 +1318,10 @@ fn zirEnumDecl( |
| 1194 | 1318 | // that points to this default value expression rather than the struct. |
| 1195 | 1319 | // But only resolve the source location if we need to emit a compile error. |
| 1196 | 1320 | const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val; |
| 1197 | enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = tag_ty }); | |
| 1321 | enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = enum_obj.tag_ty }); | |
| 1198 | 1322 | } else if (any_values) { |
| 1199 | 1323 | const tag_val = try Value.Tag.int_u64.create(&new_decl_arena.allocator, field_i); |
| 1200 | enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = tag_ty }); | |
| 1324 | enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = enum_obj.tag_ty }); | |
| 1201 | 1325 | } |
| 1202 | 1326 | } |
| 1203 | 1327 | |
| ... | ... | @@ -1237,7 +1361,13 @@ fn zirUnionDecl( |
| 1237 | 1361 | errdefer new_decl_arena.deinit(); |
| 1238 | 1362 | |
| 1239 | 1363 | const union_obj = try new_decl_arena.allocator.create(Module.Union); |
| 1240 | const union_ty = try Type.Tag.@"union".create(&new_decl_arena.allocator, union_obj); | |
| 1364 | const type_tag: Type.Tag = if (small.has_tag_type or small.auto_enum_tag) .union_tagged else .@"union"; | |
| 1365 | const union_payload = try new_decl_arena.allocator.create(Type.Payload.Union); | |
| 1366 | union_payload.* = .{ | |
| 1367 | .base = .{ .tag = type_tag }, | |
| 1368 | .data = union_obj, | |
| 1369 | }; | |
| 1370 | const union_ty = Type.initPayload(&union_payload.base); | |
| 1241 | 1371 | const union_val = try Value.Tag.ty.create(&new_decl_arena.allocator, union_ty); |
| 1242 | 1372 | const type_name = try sema.createTypeName(block, small.name_strategy); |
| 1243 | 1373 | const new_decl = try sema.mod.createAnonymousDeclNamed(&block.base, .{ |
| ... | ... | @@ -1275,20 +1405,14 @@ fn zirUnionDecl( |
| 1275 | 1405 | fn zirOpaqueDecl( |
| 1276 | 1406 | sema: *Sema, |
| 1277 | 1407 | block: *Scope.Block, |
| 1408 | extended: Zir.Inst.Extended.InstData, | |
| 1278 | 1409 | inst: Zir.Inst.Index, |
| 1279 | name_strategy: Zir.Inst.NameStrategy, | |
| 1280 | 1410 | ) CompileError!Air.Inst.Ref { |
| 1281 | 1411 | const tracy = trace(@src()); |
| 1282 | 1412 | defer tracy.end(); |
| 1283 | 1413 | |
| 1284 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 1285 | const src = inst_data.src(); | |
| 1286 | const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); | |
| 1287 | ||
| 1288 | _ = name_strategy; | |
| 1289 | _ = inst_data; | |
| 1290 | _ = src; | |
| 1291 | _ = extra; | |
| 1414 | _ = extended; | |
| 1415 | _ = inst; | |
| 1292 | 1416 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirOpaqueDecl", .{}); |
| 1293 | 1417 | } |
| 1294 | 1418 | |
| ... | ... | @@ -1349,7 +1473,10 @@ fn zirRetPtr( |
| 1349 | 1473 | return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty); |
| 1350 | 1474 | } |
| 1351 | 1475 | |
| 1352 | const ptr_type = try Module.simplePtrType(sema.arena, sema.fn_ret_ty, true, .One); | |
| 1476 | const ptr_type = try Type.ptr(sema.arena, .{ | |
| 1477 | .pointee_type = sema.fn_ret_ty, | |
| 1478 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 1479 | }); | |
| 1353 | 1480 | return block.addTy(.alloc, ptr_type); |
| 1354 | 1481 | } |
| 1355 | 1482 | |
| ... | ... | @@ -1466,7 +1593,42 @@ fn zirAllocExtended( |
| 1466 | 1593 | ) CompileError!Air.Inst.Ref { |
| 1467 | 1594 | const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); |
| 1468 | 1595 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; |
| 1469 | return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocExtended", .{}); | |
| 1596 | const ty_src = src; // TODO better source location | |
| 1597 | const align_src = src; // TODO better source location | |
| 1598 | const small = @bitCast(Zir.Inst.AllocExtended.Small, extended.small); | |
| 1599 | ||
| 1600 | var extra_index: usize = extra.end; | |
| 1601 | ||
| 1602 | const var_ty: Type = if (small.has_type) blk: { | |
| 1603 | const type_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); | |
| 1604 | extra_index += 1; | |
| 1605 | break :blk try sema.resolveType(block, ty_src, type_ref); | |
| 1606 | } else { | |
| 1607 | return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocExtended inferred", .{}); | |
| 1608 | }; | |
| 1609 | ||
| 1610 | const alignment: u16 = if (small.has_align) blk: { | |
| 1611 | const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); | |
| 1612 | extra_index += 1; | |
| 1613 | const alignment = try sema.resolveAlign(block, align_src, align_ref); | |
| 1614 | break :blk alignment; | |
| 1615 | } else 0; | |
| 1616 | ||
| 1617 | if (small.is_comptime) { | |
| 1618 | return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocExtended comptime", .{}); | |
| 1619 | } | |
| 1620 | ||
| 1621 | if (!small.is_const) { | |
| 1622 | return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocExtended var", .{}); | |
| 1623 | } | |
| 1624 | ||
| 1625 | const ptr_type = try Type.ptr(sema.arena, .{ | |
| 1626 | .pointee_type = var_ty, | |
| 1627 | .@"align" = alignment, | |
| 1628 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 1629 | }); | |
| 1630 | try sema.requireRuntimeBlock(block, src); | |
| 1631 | return block.addTy(.alloc, ptr_type); | |
| 1470 | 1632 | } |
| 1471 | 1633 | |
| 1472 | 1634 | fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -1497,7 +1659,10 @@ fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError |
| 1497 | 1659 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; |
| 1498 | 1660 | const var_decl_src = inst_data.src(); |
| 1499 | 1661 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); |
| 1500 | const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One); | |
| 1662 | const ptr_type = try Type.ptr(sema.arena, .{ | |
| 1663 | .pointee_type = var_type, | |
| 1664 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 1665 | }); | |
| 1501 | 1666 | try sema.requireRuntimeBlock(block, var_decl_src); |
| 1502 | 1667 | return block.addTy(.alloc, ptr_type); |
| 1503 | 1668 | } |
| ... | ... | @@ -1513,8 +1678,11 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 1513 | 1678 | if (block.is_comptime) { |
| 1514 | 1679 | return sema.analyzeComptimeAlloc(block, var_type); |
| 1515 | 1680 | } |
| 1516 | try sema.validateVarType(block, ty_src, var_type); | |
| 1517 | const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One); | |
| 1681 | try sema.validateVarType(block, ty_src, var_type, false); | |
| 1682 | const ptr_type = try Type.ptr(sema.arena, .{ | |
| 1683 | .pointee_type = var_type, | |
| 1684 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 1685 | }); | |
| 1518 | 1686 | try sema.requireRuntimeBlock(block, var_decl_src); |
| 1519 | 1687 | return block.addTy(.alloc, ptr_type); |
| 1520 | 1688 | } |
| ... | ... | @@ -1574,7 +1742,10 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde |
| 1574 | 1742 | try sema.mod.declareDeclDependency(sema.owner_decl, decl); |
| 1575 | 1743 | |
| 1576 | 1744 | const final_elem_ty = try decl.ty.copy(sema.arena); |
| 1577 | const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One); | |
| 1745 | const final_ptr_ty = try Type.ptr(sema.arena, .{ | |
| 1746 | .pointee_type = final_elem_ty, | |
| 1747 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 1748 | }); | |
| 1578 | 1749 | const final_ptr_ty_inst = try sema.addType(final_ptr_ty); |
| 1579 | 1750 | sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst; |
| 1580 | 1751 | |
| ... | ... | @@ -1593,10 +1764,13 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde |
| 1593 | 1764 | const peer_inst_list = inferred_alloc.data.stored_inst_list.items; |
| 1594 | 1765 | const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none); |
| 1595 | 1766 | if (var_is_mut) { |
| 1596 | try sema.validateVarType(block, ty_src, final_elem_ty); | |
| 1767 | try sema.validateVarType(block, ty_src, final_elem_ty, false); | |
| 1597 | 1768 | } |
| 1598 | 1769 | // Change it to a normal alloc. |
| 1599 | const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One); | |
| 1770 | const final_ptr_ty = try Type.ptr(sema.arena, .{ | |
| 1771 | .pointee_type = final_elem_ty, | |
| 1772 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 1773 | }); | |
| 1600 | 1774 | sema.air_instructions.set(ptr_inst, .{ |
| 1601 | 1775 | .tag = .alloc, |
| 1602 | 1776 | .data = .{ .ty = final_ptr_ty }, |
| ... | ... | @@ -1609,19 +1783,82 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind |
| 1609 | 1783 | const tracy = trace(@src()); |
| 1610 | 1784 | defer tracy.end(); |
| 1611 | 1785 | |
| 1612 | const gpa = sema.gpa; | |
| 1613 | const mod = sema.mod; | |
| 1614 | 1786 | const validate_inst = sema.code.instructions.items(.data)[inst].pl_node; |
| 1615 | const struct_init_src = validate_inst.src(); | |
| 1787 | const init_src = validate_inst.src(); | |
| 1616 | 1788 | const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); |
| 1617 | 1789 | const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len]; |
| 1790 | const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; | |
| 1791 | const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; | |
| 1792 | const object_ptr = sema.resolveInst(field_ptr_extra.lhs); | |
| 1793 | const agg_ty = sema.typeOf(object_ptr).elemType(); | |
| 1794 | switch (agg_ty.zigTypeTag()) { | |
| 1795 | .Struct => return sema.validateStructInitPtr( | |
| 1796 | block, | |
| 1797 | agg_ty.castTag(.@"struct").?.data, | |
| 1798 | init_src, | |
| 1799 | instrs, | |
| 1800 | ), | |
| 1801 | .Union => return sema.validateUnionInitPtr( | |
| 1802 | block, | |
| 1803 | agg_ty.cast(Type.Payload.Union).?.data, | |
| 1804 | init_src, | |
| 1805 | instrs, | |
| 1806 | object_ptr, | |
| 1807 | ), | |
| 1808 | else => unreachable, | |
| 1809 | } | |
| 1810 | } | |
| 1618 | 1811 | |
| 1619 | const struct_obj: *Module.Struct = s: { | |
| 1620 | const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; | |
| 1621 | const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; | |
| 1622 | const object_ptr = sema.resolveInst(field_ptr_extra.lhs); | |
| 1623 | break :s sema.typeOf(object_ptr).elemType().castTag(.@"struct").?.data; | |
| 1624 | }; | |
| 1812 | fn validateUnionInitPtr( | |
| 1813 | sema: *Sema, | |
| 1814 | block: *Scope.Block, | |
| 1815 | union_obj: *Module.Union, | |
| 1816 | init_src: LazySrcLoc, | |
| 1817 | instrs: []const Zir.Inst.Index, | |
| 1818 | union_ptr: Air.Inst.Ref, | |
| 1819 | ) CompileError!void { | |
| 1820 | const mod = sema.mod; | |
| 1821 | ||
| 1822 | if (instrs.len != 1) { | |
| 1823 | // TODO add note for other field | |
| 1824 | // TODO add note for union declared here | |
| 1825 | return mod.fail(&block.base, init_src, "only one union field can be active at once", .{}); | |
| 1826 | } | |
| 1827 | ||
| 1828 | const field_ptr = instrs[0]; | |
| 1829 | const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node; | |
| 1830 | const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node }; | |
| 1831 | const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; | |
| 1832 | const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start); | |
| 1833 | const field_index_big = union_obj.fields.getIndex(field_name) orelse | |
| 1834 | return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); | |
| 1835 | const field_index = @intCast(u32, field_index_big); | |
| 1836 | ||
| 1837 | // TODO here we need to go back and see if we need to convert the union | |
| 1838 | // to a comptime-known value. This will involve editing the AIR code we have | |
| 1839 | // generated so far - in particular deleting some runtime pointer bitcast | |
| 1840 | // instructions which are not actually needed if the initialization expression | |
| 1841 | // ends up being comptime-known. | |
| 1842 | ||
| 1843 | // Otherwise, we set the new union tag now. | |
| 1844 | const new_tag = try sema.addConstant( | |
| 1845 | union_obj.tag_ty, | |
| 1846 | try Value.Tag.enum_field_index.create(sema.arena, field_index), | |
| 1847 | ); | |
| 1848 | ||
| 1849 | try sema.requireRuntimeBlock(block, init_src); | |
| 1850 | _ = try block.addBinOp(.set_union_tag, union_ptr, new_tag); | |
| 1851 | } | |
| 1852 | ||
| 1853 | fn validateStructInitPtr( | |
| 1854 | sema: *Sema, | |
| 1855 | block: *Scope.Block, | |
| 1856 | struct_obj: *Module.Struct, | |
| 1857 | init_src: LazySrcLoc, | |
| 1858 | instrs: []const Zir.Inst.Index, | |
| 1859 | ) CompileError!void { | |
| 1860 | const gpa = sema.gpa; | |
| 1861 | const mod = sema.mod; | |
| 1625 | 1862 | |
| 1626 | 1863 | // Maps field index to field_ptr index of where it was already initialized. |
| 1627 | 1864 | const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count()); |
| ... | ... | @@ -1660,9 +1897,9 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind |
| 1660 | 1897 | const template = "missing struct field: {s}"; |
| 1661 | 1898 | const args = .{field_name}; |
| 1662 | 1899 | if (root_msg) |msg| { |
| 1663 | try mod.errNote(&block.base, struct_init_src, msg, template, args); | |
| 1900 | try mod.errNote(&block.base, init_src, msg, template, args); | |
| 1664 | 1901 | } else { |
| 1665 | root_msg = try mod.errMsg(&block.base, struct_init_src, template, args); | |
| 1902 | root_msg = try mod.errMsg(&block.base, init_src, template, args); | |
| 1666 | 1903 | } |
| 1667 | 1904 | } |
| 1668 | 1905 | if (root_msg) |msg| { |
| ... | ... | @@ -1750,7 +1987,11 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co |
| 1750 | 1987 | } |
| 1751 | 1988 | const ptr = sema.resolveInst(bin_inst.lhs); |
| 1752 | 1989 | const value = sema.resolveInst(bin_inst.rhs); |
| 1753 | const ptr_ty = try Module.simplePtrType(sema.arena, sema.typeOf(value), true, .One); | |
| 1990 | const ptr_ty = try Type.ptr(sema.arena, .{ | |
| 1991 | .pointee_type = sema.typeOf(value), | |
| 1992 | // TODO figure out which address space is appropriate here | |
| 1993 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 1994 | }); | |
| 1754 | 1995 | // TODO detect when this store should be done at compile-time. For example, |
| 1755 | 1996 | // if expressions should force it when the condition is compile-time known. |
| 1756 | 1997 | const src: LazySrcLoc = .unneeded; |
| ... | ... | @@ -1797,7 +2038,10 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) |
| 1797 | 2038 | // for the inferred allocation. |
| 1798 | 2039 | try inferred_alloc.data.stored_inst_list.append(sema.arena, operand); |
| 1799 | 2040 | // Create a runtime bitcast instruction with exactly the type the pointer wants. |
| 1800 | const ptr_ty = try Module.simplePtrType(sema.arena, operand_ty, true, .One); | |
| 2041 | const ptr_ty = try Type.ptr(sema.arena, .{ | |
| 2042 | .pointee_type = operand_ty, | |
| 2043 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 2044 | }); | |
| 1801 | 2045 | const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr); |
| 1802 | 2046 | return sema.storePtr(block, src, bitcasted_ptr, operand); |
| 1803 | 2047 | } |
| ... | ... | @@ -1834,45 +2078,6 @@ fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE |
| 1834 | 2078 | return sema.storePtr(block, src, ptr, value); |
| 1835 | 2079 | } |
| 1836 | 2080 | |
| 1837 | fn zirParamType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 1838 | const tracy = trace(@src()); | |
| 1839 | defer tracy.end(); | |
| 1840 | ||
| 1841 | const src = sema.src; | |
| 1842 | const fn_inst_src = sema.src; | |
| 1843 | ||
| 1844 | const inst_data = sema.code.instructions.items(.data)[inst].param_type; | |
| 1845 | const fn_inst = sema.resolveInst(inst_data.callee); | |
| 1846 | const fn_inst_ty = sema.typeOf(fn_inst); | |
| 1847 | const param_index = inst_data.param_index; | |
| 1848 | ||
| 1849 | const fn_ty: Type = switch (fn_inst_ty.zigTypeTag()) { | |
| 1850 | .Fn => fn_inst_ty, | |
| 1851 | .BoundFn => { | |
| 1852 | return sema.mod.fail(&block.base, fn_inst_src, "TODO implement zirParamType for method call syntax", .{}); | |
| 1853 | }, | |
| 1854 | else => { | |
| 1855 | return sema.mod.fail(&block.base, fn_inst_src, "expected function, found '{}'", .{fn_inst_ty}); | |
| 1856 | }, | |
| 1857 | }; | |
| 1858 | ||
| 1859 | const param_count = fn_ty.fnParamLen(); | |
| 1860 | if (param_index >= param_count) { | |
| 1861 | if (fn_ty.fnIsVarArgs()) { | |
| 1862 | return sema.addType(Type.initTag(.var_args_param)); | |
| 1863 | } | |
| 1864 | return sema.mod.fail(&block.base, src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{ | |
| 1865 | param_index, | |
| 1866 | fn_ty, | |
| 1867 | param_count, | |
| 1868 | }); | |
| 1869 | } | |
| 1870 | ||
| 1871 | // TODO support generic functions | |
| 1872 | const param_type = fn_ty.fnParamType(param_index); | |
| 1873 | return sema.addType(param_type); | |
| 1874 | } | |
| 1875 | ||
| 1876 | 2081 | fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 1877 | 2082 | const tracy = trace(@src()); |
| 1878 | 2083 | defer tracy.end(); |
| ... | ... | @@ -2070,10 +2275,78 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Com |
| 2070 | 2275 | const tracy = trace(@src()); |
| 2071 | 2276 | defer tracy.end(); |
| 2072 | 2277 | |
| 2073 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 2074 | const src = inst_data.src(); | |
| 2278 | const pl_node = sema.code.instructions.items(.data)[inst].pl_node; | |
| 2279 | const src = pl_node.src(); | |
| 2280 | const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); | |
| 2281 | const body = sema.code.extra[extra.end..][0..extra.data.body_len]; | |
| 2282 | ||
| 2283 | // we check this here to avoid undefined symbols | |
| 2284 | if (!@import("build_options").have_llvm) | |
| 2285 | return sema.mod.fail(&parent_block.base, src, "cannot do C import on Zig compiler not built with LLVM-extension", .{}); | |
| 2286 | ||
| 2287 | var c_import_buf = std.ArrayList(u8).init(sema.gpa); | |
| 2288 | defer c_import_buf.deinit(); | |
| 2289 | ||
| 2290 | var child_block: Scope.Block = .{ | |
| 2291 | .parent = parent_block, | |
| 2292 | .sema = sema, | |
| 2293 | .src_decl = parent_block.src_decl, | |
| 2294 | .wip_capture_scope = parent_block.wip_capture_scope, | |
| 2295 | .instructions = .{}, | |
| 2296 | .inlining = parent_block.inlining, | |
| 2297 | .is_comptime = parent_block.is_comptime, | |
| 2298 | .c_import_buf = &c_import_buf, | |
| 2299 | }; | |
| 2300 | defer child_block.instructions.deinit(sema.gpa); | |
| 2301 | ||
| 2302 | _ = try sema.analyzeBody(&child_block, body); | |
| 2303 | ||
| 2304 | const c_import_res = sema.mod.comp.cImport(c_import_buf.items) catch |err| | |
| 2305 | return sema.mod.fail(&child_block.base, src, "C import failed: {s}", .{@errorName(err)}); | |
| 2306 | ||
| 2307 | if (c_import_res.errors.len != 0) { | |
| 2308 | const msg = msg: { | |
| 2309 | const msg = try sema.mod.errMsg(&child_block.base, src, "C import failed", .{}); | |
| 2310 | errdefer msg.destroy(sema.gpa); | |
| 2311 | ||
| 2312 | if (!sema.mod.comp.bin_file.options.link_libc) | |
| 2313 | try sema.mod.errNote(&child_block.base, src, msg, "libc headers not available; compilation does not link against libc", .{}); | |
| 2314 | ||
| 2315 | for (c_import_res.errors) |_| { | |
| 2316 | // TODO integrate with LazySrcLoc | |
| 2317 | // try sema.mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]}); | |
| 2318 | // if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", | |
| 2319 | // clang_err.line + 1, | |
| 2320 | // clang_err.column + 1, | |
| 2321 | } | |
| 2322 | @import("clang.zig").Stage2ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len); | |
| 2323 | break :msg msg; | |
| 2324 | }; | |
| 2325 | return sema.mod.failWithOwnedErrorMsg(&child_block.base, msg); | |
| 2326 | } | |
| 2327 | const c_import_pkg = Package.create( | |
| 2328 | sema.gpa, | |
| 2329 | null, | |
| 2330 | c_import_res.out_zig_path, | |
| 2331 | ) catch |err| switch (err) { | |
| 2332 | error.OutOfMemory => return error.OutOfMemory, | |
| 2333 | else => unreachable, // we pass null for root_src_dir_path | |
| 2334 | }; | |
| 2335 | const std_pkg = sema.mod.main_pkg.table.get("std").?; | |
| 2336 | const builtin_pkg = sema.mod.main_pkg.table.get("builtin").?; | |
| 2337 | try c_import_pkg.add(sema.gpa, "builtin", builtin_pkg); | |
| 2338 | try c_import_pkg.add(sema.gpa, "std", std_pkg); | |
| 2075 | 2339 | |
| 2076 | return sema.mod.fail(&parent_block.base, src, "TODO: implement Sema.zirCImport", .{}); | |
| 2340 | const result = sema.mod.importPkg(c_import_pkg) catch |err| | |
| 2341 | return sema.mod.fail(&child_block.base, src, "C import failed: {s}", .{@errorName(err)}); | |
| 2342 | ||
| 2343 | sema.mod.astGenFile(result.file) catch |err| | |
| 2344 | return sema.mod.fail(&child_block.base, src, "C import failed: {s}", .{@errorName(err)}); | |
| 2345 | ||
| 2346 | try sema.mod.semaFile(result.file); | |
| 2347 | const file_root_decl = result.file.root_decl.?; | |
| 2348 | try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl); | |
| 2349 | return sema.addConstant(file_root_decl.ty, file_root_decl.val); | |
| 2077 | 2350 | } |
| 2078 | 2351 | |
| 2079 | 2352 | fn zirSuspendBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -2118,6 +2391,7 @@ fn zirBlock( |
| 2118 | 2391 | .parent = parent_block, |
| 2119 | 2392 | .sema = sema, |
| 2120 | 2393 | .src_decl = parent_block.src_decl, |
| 2394 | .wip_capture_scope = parent_block.wip_capture_scope, | |
| 2121 | 2395 | .instructions = .{}, |
| 2122 | 2396 | .label = &label, |
| 2123 | 2397 | .inlining = parent_block.inlining, |
| ... | ... | @@ -2142,8 +2416,12 @@ fn resolveBlockBody( |
| 2142 | 2416 | body: []const Zir.Inst.Index, |
| 2143 | 2417 | merges: *Scope.Block.Merges, |
| 2144 | 2418 | ) CompileError!Air.Inst.Ref { |
| 2145 | _ = try sema.analyzeBody(child_block, body); | |
| 2146 | return sema.analyzeBlockBody(parent_block, src, child_block, merges); | |
| 2419 | if (child_block.is_comptime) { | |
| 2420 | return sema.resolveBody(child_block, body); | |
| 2421 | } else { | |
| 2422 | _ = try sema.analyzeBody(child_block, body); | |
| 2423 | return sema.analyzeBlockBody(parent_block, src, child_block, merges); | |
| 2424 | } | |
| 2147 | 2425 | } |
| 2148 | 2426 | |
| 2149 | 2427 | fn analyzeBlockBody( |
| ... | ... | @@ -2256,36 +2534,66 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro |
| 2256 | 2534 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 2257 | 2535 | const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data; |
| 2258 | 2536 | const src = inst_data.src(); |
| 2259 | const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 2260 | const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 2537 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 2538 | const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 2261 | 2539 | const decl_name = sema.code.nullTerminatedString(extra.decl_name); |
| 2262 | 2540 | if (extra.namespace != .none) { |
| 2263 | 2541 | return sema.mod.fail(&block.base, src, "TODO: implement exporting with field access", .{}); |
| 2264 | 2542 | } |
| 2265 | const decl = try sema.lookupIdentifier(block, lhs_src, decl_name); | |
| 2266 | const options = try sema.resolveInstConst(block, rhs_src, extra.options); | |
| 2267 | const struct_obj = options.ty.castTag(.@"struct").?.data; | |
| 2268 | const fields = options.val.castTag(.@"struct").?.data[0..struct_obj.fields.count()]; | |
| 2269 | const name_index = struct_obj.fields.getIndex("name").?; | |
| 2270 | const linkage_index = struct_obj.fields.getIndex("linkage").?; | |
| 2271 | const section_index = struct_obj.fields.getIndex("section").?; | |
| 2272 | const export_name = try fields[name_index].toAllocatedBytes(sema.arena); | |
| 2273 | const linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage); | |
| 2543 | const decl = try sema.lookupIdentifier(block, operand_src, decl_name); | |
| 2544 | const options = try sema.resolveExportOptions(block, options_src, extra.options); | |
| 2545 | try sema.mod.analyzeExport(block, src, options, decl); | |
| 2546 | } | |
| 2274 | 2547 | |
| 2275 | if (linkage != .Strong) { | |
| 2276 | return sema.mod.fail(&block.base, src, "TODO: implement exporting with non-strong linkage", .{}); | |
| 2277 | } | |
| 2278 | if (!fields[section_index].isNull()) { | |
| 2279 | return sema.mod.fail(&block.base, src, "TODO: implement exporting with linksection", .{}); | |
| 2280 | } | |
| 2548 | fn zirExportValue(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | |
| 2549 | const tracy = trace(@src()); | |
| 2550 | defer tracy.end(); | |
| 2281 | 2551 | |
| 2282 | try sema.mod.analyzeExport(&block.base, src, export_name, decl); | |
| 2552 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 2553 | const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data; | |
| 2554 | const src = inst_data.src(); | |
| 2555 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 2556 | const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 2557 | const operand = try sema.resolveInstConst(block, operand_src, extra.operand); | |
| 2558 | const options = try sema.resolveExportOptions(block, options_src, extra.options); | |
| 2559 | const decl = switch (operand.val.tag()) { | |
| 2560 | .function => operand.val.castTag(.function).?.data.owner_decl, | |
| 2561 | else => return sema.mod.fail(&block.base, operand_src, "TODO implement exporting arbitrary Value objects", .{}), // TODO put this Value into an anonymous Decl and then export it. | |
| 2562 | }; | |
| 2563 | try sema.mod.analyzeExport(block, src, options, decl); | |
| 2283 | 2564 | } |
| 2284 | 2565 | |
| 2285 | 2566 | fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { |
| 2567 | const mod = sema.mod; | |
| 2286 | 2568 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 2569 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 2287 | 2570 | const src: LazySrcLoc = inst_data.src(); |
| 2288 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetAlignStack", .{}); | |
| 2571 | const alignment = try sema.resolveAlign(block, operand_src, inst_data.operand); | |
| 2572 | if (alignment > 256) { | |
| 2573 | return mod.fail(&block.base, src, "attempt to @setAlignStack({d}); maximum is 256", .{ | |
| 2574 | alignment, | |
| 2575 | }); | |
| 2576 | } | |
| 2577 | const func = sema.owner_func orelse | |
| 2578 | return mod.fail(&block.base, src, "@setAlignStack outside function body", .{}); | |
| 2579 | ||
| 2580 | switch (func.owner_decl.ty.fnCallingConvention()) { | |
| 2581 | .Naked => return mod.fail(&block.base, src, "@setAlignStack in naked function", .{}), | |
| 2582 | .Inline => return mod.fail(&block.base, src, "@setAlignStack in inline function", .{}), | |
| 2583 | else => {}, | |
| 2584 | } | |
| 2585 | ||
| 2586 | const gop = try mod.align_stack_fns.getOrPut(mod.gpa, func); | |
| 2587 | if (gop.found_existing) { | |
| 2588 | const msg = msg: { | |
| 2589 | const msg = try mod.errMsg(&block.base, src, "multiple @setAlignStack in the same function body", .{}); | |
| 2590 | errdefer msg.destroy(mod.gpa); | |
| 2591 | try mod.errNote(&block.base, src, msg, "other instance here", .{}); | |
| 2592 | break :msg msg; | |
| 2593 | }; | |
| 2594 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 2595 | } | |
| 2596 | gop.value_ptr.* = .{ .alignment = alignment, .src = src }; | |
| 2289 | 2597 | } |
| 2290 | 2598 | |
| 2291 | 2599 | fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { |
| ... | ... | @@ -2308,20 +2616,21 @@ fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) C |
| 2308 | 2616 | block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand); |
| 2309 | 2617 | } |
| 2310 | 2618 | |
| 2311 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | |
| 2312 | const tracy = trace(@src()); | |
| 2313 | defer tracy.end(); | |
| 2619 | fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | |
| 2620 | if (block.is_comptime) return; | |
| 2314 | 2621 | |
| 2315 | const src_node = sema.code.instructions.items(.data)[inst].node; | |
| 2316 | const src: LazySrcLoc = .{ .node_offset = src_node }; | |
| 2317 | try sema.requireRuntimeBlock(block, src); | |
| 2318 | _ = try block.addNoOp(.breakpoint); | |
| 2319 | } | |
| 2622 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | |
| 2623 | const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 2624 | const order = try sema.resolveAtomicOrder(block, order_src, inst_data.operand); | |
| 2320 | 2625 | |
| 2321 | fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | |
| 2322 | const src_node = sema.code.instructions.items(.data)[inst].node; | |
| 2323 | const src: LazySrcLoc = .{ .node_offset = src_node }; | |
| 2324 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirFence", .{}); | |
| 2626 | if (@enumToInt(order) < @enumToInt(std.builtin.AtomicOrder.Acquire)) { | |
| 2627 | return sema.mod.fail(&block.base, order_src, "atomic ordering must be Acquire or stricter", .{}); | |
| 2628 | } | |
| 2629 | ||
| 2630 | _ = try block.addInst(.{ | |
| 2631 | .tag = .fence, | |
| 2632 | .data = .{ .fence = order }, | |
| 2633 | }); | |
| 2325 | 2634 | } |
| 2326 | 2635 | |
| 2327 | 2636 | fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { |
| ... | ... | @@ -2489,8 +2798,6 @@ fn zirCall( |
| 2489 | 2798 | sema: *Sema, |
| 2490 | 2799 | block: *Scope.Block, |
| 2491 | 2800 | inst: Zir.Inst.Index, |
| 2492 | modifier: std.builtin.CallOptions.Modifier, | |
| 2493 | ensure_result_used: bool, | |
| 2494 | 2801 | ) CompileError!Air.Inst.Ref { |
| 2495 | 2802 | const tracy = trace(@src()); |
| 2496 | 2803 | defer tracy.end(); |
| ... | ... | @@ -2499,14 +2806,31 @@ fn zirCall( |
| 2499 | 2806 | const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; |
| 2500 | 2807 | const call_src = inst_data.src(); |
| 2501 | 2808 | const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index); |
| 2502 | const args = sema.code.refSlice(extra.end, extra.data.args_len); | |
| 2809 | const args = sema.code.refSlice(extra.end, extra.data.flags.args_len); | |
| 2810 | ||
| 2811 | const modifier = @intToEnum(std.builtin.CallOptions.Modifier, extra.data.flags.packed_modifier); | |
| 2812 | const ensure_result_used = extra.data.flags.ensure_result_used; | |
| 2813 | ||
| 2814 | var func = sema.resolveInst(extra.data.callee); | |
| 2815 | var resolved_args: []Air.Inst.Ref = undefined; | |
| 2503 | 2816 | |
| 2504 | const func = sema.resolveInst(extra.data.callee); | |
| 2505 | // TODO handle function calls of generic functions | |
| 2506 | const resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len); | |
| 2507 | for (args) |zir_arg, i| { | |
| 2508 | // the args are already casted to the result of a param type instruction. | |
| 2509 | resolved_args[i] = sema.resolveInst(zir_arg); | |
| 2817 | const func_type = sema.typeOf(func); | |
| 2818 | ||
| 2819 | // Desugar bound functions here | |
| 2820 | if (func_type.tag() == .bound_fn) { | |
| 2821 | const bound_func = try sema.resolveValue(block, func_src, func); | |
| 2822 | const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data; | |
| 2823 | func = bound_data.func_inst; | |
| 2824 | resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len + 1); | |
| 2825 | resolved_args[0] = bound_data.arg0_inst; | |
| 2826 | for (args) |zir_arg, i| { | |
| 2827 | resolved_args[i + 1] = sema.resolveInst(zir_arg); | |
| 2828 | } | |
| 2829 | } else { | |
| 2830 | resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len); | |
| 2831 | for (args) |zir_arg, i| { | |
| 2832 | resolved_args[i] = sema.resolveInst(zir_arg); | |
| 2833 | } | |
| 2510 | 2834 | } |
| 2511 | 2835 | |
| 2512 | 2836 | return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args); |
| ... | ... | @@ -2694,10 +3018,14 @@ fn analyzeCall( |
| 2694 | 3018 | sema.func = module_fn; |
| 2695 | 3019 | defer sema.func = parent_func; |
| 2696 | 3020 | |
| 3021 | var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, module_fn.owner_decl.src_scope); | |
| 3022 | defer wip_captures.deinit(); | |
| 3023 | ||
| 2697 | 3024 | var child_block: Scope.Block = .{ |
| 2698 | 3025 | .parent = null, |
| 2699 | 3026 | .sema = sema, |
| 2700 | 3027 | .src_decl = module_fn.owner_decl, |
| 3028 | .wip_capture_scope = wip_captures.scope, | |
| 2701 | 3029 | .instructions = .{}, |
| 2702 | 3030 | .label = null, |
| 2703 | 3031 | .inlining = &inlining, |
| ... | ... | @@ -2835,6 +3163,8 @@ fn analyzeCall( |
| 2835 | 3163 | |
| 2836 | 3164 | // TODO: check whether any external comptime memory was mutated by the |
| 2837 | 3165 | // comptime function call. If so, then do not memoize the call here. |
| 3166 | // TODO: re-evaluate whether memoized_calls needs its own arena. I think | |
| 3167 | // it should be fine to use the Decl arena for the function. | |
| 2838 | 3168 | { |
| 2839 | 3169 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); |
| 2840 | 3170 | errdefer arena_allocator.deinit(); |
| ... | ... | @@ -2845,7 +3175,7 @@ fn analyzeCall( |
| 2845 | 3175 | } |
| 2846 | 3176 | |
| 2847 | 3177 | try mod.memoized_calls.put(gpa, memoized_call_key, .{ |
| 2848 | .val = result_val, | |
| 3178 | .val = try result_val.copy(arena), | |
| 2849 | 3179 | .arena = arena_allocator.state, |
| 2850 | 3180 | }); |
| 2851 | 3181 | delete_memoized_call_key = false; |
| ... | ... | @@ -2860,6 +3190,9 @@ fn analyzeCall( |
| 2860 | 3190 | |
| 2861 | 3191 | break :res2 result; |
| 2862 | 3192 | }; |
| 3193 | ||
| 3194 | try wip_captures.finalize(); | |
| 3195 | ||
| 2863 | 3196 | break :res res2; |
| 2864 | 3197 | } else if (func_ty_info.is_generic) res: { |
| 2865 | 3198 | const func_val = try sema.resolveConstValue(block, func_src, func); |
| ... | ... | @@ -2942,7 +3275,8 @@ fn analyzeCall( |
| 2942 | 3275 | try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); |
| 2943 | 3276 | |
| 2944 | 3277 | // Create a Decl for the new function. |
| 2945 | const new_decl = try mod.allocateNewDecl(namespace, module_fn.owner_decl.src_node); | |
| 3278 | const src_decl = namespace.getDecl(); | |
| 3279 | const new_decl = try mod.allocateNewDecl(namespace, module_fn.owner_decl.src_node, src_decl.src_scope); | |
| 2946 | 3280 | // TODO better names for generic function instantiations |
| 2947 | 3281 | const name_index = mod.getNextAnonNameIndex(); |
| 2948 | 3282 | new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{ |
| ... | ... | @@ -2952,7 +3286,8 @@ fn analyzeCall( |
| 2952 | 3286 | new_decl.is_pub = module_fn.owner_decl.is_pub; |
| 2953 | 3287 | new_decl.is_exported = module_fn.owner_decl.is_exported; |
| 2954 | 3288 | new_decl.has_align = module_fn.owner_decl.has_align; |
| 2955 | new_decl.has_linksection = module_fn.owner_decl.has_linksection; | |
| 3289 | new_decl.has_linksection_or_addrspace = module_fn.owner_decl.has_linksection_or_addrspace; | |
| 3290 | new_decl.@"addrspace" = module_fn.owner_decl.@"addrspace"; | |
| 2956 | 3291 | new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index; |
| 2957 | 3292 | new_decl.alive = true; // This Decl is called at runtime. |
| 2958 | 3293 | new_decl.has_tv = true; |
| ... | ... | @@ -2973,6 +3308,7 @@ fn analyzeCall( |
| 2973 | 3308 | .mod = mod, |
| 2974 | 3309 | .gpa = gpa, |
| 2975 | 3310 | .arena = sema.arena, |
| 3311 | .perm_arena = &new_decl_arena.allocator, | |
| 2976 | 3312 | .code = fn_zir, |
| 2977 | 3313 | .owner_decl = new_decl, |
| 2978 | 3314 | .namespace = namespace, |
| ... | ... | @@ -2985,10 +3321,14 @@ fn analyzeCall( |
| 2985 | 3321 | }; |
| 2986 | 3322 | defer child_sema.deinit(); |
| 2987 | 3323 | |
| 3324 | var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope); | |
| 3325 | defer wip_captures.deinit(); | |
| 3326 | ||
| 2988 | 3327 | var child_block: Scope.Block = .{ |
| 2989 | 3328 | .parent = null, |
| 2990 | 3329 | .sema = &child_sema, |
| 2991 | 3330 | .src_decl = new_decl, |
| 3331 | .wip_capture_scope = wip_captures.scope, | |
| 2992 | 3332 | .instructions = .{}, |
| 2993 | 3333 | .inlining = null, |
| 2994 | 3334 | .is_comptime = true, |
| ... | ... | @@ -3022,14 +3362,16 @@ fn analyzeCall( |
| 3022 | 3362 | } |
| 3023 | 3363 | const arg_src = call_src; // TODO: better source location |
| 3024 | 3364 | const arg = uncasted_args[arg_i]; |
| 3025 | if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| { | |
| 3026 | const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); | |
| 3027 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); | |
| 3028 | } else if (is_comptime) { | |
| 3029 | return sema.failWithNeededComptime(block, arg_src); | |
| 3365 | if (is_comptime) { | |
| 3366 | if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| { | |
| 3367 | const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); | |
| 3368 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); | |
| 3369 | } else { | |
| 3370 | return sema.failWithNeededComptime(block, arg_src); | |
| 3371 | } | |
| 3030 | 3372 | } else if (is_anytype) { |
| 3031 | 3373 | // We insert into the map an instruction which is runtime-known |
| 3032 | // but has the type of the comptime argument. | |
| 3374 | // but has the type of the argument. | |
| 3033 | 3375 | const child_arg = try child_block.addArg(sema.typeOf(arg), 0); |
| 3034 | 3376 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); |
| 3035 | 3377 | } |
| ... | ... | @@ -3076,6 +3418,8 @@ fn analyzeCall( |
| 3076 | 3418 | arg_i += 1; |
| 3077 | 3419 | } |
| 3078 | 3420 | |
| 3421 | try wip_captures.finalize(); | |
| 3422 | ||
| 3079 | 3423 | // Populate the Decl ty/val with the function and its type. |
| 3080 | 3424 | new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator); |
| 3081 | 3425 | new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func); |
| ... | ... | @@ -3436,7 +3780,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com |
| 3436 | 3780 | }, |
| 3437 | 3781 | .error_set => { |
| 3438 | 3782 | const lhs_set = lhs_ty.castTag(.error_set).?.data; |
| 3439 | try set.ensureCapacity(sema.gpa, set.count() + lhs_set.names_len); | |
| 3783 | try set.ensureUnusedCapacity(sema.gpa, lhs_set.names_len); | |
| 3440 | 3784 | for (lhs_set.names_ptr[0..lhs_set.names_len]) |name| { |
| 3441 | 3785 | set.putAssumeCapacityNoClobber(name, {}); |
| 3442 | 3786 | } |
| ... | ... | @@ -3450,7 +3794,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com |
| 3450 | 3794 | }, |
| 3451 | 3795 | .error_set => { |
| 3452 | 3796 | const rhs_set = rhs_ty.castTag(.error_set).?.data; |
| 3453 | try set.ensureCapacity(sema.gpa, set.count() + rhs_set.names_len); | |
| 3797 | try set.ensureUnusedCapacity(sema.gpa, rhs_set.names_len); | |
| 3454 | 3798 | for (rhs_set.names_ptr[0..rhs_set.names_len]) |name| { |
| 3455 | 3799 | set.putAssumeCapacity(name, {}); |
| 3456 | 3800 | } |
| ... | ... | @@ -3606,7 +3950,11 @@ fn zirOptionalPayloadPtr( |
| 3606 | 3950 | } |
| 3607 | 3951 | |
| 3608 | 3952 | const child_type = try opt_type.optionalChildAlloc(sema.arena); |
| 3609 | const child_pointer = try Module.simplePtrType(sema.arena, child_type, !optional_ptr_ty.isConstPtr(), .One); | |
| 3953 | const child_pointer = try Type.ptr(sema.arena, .{ | |
| 3954 | .pointee_type = child_type, | |
| 3955 | .mutable = !optional_ptr_ty.isConstPtr(), | |
| 3956 | .@"addrspace" = optional_ptr_ty.ptrAddressSpace(), | |
| 3957 | }); | |
| 3610 | 3958 | |
| 3611 | 3959 | if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| { |
| 3612 | 3960 | if (try pointer_val.pointerDeref(sema.arena)) |val| { |
| ... | ... | @@ -3721,7 +4069,11 @@ fn zirErrUnionPayloadPtr( |
| 3721 | 4069 | return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand_ty.elemType()}); |
| 3722 | 4070 | |
| 3723 | 4071 | const payload_ty = operand_ty.elemType().errorUnionPayload(); |
| 3724 | const operand_pointer_ty = try Module.simplePtrType(sema.arena, payload_ty, !operand_ty.isConstPtr(), .One); | |
| 4072 | const operand_pointer_ty = try Type.ptr(sema.arena, .{ | |
| 4073 | .pointee_type = payload_ty, | |
| 4074 | .mutable = !operand_ty.isConstPtr(), | |
| 4075 | .@"addrspace" = operand_ty.ptrAddressSpace(), | |
| 4076 | }); | |
| 3725 | 4077 | |
| 3726 | 4078 | if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { |
| 3727 | 4079 | if (try pointer_val.pointerDeref(sema.arena)) |val| { |
| ... | ... | @@ -4236,6 +4588,19 @@ fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 4236 | 4588 | return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); |
| 4237 | 4589 | } |
| 4238 | 4590 | |
| 4591 | fn zirFieldCallBind(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 4592 | const tracy = trace(@src()); | |
| 4593 | defer tracy.end(); | |
| 4594 | ||
| 4595 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 4596 | const src = inst_data.src(); | |
| 4597 | const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; | |
| 4598 | const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; | |
| 4599 | const field_name = sema.code.nullTerminatedString(extra.field_name_start); | |
| 4600 | const object_ptr = sema.resolveInst(extra.lhs); | |
| 4601 | return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); | |
| 4602 | } | |
| 4603 | ||
| 4239 | 4604 | fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 4240 | 4605 | const tracy = trace(@src()); |
| 4241 | 4606 | defer tracy.end(); |
| ... | ... | @@ -4262,6 +4627,19 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp |
| 4262 | 4627 | return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); |
| 4263 | 4628 | } |
| 4264 | 4629 | |
| 4630 | fn zirFieldCallBindNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 4631 | const tracy = trace(@src()); | |
| 4632 | defer tracy.end(); | |
| 4633 | ||
| 4634 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 4635 | const src = inst_data.src(); | |
| 4636 | const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 4637 | const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; | |
| 4638 | const object_ptr = sema.resolveInst(extra.lhs); | |
| 4639 | const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); | |
| 4640 | return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); | |
| 4641 | } | |
| 4642 | ||
| 4265 | 4643 | fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 4266 | 4644 | const tracy = trace(@src()); |
| 4267 | 4645 | defer tracy.end(); |
| ... | ... | @@ -4275,8 +4653,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 4275 | 4653 | const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs); |
| 4276 | 4654 | const operand = sema.resolveInst(extra.rhs); |
| 4277 | 4655 | |
| 4278 | const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_type); | |
| 4279 | _ = try sema.requireIntegerType(block, operand_src, sema.typeOf(operand)); | |
| 4656 | const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_type); | |
| 4657 | _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand)); | |
| 4280 | 4658 | |
| 4281 | 4659 | if (try sema.isComptimeKnown(block, operand_src, operand)) { |
| 4282 | 4660 | return sema.coerce(block, dest_type, operand, operand_src); |
| ... | ... | @@ -4284,7 +4662,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 4284 | 4662 | return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_int'", .{}); |
| 4285 | 4663 | } |
| 4286 | 4664 | |
| 4287 | return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten int", .{}); | |
| 4665 | try sema.requireRuntimeBlock(block, operand_src); | |
| 4666 | return block.addTyOp(.intcast, dest_type, operand); | |
| 4288 | 4667 | } |
| 4289 | 4668 | |
| 4290 | 4669 | fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -4338,11 +4717,18 @@ fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE |
| 4338 | 4717 | |
| 4339 | 4718 | if (try sema.isComptimeKnown(block, operand_src, operand)) { |
| 4340 | 4719 | return sema.coerce(block, dest_type, operand, operand_src); |
| 4341 | } else if (dest_is_comptime_float) { | |
| 4720 | } | |
| 4721 | if (dest_is_comptime_float) { | |
| 4342 | 4722 | return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_float'", .{}); |
| 4343 | 4723 | } |
| 4344 | ||
| 4345 | return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten float", .{}); | |
| 4724 | const target = sema.mod.getTarget(); | |
| 4725 | const src_bits = operand_ty.floatBits(target); | |
| 4726 | const dst_bits = dest_type.floatBits(target); | |
| 4727 | if (dst_bits >= src_bits) { | |
| 4728 | return sema.coerce(block, dest_type, operand, operand_src); | |
| 4729 | } | |
| 4730 | try sema.requireRuntimeBlock(block, operand_src); | |
| 4731 | return block.addTyOp(.fptrunc, dest_type, operand); | |
| 4346 | 4732 | } |
| 4347 | 4733 | |
| 4348 | 4734 | fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -4778,8 +5164,8 @@ fn analyzeSwitch( |
| 4778 | 5164 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 4779 | 5165 | defer arena.deinit(); |
| 4780 | 5166 | |
| 4781 | const min_int = try operand_ty.minInt(&arena, mod.getTarget()); | |
| 4782 | const max_int = try operand_ty.maxInt(&arena, mod.getTarget()); | |
| 5167 | const min_int = try operand_ty.minInt(&arena.allocator, mod.getTarget()); | |
| 5168 | const max_int = try operand_ty.maxInt(&arena.allocator, mod.getTarget()); | |
| 4783 | 5169 | if (try range_set.spans(min_int, max_int, operand_ty)) { |
| 4784 | 5170 | if (special_prong == .@"else") { |
| 4785 | 5171 | return mod.fail( |
| ... | ... | @@ -4971,6 +5357,7 @@ fn analyzeSwitch( |
| 4971 | 5357 | .parent = block, |
| 4972 | 5358 | .sema = sema, |
| 4973 | 5359 | .src_decl = block.src_decl, |
| 5360 | .wip_capture_scope = block.wip_capture_scope, | |
| 4974 | 5361 | .instructions = .{}, |
| 4975 | 5362 | .label = &label, |
| 4976 | 5363 | .inlining = block.inlining, |
| ... | ... | @@ -5075,12 +5462,19 @@ fn analyzeSwitch( |
| 5075 | 5462 | const body = sema.code.extra[extra_index..][0..body_len]; |
| 5076 | 5463 | extra_index += body_len; |
| 5077 | 5464 | |
| 5465 | var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope); | |
| 5466 | defer wip_captures.deinit(); | |
| 5467 | ||
| 5078 | 5468 | case_block.instructions.shrinkRetainingCapacity(0); |
| 5469 | case_block.wip_capture_scope = wip_captures.scope; | |
| 5470 | ||
| 5079 | 5471 | const item = sema.resolveInst(item_ref); |
| 5080 | 5472 | // `item` is already guaranteed to be constant known. |
| 5081 | 5473 | |
| 5082 | 5474 | _ = try sema.analyzeBody(&case_block, body); |
| 5083 | 5475 | |
| 5476 | try wip_captures.finalize(); | |
| 5477 | ||
| 5084 | 5478 | try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len); |
| 5085 | 5479 | cases_extra.appendAssumeCapacity(1); // items_len |
| 5086 | 5480 | cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); |
| ... | ... | @@ -5108,6 +5502,7 @@ fn analyzeSwitch( |
| 5108 | 5502 | extra_index += items_len; |
| 5109 | 5503 | |
| 5110 | 5504 | case_block.instructions.shrinkRetainingCapacity(0); |
| 5505 | case_block.wip_capture_scope = child_block.wip_capture_scope; | |
| 5111 | 5506 | |
| 5112 | 5507 | var any_ok: Air.Inst.Ref = .none; |
| 5113 | 5508 | |
| ... | ... | @@ -5186,11 +5581,18 @@ fn analyzeSwitch( |
| 5186 | 5581 | var cond_body = case_block.instructions.toOwnedSlice(gpa); |
| 5187 | 5582 | defer gpa.free(cond_body); |
| 5188 | 5583 | |
| 5584 | var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope); | |
| 5585 | defer wip_captures.deinit(); | |
| 5586 | ||
| 5189 | 5587 | case_block.instructions.shrinkRetainingCapacity(0); |
| 5588 | case_block.wip_capture_scope = wip_captures.scope; | |
| 5589 | ||
| 5190 | 5590 | const body = sema.code.extra[extra_index..][0..body_len]; |
| 5191 | 5591 | extra_index += body_len; |
| 5192 | 5592 | _ = try sema.analyzeBody(&case_block, body); |
| 5193 | 5593 | |
| 5594 | try wip_captures.finalize(); | |
| 5595 | ||
| 5194 | 5596 | if (is_first) { |
| 5195 | 5597 | is_first = false; |
| 5196 | 5598 | first_else_body = cond_body; |
| ... | ... | @@ -5216,9 +5618,16 @@ fn analyzeSwitch( |
| 5216 | 5618 | |
| 5217 | 5619 | var final_else_body: []const Air.Inst.Index = &.{}; |
| 5218 | 5620 | if (special.body.len != 0) { |
| 5621 | var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope); | |
| 5622 | defer wip_captures.deinit(); | |
| 5623 | ||
| 5219 | 5624 | case_block.instructions.shrinkRetainingCapacity(0); |
| 5625 | case_block.wip_capture_scope = wip_captures.scope; | |
| 5626 | ||
| 5220 | 5627 | _ = try sema.analyzeBody(&case_block, special.body); |
| 5221 | 5628 | |
| 5629 | try wip_captures.finalize(); | |
| 5630 | ||
| 5222 | 5631 | if (is_first) { |
| 5223 | 5632 | final_else_body = case_block.instructions.items; |
| 5224 | 5633 | } else { |
| ... | ... | @@ -5500,7 +5909,7 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro |
| 5500 | 5909 | try mod.semaFile(result.file); |
| 5501 | 5910 | const file_root_decl = result.file.root_decl.?; |
| 5502 | 5911 | try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl); |
| 5503 | return sema.addType(file_root_decl.ty); | |
| 5912 | return sema.addConstant(file_root_decl.ty, file_root_decl.val); | |
| 5504 | 5913 | } |
| 5505 | 5914 | |
| 5506 | 5915 | fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -5509,29 +5918,57 @@ fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co |
| 5509 | 5918 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{}); |
| 5510 | 5919 | } |
| 5511 | 5920 | |
| 5512 | fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 5921 | fn zirShl( | |
| 5922 | sema: *Sema, | |
| 5923 | block: *Scope.Block, | |
| 5924 | inst: Zir.Inst.Index, | |
| 5925 | air_tag: Air.Inst.Tag, | |
| 5926 | ) CompileError!Air.Inst.Ref { | |
| 5513 | 5927 | const tracy = trace(@src()); |
| 5514 | 5928 | defer tracy.end(); |
| 5515 | 5929 | |
| 5516 | 5930 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 5517 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; | |
| 5518 | 5931 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| 5519 | 5932 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| 5520 | 5933 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 5521 | 5934 | const lhs = sema.resolveInst(extra.lhs); |
| 5522 | 5935 | const rhs = sema.resolveInst(extra.rhs); |
| 5523 | 5936 | |
| 5524 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { | |
| 5525 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { | |
| 5526 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | |
| 5527 | return sema.addConstUndef(sema.typeOf(lhs)); | |
| 5528 | } | |
| 5529 | return sema.mod.fail(&block.base, src, "TODO implement comptime shl", .{}); | |
| 5937 | // TODO coerce rhs if air_tag is not shl_sat | |
| 5938 | ||
| 5939 | const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); | |
| 5940 | const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); | |
| 5941 | ||
| 5942 | const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { | |
| 5943 | const lhs_ty = sema.typeOf(lhs); | |
| 5944 | ||
| 5945 | if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty); | |
| 5946 | const rhs_val = maybe_rhs_val orelse break :rs rhs_src; | |
| 5947 | if (rhs_val.isUndef()) return sema.addConstUndef(lhs_ty); | |
| 5948 | ||
| 5949 | // If rhs is 0, return lhs without doing any calculations. | |
| 5950 | if (rhs_val.compareWithZero(.eq)) { | |
| 5951 | return sema.addConstant(lhs_ty, lhs_val); | |
| 5530 | 5952 | } |
| 5531 | } | |
| 5953 | const val = try lhs_val.shl(rhs_val, sema.arena); | |
| 5954 | switch (air_tag) { | |
| 5955 | .shl_exact => return sema.mod.fail(&block.base, lhs_src, "TODO implement Sema for comptime shl_exact", .{}), | |
| 5956 | .shl_sat => return sema.mod.fail(&block.base, lhs_src, "TODO implement Sema for comptime shl_sat", .{}), | |
| 5957 | .shl => {}, | |
| 5958 | else => unreachable, | |
| 5959 | } | |
| 5960 | return sema.addConstant(lhs_ty, val); | |
| 5961 | } else rs: { | |
| 5962 | if (maybe_rhs_val) |rhs_val| { | |
| 5963 | if (rhs_val.isUndef()) return sema.addConstUndef(sema.typeOf(lhs)); | |
| 5964 | } | |
| 5965 | break :rs lhs_src; | |
| 5966 | }; | |
| 5532 | 5967 | |
| 5533 | try sema.requireRuntimeBlock(block, src); | |
| 5534 | return block.addBinOp(.shl, lhs, rhs); | |
| 5968 | // TODO: insert runtime safety check for shl_exact | |
| 5969 | ||
| 5970 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 5971 | return block.addBinOp(air_tag, lhs, rhs); | |
| 5535 | 5972 | } |
| 5536 | 5973 | |
| 5537 | 5974 | fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -5619,10 +6056,13 @@ fn zirBitwise( |
| 5619 | 6056 | |
| 5620 | 6057 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { |
| 5621 | 6058 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { |
| 5622 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | |
| 5623 | return sema.addConstUndef(resolved_type); | |
| 5624 | } | |
| 5625 | return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{}); | |
| 6059 | const result_val = switch (air_tag) { | |
| 6060 | .bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena), | |
| 6061 | .bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena), | |
| 6062 | .xor => try lhs_val.bitwiseXor(rhs_val, sema.arena), | |
| 6063 | else => unreachable, | |
| 6064 | }; | |
| 6065 | return sema.addConstant(scalar_type, result_val); | |
| 5626 | 6066 | } |
| 5627 | 6067 | } |
| 5628 | 6068 | |
| ... | ... | @@ -5671,38 +6111,36 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 5671 | 6111 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { |
| 5672 | 6112 | if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { |
| 5673 | 6113 | const final_len = lhs_info.len + rhs_info.len; |
| 5674 | if (lhs_ty.zigTypeTag() == .Pointer) { | |
| 5675 | var anon_decl = try block.startAnonDecl(); | |
| 5676 | defer anon_decl.deinit(); | |
| 6114 | const is_pointer = lhs_ty.zigTypeTag() == .Pointer; | |
| 6115 | var anon_decl = try block.startAnonDecl(); | |
| 6116 | defer anon_decl.deinit(); | |
| 5677 | 6117 | |
| 5678 | const lhs_sub_val = (try lhs_val.pointerDeref(anon_decl.arena())).?; | |
| 5679 | const rhs_sub_val = (try rhs_val.pointerDeref(anon_decl.arena())).?; | |
| 5680 | const buf = try anon_decl.arena().alloc(Value, final_len); | |
| 5681 | { | |
| 5682 | var i: u64 = 0; | |
| 5683 | while (i < lhs_info.len) : (i += 1) { | |
| 5684 | const val = try lhs_sub_val.elemValue(sema.arena, i); | |
| 5685 | buf[i] = try val.copy(anon_decl.arena()); | |
| 5686 | } | |
| 6118 | const lhs_sub_val = if (is_pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val; | |
| 6119 | const rhs_sub_val = if (is_pointer) (try rhs_val.pointerDeref(anon_decl.arena())).? else rhs_val; | |
| 6120 | const buf = try anon_decl.arena().alloc(Value, final_len); | |
| 6121 | { | |
| 6122 | var i: u64 = 0; | |
| 6123 | while (i < lhs_info.len) : (i += 1) { | |
| 6124 | const val = try lhs_sub_val.elemValue(sema.arena, i); | |
| 6125 | buf[i] = try val.copy(anon_decl.arena()); | |
| 5687 | 6126 | } |
| 5688 | { | |
| 5689 | var i: u64 = 0; | |
| 5690 | while (i < rhs_info.len) : (i += 1) { | |
| 5691 | const val = try rhs_sub_val.elemValue(sema.arena, i); | |
| 5692 | buf[lhs_info.len + i] = try val.copy(anon_decl.arena()); | |
| 5693 | } | |
| 6127 | } | |
| 6128 | { | |
| 6129 | var i: u64 = 0; | |
| 6130 | while (i < rhs_info.len) : (i += 1) { | |
| 6131 | const val = try rhs_sub_val.elemValue(sema.arena, i); | |
| 6132 | buf[lhs_info.len + i] = try val.copy(anon_decl.arena()); | |
| 5694 | 6133 | } |
| 5695 | const ty = if (res_sent) |rs| | |
| 5696 | try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type, .sentinel = rs }) | |
| 5697 | else | |
| 5698 | try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type }); | |
| 5699 | const val = try Value.Tag.array.create(anon_decl.arena(), buf); | |
| 5700 | return sema.analyzeDeclRef(try anon_decl.finish( | |
| 5701 | ty, | |
| 5702 | val, | |
| 5703 | )); | |
| 5704 | 6134 | } |
| 5705 | return sema.mod.fail(&block.base, lhs_src, "TODO array_cat more types of Values", .{}); | |
| 6135 | const ty = if (res_sent) |rs| | |
| 6136 | try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type, .sentinel = rs }) | |
| 6137 | else | |
| 6138 | try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type }); | |
| 6139 | const val = try Value.Tag.array.create(anon_decl.arena(), buf); | |
| 6140 | return if (is_pointer) | |
| 6141 | sema.analyzeDeclRef(try anon_decl.finish(ty, val)) | |
| 6142 | else | |
| 6143 | sema.analyzeDeclVal(block, .unneeded, try anon_decl.finish(ty, val)); | |
| 5706 | 6144 | } else { |
| 5707 | 6145 | return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_cat", .{}); |
| 5708 | 6146 | } |
| ... | ... | @@ -5742,32 +6180,30 @@ fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 5742 | 6180 | |
| 5743 | 6181 | const final_len = std.math.mul(u64, mulinfo.len, tomulby) catch return sema.mod.fail(&block.base, rhs_src, "operation results in overflow", .{}); |
| 5744 | 6182 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { |
| 5745 | if (lhs_ty.zigTypeTag() == .Pointer) { | |
| 5746 | var anon_decl = try block.startAnonDecl(); | |
| 5747 | defer anon_decl.deinit(); | |
| 5748 | const lhs_sub_val = (try lhs_val.pointerDeref(anon_decl.arena())).?; | |
| 5749 | ||
| 5750 | const final_ty = if (mulinfo.sentinel) |sent| | |
| 5751 | try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type, .sentinel = sent }) | |
| 5752 | else | |
| 5753 | try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type }); | |
| 5754 | ||
| 5755 | const buf = try anon_decl.arena().alloc(Value, final_len); | |
| 5756 | var i: u64 = 0; | |
| 5757 | while (i < tomulby) : (i += 1) { | |
| 5758 | var j: u64 = 0; | |
| 5759 | while (j < mulinfo.len) : (j += 1) { | |
| 5760 | const val = try lhs_sub_val.elemValue(sema.arena, j); | |
| 5761 | buf[mulinfo.len * i + j] = try val.copy(anon_decl.arena()); | |
| 5762 | } | |
| 6183 | var anon_decl = try block.startAnonDecl(); | |
| 6184 | defer anon_decl.deinit(); | |
| 6185 | const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val; | |
| 6186 | const final_ty = if (mulinfo.sentinel) |sent| | |
| 6187 | try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type, .sentinel = sent }) | |
| 6188 | else | |
| 6189 | try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type }); | |
| 6190 | const buf = try anon_decl.arena().alloc(Value, final_len); | |
| 6191 | ||
| 6192 | // the actual loop | |
| 6193 | var i: u64 = 0; | |
| 6194 | while (i < tomulby) : (i += 1) { | |
| 6195 | var j: u64 = 0; | |
| 6196 | while (j < mulinfo.len) : (j += 1) { | |
| 6197 | const val = try lhs_sub_val.elemValue(sema.arena, j); | |
| 6198 | buf[mulinfo.len * i + j] = try val.copy(anon_decl.arena()); | |
| 5763 | 6199 | } |
| 5764 | const val = try Value.Tag.array.create(anon_decl.arena(), buf); | |
| 5765 | return sema.analyzeDeclRef(try anon_decl.finish( | |
| 5766 | final_ty, | |
| 5767 | val, | |
| 5768 | )); | |
| 5769 | 6200 | } |
| 5770 | return sema.mod.fail(&block.base, lhs_src, "TODO array_mul more types of Values", .{}); | |
| 6201 | const val = try Value.Tag.array.create(anon_decl.arena(), buf); | |
| 6202 | if (lhs_ty.zigTypeTag() == .Pointer) { | |
| 6203 | return sema.analyzeDeclRef(try anon_decl.finish(final_ty, val)); | |
| 6204 | } else { | |
| 6205 | return sema.analyzeDeclVal(block, .unneeded, try anon_decl.finish(final_ty, val)); | |
| 6206 | } | |
| 5771 | 6207 | } |
| 5772 | 6208 | return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_mul", .{}); |
| 5773 | 6209 | } |
| ... | ... | @@ -5791,11 +6227,15 @@ fn zirNegate( |
| 5791 | 6227 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); |
| 5792 | 6228 | } |
| 5793 | 6229 | |
| 5794 | fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 6230 | fn zirArithmetic( | |
| 6231 | sema: *Sema, | |
| 6232 | block: *Scope.Block, | |
| 6233 | inst: Zir.Inst.Index, | |
| 6234 | zir_tag: Zir.Inst.Tag, | |
| 6235 | ) CompileError!Air.Inst.Ref { | |
| 5795 | 6236 | const tracy = trace(@src()); |
| 5796 | 6237 | defer tracy.end(); |
| 5797 | 6238 | |
| 5798 | const tag_override = block.sema.code.instructions.items(.tag)[inst]; | |
| 5799 | 6239 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 5800 | 6240 | sema.src = .{ .node_offset_bin_op = inst_data.src_node }; |
| 5801 | 6241 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| ... | ... | @@ -5804,7 +6244,7 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile |
| 5804 | 6244 | const lhs = sema.resolveInst(extra.lhs); |
| 5805 | 6245 | const rhs = sema.resolveInst(extra.rhs); |
| 5806 | 6246 | |
| 5807 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, sema.src, lhs_src, rhs_src); | |
| 6247 | return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src); | |
| 5808 | 6248 | } |
| 5809 | 6249 | |
| 5810 | 6250 | fn zirOverflowArithmetic( |
| ... | ... | @@ -5821,22 +6261,10 @@ fn zirOverflowArithmetic( |
| 5821 | 6261 | return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{}); |
| 5822 | 6262 | } |
| 5823 | 6263 | |
| 5824 | fn zirSatArithmetic( | |
| 5825 | sema: *Sema, | |
| 5826 | block: *Scope.Block, | |
| 5827 | extended: Zir.Inst.Extended.InstData, | |
| 5828 | ) CompileError!Air.Inst.Ref { | |
| 5829 | const tracy = trace(@src()); | |
| 5830 | defer tracy.end(); | |
| 5831 | ||
| 5832 | const extra = sema.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data; | |
| 5833 | const src: LazySrcLoc = .{ .node_offset = extra.node }; | |
| 5834 | return sema.mod.fail(&block.base, src, "TODO implement Sema.zirSatArithmetic", .{}); | |
| 5835 | } | |
| 5836 | ||
| 5837 | 6264 | fn analyzeArithmetic( |
| 5838 | 6265 | sema: *Sema, |
| 5839 | 6266 | block: *Scope.Block, |
| 6267 | /// TODO performance investigation: make this comptime? | |
| 5840 | 6268 | zir_tag: Zir.Inst.Tag, |
| 5841 | 6269 | lhs: Air.Inst.Ref, |
| 5842 | 6270 | rhs: Air.Inst.Ref, |
| ... | ... | @@ -5854,7 +6282,7 @@ fn analyzeArithmetic( |
| 5854 | 6282 | lhs_ty.arrayLen(), rhs_ty.arrayLen(), |
| 5855 | 6283 | }); |
| 5856 | 6284 | } |
| 5857 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBinOp", .{}); | |
| 6285 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in Sema.analyzeArithmetic", .{}); | |
| 5858 | 6286 | } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) { |
| 5859 | 6287 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ |
| 5860 | 6288 | lhs_ty, rhs_ty, |
| ... | ... | @@ -5897,7 +6325,9 @@ fn analyzeArithmetic( |
| 5897 | 6325 | }; |
| 5898 | 6326 | |
| 5899 | 6327 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 5900 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); | |
| 6328 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ | |
| 6329 | .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, | |
| 6330 | }); | |
| 5901 | 6331 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 5902 | 6332 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 5903 | 6333 | |
| ... | ... | @@ -5917,132 +6347,627 @@ fn analyzeArithmetic( |
| 5917 | 6347 | }); |
| 5918 | 6348 | } |
| 5919 | 6349 | |
| 5920 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { | |
| 5921 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { | |
| 5922 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | |
| 5923 | return sema.addConstUndef(resolved_type); | |
| 5924 | } | |
| 5925 | // incase rhs is 0, simply return lhs without doing any calculations | |
| 5926 | // TODO Once division is implemented we should throw an error when dividing by 0. | |
| 5927 | if (rhs_val.compareWithZero(.eq)) { | |
| 5928 | switch (zir_tag) { | |
| 5929 | .add, .addwrap, .sub, .subwrap => { | |
| 5930 | return sema.addConstant(scalar_type, lhs_val); | |
| 5931 | }, | |
| 5932 | else => {}, | |
| 6350 | const target = sema.mod.getTarget(); | |
| 6351 | const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs); | |
| 6352 | const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs); | |
| 6353 | const rs: struct { src: LazySrcLoc, air_tag: Air.Inst.Tag } = rs: { | |
| 6354 | switch (zir_tag) { | |
| 6355 | .add => { | |
| 6356 | // For integers: | |
| 6357 | // If either of the operands are zero, then the other operand is | |
| 6358 | // returned, even if it is undefined. | |
| 6359 | // If either of the operands are undefined, it's a compile error | |
| 6360 | // because there is a possible value for which the addition would | |
| 6361 | // overflow (max_int), causing illegal behavior. | |
| 6362 | // For floats: either operand being undef makes the result undef. | |
| 6363 | if (maybe_lhs_val) |lhs_val| { | |
| 6364 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { | |
| 6365 | return casted_rhs; | |
| 6366 | } | |
| 5933 | 6367 | } |
| 5934 | } | |
| 5935 | ||
| 5936 | const value = switch (zir_tag) { | |
| 5937 | .add => blk: { | |
| 5938 | const val = if (is_int) | |
| 5939 | try lhs_val.intAdd(rhs_val, sema.arena) | |
| 5940 | else | |
| 5941 | try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena); | |
| 5942 | break :blk val; | |
| 5943 | }, | |
| 5944 | .sub => blk: { | |
| 5945 | const val = if (is_int) | |
| 5946 | try lhs_val.intSub(rhs_val, sema.arena) | |
| 5947 | else | |
| 5948 | try lhs_val.floatSub(rhs_val, scalar_type, sema.arena); | |
| 5949 | break :blk val; | |
| 5950 | }, | |
| 5951 | .div => blk: { | |
| 5952 | const val = if (is_int) | |
| 5953 | try lhs_val.intDiv(rhs_val, sema.arena) | |
| 5954 | else | |
| 5955 | try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena); | |
| 5956 | break :blk val; | |
| 5957 | }, | |
| 5958 | .mul => blk: { | |
| 5959 | const val = if (is_int) | |
| 5960 | try lhs_val.intMul(rhs_val, sema.arena) | |
| 5961 | else | |
| 5962 | try lhs_val.floatMul(rhs_val, scalar_type, sema.arena); | |
| 5963 | break :blk val; | |
| 5964 | }, | |
| 5965 | else => return sema.mod.fail(&block.base, src, "TODO implement comptime arithmetic for operand '{s}'", .{@tagName(zir_tag)}), | |
| 5966 | }; | |
| 5967 | ||
| 5968 | log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value }); | |
| 5969 | ||
| 5970 | return sema.addConstant(scalar_type, value); | |
| 5971 | } else { | |
| 5972 | try sema.requireRuntimeBlock(block, rhs_src); | |
| 5973 | } | |
| 5974 | } else { | |
| 5975 | try sema.requireRuntimeBlock(block, lhs_src); | |
| 5976 | } | |
| 5977 | ||
| 5978 | if (zir_tag == .mod_rem) { | |
| 5979 | const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isFloat(); | |
| 5980 | const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isFloat(); | |
| 5981 | if (dirty_lhs or dirty_rhs) { | |
| 5982 | return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty }); | |
| 5983 | } | |
| 5984 | } | |
| 5985 | ||
| 5986 | const air_tag: Air.Inst.Tag = switch (zir_tag) { | |
| 5987 | .add => .add, | |
| 5988 | .addwrap => .addwrap, | |
| 5989 | .sub => .sub, | |
| 5990 | .subwrap => .subwrap, | |
| 5991 | .mul => .mul, | |
| 5992 | .mulwrap => .mulwrap, | |
| 5993 | .div => .div, | |
| 5994 | .mod_rem => .rem, | |
| 5995 | .rem => .rem, | |
| 5996 | else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}'", .{@tagName(zir_tag)}), | |
| 5997 | }; | |
| 5998 | ||
| 5999 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); | |
| 6000 | } | |
| 6001 | ||
| 6002 | fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 6003 | const tracy = trace(@src()); | |
| 6004 | defer tracy.end(); | |
| 6005 | ||
| 6006 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | |
| 6007 | const src = inst_data.src(); | |
| 6008 | const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node }; | |
| 6009 | const ptr = sema.resolveInst(inst_data.operand); | |
| 6010 | return sema.analyzeLoad(block, src, ptr, ptr_src); | |
| 6011 | } | |
| 6012 | ||
| 6013 | fn zirAsm( | |
| 6014 | sema: *Sema, | |
| 6015 | block: *Scope.Block, | |
| 6016 | extended: Zir.Inst.Extended.InstData, | |
| 6017 | inst: Zir.Inst.Index, | |
| 6018 | ) CompileError!Air.Inst.Ref { | |
| 6019 | const tracy = trace(@src()); | |
| 6020 | defer tracy.end(); | |
| 6021 | ||
| 6022 | const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); | |
| 6023 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; | |
| 6024 | const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node }; | |
| 6025 | const outputs_len = @truncate(u5, extended.small); | |
| 6026 | const inputs_len = @truncate(u5, extended.small >> 5); | |
| 6027 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 6028 | ||
| 6029 | if (outputs_len > 1) { | |
| 6030 | return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with more than 1 output", .{}); | |
| 6031 | } | |
| 6032 | ||
| 6033 | var extra_i = extra.end; | |
| 6034 | var output_type_bits = extra.data.output_type_bits; | |
| 6035 | ||
| 6036 | const Output = struct { constraint: []const u8, ty: Type }; | |
| 6037 | const output: ?Output = if (outputs_len == 0) null else blk: { | |
| 6038 | const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); | |
| 6039 | extra_i = output.end; | |
| 6040 | ||
| 6041 | const is_type = @truncate(u1, output_type_bits) != 0; | |
| 6042 | output_type_bits >>= 1; | |
| 6368 | if (maybe_rhs_val) |rhs_val| { | |
| 6369 | if (rhs_val.isUndef()) { | |
| 6370 | if (is_int) { | |
| 6371 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6372 | } else { | |
| 6373 | return sema.addConstUndef(scalar_type); | |
| 6374 | } | |
| 6375 | } | |
| 6376 | if (rhs_val.compareWithZero(.eq)) { | |
| 6377 | return casted_lhs; | |
| 6378 | } | |
| 6379 | } | |
| 6380 | if (maybe_lhs_val) |lhs_val| { | |
| 6381 | if (lhs_val.isUndef()) { | |
| 6382 | if (is_int) { | |
| 6383 | return sema.failWithUseOfUndef(block, lhs_src); | |
| 6384 | } else { | |
| 6385 | return sema.addConstUndef(scalar_type); | |
| 6386 | } | |
| 6387 | } | |
| 6388 | if (maybe_rhs_val) |rhs_val| { | |
| 6389 | if (is_int) { | |
| 6390 | return sema.addConstant( | |
| 6391 | scalar_type, | |
| 6392 | try lhs_val.intAdd(rhs_val, sema.arena), | |
| 6393 | ); | |
| 6394 | } else { | |
| 6395 | return sema.addConstant( | |
| 6396 | scalar_type, | |
| 6397 | try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena), | |
| 6398 | ); | |
| 6399 | } | |
| 6400 | } else break :rs .{ .src = rhs_src, .air_tag = .add }; | |
| 6401 | } else break :rs .{ .src = lhs_src, .air_tag = .add }; | |
| 6402 | }, | |
| 6403 | .addwrap => { | |
| 6404 | // Integers only; floats are checked above. | |
| 6405 | // If either of the operands are zero, the other operand is returned. | |
| 6406 | // If either of the operands are undefined, the result is undefined. | |
| 6407 | if (maybe_lhs_val) |lhs_val| { | |
| 6408 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { | |
| 6409 | return casted_rhs; | |
| 6410 | } | |
| 6411 | } | |
| 6412 | if (maybe_rhs_val) |rhs_val| { | |
| 6413 | if (rhs_val.isUndef()) { | |
| 6414 | return sema.addConstUndef(scalar_type); | |
| 6415 | } | |
| 6416 | if (rhs_val.compareWithZero(.eq)) { | |
| 6417 | return casted_lhs; | |
| 6418 | } | |
| 6419 | if (maybe_lhs_val) |lhs_val| { | |
| 6420 | return sema.addConstant( | |
| 6421 | scalar_type, | |
| 6422 | try lhs_val.numberAddWrap(rhs_val, scalar_type, sema.arena, target), | |
| 6423 | ); | |
| 6424 | } else break :rs .{ .src = lhs_src, .air_tag = .addwrap }; | |
| 6425 | } else break :rs .{ .src = rhs_src, .air_tag = .addwrap }; | |
| 6426 | }, | |
| 6427 | .add_sat => { | |
| 6428 | // Integers only; floats are checked above. | |
| 6429 | // If either of the operands are zero, then the other operand is returned. | |
| 6430 | // If either of the operands are undefined, the result is undefined. | |
| 6431 | if (maybe_lhs_val) |lhs_val| { | |
| 6432 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { | |
| 6433 | return casted_rhs; | |
| 6434 | } | |
| 6435 | } | |
| 6436 | if (maybe_rhs_val) |rhs_val| { | |
| 6437 | if (rhs_val.isUndef()) { | |
| 6438 | return sema.addConstUndef(scalar_type); | |
| 6439 | } | |
| 6440 | if (rhs_val.compareWithZero(.eq)) { | |
| 6441 | return casted_lhs; | |
| 6442 | } | |
| 6443 | if (maybe_lhs_val) |lhs_val| { | |
| 6444 | return sema.addConstant( | |
| 6445 | scalar_type, | |
| 6446 | try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target), | |
| 6447 | ); | |
| 6448 | } else break :rs .{ .src = lhs_src, .air_tag = .add_sat }; | |
| 6449 | } else break :rs .{ .src = rhs_src, .air_tag = .add_sat }; | |
| 6450 | }, | |
| 6451 | .sub => { | |
| 6452 | // For integers: | |
| 6453 | // If the rhs is zero, then the other operand is | |
| 6454 | // returned, even if it is undefined. | |
| 6455 | // If either of the operands are undefined, it's a compile error | |
| 6456 | // because there is a possible value for which the subtraction would | |
| 6457 | // overflow, causing illegal behavior. | |
| 6458 | // For floats: either operand being undef makes the result undef. | |
| 6459 | if (maybe_rhs_val) |rhs_val| { | |
| 6460 | if (rhs_val.isUndef()) { | |
| 6461 | if (is_int) { | |
| 6462 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6463 | } else { | |
| 6464 | return sema.addConstUndef(scalar_type); | |
| 6465 | } | |
| 6466 | } | |
| 6467 | if (rhs_val.compareWithZero(.eq)) { | |
| 6468 | return casted_lhs; | |
| 6469 | } | |
| 6470 | } | |
| 6471 | if (maybe_lhs_val) |lhs_val| { | |
| 6472 | if (lhs_val.isUndef()) { | |
| 6473 | if (is_int) { | |
| 6474 | return sema.failWithUseOfUndef(block, lhs_src); | |
| 6475 | } else { | |
| 6476 | return sema.addConstUndef(scalar_type); | |
| 6477 | } | |
| 6478 | } | |
| 6479 | if (maybe_rhs_val) |rhs_val| { | |
| 6480 | if (is_int) { | |
| 6481 | return sema.addConstant( | |
| 6482 | scalar_type, | |
| 6483 | try lhs_val.intSub(rhs_val, sema.arena), | |
| 6484 | ); | |
| 6485 | } else { | |
| 6486 | return sema.addConstant( | |
| 6487 | scalar_type, | |
| 6488 | try lhs_val.floatSub(rhs_val, scalar_type, sema.arena), | |
| 6489 | ); | |
| 6490 | } | |
| 6491 | } else break :rs .{ .src = rhs_src, .air_tag = .sub }; | |
| 6492 | } else break :rs .{ .src = lhs_src, .air_tag = .sub }; | |
| 6493 | }, | |
| 6494 | .subwrap => { | |
| 6495 | // Integers only; floats are checked above. | |
| 6496 | // If the RHS is zero, then the other operand is returned, even if it is undefined. | |
| 6497 | // If either of the operands are undefined, the result is undefined. | |
| 6498 | if (maybe_rhs_val) |rhs_val| { | |
| 6499 | if (rhs_val.isUndef()) { | |
| 6500 | return sema.addConstUndef(scalar_type); | |
| 6501 | } | |
| 6502 | if (rhs_val.compareWithZero(.eq)) { | |
| 6503 | return casted_lhs; | |
| 6504 | } | |
| 6505 | } | |
| 6506 | if (maybe_lhs_val) |lhs_val| { | |
| 6507 | if (lhs_val.isUndef()) { | |
| 6508 | return sema.addConstUndef(scalar_type); | |
| 6509 | } | |
| 6510 | if (maybe_rhs_val) |rhs_val| { | |
| 6511 | return sema.addConstant( | |
| 6512 | scalar_type, | |
| 6513 | try lhs_val.numberSubWrap(rhs_val, scalar_type, sema.arena, target), | |
| 6514 | ); | |
| 6515 | } else break :rs .{ .src = rhs_src, .air_tag = .subwrap }; | |
| 6516 | } else break :rs .{ .src = lhs_src, .air_tag = .subwrap }; | |
| 6517 | }, | |
| 6518 | .sub_sat => { | |
| 6519 | // Integers only; floats are checked above. | |
| 6520 | // If the RHS is zero, result is LHS. | |
| 6521 | // If either of the operands are undefined, result is undefined. | |
| 6522 | if (maybe_rhs_val) |rhs_val| { | |
| 6523 | if (rhs_val.isUndef()) { | |
| 6524 | return sema.addConstUndef(scalar_type); | |
| 6525 | } | |
| 6526 | if (rhs_val.compareWithZero(.eq)) { | |
| 6527 | return casted_lhs; | |
| 6528 | } | |
| 6529 | } | |
| 6530 | if (maybe_lhs_val) |lhs_val| { | |
| 6531 | if (lhs_val.isUndef()) { | |
| 6532 | return sema.addConstUndef(scalar_type); | |
| 6533 | } | |
| 6534 | if (maybe_rhs_val) |rhs_val| { | |
| 6535 | return sema.addConstant( | |
| 6536 | scalar_type, | |
| 6537 | try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target), | |
| 6538 | ); | |
| 6539 | } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat }; | |
| 6540 | } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; | |
| 6541 | }, | |
| 6542 | .div => { | |
| 6543 | // For integers: | |
| 6544 | // If the lhs is zero, then zero is returned regardless of rhs. | |
| 6545 | // If the rhs is zero, compile error for division by zero. | |
| 6546 | // If the rhs is undefined, compile error because there is a possible | |
| 6547 | // value (zero) for which the division would be illegal behavior. | |
| 6548 | // If the lhs is undefined: | |
| 6549 | // * if lhs type is signed: | |
| 6550 | // * if rhs is comptime-known and not -1, result is undefined | |
| 6551 | // * if rhs is -1 or runtime-known, compile error because there is a | |
| 6552 | // possible value (-min_int * -1) for which division would be | |
| 6553 | // illegal behavior. | |
| 6554 | // * if lhs type is unsigned, undef is returned regardless of rhs. | |
| 6555 | // For floats: | |
| 6556 | // If the rhs is zero, compile error for division by zero. | |
| 6557 | // If the rhs is undefined, compile error because there is a possible | |
| 6558 | // value (zero) for which the division would be illegal behavior. | |
| 6559 | // If the lhs is undefined, result is undefined. | |
| 6560 | if (maybe_lhs_val) |lhs_val| { | |
| 6561 | if (!lhs_val.isUndef()) { | |
| 6562 | if (lhs_val.compareWithZero(.eq)) { | |
| 6563 | return sema.addConstant(scalar_type, Value.zero); | |
| 6564 | } | |
| 6565 | } | |
| 6566 | } | |
| 6567 | if (maybe_rhs_val) |rhs_val| { | |
| 6568 | if (rhs_val.isUndef()) { | |
| 6569 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6570 | } | |
| 6571 | if (rhs_val.compareWithZero(.eq)) { | |
| 6572 | return sema.failWithDivideByZero(block, rhs_src); | |
| 6573 | } | |
| 6574 | } | |
| 6575 | if (maybe_lhs_val) |lhs_val| { | |
| 6576 | if (lhs_val.isUndef()) { | |
| 6577 | if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { | |
| 6578 | if (maybe_rhs_val) |rhs_val| { | |
| 6579 | if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { | |
| 6580 | return sema.addConstUndef(scalar_type); | |
| 6581 | } | |
| 6582 | } | |
| 6583 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6584 | } | |
| 6585 | return sema.addConstUndef(scalar_type); | |
| 6586 | } | |
| 6043 | 6587 | |
| 6044 | if (!is_type) { | |
| 6045 | return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with non `->` output", .{}); | |
| 6588 | if (maybe_rhs_val) |rhs_val| { | |
| 6589 | if (is_int) { | |
| 6590 | return sema.addConstant( | |
| 6591 | scalar_type, | |
| 6592 | try lhs_val.intDiv(rhs_val, sema.arena), | |
| 6593 | ); | |
| 6594 | } else { | |
| 6595 | return sema.addConstant( | |
| 6596 | scalar_type, | |
| 6597 | try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena), | |
| 6598 | ); | |
| 6599 | } | |
| 6600 | } else break :rs .{ .src = rhs_src, .air_tag = .div }; | |
| 6601 | } else break :rs .{ .src = lhs_src, .air_tag = .div }; | |
| 6602 | }, | |
| 6603 | .mul => { | |
| 6604 | // For integers: | |
| 6605 | // If either of the operands are zero, the result is zero. | |
| 6606 | // If either of the operands are one, the result is the other | |
| 6607 | // operand, even if it is undefined. | |
| 6608 | // If either of the operands are undefined, it's a compile error | |
| 6609 | // because there is a possible value for which the addition would | |
| 6610 | // overflow (max_int), causing illegal behavior. | |
| 6611 | // For floats: either operand being undef makes the result undef. | |
| 6612 | if (maybe_lhs_val) |lhs_val| { | |
| 6613 | if (!lhs_val.isUndef()) { | |
| 6614 | if (lhs_val.compareWithZero(.eq)) { | |
| 6615 | return sema.addConstant(scalar_type, Value.zero); | |
| 6616 | } | |
| 6617 | if (lhs_val.compare(.eq, Value.one, scalar_type)) { | |
| 6618 | return casted_rhs; | |
| 6619 | } | |
| 6620 | } | |
| 6621 | } | |
| 6622 | if (maybe_rhs_val) |rhs_val| { | |
| 6623 | if (rhs_val.isUndef()) { | |
| 6624 | if (is_int) { | |
| 6625 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6626 | } else { | |
| 6627 | return sema.addConstUndef(scalar_type); | |
| 6628 | } | |
| 6629 | } | |
| 6630 | if (rhs_val.compareWithZero(.eq)) { | |
| 6631 | return sema.addConstant(scalar_type, Value.zero); | |
| 6632 | } | |
| 6633 | if (rhs_val.compare(.eq, Value.one, scalar_type)) { | |
| 6634 | return casted_lhs; | |
| 6635 | } | |
| 6636 | if (maybe_lhs_val) |lhs_val| { | |
| 6637 | if (lhs_val.isUndef()) { | |
| 6638 | if (is_int) { | |
| 6639 | return sema.failWithUseOfUndef(block, lhs_src); | |
| 6640 | } else { | |
| 6641 | return sema.addConstUndef(scalar_type); | |
| 6642 | } | |
| 6643 | } | |
| 6644 | if (is_int) { | |
| 6645 | return sema.addConstant( | |
| 6646 | scalar_type, | |
| 6647 | try lhs_val.intMul(rhs_val, sema.arena), | |
| 6648 | ); | |
| 6649 | } else { | |
| 6650 | return sema.addConstant( | |
| 6651 | scalar_type, | |
| 6652 | try lhs_val.floatMul(rhs_val, scalar_type, sema.arena), | |
| 6653 | ); | |
| 6654 | } | |
| 6655 | } else break :rs .{ .src = lhs_src, .air_tag = .mul }; | |
| 6656 | } else break :rs .{ .src = rhs_src, .air_tag = .mul }; | |
| 6657 | }, | |
| 6658 | .mulwrap => { | |
| 6659 | // Integers only; floats are handled above. | |
| 6660 | // If either of the operands are zero, result is zero. | |
| 6661 | // If either of the operands are one, result is the other operand. | |
| 6662 | // If either of the operands are undefined, result is undefined. | |
| 6663 | if (maybe_lhs_val) |lhs_val| { | |
| 6664 | if (!lhs_val.isUndef()) { | |
| 6665 | if (lhs_val.compareWithZero(.eq)) { | |
| 6666 | return sema.addConstant(scalar_type, Value.zero); | |
| 6667 | } | |
| 6668 | if (lhs_val.compare(.eq, Value.one, scalar_type)) { | |
| 6669 | return casted_rhs; | |
| 6670 | } | |
| 6671 | } | |
| 6672 | } | |
| 6673 | if (maybe_rhs_val) |rhs_val| { | |
| 6674 | if (rhs_val.isUndef()) { | |
| 6675 | return sema.addConstUndef(scalar_type); | |
| 6676 | } | |
| 6677 | if (rhs_val.compareWithZero(.eq)) { | |
| 6678 | return sema.addConstant(scalar_type, Value.zero); | |
| 6679 | } | |
| 6680 | if (rhs_val.compare(.eq, Value.one, scalar_type)) { | |
| 6681 | return casted_lhs; | |
| 6682 | } | |
| 6683 | if (maybe_lhs_val) |lhs_val| { | |
| 6684 | if (lhs_val.isUndef()) { | |
| 6685 | return sema.addConstUndef(scalar_type); | |
| 6686 | } | |
| 6687 | return sema.addConstant( | |
| 6688 | scalar_type, | |
| 6689 | try lhs_val.numberMulWrap(rhs_val, scalar_type, sema.arena, target), | |
| 6690 | ); | |
| 6691 | } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap }; | |
| 6692 | } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap }; | |
| 6693 | }, | |
| 6694 | .mul_sat => { | |
| 6695 | // Integers only; floats are checked above. | |
| 6696 | // If either of the operands are zero, result is zero. | |
| 6697 | // If either of the operands are one, result is the other operand. | |
| 6698 | // If either of the operands are undefined, result is undefined. | |
| 6699 | if (maybe_lhs_val) |lhs_val| { | |
| 6700 | if (!lhs_val.isUndef()) { | |
| 6701 | if (lhs_val.compareWithZero(.eq)) { | |
| 6702 | return sema.addConstant(scalar_type, Value.zero); | |
| 6703 | } | |
| 6704 | if (lhs_val.compare(.eq, Value.one, scalar_type)) { | |
| 6705 | return casted_rhs; | |
| 6706 | } | |
| 6707 | } | |
| 6708 | } | |
| 6709 | if (maybe_rhs_val) |rhs_val| { | |
| 6710 | if (rhs_val.isUndef()) { | |
| 6711 | return sema.addConstUndef(scalar_type); | |
| 6712 | } | |
| 6713 | if (rhs_val.compareWithZero(.eq)) { | |
| 6714 | return sema.addConstant(scalar_type, Value.zero); | |
| 6715 | } | |
| 6716 | if (rhs_val.compare(.eq, Value.one, scalar_type)) { | |
| 6717 | return casted_lhs; | |
| 6718 | } | |
| 6719 | if (maybe_lhs_val) |lhs_val| { | |
| 6720 | if (lhs_val.isUndef()) { | |
| 6721 | return sema.addConstUndef(scalar_type); | |
| 6722 | } | |
| 6723 | return sema.addConstant( | |
| 6724 | scalar_type, | |
| 6725 | try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target), | |
| 6726 | ); | |
| 6727 | } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat }; | |
| 6728 | } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat }; | |
| 6729 | }, | |
| 6730 | .mod_rem => { | |
| 6731 | // For integers: | |
| 6732 | // Either operand being undef is a compile error because there exists | |
| 6733 | // a possible value (TODO what is it?) that would invoke illegal behavior. | |
| 6734 | // TODO: can lhs zero be handled better? | |
| 6735 | // TODO: can lhs undef be handled better? | |
| 6736 | // | |
| 6737 | // For floats: | |
| 6738 | // If the rhs is zero, compile error for division by zero. | |
| 6739 | // If the rhs is undefined, compile error because there is a possible | |
| 6740 | // value (zero) for which the division would be illegal behavior. | |
| 6741 | // If the lhs is undefined, result is undefined. | |
| 6742 | // | |
| 6743 | // For either one: if the result would be different between @mod and @rem, | |
| 6744 | // then emit a compile error saying you have to pick one. | |
| 6745 | if (is_int) { | |
| 6746 | if (maybe_lhs_val) |lhs_val| { | |
| 6747 | if (lhs_val.isUndef()) { | |
| 6748 | return sema.failWithUseOfUndef(block, lhs_src); | |
| 6749 | } | |
| 6750 | if (lhs_val.compareWithZero(.lt)) { | |
| 6751 | return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); | |
| 6752 | } | |
| 6753 | } else if (lhs_ty.isSignedInt()) { | |
| 6754 | return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); | |
| 6755 | } | |
| 6756 | if (maybe_rhs_val) |rhs_val| { | |
| 6757 | if (rhs_val.isUndef()) { | |
| 6758 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6759 | } | |
| 6760 | if (rhs_val.compareWithZero(.eq)) { | |
| 6761 | return sema.failWithDivideByZero(block, rhs_src); | |
| 6762 | } | |
| 6763 | if (rhs_val.compareWithZero(.lt)) { | |
| 6764 | return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); | |
| 6765 | } | |
| 6766 | if (maybe_lhs_val) |lhs_val| { | |
| 6767 | return sema.addConstant( | |
| 6768 | scalar_type, | |
| 6769 | try lhs_val.intRem(rhs_val, sema.arena), | |
| 6770 | ); | |
| 6771 | } | |
| 6772 | break :rs .{ .src = lhs_src, .air_tag = .rem }; | |
| 6773 | } else if (rhs_ty.isSignedInt()) { | |
| 6774 | return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); | |
| 6775 | } else { | |
| 6776 | break :rs .{ .src = rhs_src, .air_tag = .rem }; | |
| 6777 | } | |
| 6778 | } | |
| 6779 | // float operands | |
| 6780 | if (maybe_rhs_val) |rhs_val| { | |
| 6781 | if (rhs_val.isUndef()) { | |
| 6782 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6783 | } | |
| 6784 | if (rhs_val.compareWithZero(.eq)) { | |
| 6785 | return sema.failWithDivideByZero(block, rhs_src); | |
| 6786 | } | |
| 6787 | if (rhs_val.compareWithZero(.lt)) { | |
| 6788 | return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); | |
| 6789 | } | |
| 6790 | if (maybe_lhs_val) |lhs_val| { | |
| 6791 | if (lhs_val.isUndef() or lhs_val.compareWithZero(.lt)) { | |
| 6792 | return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); | |
| 6793 | } | |
| 6794 | return sema.addConstant( | |
| 6795 | scalar_type, | |
| 6796 | try lhs_val.floatRem(rhs_val, sema.arena), | |
| 6797 | ); | |
| 6798 | } else { | |
| 6799 | return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); | |
| 6800 | } | |
| 6801 | } else { | |
| 6802 | return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); | |
| 6803 | } | |
| 6804 | }, | |
| 6805 | .rem => { | |
| 6806 | // For integers: | |
| 6807 | // Either operand being undef is a compile error because there exists | |
| 6808 | // a possible value (TODO what is it?) that would invoke illegal behavior. | |
| 6809 | // TODO: can lhs zero be handled better? | |
| 6810 | // TODO: can lhs undef be handled better? | |
| 6811 | // | |
| 6812 | // For floats: | |
| 6813 | // If the rhs is zero, compile error for division by zero. | |
| 6814 | // If the rhs is undefined, compile error because there is a possible | |
| 6815 | // value (zero) for which the division would be illegal behavior. | |
| 6816 | // If the lhs is undefined, result is undefined. | |
| 6817 | if (is_int) { | |
| 6818 | if (maybe_lhs_val) |lhs_val| { | |
| 6819 | if (lhs_val.isUndef()) { | |
| 6820 | return sema.failWithUseOfUndef(block, lhs_src); | |
| 6821 | } | |
| 6822 | } | |
| 6823 | if (maybe_rhs_val) |rhs_val| { | |
| 6824 | if (rhs_val.isUndef()) { | |
| 6825 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6826 | } | |
| 6827 | if (rhs_val.compareWithZero(.eq)) { | |
| 6828 | return sema.failWithDivideByZero(block, rhs_src); | |
| 6829 | } | |
| 6830 | if (maybe_lhs_val) |lhs_val| { | |
| 6831 | return sema.addConstant( | |
| 6832 | scalar_type, | |
| 6833 | try lhs_val.intRem(rhs_val, sema.arena), | |
| 6834 | ); | |
| 6835 | } | |
| 6836 | break :rs .{ .src = lhs_src, .air_tag = .rem }; | |
| 6837 | } else { | |
| 6838 | break :rs .{ .src = rhs_src, .air_tag = .rem }; | |
| 6839 | } | |
| 6840 | } | |
| 6841 | // float operands | |
| 6842 | if (maybe_rhs_val) |rhs_val| { | |
| 6843 | if (rhs_val.isUndef()) { | |
| 6844 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6845 | } | |
| 6846 | if (rhs_val.compareWithZero(.eq)) { | |
| 6847 | return sema.failWithDivideByZero(block, rhs_src); | |
| 6848 | } | |
| 6849 | } | |
| 6850 | if (maybe_lhs_val) |lhs_val| { | |
| 6851 | if (lhs_val.isUndef()) { | |
| 6852 | return sema.addConstUndef(scalar_type); | |
| 6853 | } | |
| 6854 | if (maybe_rhs_val) |rhs_val| { | |
| 6855 | return sema.addConstant( | |
| 6856 | scalar_type, | |
| 6857 | try lhs_val.floatRem(rhs_val, sema.arena), | |
| 6858 | ); | |
| 6859 | } else break :rs .{ .src = rhs_src, .air_tag = .rem }; | |
| 6860 | } else break :rs .{ .src = lhs_src, .air_tag = .rem }; | |
| 6861 | }, | |
| 6862 | .mod => { | |
| 6863 | // For integers: | |
| 6864 | // Either operand being undef is a compile error because there exists | |
| 6865 | // a possible value (TODO what is it?) that would invoke illegal behavior. | |
| 6866 | // TODO: can lhs zero be handled better? | |
| 6867 | // TODO: can lhs undef be handled better? | |
| 6868 | // | |
| 6869 | // For floats: | |
| 6870 | // If the rhs is zero, compile error for division by zero. | |
| 6871 | // If the rhs is undefined, compile error because there is a possible | |
| 6872 | // value (zero) for which the division would be illegal behavior. | |
| 6873 | // If the lhs is undefined, result is undefined. | |
| 6874 | if (is_int) { | |
| 6875 | if (maybe_lhs_val) |lhs_val| { | |
| 6876 | if (lhs_val.isUndef()) { | |
| 6877 | return sema.failWithUseOfUndef(block, lhs_src); | |
| 6878 | } | |
| 6879 | } | |
| 6880 | if (maybe_rhs_val) |rhs_val| { | |
| 6881 | if (rhs_val.isUndef()) { | |
| 6882 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6883 | } | |
| 6884 | if (rhs_val.compareWithZero(.eq)) { | |
| 6885 | return sema.failWithDivideByZero(block, rhs_src); | |
| 6886 | } | |
| 6887 | if (maybe_lhs_val) |lhs_val| { | |
| 6888 | return sema.addConstant( | |
| 6889 | scalar_type, | |
| 6890 | try lhs_val.intMod(rhs_val, sema.arena), | |
| 6891 | ); | |
| 6892 | } | |
| 6893 | break :rs .{ .src = lhs_src, .air_tag = .mod }; | |
| 6894 | } else { | |
| 6895 | break :rs .{ .src = rhs_src, .air_tag = .mod }; | |
| 6896 | } | |
| 6897 | } | |
| 6898 | // float operands | |
| 6899 | if (maybe_rhs_val) |rhs_val| { | |
| 6900 | if (rhs_val.isUndef()) { | |
| 6901 | return sema.failWithUseOfUndef(block, rhs_src); | |
| 6902 | } | |
| 6903 | if (rhs_val.compareWithZero(.eq)) { | |
| 6904 | return sema.failWithDivideByZero(block, rhs_src); | |
| 6905 | } | |
| 6906 | } | |
| 6907 | if (maybe_lhs_val) |lhs_val| { | |
| 6908 | if (lhs_val.isUndef()) { | |
| 6909 | return sema.addConstUndef(scalar_type); | |
| 6910 | } | |
| 6911 | if (maybe_rhs_val) |rhs_val| { | |
| 6912 | return sema.addConstant( | |
| 6913 | scalar_type, | |
| 6914 | try lhs_val.floatMod(rhs_val, sema.arena), | |
| 6915 | ); | |
| 6916 | } else break :rs .{ .src = rhs_src, .air_tag = .mod }; | |
| 6917 | } else break :rs .{ .src = lhs_src, .air_tag = .mod }; | |
| 6918 | }, | |
| 6919 | else => unreachable, | |
| 6920 | } | |
| 6921 | }; | |
| 6922 | ||
| 6923 | try sema.requireRuntimeBlock(block, rs.src); | |
| 6924 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); | |
| 6925 | } | |
| 6926 | ||
| 6927 | fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 6928 | const tracy = trace(@src()); | |
| 6929 | defer tracy.end(); | |
| 6930 | ||
| 6931 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | |
| 6932 | const src = inst_data.src(); | |
| 6933 | const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node }; | |
| 6934 | const ptr = sema.resolveInst(inst_data.operand); | |
| 6935 | return sema.analyzeLoad(block, src, ptr, ptr_src); | |
| 6936 | } | |
| 6937 | ||
| 6938 | fn zirAsm( | |
| 6939 | sema: *Sema, | |
| 6940 | block: *Scope.Block, | |
| 6941 | extended: Zir.Inst.Extended.InstData, | |
| 6942 | inst: Zir.Inst.Index, | |
| 6943 | ) CompileError!Air.Inst.Ref { | |
| 6944 | const tracy = trace(@src()); | |
| 6945 | defer tracy.end(); | |
| 6946 | ||
| 6947 | const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); | |
| 6948 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; | |
| 6949 | const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node }; | |
| 6950 | const outputs_len = @truncate(u5, extended.small); | |
| 6951 | const inputs_len = @truncate(u5, extended.small >> 5); | |
| 6952 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 6953 | ||
| 6954 | if (outputs_len > 1) { | |
| 6955 | return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with more than 1 output", .{}); | |
| 6956 | } | |
| 6957 | ||
| 6958 | var extra_i = extra.end; | |
| 6959 | var output_type_bits = extra.data.output_type_bits; | |
| 6960 | ||
| 6961 | const Output = struct { constraint: []const u8, ty: Type }; | |
| 6962 | const output: ?Output = if (outputs_len == 0) null else blk: { | |
| 6963 | const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); | |
| 6964 | extra_i = output.end; | |
| 6965 | ||
| 6966 | const is_type = @truncate(u1, output_type_bits) != 0; | |
| 6967 | output_type_bits >>= 1; | |
| 6968 | ||
| 6969 | if (!is_type) { | |
| 6970 | return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with non `->` output", .{}); | |
| 6046 | 6971 | } |
| 6047 | 6972 | |
| 6048 | 6973 | const constraint = sema.code.nullTerminatedString(output.data.constraint); |
| ... | ... | @@ -6134,10 +7059,11 @@ fn zirCmpEq( |
| 6134 | 7059 | const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; |
| 6135 | 7060 | return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type}); |
| 6136 | 7061 | } |
| 6137 | if (((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | |
| 6138 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | |
| 6139 | { | |
| 6140 | return mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | |
| 7062 | if (lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) { | |
| 7063 | return sema.analyzeCmpUnionTag(block, rhs, rhs_src, lhs, lhs_src, op); | |
| 7064 | } | |
| 7065 | if (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union) { | |
| 7066 | return sema.analyzeCmpUnionTag(block, lhs, lhs_src, rhs, rhs_src, op); | |
| 6141 | 7067 | } |
| 6142 | 7068 | if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { |
| 6143 | 7069 | const runtime_src: LazySrcLoc = src: { |
| ... | ... | @@ -6178,6 +7104,28 @@ fn zirCmpEq( |
| 6178 | 7104 | return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true); |
| 6179 | 7105 | } |
| 6180 | 7106 | |
| 7107 | fn analyzeCmpUnionTag( | |
| 7108 | sema: *Sema, | |
| 7109 | block: *Scope.Block, | |
| 7110 | un: Air.Inst.Ref, | |
| 7111 | un_src: LazySrcLoc, | |
| 7112 | tag: Air.Inst.Ref, | |
| 7113 | tag_src: LazySrcLoc, | |
| 7114 | op: std.math.CompareOperator, | |
| 7115 | ) CompileError!Air.Inst.Ref { | |
| 7116 | const union_ty = sema.typeOf(un); | |
| 7117 | const union_tag_ty = union_ty.unionTagType() orelse { | |
| 7118 | // TODO note at declaration site that says "union foo is not tagged" | |
| 7119 | return sema.mod.fail(&block.base, un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); | |
| 7120 | }; | |
| 7121 | // Coerce both the union and the tag to the union's tag type, and then execute the | |
| 7122 | // enum comparison codepath. | |
| 7123 | const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); | |
| 7124 | const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); | |
| 7125 | ||
| 7126 | return sema.cmpSelf(block, coerced_union, coerced_tag, op, un_src, tag_src); | |
| 7127 | } | |
| 7128 | ||
| 6181 | 7129 | /// Only called for non-equality operators. See also `zirCmpEq`. |
| 6182 | 7130 | fn zirCmp( |
| 6183 | 7131 | sema: *Sema, |
| ... | ... | @@ -6224,10 +7172,21 @@ fn analyzeCmp( |
| 6224 | 7172 | @tagName(op), resolved_type, |
| 6225 | 7173 | }); |
| 6226 | 7174 | } |
| 6227 | ||
| 6228 | 7175 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 6229 | 7176 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 7177 | return sema.cmpSelf(block, casted_lhs, casted_rhs, op, lhs_src, rhs_src); | |
| 7178 | } | |
| 6230 | 7179 | |
| 7180 | fn cmpSelf( | |
| 7181 | sema: *Sema, | |
| 7182 | block: *Scope.Block, | |
| 7183 | casted_lhs: Air.Inst.Ref, | |
| 7184 | casted_rhs: Air.Inst.Ref, | |
| 7185 | op: std.math.CompareOperator, | |
| 7186 | lhs_src: LazySrcLoc, | |
| 7187 | rhs_src: LazySrcLoc, | |
| 7188 | ) CompileError!Air.Inst.Ref { | |
| 7189 | const resolved_type = sema.typeOf(casted_lhs); | |
| 6231 | 7190 | const runtime_src: LazySrcLoc = src: { |
| 6232 | 7191 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { |
| 6233 | 7192 | if (lhs_val.isUndef()) return sema.addConstUndef(resolved_type); |
| ... | ... | @@ -6294,10 +7253,42 @@ fn runtimeBoolCmp( |
| 6294 | 7253 | |
| 6295 | 7254 | fn zirSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 6296 | 7255 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 7256 | const src = inst_data.src(); | |
| 6297 | 7257 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 6298 | 7258 | const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand); |
| 7259 | try sema.resolveTypeLayout(block, src, operand_ty); | |
| 6299 | 7260 | const target = sema.mod.getTarget(); |
| 6300 | const abi_size = operand_ty.abiSize(target); | |
| 7261 | const abi_size = switch (operand_ty.zigTypeTag()) { | |
| 7262 | .Fn => unreachable, | |
| 7263 | .NoReturn, | |
| 7264 | .Undefined, | |
| 7265 | .Null, | |
| 7266 | .BoundFn, | |
| 7267 | .Opaque, | |
| 7268 | => return sema.mod.fail(&block.base, src, "no size available for type '{}'", .{operand_ty}), | |
| 7269 | .Type, | |
| 7270 | .EnumLiteral, | |
| 7271 | .ComptimeFloat, | |
| 7272 | .ComptimeInt, | |
| 7273 | .Void, | |
| 7274 | => 0, | |
| 7275 | ||
| 7276 | .Bool, | |
| 7277 | .Int, | |
| 7278 | .Float, | |
| 7279 | .Pointer, | |
| 7280 | .Array, | |
| 7281 | .Struct, | |
| 7282 | .Optional, | |
| 7283 | .ErrorUnion, | |
| 7284 | .ErrorSet, | |
| 7285 | .Enum, | |
| 7286 | .Union, | |
| 7287 | .Vector, | |
| 7288 | .Frame, | |
| 7289 | .AnyFrame, | |
| 7290 | => operand_ty.abiSize(target), | |
| 7291 | }; | |
| 6301 | 7292 | return sema.addIntUnsigned(Type.initTag(.comptime_int), abi_size); |
| 6302 | 7293 | } |
| 6303 | 7294 | |
| ... | ... | @@ -6315,8 +7306,45 @@ fn zirThis( |
| 6315 | 7306 | block: *Scope.Block, |
| 6316 | 7307 | extended: Zir.Inst.Extended.InstData, |
| 6317 | 7308 | ) CompileError!Air.Inst.Ref { |
| 7309 | const this_decl = block.base.namespace().getDecl(); | |
| 6318 | 7310 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; |
| 6319 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirThis", .{}); | |
| 7311 | return sema.analyzeDeclVal(block, src, this_decl); | |
| 7312 | } | |
| 7313 | ||
| 7314 | fn zirClosureCapture( | |
| 7315 | sema: *Sema, | |
| 7316 | block: *Scope.Block, | |
| 7317 | inst: Zir.Inst.Index, | |
| 7318 | ) CompileError!void { | |
| 7319 | // TODO: Compile error when closed over values are modified | |
| 7320 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | |
| 7321 | const tv = try sema.resolveInstConst(block, inst_data.src(), inst_data.operand); | |
| 7322 | try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, .{ | |
| 7323 | .ty = try tv.ty.copy(sema.perm_arena), | |
| 7324 | .val = try tv.val.copy(sema.perm_arena), | |
| 7325 | }); | |
| 7326 | } | |
| 7327 | ||
| 7328 | fn zirClosureGet( | |
| 7329 | sema: *Sema, | |
| 7330 | block: *Scope.Block, | |
| 7331 | inst: Zir.Inst.Index, | |
| 7332 | ) CompileError!Air.Inst.Ref { | |
| 7333 | // TODO CLOSURE: Test this with inline functions | |
| 7334 | const inst_data = sema.code.instructions.items(.data)[inst].inst_node; | |
| 7335 | var scope: *CaptureScope = block.src_decl.src_scope.?; | |
| 7336 | // Note: The target closure must be in this scope list. | |
| 7337 | // If it's not here, the zir is invalid, or the list is broken. | |
| 7338 | const tv = while (true) { | |
| 7339 | // Note: We don't need to add a dependency here, because | |
| 7340 | // decls always depend on their lexical parents. | |
| 7341 | if (scope.captures.getPtr(inst_data.inst)) |tv| { | |
| 7342 | break tv; | |
| 7343 | } | |
| 7344 | scope = scope.parent.?; | |
| 7345 | } else unreachable; | |
| 7346 | ||
| 7347 | return sema.addConstant(tv.ty, tv.val); | |
| 6320 | 7348 | } |
| 6321 | 7349 | |
| 6322 | 7350 | fn zirRetAddr( |
| ... | ... | @@ -6345,19 +7373,80 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 6345 | 7373 | const target = sema.mod.getTarget(); |
| 6346 | 7374 | |
| 6347 | 7375 | switch (ty.zigTypeTag()) { |
| 7376 | .Type => return sema.addConstant( | |
| 7377 | type_info_ty, | |
| 7378 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7379 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Type)), | |
| 7380 | .val = Value.initTag(.unreachable_value), | |
| 7381 | }), | |
| 7382 | ), | |
| 7383 | .Void => return sema.addConstant( | |
| 7384 | type_info_ty, | |
| 7385 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7386 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Void)), | |
| 7387 | .val = Value.initTag(.unreachable_value), | |
| 7388 | }), | |
| 7389 | ), | |
| 7390 | .Bool => return sema.addConstant( | |
| 7391 | type_info_ty, | |
| 7392 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7393 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Bool)), | |
| 7394 | .val = Value.initTag(.unreachable_value), | |
| 7395 | }), | |
| 7396 | ), | |
| 7397 | .NoReturn => return sema.addConstant( | |
| 7398 | type_info_ty, | |
| 7399 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7400 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.NoReturn)), | |
| 7401 | .val = Value.initTag(.unreachable_value), | |
| 7402 | }), | |
| 7403 | ), | |
| 7404 | .ComptimeFloat => return sema.addConstant( | |
| 7405 | type_info_ty, | |
| 7406 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7407 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeFloat)), | |
| 7408 | .val = Value.initTag(.unreachable_value), | |
| 7409 | }), | |
| 7410 | ), | |
| 7411 | .ComptimeInt => return sema.addConstant( | |
| 7412 | type_info_ty, | |
| 7413 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7414 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeInt)), | |
| 7415 | .val = Value.initTag(.unreachable_value), | |
| 7416 | }), | |
| 7417 | ), | |
| 7418 | .Undefined => return sema.addConstant( | |
| 7419 | type_info_ty, | |
| 7420 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7421 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Undefined)), | |
| 7422 | .val = Value.initTag(.unreachable_value), | |
| 7423 | }), | |
| 7424 | ), | |
| 7425 | .Null => return sema.addConstant( | |
| 7426 | type_info_ty, | |
| 7427 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7428 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Null)), | |
| 7429 | .val = Value.initTag(.unreachable_value), | |
| 7430 | }), | |
| 7431 | ), | |
| 7432 | .EnumLiteral => return sema.addConstant( | |
| 7433 | type_info_ty, | |
| 7434 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7435 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.EnumLiteral)), | |
| 7436 | .val = Value.initTag(.unreachable_value), | |
| 7437 | }), | |
| 7438 | ), | |
| 6348 | 7439 | .Fn => { |
| 7440 | const info = ty.fnInfo(); | |
| 6349 | 7441 | const field_values = try sema.arena.alloc(Value, 6); |
| 6350 | 7442 | // calling_convention: CallingConvention, |
| 6351 | field_values[0] = try Value.Tag.enum_field_index.create( | |
| 6352 | sema.arena, | |
| 6353 | @enumToInt(ty.fnCallingConvention()), | |
| 6354 | ); | |
| 7443 | field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.cc)); | |
| 6355 | 7444 | // alignment: comptime_int, |
| 6356 | 7445 | field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(target)); |
| 6357 | 7446 | // is_generic: bool, |
| 6358 | field_values[2] = Value.initTag(.bool_false); // TODO | |
| 7447 | field_values[2] = if (info.is_generic) Value.initTag(.bool_true) else Value.initTag(.bool_false); | |
| 6359 | 7448 | // is_var_args: bool, |
| 6360 | field_values[3] = Value.initTag(.bool_false); // TODO | |
| 7449 | field_values[3] = if (info.is_var_args) Value.initTag(.bool_true) else Value.initTag(.bool_false); | |
| 6361 | 7450 | // return_type: ?type, |
| 6362 | 7451 | field_values[4] = try Value.Tag.ty.create(sema.arena, ty.fnReturnType()); |
| 6363 | 7452 | // args: []const FnArg, |
| ... | ... | @@ -6366,10 +7455,7 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 6366 | 7455 | return sema.addConstant( |
| 6367 | 7456 | type_info_ty, |
| 6368 | 7457 | try Value.Tag.@"union".create(sema.arena, .{ |
| 6369 | .tag = try Value.Tag.enum_field_index.create( | |
| 6370 | sema.arena, | |
| 6371 | @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Fn), | |
| 6372 | ), | |
| 7458 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Fn)), | |
| 6373 | 7459 | .val = try Value.Tag.@"struct".create(sema.arena, field_values), |
| 6374 | 7460 | }), |
| 6375 | 7461 | ); |
| ... | ... | @@ -6388,10 +7474,92 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 6388 | 7474 | return sema.addConstant( |
| 6389 | 7475 | type_info_ty, |
| 6390 | 7476 | try Value.Tag.@"union".create(sema.arena, .{ |
| 6391 | .tag = try Value.Tag.enum_field_index.create( | |
| 6392 | sema.arena, | |
| 6393 | @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Int), | |
| 6394 | ), | |
| 7477 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Int)), | |
| 7478 | .val = try Value.Tag.@"struct".create(sema.arena, field_values), | |
| 7479 | }), | |
| 7480 | ); | |
| 7481 | }, | |
| 7482 | .Float => { | |
| 7483 | const field_values = try sema.arena.alloc(Value, 1); | |
| 7484 | // bits: comptime_int, | |
| 7485 | field_values[0] = try Value.Tag.int_u64.create(sema.arena, ty.bitSize(target)); | |
| 7486 | ||
| 7487 | return sema.addConstant( | |
| 7488 | type_info_ty, | |
| 7489 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7490 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Float)), | |
| 7491 | .val = try Value.Tag.@"struct".create(sema.arena, field_values), | |
| 7492 | }), | |
| 7493 | ); | |
| 7494 | }, | |
| 7495 | .Pointer => { | |
| 7496 | const info = ty.ptrInfo().data; | |
| 7497 | const field_values = try sema.arena.alloc(Value, 7); | |
| 7498 | // size: Size, | |
| 7499 | field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.size)); | |
| 7500 | // is_const: bool, | |
| 7501 | field_values[1] = if (!info.mutable) Value.initTag(.bool_true) else Value.initTag(.bool_false); | |
| 7502 | // is_volatile: bool, | |
| 7503 | field_values[2] = if (info.@"volatile") Value.initTag(.bool_true) else Value.initTag(.bool_false); | |
| 7504 | // alignment: comptime_int, | |
| 7505 | field_values[3] = try Value.Tag.int_u64.create(sema.arena, info.@"align"); | |
| 7506 | // child: type, | |
| 7507 | field_values[4] = try Value.Tag.ty.create(sema.arena, info.pointee_type); | |
| 7508 | // is_allowzero: bool, | |
| 7509 | field_values[5] = if (info.@"allowzero") Value.initTag(.bool_true) else Value.initTag(.bool_false); | |
| 7510 | // sentinel: anytype, | |
| 7511 | field_values[6] = if (info.sentinel) |some| try Value.Tag.opt_payload.create(sema.arena, some) else Value.initTag(.null_value); | |
| 7512 | ||
| 7513 | return sema.addConstant( | |
| 7514 | type_info_ty, | |
| 7515 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7516 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Pointer)), | |
| 7517 | .val = try Value.Tag.@"struct".create(sema.arena, field_values), | |
| 7518 | }), | |
| 7519 | ); | |
| 7520 | }, | |
| 7521 | .Array => { | |
| 7522 | const info = ty.arrayInfo(); | |
| 7523 | const field_values = try sema.arena.alloc(Value, 3); | |
| 7524 | // len: comptime_int, | |
| 7525 | field_values[0] = try Value.Tag.int_u64.create(sema.arena, info.len); | |
| 7526 | // child: type, | |
| 7527 | field_values[1] = try Value.Tag.ty.create(sema.arena, info.elem_type); | |
| 7528 | // sentinel: anytype, | |
| 7529 | field_values[2] = if (info.sentinel) |some| try Value.Tag.opt_payload.create(sema.arena, some) else Value.initTag(.null_value); | |
| 7530 | ||
| 7531 | return sema.addConstant( | |
| 7532 | type_info_ty, | |
| 7533 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7534 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Array)), | |
| 7535 | .val = try Value.Tag.@"struct".create(sema.arena, field_values), | |
| 7536 | }), | |
| 7537 | ); | |
| 7538 | }, | |
| 7539 | .Optional => { | |
| 7540 | const field_values = try sema.arena.alloc(Value, 1); | |
| 7541 | // child: type, | |
| 7542 | field_values[0] = try Value.Tag.ty.create(sema.arena, try ty.optionalChildAlloc(sema.arena)); | |
| 7543 | ||
| 7544 | return sema.addConstant( | |
| 7545 | type_info_ty, | |
| 7546 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7547 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Optional)), | |
| 7548 | .val = try Value.Tag.@"struct".create(sema.arena, field_values), | |
| 7549 | }), | |
| 7550 | ); | |
| 7551 | }, | |
| 7552 | .ErrorUnion => { | |
| 7553 | const field_values = try sema.arena.alloc(Value, 2); | |
| 7554 | // error_set: type, | |
| 7555 | field_values[0] = try Value.Tag.ty.create(sema.arena, ty.errorUnionSet()); | |
| 7556 | // payload: type, | |
| 7557 | field_values[1] = try Value.Tag.ty.create(sema.arena, ty.errorUnionPayload()); | |
| 7558 | ||
| 7559 | return sema.addConstant( | |
| 7560 | type_info_ty, | |
| 7561 | try Value.Tag.@"union".create(sema.arena, .{ | |
| 7562 | .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ErrorUnion)), | |
| 6395 | 7563 | .val = try Value.Tag.@"struct".create(sema.arena, field_values), |
| 6396 | 7564 | }), |
| 6397 | 7565 | ); |
| ... | ... | @@ -6808,7 +7976,7 @@ fn analyzeRet( |
| 6808 | 7976 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { |
| 6809 | 7977 | // extend this swich as additional operators are implemented |
| 6810 | 7978 | return switch (tag) { |
| 6811 | .add, .sub, .mul, .div => true, | |
| 7979 | .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true, | |
| 6812 | 7980 | else => false, |
| 6813 | 7981 | }; |
| 6814 | 7982 | } |
| ... | ... | @@ -6819,18 +7987,14 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp |
| 6819 | 7987 | |
| 6820 | 7988 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple; |
| 6821 | 7989 | const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type); |
| 6822 | const ty = try Module.ptrType( | |
| 6823 | sema.arena, | |
| 6824 | elem_type, | |
| 6825 | null, | |
| 6826 | 0, | |
| 6827 | 0, | |
| 6828 | 0, | |
| 6829 | inst_data.is_mutable, | |
| 6830 | inst_data.is_allowzero, | |
| 6831 | inst_data.is_volatile, | |
| 6832 | inst_data.size, | |
| 6833 | ); | |
| 7990 | const ty = try Type.ptr(sema.arena, .{ | |
| 7991 | .pointee_type = elem_type, | |
| 7992 | .@"addrspace" = .generic, | |
| 7993 | .mutable = inst_data.is_mutable, | |
| 7994 | .@"allowzero" = inst_data.is_allowzero, | |
| 7995 | .@"volatile" = inst_data.is_volatile, | |
| 7996 | .size = inst_data.size, | |
| 7997 | }); | |
| 6834 | 7998 | return sema.addType(ty); |
| 6835 | 7999 | } |
| 6836 | 8000 | |
| ... | ... | @@ -6856,6 +8020,12 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 6856 | 8020 | break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32); |
| 6857 | 8021 | } else 0; |
| 6858 | 8022 | |
| 8023 | const address_space = if (inst_data.flags.has_addrspace) blk: { | |
| 8024 | const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); | |
| 8025 | extra_i += 1; | |
| 8026 | break :blk try sema.analyzeAddrspace(block, .unneeded, ref, .pointer); | |
| 8027 | } else .generic; | |
| 8028 | ||
| 6859 | 8029 | const bit_start = if (inst_data.flags.has_bit_range) blk: { |
| 6860 | 8030 | const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); |
| 6861 | 8031 | extra_i += 1; |
| ... | ... | @@ -6873,18 +8043,18 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 6873 | 8043 | |
| 6874 | 8044 | const elem_type = try sema.resolveType(block, .unneeded, extra.data.elem_type); |
| 6875 | 8045 | |
| 6876 | const ty = try Module.ptrType( | |
| 6877 | sema.arena, | |
| 6878 | elem_type, | |
| 6879 | sentinel, | |
| 6880 | abi_align, | |
| 6881 | bit_start, | |
| 6882 | bit_end, | |
| 6883 | inst_data.flags.is_mutable, | |
| 6884 | inst_data.flags.is_allowzero, | |
| 6885 | inst_data.flags.is_volatile, | |
| 6886 | inst_data.size, | |
| 6887 | ); | |
| 8046 | const ty = try Type.ptr(sema.arena, .{ | |
| 8047 | .pointee_type = elem_type, | |
| 8048 | .sentinel = sentinel, | |
| 8049 | .@"align" = abi_align, | |
| 8050 | .@"addrspace" = address_space, | |
| 8051 | .bit_offset = bit_start, | |
| 8052 | .host_size = bit_end, | |
| 8053 | .mutable = inst_data.flags.is_mutable, | |
| 8054 | .@"allowzero" = inst_data.flags.is_allowzero, | |
| 8055 | .@"volatile" = inst_data.flags.is_volatile, | |
| 8056 | .size = inst_data.size, | |
| 8057 | }); | |
| 6888 | 8058 | return sema.addType(ty); |
| 6889 | 8059 | } |
| 6890 | 8060 | |
| ... | ... | @@ -7059,8 +8229,49 @@ fn zirArrayInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: |
| 7059 | 8229 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 7060 | 8230 | const src = inst_data.src(); |
| 7061 | 8231 | |
| 7062 | _ = is_ref; | |
| 7063 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInit", .{}); | |
| 8232 | const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); | |
| 8233 | const args = sema.code.refSlice(extra.end, extra.data.operands_len); | |
| 8234 | ||
| 8235 | var resolved_args = try sema.mod.gpa.alloc(Air.Inst.Ref, args.len); | |
| 8236 | for (args) |arg, i| resolved_args[i] = sema.resolveInst(arg); | |
| 8237 | ||
| 8238 | var all_args_comptime = for (resolved_args) |arg| { | |
| 8239 | if ((try sema.resolveMaybeUndefVal(block, src, arg)) == null) break false; | |
| 8240 | } else true; | |
| 8241 | ||
| 8242 | if (all_args_comptime) { | |
| 8243 | var anon_decl = try block.startAnonDecl(); | |
| 8244 | defer anon_decl.deinit(); | |
| 8245 | assert(!(resolved_args.len == 0)); | |
| 8246 | const final_ty = try Type.Tag.array.create(anon_decl.arena(), .{ .len = resolved_args.len, .elem_type = sema.typeOf(resolved_args[0]) }); | |
| 8247 | const buf = try anon_decl.arena().alloc(Value, resolved_args.len); | |
| 8248 | for (resolved_args) |arg, i| { | |
| 8249 | buf[i] = (try sema.resolveMaybeUndefVal(block, src, arg)).?; | |
| 8250 | } | |
| 8251 | ||
| 8252 | const val = try Value.Tag.array.create(anon_decl.arena(), buf); | |
| 8253 | if (is_ref) | |
| 8254 | return sema.analyzeDeclRef(try anon_decl.finish(final_ty, val)) | |
| 8255 | else | |
| 8256 | return sema.analyzeDeclVal(block, .unneeded, try anon_decl.finish(final_ty, val)); | |
| 8257 | } | |
| 8258 | ||
| 8259 | assert(!(resolved_args.len == 0)); | |
| 8260 | const array_ty = try Type.Tag.array.create(sema.arena, .{ .len = resolved_args.len, .elem_type = sema.typeOf(resolved_args[0]) }); | |
| 8261 | const final_ty = try Type.ptr(sema.arena, .{ | |
| 8262 | .pointee_type = array_ty, | |
| 8263 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 8264 | }); | |
| 8265 | const alloc = try block.addTy(.alloc, final_ty); | |
| 8266 | ||
| 8267 | for (resolved_args) |arg, i| { | |
| 8268 | const pointer_to_array_at_index = try block.addBinOp(.ptr_elem_ptr, alloc, try sema.addIntUnsigned(Type.initTag(.u64), i)); | |
| 8269 | _ = try block.addBinOp(.store, pointer_to_array_at_index, arg); | |
| 8270 | } | |
| 8271 | return if (is_ref) | |
| 8272 | alloc | |
| 8273 | else | |
| 8274 | try sema.analyzeLoad(block, .unneeded, alloc, .unneeded); | |
| 7064 | 8275 | } |
| 7065 | 8276 | |
| 7066 | 8277 | fn zirArrayInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -7132,8 +8343,11 @@ fn zirFrameAddress( |
| 7132 | 8343 | |
| 7133 | 8344 | fn zirAlignOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7134 | 8345 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 7135 | const src = inst_data.src(); | |
| 7136 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirAlignOf", .{}); | |
| 8346 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 8347 | const ty = try sema.resolveType(block, operand_src, inst_data.operand); | |
| 8348 | const target = sema.mod.getTarget(); | |
| 8349 | const abi_align = ty.abiAlignment(target); | |
| 8350 | return sema.addIntUnsigned(Type.comptime_int, abi_align); | |
| 7137 | 8351 | } |
| 7138 | 8352 | |
| 7139 | 8353 | fn zirBoolToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -7246,13 +8460,30 @@ fn zirFrameSize(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE |
| 7246 | 8460 | fn zirFloatToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7247 | 8461 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 7248 | 8462 | const src = inst_data.src(); |
| 8463 | // TODO don't forget the safety check! | |
| 7249 | 8464 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{}); |
| 7250 | 8465 | } |
| 7251 | 8466 | |
| 7252 | 8467 | fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7253 | 8468 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 7254 | const src = inst_data.src(); | |
| 7255 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToFloat", .{}); | |
| 8469 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | |
| 8470 | const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 8471 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 8472 | const dest_ty = try sema.resolveType(block, ty_src, extra.lhs); | |
| 8473 | const operand = sema.resolveInst(extra.rhs); | |
| 8474 | const operand_ty = sema.typeOf(operand); | |
| 8475 | ||
| 8476 | _ = try sema.checkIntType(block, ty_src, dest_ty); | |
| 8477 | try sema.checkFloatType(block, operand_src, operand_ty); | |
| 8478 | ||
| 8479 | if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | |
| 8480 | const target = sema.mod.getTarget(); | |
| 8481 | const result_val = try val.intToFloat(sema.arena, dest_ty, target); | |
| 8482 | return sema.addConstant(dest_ty, result_val); | |
| 8483 | } | |
| 8484 | ||
| 8485 | try sema.requireRuntimeBlock(block, operand_src); | |
| 8486 | return block.addTyOp(.int_to_float, dest_ty, operand); | |
| 7256 | 8487 | } |
| 7257 | 8488 | |
| 7258 | 8489 | fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -7351,8 +8582,8 @@ fn zirTruncate(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 7351 | 8582 | const operand = sema.resolveInst(extra.rhs); |
| 7352 | 8583 | const operand_ty = sema.typeOf(operand); |
| 7353 | 8584 | const mod = sema.mod; |
| 7354 | const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_ty); | |
| 7355 | const src_is_comptime_int = try sema.requireIntegerType(block, operand_src, operand_ty); | |
| 8585 | const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty); | |
| 8586 | const src_is_comptime_int = try sema.checkIntType(block, operand_src, operand_ty); | |
| 7356 | 8587 | |
| 7357 | 8588 | if (dest_is_comptime_int) { |
| 7358 | 8589 | return sema.coerce(block, dest_ty, operand, operand_src); |
| ... | ... | @@ -7410,14 +8641,56 @@ fn zirAlignCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE |
| 7410 | 8641 | |
| 7411 | 8642 | fn zirClz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7412 | 8643 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 7413 | const src = inst_data.src(); | |
| 7414 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirClz", .{}); | |
| 8644 | const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 8645 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 8646 | const operand = sema.resolveInst(inst_data.operand); | |
| 8647 | const operand_ty = sema.typeOf(operand); | |
| 8648 | // TODO implement support for vectors | |
| 8649 | if (operand_ty.zigTypeTag() != .Int) { | |
| 8650 | return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{ | |
| 8651 | operand_ty, | |
| 8652 | }); | |
| 8653 | } | |
| 8654 | const target = sema.mod.getTarget(); | |
| 8655 | const bits = operand_ty.intInfo(target).bits; | |
| 8656 | if (bits == 0) return Air.Inst.Ref.zero; | |
| 8657 | ||
| 8658 | const result_ty = try Type.smallestUnsignedInt(sema.arena, bits); | |
| 8659 | ||
| 8660 | const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | |
| 8661 | if (val.isUndef()) return sema.addConstUndef(result_ty); | |
| 8662 | return sema.addIntUnsigned(result_ty, val.clz(operand_ty, target)); | |
| 8663 | } else operand_src; | |
| 8664 | ||
| 8665 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 8666 | return block.addTyOp(.clz, result_ty, operand); | |
| 7415 | 8667 | } |
| 7416 | 8668 | |
| 7417 | 8669 | fn zirCtz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7418 | 8670 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 7419 | const src = inst_data.src(); | |
| 7420 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirCtz", .{}); | |
| 8671 | const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 8672 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 8673 | const operand = sema.resolveInst(inst_data.operand); | |
| 8674 | const operand_ty = sema.typeOf(operand); | |
| 8675 | // TODO implement support for vectors | |
| 8676 | if (operand_ty.zigTypeTag() != .Int) { | |
| 8677 | return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{ | |
| 8678 | operand_ty, | |
| 8679 | }); | |
| 8680 | } | |
| 8681 | const target = sema.mod.getTarget(); | |
| 8682 | const bits = operand_ty.intInfo(target).bits; | |
| 8683 | if (bits == 0) return Air.Inst.Ref.zero; | |
| 8684 | ||
| 8685 | const result_ty = try Type.smallestUnsignedInt(sema.arena, bits); | |
| 8686 | ||
| 8687 | const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | |
| 8688 | if (val.isUndef()) return sema.addConstUndef(result_ty); | |
| 8689 | return sema.mod.fail(&block.base, operand_src, "TODO: implement comptime @ctz", .{}); | |
| 8690 | } else operand_src; | |
| 8691 | ||
| 8692 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 8693 | return block.addTyOp(.ctz, result_ty, operand); | |
| 7421 | 8694 | } |
| 7422 | 8695 | |
| 7423 | 8696 | fn zirPopCount(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -7450,26 +8723,10 @@ fn zirDivFloor(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 7450 | 8723 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivFloor", .{}); |
| 7451 | 8724 | } |
| 7452 | 8725 | |
| 7453 | fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 7454 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 7455 | const src = inst_data.src(); | |
| 7456 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{}); | |
| 7457 | } | |
| 7458 | ||
| 7459 | fn zirMod(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 7460 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 7461 | const src = inst_data.src(); | |
| 7462 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMod", .{}); | |
| 7463 | } | |
| 7464 | ||
| 7465 | fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 7466 | return sema.zirArithmetic(block, inst); | |
| 7467 | } | |
| 7468 | ||
| 7469 | fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 8726 | fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 7470 | 8727 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 7471 | 8728 | const src = inst_data.src(); |
| 7472 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirShlExact", .{}); | |
| 8729 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{}); | |
| 7473 | 8730 | } |
| 7474 | 8731 | |
| 7475 | 8732 | fn zirShrExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -7490,6 +8747,29 @@ fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 7490 | 8747 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{}); |
| 7491 | 8748 | } |
| 7492 | 8749 | |
| 8750 | /// Returns `true` if the type was a comptime_int. | |
| 8751 | fn checkIntType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!bool { | |
| 8752 | switch (ty.zigTypeTag()) { | |
| 8753 | .ComptimeInt => return true, | |
| 8754 | .Int => return false, | |
| 8755 | else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}), | |
| 8756 | } | |
| 8757 | } | |
| 8758 | ||
| 8759 | fn checkFloatType( | |
| 8760 | sema: *Sema, | |
| 8761 | block: *Scope.Block, | |
| 8762 | ty_src: LazySrcLoc, | |
| 8763 | ty: Type, | |
| 8764 | ) CompileError!void { | |
| 8765 | switch (ty.zigTypeTag()) { | |
| 8766 | .ComptimeFloat, .Float => {}, | |
| 8767 | else => return sema.mod.fail(&block.base, ty_src, "expected float type, found '{}'", .{ | |
| 8768 | ty, | |
| 8769 | }), | |
| 8770 | } | |
| 8771 | } | |
| 8772 | ||
| 7493 | 8773 | fn checkAtomicOperandType( |
| 7494 | 8774 | sema: *Sema, |
| 7495 | 8775 | block: *Scope.Block, |
| ... | ... | @@ -7501,7 +8781,7 @@ fn checkAtomicOperandType( |
| 7501 | 8781 | const max_atomic_bits = target_util.largestAtomicBits(target); |
| 7502 | 8782 | const int_ty = switch (ty.zigTypeTag()) { |
| 7503 | 8783 | .Int => ty, |
| 7504 | .Enum => ty.enumTagType(&buffer), | |
| 8784 | .Enum => ty.intTagType(&buffer), | |
| 7505 | 8785 | .Float => { |
| 7506 | 8786 | const bit_count = ty.floatBits(target); |
| 7507 | 8787 | if (bit_count > max_atomic_bits) { |
| ... | ... | @@ -7515,12 +8795,16 @@ fn checkAtomicOperandType( |
| 7515 | 8795 | return; |
| 7516 | 8796 | }, |
| 7517 | 8797 | .Bool => return, // Will be treated as `u8`. |
| 7518 | else => return sema.mod.fail( | |
| 7519 | &block.base, | |
| 7520 | ty_src, | |
| 7521 | "expected bool, integer, float, enum, or pointer type; found {}", | |
| 7522 | .{ty}, | |
| 7523 | ), | |
| 8798 | else => { | |
| 8799 | if (ty.isPtrAtRuntime()) return; | |
| 8800 | ||
| 8801 | return sema.mod.fail( | |
| 8802 | &block.base, | |
| 8803 | ty_src, | |
| 8804 | "expected bool, integer, float, enum, or pointer type; found {}", | |
| 8805 | .{ty}, | |
| 8806 | ); | |
| 8807 | }, | |
| 7524 | 8808 | }; |
| 7525 | 8809 | const bit_count = int_ty.intInfo(target).bits; |
| 7526 | 8810 | if (bit_count > max_atomic_bits) { |
| ... | ... | @@ -7533,6 +8817,31 @@ fn checkAtomicOperandType( |
| 7533 | 8817 | } |
| 7534 | 8818 | } |
| 7535 | 8819 | |
| 8820 | fn resolveExportOptions( | |
| 8821 | sema: *Sema, | |
| 8822 | block: *Scope.Block, | |
| 8823 | src: LazySrcLoc, | |
| 8824 | zir_ref: Zir.Inst.Ref, | |
| 8825 | ) CompileError!std.builtin.ExportOptions { | |
| 8826 | const export_options_ty = try sema.getBuiltinType(block, src, "ExportOptions"); | |
| 8827 | const air_ref = sema.resolveInst(zir_ref); | |
| 8828 | const coerced = try sema.coerce(block, export_options_ty, air_ref, src); | |
| 8829 | const val = try sema.resolveConstValue(block, src, coerced); | |
| 8830 | const fields = val.castTag(.@"struct").?.data; | |
| 8831 | const struct_obj = export_options_ty.castTag(.@"struct").?.data; | |
| 8832 | const name_index = struct_obj.fields.getIndex("name").?; | |
| 8833 | const linkage_index = struct_obj.fields.getIndex("linkage").?; | |
| 8834 | const section_index = struct_obj.fields.getIndex("section").?; | |
| 8835 | if (!fields[section_index].isNull()) { | |
| 8836 | return sema.mod.fail(&block.base, src, "TODO: implement exporting with linksection", .{}); | |
| 8837 | } | |
| 8838 | return std.builtin.ExportOptions{ | |
| 8839 | .name = try fields[name_index].toAllocatedBytes(sema.arena), | |
| 8840 | .linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage), | |
| 8841 | .section = null, // TODO | |
| 8842 | }; | |
| 8843 | } | |
| 8844 | ||
| 7536 | 8845 | fn resolveAtomicOrder( |
| 7537 | 8846 | sema: *Sema, |
| 7538 | 8847 | block: *Scope.Block, |
| ... | ... | @@ -7546,6 +8855,19 @@ fn resolveAtomicOrder( |
| 7546 | 8855 | return val.toEnum(std.builtin.AtomicOrder); |
| 7547 | 8856 | } |
| 7548 | 8857 | |
| 8858 | fn resolveAtomicRmwOp( | |
| 8859 | sema: *Sema, | |
| 8860 | block: *Scope.Block, | |
| 8861 | src: LazySrcLoc, | |
| 8862 | zir_ref: Zir.Inst.Ref, | |
| 8863 | ) CompileError!std.builtin.AtomicRmwOp { | |
| 8864 | const atomic_rmw_op_ty = try sema.getBuiltinType(block, src, "AtomicRmwOp"); | |
| 8865 | const air_ref = sema.resolveInst(zir_ref); | |
| 8866 | const coerced = try sema.coerce(block, atomic_rmw_op_ty, air_ref, src); | |
| 8867 | const val = try sema.resolveConstValue(block, src, coerced); | |
| 8868 | return val.toEnum(std.builtin.AtomicRmwOp); | |
| 8869 | } | |
| 8870 | ||
| 7549 | 8871 | fn zirCmpxchg( |
| 7550 | 8872 | sema: *Sema, |
| 7551 | 8873 | block: *Scope.Block, |
| ... | ... | @@ -7604,6 +8926,8 @@ fn zirCmpxchg( |
| 7604 | 8926 | if (try sema.resolveMaybeUndefVal(block, expected_src, expected_value)) |expected_val| { |
| 7605 | 8927 | if (try sema.resolveMaybeUndefVal(block, new_value_src, new_value)) |new_val| { |
| 7606 | 8928 | if (expected_val.isUndef() or new_val.isUndef()) { |
| 8929 | // TODO: this should probably cause the memory stored at the pointer | |
| 8930 | // to become undef as well | |
| 7607 | 8931 | return sema.addConstUndef(result_ty); |
| 7608 | 8932 | } |
| 7609 | 8933 | const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src; |
| ... | ... | @@ -7661,20 +8985,160 @@ fn zirSelect(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro |
| 7661 | 8985 | |
| 7662 | 8986 | fn zirAtomicLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7663 | 8987 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 7664 | const src = inst_data.src(); | |
| 7665 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicLoad", .{}); | |
| 8988 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | |
| 8989 | // zig fmt: off | |
| 8990 | const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 8991 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 8992 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | |
| 8993 | // zig fmt: on | |
| 8994 | const ptr = sema.resolveInst(extra.lhs); | |
| 8995 | const elem_ty = sema.typeOf(ptr).elemType(); | |
| 8996 | try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); | |
| 8997 | const order = try sema.resolveAtomicOrder(block, order_src, extra.rhs); | |
| 8998 | ||
| 8999 | switch (order) { | |
| 9000 | .Release, .AcqRel => { | |
| 9001 | return sema.mod.fail( | |
| 9002 | &block.base, | |
| 9003 | order_src, | |
| 9004 | "@atomicLoad atomic ordering must not be Release or AcqRel", | |
| 9005 | .{}, | |
| 9006 | ); | |
| 9007 | }, | |
| 9008 | else => {}, | |
| 9009 | } | |
| 9010 | ||
| 9011 | if (try sema.typeHasOnePossibleValue(block, elem_ty_src, elem_ty)) |val| { | |
| 9012 | return sema.addConstant(elem_ty, val); | |
| 9013 | } | |
| 9014 | ||
| 9015 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { | |
| 9016 | if (try ptr_val.pointerDeref(sema.arena)) |elem_val| { | |
| 9017 | return sema.addConstant(elem_ty, elem_val); | |
| 9018 | } | |
| 9019 | } | |
| 9020 | ||
| 9021 | try sema.requireRuntimeBlock(block, ptr_src); | |
| 9022 | return block.addInst(.{ | |
| 9023 | .tag = .atomic_load, | |
| 9024 | .data = .{ .atomic_load = .{ | |
| 9025 | .ptr = ptr, | |
| 9026 | .order = order, | |
| 9027 | } }, | |
| 9028 | }); | |
| 7666 | 9029 | } |
| 7667 | 9030 | |
| 7668 | 9031 | fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9032 | const mod = sema.mod; | |
| 7669 | 9033 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 9034 | const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; | |
| 7670 | 9035 | const src = inst_data.src(); |
| 7671 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicRmw", .{}); | |
| 9036 | // zig fmt: off | |
| 9037 | const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 9038 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 9039 | const op_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | |
| 9040 | const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; | |
| 9041 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; | |
| 9042 | // zig fmt: on | |
| 9043 | const ptr = sema.resolveInst(extra.ptr); | |
| 9044 | const operand_ty = sema.typeOf(ptr).elemType(); | |
| 9045 | try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty); | |
| 9046 | const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); | |
| 9047 | ||
| 9048 | switch (operand_ty.zigTypeTag()) { | |
| 9049 | .Enum => if (op != .Xchg) { | |
| 9050 | return mod.fail(&block.base, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); | |
| 9051 | }, | |
| 9052 | .Bool => if (op != .Xchg) { | |
| 9053 | return mod.fail(&block.base, op_src, "@atomicRmw with bool only allowed with .Xchg", .{}); | |
| 9054 | }, | |
| 9055 | .Float => switch (op) { | |
| 9056 | .Xchg, .Add, .Sub => {}, | |
| 9057 | else => return mod.fail(&block.base, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, and .Sub", .{}), | |
| 9058 | }, | |
| 9059 | else => {}, | |
| 9060 | } | |
| 9061 | const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src); | |
| 9062 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); | |
| 9063 | ||
| 9064 | if (order == .Unordered) { | |
| 9065 | return mod.fail(&block.base, order_src, "@atomicRmw atomic ordering must not be Unordered", .{}); | |
| 9066 | } | |
| 9067 | ||
| 9068 | // special case zero bit types | |
| 9069 | if (try sema.typeHasOnePossibleValue(block, operand_ty_src, operand_ty)) |val| { | |
| 9070 | return sema.addConstant(operand_ty, val); | |
| 9071 | } | |
| 9072 | ||
| 9073 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { | |
| 9074 | if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { | |
| 9075 | const target = sema.mod.getTarget(); | |
| 9076 | const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src; | |
| 9077 | const new_val = switch (op) { | |
| 9078 | // zig fmt: off | |
| 9079 | .Xchg => operand_val, | |
| 9080 | .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target), | |
| 9081 | .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target), | |
| 9082 | .And => try stored_val.bitwiseAnd (operand_val, sema.arena), | |
| 9083 | .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target), | |
| 9084 | .Or => try stored_val.bitwiseOr (operand_val, sema.arena), | |
| 9085 | .Xor => try stored_val.bitwiseXor (operand_val, sema.arena), | |
| 9086 | .Max => try stored_val.numberMax (operand_val, sema.arena), | |
| 9087 | .Min => try stored_val.numberMin (operand_val, sema.arena), | |
| 9088 | // zig fmt: on | |
| 9089 | }; | |
| 9090 | try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); | |
| 9091 | return sema.addConstant(operand_ty, stored_val); | |
| 9092 | } else break :rs operand_src; | |
| 9093 | } else ptr_src; | |
| 9094 | ||
| 9095 | const flags: u32 = @as(u32, @enumToInt(order)) | (@as(u32, @enumToInt(op)) << 3); | |
| 9096 | ||
| 9097 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 9098 | return block.addInst(.{ | |
| 9099 | .tag = .atomic_rmw, | |
| 9100 | .data = .{ .pl_op = .{ | |
| 9101 | .operand = ptr, | |
| 9102 | .payload = try sema.addExtra(Air.AtomicRmw{ | |
| 9103 | .operand = operand, | |
| 9104 | .flags = flags, | |
| 9105 | }), | |
| 9106 | } }, | |
| 9107 | }); | |
| 7672 | 9108 | } |
| 7673 | 9109 | |
| 7674 | fn zirAtomicStore(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 9110 | fn zirAtomicStore(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | |
| 7675 | 9111 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 9112 | const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; | |
| 7676 | 9113 | const src = inst_data.src(); |
| 7677 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicStore", .{}); | |
| 9114 | // zig fmt: off | |
| 9115 | const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 9116 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 9117 | const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | |
| 9118 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; | |
| 9119 | // zig fmt: on | |
| 9120 | const ptr = sema.resolveInst(extra.ptr); | |
| 9121 | const operand_ty = sema.typeOf(ptr).elemType(); | |
| 9122 | try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty); | |
| 9123 | const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src); | |
| 9124 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); | |
| 9125 | ||
| 9126 | const air_tag: Air.Inst.Tag = switch (order) { | |
| 9127 | .Acquire, .AcqRel => { | |
| 9128 | return sema.mod.fail( | |
| 9129 | &block.base, | |
| 9130 | order_src, | |
| 9131 | "@atomicStore atomic ordering must not be Acquire or AcqRel", | |
| 9132 | .{}, | |
| 9133 | ); | |
| 9134 | }, | |
| 9135 | .Unordered => .atomic_store_unordered, | |
| 9136 | .Monotonic => .atomic_store_monotonic, | |
| 9137 | .Release => .atomic_store_release, | |
| 9138 | .SeqCst => .atomic_store_seq_cst, | |
| 9139 | }; | |
| 9140 | ||
| 9141 | return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); | |
| 7678 | 9142 | } |
| 7679 | 9143 | |
| 7680 | 9144 | fn zirMulAdd(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -7707,16 +9171,115 @@ fn zirMaximum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 7707 | 9171 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{}); |
| 7708 | 9172 | } |
| 7709 | 9173 | |
| 7710 | fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 9174 | fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | |
| 7711 | 9175 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 9176 | const extra = sema.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data; | |
| 7712 | 9177 | const src = inst_data.src(); |
| 7713 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy", .{}); | |
| 9178 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 9179 | const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 9180 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | |
| 9181 | const dest_ptr = sema.resolveInst(extra.dest); | |
| 9182 | const dest_ptr_ty = sema.typeOf(dest_ptr); | |
| 9183 | ||
| 9184 | if (dest_ptr_ty.zigTypeTag() != .Pointer) { | |
| 9185 | return sema.mod.fail(&block.base, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty}); | |
| 9186 | } | |
| 9187 | if (dest_ptr_ty.isConstPtr()) { | |
| 9188 | return sema.mod.fail(&block.base, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty}); | |
| 9189 | } | |
| 9190 | ||
| 9191 | const uncasted_src_ptr = sema.resolveInst(extra.source); | |
| 9192 | const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); | |
| 9193 | if (uncasted_src_ptr_ty.zigTypeTag() != .Pointer) { | |
| 9194 | return sema.mod.fail(&block.base, src_src, "expected pointer, found '{}'", .{ | |
| 9195 | uncasted_src_ptr_ty, | |
| 9196 | }); | |
| 9197 | } | |
| 9198 | const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; | |
| 9199 | const wanted_src_ptr_ty = try Type.ptr(sema.arena, .{ | |
| 9200 | .pointee_type = dest_ptr_ty.elemType2(), | |
| 9201 | .@"align" = src_ptr_info.@"align", | |
| 9202 | .@"addrspace" = src_ptr_info.@"addrspace", | |
| 9203 | .mutable = false, | |
| 9204 | .@"allowzero" = src_ptr_info.@"allowzero", | |
| 9205 | .@"volatile" = src_ptr_info.@"volatile", | |
| 9206 | .size = .Many, | |
| 9207 | }); | |
| 9208 | const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src); | |
| 9209 | const len = try sema.coerce(block, Type.initTag(.usize), sema.resolveInst(extra.byte_count), len_src); | |
| 9210 | ||
| 9211 | const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr); | |
| 9212 | const maybe_src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr); | |
| 9213 | const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len); | |
| 9214 | ||
| 9215 | const runtime_src = if (maybe_dest_ptr_val) |dest_ptr_val| rs: { | |
| 9216 | if (maybe_src_ptr_val) |src_ptr_val| { | |
| 9217 | if (maybe_len_val) |len_val| { | |
| 9218 | _ = dest_ptr_val; | |
| 9219 | _ = src_ptr_val; | |
| 9220 | _ = len_val; | |
| 9221 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy at comptime", .{}); | |
| 9222 | } else break :rs len_src; | |
| 9223 | } else break :rs src_src; | |
| 9224 | } else dest_src; | |
| 9225 | ||
| 9226 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 9227 | _ = try block.addInst(.{ | |
| 9228 | .tag = .memcpy, | |
| 9229 | .data = .{ .pl_op = .{ | |
| 9230 | .operand = dest_ptr, | |
| 9231 | .payload = try sema.addExtra(Air.Bin{ | |
| 9232 | .lhs = src_ptr, | |
| 9233 | .rhs = len, | |
| 9234 | }), | |
| 9235 | } }, | |
| 9236 | }); | |
| 7714 | 9237 | } |
| 7715 | 9238 | |
| 7716 | fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 9239 | fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { | |
| 7717 | 9240 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 9241 | const extra = sema.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data; | |
| 7718 | 9242 | const src = inst_data.src(); |
| 7719 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{}); | |
| 9243 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 9244 | const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 9245 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | |
| 9246 | const dest_ptr = sema.resolveInst(extra.dest); | |
| 9247 | const dest_ptr_ty = sema.typeOf(dest_ptr); | |
| 9248 | if (dest_ptr_ty.zigTypeTag() != .Pointer) { | |
| 9249 | return sema.mod.fail(&block.base, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty}); | |
| 9250 | } | |
| 9251 | if (dest_ptr_ty.isConstPtr()) { | |
| 9252 | return sema.mod.fail(&block.base, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty}); | |
| 9253 | } | |
| 9254 | const elem_ty = dest_ptr_ty.elemType2(); | |
| 9255 | const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src); | |
| 9256 | const len = try sema.coerce(block, Type.initTag(.usize), sema.resolveInst(extra.byte_count), len_src); | |
| 9257 | ||
| 9258 | const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr); | |
| 9259 | const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len); | |
| 9260 | ||
| 9261 | const runtime_src = if (maybe_dest_ptr_val) |ptr_val| rs: { | |
| 9262 | if (maybe_len_val) |len_val| { | |
| 9263 | if (try sema.resolveMaybeUndefVal(block, value_src, value)) |val| { | |
| 9264 | _ = ptr_val; | |
| 9265 | _ = len_val; | |
| 9266 | _ = val; | |
| 9267 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset at comptime", .{}); | |
| 9268 | } else break :rs value_src; | |
| 9269 | } else break :rs len_src; | |
| 9270 | } else dest_src; | |
| 9271 | ||
| 9272 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 9273 | _ = try block.addInst(.{ | |
| 9274 | .tag = .memset, | |
| 9275 | .data = .{ .pl_op = .{ | |
| 9276 | .operand = dest_ptr, | |
| 9277 | .payload = try sema.addExtra(Air.Bin{ | |
| 9278 | .lhs = value, | |
| 9279 | .rhs = len, | |
| 9280 | }), | |
| 9281 | } }, | |
| 9282 | }); | |
| 7720 | 9283 | } |
| 7721 | 9284 | |
| 7722 | 9285 | fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -7761,7 +9324,6 @@ fn zirVarExtended( |
| 7761 | 9324 | const mut_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at mut token |
| 7762 | 9325 | const init_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at init expr |
| 7763 | 9326 | const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small); |
| 7764 | const var_ty = try sema.resolveType(block, ty_src, extra.data.var_type); | |
| 7765 | 9327 | |
| 7766 | 9328 | var extra_index: usize = extra.end; |
| 7767 | 9329 | |
| ... | ... | @@ -7781,19 +9343,29 @@ fn zirVarExtended( |
| 7781 | 9343 | // break :blk align_tv.val; |
| 7782 | 9344 | //} else Value.initTag(.null_value); |
| 7783 | 9345 | |
| 7784 | const init_val: Value = if (small.has_init) blk: { | |
| 9346 | const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: { | |
| 7785 | 9347 | const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); |
| 7786 | 9348 | extra_index += 1; |
| 7787 | const init_air_inst = sema.resolveInst(init_ref); | |
| 7788 | break :blk (try sema.resolveMaybeUndefVal(block, init_src, init_air_inst)) orelse | |
| 9349 | break :blk sema.resolveInst(init_ref); | |
| 9350 | } else .none; | |
| 9351 | ||
| 9352 | const have_ty = extra.data.var_type != .none; | |
| 9353 | const var_ty = if (have_ty) | |
| 9354 | try sema.resolveType(block, ty_src, extra.data.var_type) | |
| 9355 | else | |
| 9356 | sema.typeOf(uncasted_init); | |
| 9357 | ||
| 9358 | const init_val = if (uncasted_init != .none) blk: { | |
| 9359 | const init = if (have_ty) | |
| 9360 | try sema.coerce(block, var_ty, uncasted_init, init_src) | |
| 9361 | else | |
| 9362 | uncasted_init; | |
| 9363 | ||
| 9364 | break :blk (try sema.resolveMaybeUndefVal(block, init_src, init)) orelse | |
| 7789 | 9365 | return sema.failWithNeededComptime(block, init_src); |
| 7790 | 9366 | } else Value.initTag(.unreachable_value); |
| 7791 | 9367 | |
| 7792 | if (!var_ty.isValidVarType(small.is_extern)) { | |
| 7793 | return sema.mod.fail(&block.base, mut_src, "variable of type '{}' must be const", .{ | |
| 7794 | var_ty, | |
| 7795 | }); | |
| 7796 | } | |
| 9368 | try sema.validateVarType(block, mut_src, var_ty, small.is_extern); | |
| 7797 | 9369 | |
| 7798 | 9370 | if (lib_name != null) { |
| 7799 | 9371 | // Look at the sema code for functions which has this logic, it just needs to |
| ... | ... | @@ -7890,7 +9462,10 @@ fn zirCUndef( |
| 7890 | 9462 | ) CompileError!Air.Inst.Ref { |
| 7891 | 9463 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 7892 | 9464 | const src: LazySrcLoc = .{ .node_offset = extra.node }; |
| 7893 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCUndef", .{}); | |
| 9465 | ||
| 9466 | const name = try sema.resolveConstString(block, src, extra.operand); | |
| 9467 | try block.c_import_buf.?.writer().print("#undefine {s}\n", .{name}); | |
| 9468 | return Air.Inst.Ref.void_value; | |
| 7894 | 9469 | } |
| 7895 | 9470 | |
| 7896 | 9471 | fn zirCInclude( |
| ... | ... | @@ -7900,7 +9475,10 @@ fn zirCInclude( |
| 7900 | 9475 | ) CompileError!Air.Inst.Ref { |
| 7901 | 9476 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 7902 | 9477 | const src: LazySrcLoc = .{ .node_offset = extra.node }; |
| 7903 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCInclude", .{}); | |
| 9478 | ||
| 9479 | const name = try sema.resolveConstString(block, src, extra.operand); | |
| 9480 | try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name}); | |
| 9481 | return Air.Inst.Ref.void_value; | |
| 7904 | 9482 | } |
| 7905 | 9483 | |
| 7906 | 9484 | fn zirCDefine( |
| ... | ... | @@ -7910,7 +9488,15 @@ fn zirCDefine( |
| 7910 | 9488 | ) CompileError!Air.Inst.Ref { |
| 7911 | 9489 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 7912 | 9490 | const src: LazySrcLoc = .{ .node_offset = extra.node }; |
| 7913 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirCDefine", .{}); | |
| 9491 | ||
| 9492 | const name = try sema.resolveConstString(block, src, extra.lhs); | |
| 9493 | if (sema.typeOf(extra.rhs).zigTypeTag() != .Void) { | |
| 9494 | const value = try sema.resolveConstString(block, src, extra.rhs); | |
| 9495 | try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value }); | |
| 9496 | } else { | |
| 9497 | try block.c_import_buf.?.writer().print("#define {s}\n", .{name}); | |
| 9498 | } | |
| 9499 | return Air.Inst.Ref.void_value; | |
| 7914 | 9500 | } |
| 7915 | 9501 | |
| 7916 | 9502 | fn zirWasmMemorySize( |
| ... | ... | @@ -7956,17 +9542,54 @@ fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void |
| 7956 | 9542 | try sema.requireFunctionBlock(block, src); |
| 7957 | 9543 | } |
| 7958 | 9544 | |
| 7959 | fn requireIntegerType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !bool { | |
| 7960 | switch (ty.zigTypeTag()) { | |
| 7961 | .ComptimeInt => return true, | |
| 7962 | .Int => return false, | |
| 7963 | else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}), | |
| 7964 | } | |
| 7965 | } | |
| 9545 | /// Emit a compile error if type cannot be used for a runtime variable. | |
| 9546 | fn validateVarType( | |
| 9547 | sema: *Sema, | |
| 9548 | block: *Scope.Block, | |
| 9549 | src: LazySrcLoc, | |
| 9550 | var_ty: Type, | |
| 9551 | is_extern: bool, | |
| 9552 | ) CompileError!void { | |
| 9553 | var ty = var_ty; | |
| 9554 | const ok: bool = while (true) switch (ty.zigTypeTag()) { | |
| 9555 | .Bool, | |
| 9556 | .Int, | |
| 9557 | .Float, | |
| 9558 | .ErrorSet, | |
| 9559 | .Enum, | |
| 9560 | .Frame, | |
| 9561 | .AnyFrame, | |
| 9562 | => break true, | |
| 9563 | ||
| 9564 | .BoundFn, | |
| 9565 | .ComptimeFloat, | |
| 9566 | .ComptimeInt, | |
| 9567 | .EnumLiteral, | |
| 9568 | .NoReturn, | |
| 9569 | .Type, | |
| 9570 | .Void, | |
| 9571 | .Undefined, | |
| 9572 | .Null, | |
| 9573 | => break false, | |
| 9574 | ||
| 9575 | .Opaque => break is_extern, | |
| 9576 | ||
| 9577 | .Optional => { | |
| 9578 | var buf: Type.Payload.ElemType = undefined; | |
| 9579 | const child_ty = ty.optionalChild(&buf); | |
| 9580 | return validateVarType(sema, block, src, child_ty, is_extern); | |
| 9581 | }, | |
| 9582 | .Pointer, .Array, .Vector => ty = ty.elemType(), | |
| 9583 | .ErrorUnion => ty = ty.errorUnionPayload(), | |
| 7966 | 9584 | |
| 7967 | fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void { | |
| 7968 | if (!ty.isValidVarType(false)) { | |
| 7969 | return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty}); | |
| 9585 | .Fn => @panic("TODO fn validateVarType"), | |
| 9586 | .Struct, .Union => { | |
| 9587 | const resolved_ty = try sema.resolveTypeFields(block, src, ty); | |
| 9588 | break !resolved_ty.requiresComptime(); | |
| 9589 | }, | |
| 9590 | } else unreachable; // TODO should not need else unreachable | |
| 9591 | if (!ok) { | |
| 9592 | return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{var_ty}); | |
| 7970 | 9593 | } |
| 7971 | 9594 | } |
| 7972 | 9595 | |
| ... | ... | @@ -7990,6 +9613,7 @@ fn addSafetyCheck( |
| 7990 | 9613 | var fail_block: Scope.Block = .{ |
| 7991 | 9614 | .parent = parent_block, |
| 7992 | 9615 | .sema = sema, |
| 9616 | .wip_capture_scope = parent_block.wip_capture_scope, | |
| 7993 | 9617 | .src_decl = parent_block.src_decl, |
| 7994 | 9618 | .instructions = .{}, |
| 7995 | 9619 | .inlining = parent_block.inlining, |
| ... | ... | @@ -8067,7 +9691,10 @@ fn panicWithMsg( |
| 8067 | 9691 | const panic_fn = try sema.getBuiltin(block, src, "panic"); |
| 8068 | 9692 | const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); |
| 8069 | 9693 | const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); |
| 8070 | const ptr_stack_trace_ty = try Module.simplePtrType(arena, stack_trace_ty, true, .One); | |
| 9694 | const ptr_stack_trace_ty = try Type.ptr(arena, .{ | |
| 9695 | .pointee_type = stack_trace_ty, | |
| 9696 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant), // TODO might need a place that is more dynamic | |
| 9697 | }); | |
| 8071 | 9698 | const null_stack_trace = try sema.addConstant( |
| 8072 | 9699 | try Module.optionalType(arena, ptr_stack_trace_ty), |
| 8073 | 9700 | Value.initTag(.null_value), |
| ... | ... | @@ -8083,7 +9710,7 @@ fn safetyPanic( |
| 8083 | 9710 | block: *Scope.Block, |
| 8084 | 9711 | src: LazySrcLoc, |
| 8085 | 9712 | panic_id: PanicId, |
| 8086 | ) !Zir.Inst.Index { | |
| 9713 | ) CompileError!Zir.Inst.Index { | |
| 8087 | 9714 | const msg = switch (panic_id) { |
| 8088 | 9715 | .unreach => "reached unreachable code", |
| 8089 | 9716 | .unwrap_null => "attempt to use null value", |
| ... | ... | @@ -8151,7 +9778,7 @@ fn fieldVal( |
| 8151 | 9778 | .Pointer => switch (object_ty.ptrSize()) { |
| 8152 | 9779 | .Slice => { |
| 8153 | 9780 | if (mem.eql(u8, field_name, "ptr")) { |
| 8154 | const buf = try arena.create(Type.Payload.ElemType); | |
| 9781 | const buf = try arena.create(Type.SlicePtrFieldTypeBuffer); | |
| 8155 | 9782 | const result_ty = object_ty.slicePtrFieldType(buf); |
| 8156 | 9783 | if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| { |
| 8157 | 9784 | if (val.isUndef()) return sema.addConstUndef(result_ty); |
| ... | ... | @@ -8185,21 +9812,32 @@ fn fieldVal( |
| 8185 | 9812 | } |
| 8186 | 9813 | }, |
| 8187 | 9814 | .One => { |
| 8188 | const elem_ty = object_ty.elemType(); | |
| 8189 | if (elem_ty.zigTypeTag() == .Array) { | |
| 8190 | if (mem.eql(u8, field_name, "len")) { | |
| 8191 | return sema.addConstant( | |
| 8192 | Type.initTag(.comptime_int), | |
| 8193 | try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()), | |
| 8194 | ); | |
| 8195 | } else { | |
| 8196 | return mod.fail( | |
| 8197 | &block.base, | |
| 8198 | field_name_src, | |
| 8199 | "no member named '{s}' in '{}'", | |
| 8200 | .{ field_name, object_ty }, | |
| 8201 | ); | |
| 8202 | } | |
| 9815 | const ptr_child = object_ty.elemType(); | |
| 9816 | switch (ptr_child.zigTypeTag()) { | |
| 9817 | .Array => { | |
| 9818 | if (mem.eql(u8, field_name, "len")) { | |
| 9819 | return sema.addConstant( | |
| 9820 | Type.initTag(.comptime_int), | |
| 9821 | try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()), | |
| 9822 | ); | |
| 9823 | } else { | |
| 9824 | return mod.fail( | |
| 9825 | &block.base, | |
| 9826 | field_name_src, | |
| 9827 | "no member named '{s}' in '{}'", | |
| 9828 | .{ field_name, object_ty }, | |
| 9829 | ); | |
| 9830 | } | |
| 9831 | }, | |
| 9832 | .Struct => { | |
| 9833 | const struct_ptr_deref = try sema.analyzeLoad(block, src, object, object_src); | |
| 9834 | return sema.unionFieldVal(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child); | |
| 9835 | }, | |
| 9836 | .Union => { | |
| 9837 | const union_ptr_deref = try sema.analyzeLoad(block, src, object, object_src); | |
| 9838 | return sema.unionFieldVal(block, src, union_ptr_deref, field_name, field_name_src, ptr_child); | |
| 9839 | }, | |
| 9840 | else => {}, | |
| 8203 | 9841 | } |
| 8204 | 9842 | }, |
| 8205 | 9843 | .Many, .C => {}, |
| ... | ... | @@ -8323,9 +9961,8 @@ fn fieldPtr( |
| 8323 | 9961 | ); |
| 8324 | 9962 | } |
| 8325 | 9963 | }, |
| 8326 | .Pointer => { | |
| 8327 | const ptr_child = object_ty.elemType(); | |
| 8328 | if (ptr_child.isSlice()) { | |
| 9964 | .Pointer => switch (object_ty.ptrSize()) { | |
| 9965 | .Slice => { | |
| 8329 | 9966 | // Here for the ptr and len fields what we need to do is the situation |
| 8330 | 9967 | // when a temporary has its address taken, e.g. `&a[c..d].len`. |
| 8331 | 9968 | // This value may be known at compile-time or runtime. In the former |
| ... | ... | @@ -8355,26 +9992,39 @@ fn fieldPtr( |
| 8355 | 9992 | .{ field_name, object_ty }, |
| 8356 | 9993 | ); |
| 8357 | 9994 | } |
| 8358 | } else switch (ptr_child.zigTypeTag()) { | |
| 8359 | .Array => { | |
| 8360 | if (mem.eql(u8, field_name, "len")) { | |
| 8361 | var anon_decl = try block.startAnonDecl(); | |
| 8362 | defer anon_decl.deinit(); | |
| 8363 | return sema.analyzeDeclRef(try anon_decl.finish( | |
| 8364 | Type.initTag(.comptime_int), | |
| 8365 | try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()), | |
| 8366 | )); | |
| 8367 | } else { | |
| 8368 | return mod.fail( | |
| 8369 | &block.base, | |
| 8370 | field_name_src, | |
| 8371 | "no member named '{s}' in '{}'", | |
| 8372 | .{ field_name, object_ty }, | |
| 8373 | ); | |
| 8374 | } | |
| 8375 | }, | |
| 8376 | else => {}, | |
| 8377 | } | |
| 9995 | }, | |
| 9996 | .One => { | |
| 9997 | const ptr_child = object_ty.elemType(); | |
| 9998 | switch (ptr_child.zigTypeTag()) { | |
| 9999 | .Array => { | |
| 10000 | if (mem.eql(u8, field_name, "len")) { | |
| 10001 | var anon_decl = try block.startAnonDecl(); | |
| 10002 | defer anon_decl.deinit(); | |
| 10003 | return sema.analyzeDeclRef(try anon_decl.finish( | |
| 10004 | Type.initTag(.comptime_int), | |
| 10005 | try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()), | |
| 10006 | )); | |
| 10007 | } else { | |
| 10008 | return mod.fail( | |
| 10009 | &block.base, | |
| 10010 | field_name_src, | |
| 10011 | "no member named '{s}' in '{}'", | |
| 10012 | .{ field_name, object_ty }, | |
| 10013 | ); | |
| 10014 | } | |
| 10015 | }, | |
| 10016 | .Struct => { | |
| 10017 | const struct_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); | |
| 10018 | return sema.structFieldPtr(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child); | |
| 10019 | }, | |
| 10020 | .Union => { | |
| 10021 | const union_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); | |
| 10022 | return sema.unionFieldPtr(block, src, union_ptr_deref, field_name, field_name_src, ptr_child); | |
| 10023 | }, | |
| 10024 | else => {}, | |
| 10025 | } | |
| 10026 | }, | |
| 10027 | .Many, .C => {}, | |
| 8378 | 10028 | }, |
| 8379 | 10029 | .Type => { |
| 8380 | 10030 | _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr); |
| ... | ... | @@ -8460,11 +10110,153 @@ fn fieldPtr( |
| 8460 | 10110 | else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}), |
| 8461 | 10111 | } |
| 8462 | 10112 | }, |
| 8463 | .Struct => return sema.structFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty), | |
| 8464 | .Union => return sema.unionFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty), | |
| 10113 | .Struct => return sema.structFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty), | |
| 10114 | .Union => return sema.unionFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty), | |
| 10115 | else => {}, | |
| 10116 | } | |
| 10117 | return mod.fail(&block.base, src, "type '{}' does not support field access", .{object_ty}); | |
| 10118 | } | |
| 10119 | ||
| 10120 | fn fieldCallBind( | |
| 10121 | sema: *Sema, | |
| 10122 | block: *Scope.Block, | |
| 10123 | src: LazySrcLoc, | |
| 10124 | raw_ptr: Air.Inst.Ref, | |
| 10125 | field_name: []const u8, | |
| 10126 | field_name_src: LazySrcLoc, | |
| 10127 | ) CompileError!Air.Inst.Ref { | |
| 10128 | // When editing this function, note that there is corresponding logic to be edited | |
| 10129 | // in `fieldVal`. This function takes a pointer and returns a pointer. | |
| 10130 | ||
| 10131 | const mod = sema.mod; | |
| 10132 | const raw_ptr_src = src; // TODO better source location | |
| 10133 | const raw_ptr_ty = sema.typeOf(raw_ptr); | |
| 10134 | const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and raw_ptr_ty.ptrSize() == .One) | |
| 10135 | raw_ptr_ty.childType() | |
| 10136 | else | |
| 10137 | return mod.fail(&block.base, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty}); | |
| 10138 | ||
| 10139 | // Optionally dereference a second pointer to get the concrete type. | |
| 10140 | const is_double_ptr = inner_ty.zigTypeTag() == .Pointer and inner_ty.ptrSize() == .One; | |
| 10141 | const concrete_ty = if (is_double_ptr) inner_ty.childType() else inner_ty; | |
| 10142 | const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty; | |
| 10143 | const object_ptr = if (is_double_ptr) | |
| 10144 | try sema.analyzeLoad(block, src, raw_ptr, src) | |
| 10145 | else | |
| 10146 | raw_ptr; | |
| 10147 | ||
| 10148 | const arena = sema.arena; | |
| 10149 | find_field: { | |
| 10150 | switch (concrete_ty.zigTypeTag()) { | |
| 10151 | .Struct => { | |
| 10152 | const struct_ty = try sema.resolveTypeFields(block, src, concrete_ty); | |
| 10153 | const struct_obj = struct_ty.castTag(.@"struct").?.data; | |
| 10154 | ||
| 10155 | const field_index = struct_obj.fields.getIndex(field_name) orelse | |
| 10156 | break :find_field; | |
| 10157 | const field = struct_obj.fields.values()[field_index]; | |
| 10158 | ||
| 10159 | const ptr_field_ty = try Type.ptr(arena, .{ | |
| 10160 | .pointee_type = field.ty, | |
| 10161 | .mutable = ptr_ty.ptrIsMutable(), | |
| 10162 | .@"addrspace" = ptr_ty.ptrAddressSpace(), | |
| 10163 | }); | |
| 10164 | ||
| 10165 | if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| { | |
| 10166 | const pointer = try sema.addConstant( | |
| 10167 | ptr_field_ty, | |
| 10168 | try Value.Tag.field_ptr.create(arena, .{ | |
| 10169 | .container_ptr = struct_ptr_val, | |
| 10170 | .field_index = field_index, | |
| 10171 | }), | |
| 10172 | ); | |
| 10173 | return sema.analyzeLoad(block, src, pointer, src); | |
| 10174 | } | |
| 10175 | ||
| 10176 | try sema.requireRuntimeBlock(block, src); | |
| 10177 | const ptr_inst = ptr_inst: { | |
| 10178 | const tag: Air.Inst.Tag = switch (field_index) { | |
| 10179 | 0 => .struct_field_ptr_index_0, | |
| 10180 | 1 => .struct_field_ptr_index_1, | |
| 10181 | 2 => .struct_field_ptr_index_2, | |
| 10182 | 3 => .struct_field_ptr_index_3, | |
| 10183 | else => { | |
| 10184 | break :ptr_inst try block.addInst(.{ | |
| 10185 | .tag = .struct_field_ptr, | |
| 10186 | .data = .{ .ty_pl = .{ | |
| 10187 | .ty = try sema.addType(ptr_field_ty), | |
| 10188 | .payload = try sema.addExtra(Air.StructField{ | |
| 10189 | .struct_operand = object_ptr, | |
| 10190 | .field_index = @intCast(u32, field_index), | |
| 10191 | }), | |
| 10192 | } }, | |
| 10193 | }); | |
| 10194 | }, | |
| 10195 | }; | |
| 10196 | break :ptr_inst try block.addInst(.{ | |
| 10197 | .tag = tag, | |
| 10198 | .data = .{ .ty_op = .{ | |
| 10199 | .ty = try sema.addType(ptr_field_ty), | |
| 10200 | .operand = object_ptr, | |
| 10201 | } }, | |
| 10202 | }); | |
| 10203 | }; | |
| 10204 | return sema.analyzeLoad(block, src, ptr_inst, src); | |
| 10205 | }, | |
| 10206 | .Union => return sema.mod.fail(&block.base, src, "TODO implement field calls on unions", .{}), | |
| 10207 | .Type => { | |
| 10208 | const namespace = try sema.analyzeLoad(block, src, object_ptr, src); | |
| 10209 | return sema.fieldVal(block, src, namespace, field_name, field_name_src); | |
| 10210 | }, | |
| 10211 | else => {}, | |
| 10212 | } | |
| 10213 | } | |
| 10214 | ||
| 10215 | // If we get here, we need to look for a decl in the struct type instead. | |
| 10216 | switch (concrete_ty.zigTypeTag()) { | |
| 10217 | .Struct, .Opaque, .Union, .Enum => { | |
| 10218 | if (concrete_ty.getNamespace()) |namespace| { | |
| 10219 | if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| { | |
| 10220 | const decl_val = try sema.analyzeLoad(block, src, inst, src); | |
| 10221 | const decl_type = sema.typeOf(decl_val); | |
| 10222 | if (decl_type.zigTypeTag() == .Fn and | |
| 10223 | decl_type.fnParamLen() >= 1) | |
| 10224 | { | |
| 10225 | const first_param_type = decl_type.fnParamType(0); | |
| 10226 | const first_param_tag = first_param_type.tag(); | |
| 10227 | // zig fmt: off | |
| 10228 | if (first_param_tag == .var_args_param or | |
| 10229 | first_param_tag == .generic_poison or ( | |
| 10230 | first_param_type.zigTypeTag() == .Pointer and | |
| 10231 | first_param_type.ptrSize() == .One and | |
| 10232 | first_param_type.childType().eql(concrete_ty))) | |
| 10233 | { | |
| 10234 | // zig fmt: on | |
| 10235 | // TODO: bound fn calls on rvalues should probably | |
| 10236 | // generate a by-value argument somehow. | |
| 10237 | const ty = Type.Tag.bound_fn.init(); | |
| 10238 | const value = try Value.Tag.bound_fn.create(arena, .{ | |
| 10239 | .func_inst = decl_val, | |
| 10240 | .arg0_inst = object_ptr, | |
| 10241 | }); | |
| 10242 | return sema.addConstant(ty, value); | |
| 10243 | } else if (first_param_type.eql(concrete_ty)) { | |
| 10244 | var deref = try sema.analyzeLoad(block, src, object_ptr, src); | |
| 10245 | const ty = Type.Tag.bound_fn.init(); | |
| 10246 | const value = try Value.Tag.bound_fn.create(arena, .{ | |
| 10247 | .func_inst = decl_val, | |
| 10248 | .arg0_inst = deref, | |
| 10249 | }); | |
| 10250 | return sema.addConstant(ty, value); | |
| 10251 | } | |
| 10252 | } | |
| 10253 | } | |
| 10254 | } | |
| 10255 | }, | |
| 8465 | 10256 | else => {}, |
| 8466 | 10257 | } |
| 8467 | return mod.fail(&block.base, src, "type '{}' does not support field access", .{object_ty}); | |
| 10258 | ||
| 10259 | return mod.fail(&block.base, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty, field_name }); | |
| 8468 | 10260 | } |
| 8469 | 10261 | |
| 8470 | 10262 | fn namespaceLookup( |
| ... | ... | @@ -8516,13 +10308,19 @@ fn structFieldPtr( |
| 8516 | 10308 | const arena = sema.arena; |
| 8517 | 10309 | assert(unresolved_struct_ty.zigTypeTag() == .Struct); |
| 8518 | 10310 | |
| 10311 | const struct_ptr_ty = sema.typeOf(struct_ptr); | |
| 8519 | 10312 | const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty); |
| 8520 | 10313 | const struct_obj = struct_ty.castTag(.@"struct").?.data; |
| 8521 | 10314 | |
| 8522 | const field_index = struct_obj.fields.getIndex(field_name) orelse | |
| 10315 | const field_index_big = struct_obj.fields.getIndex(field_name) orelse | |
| 8523 | 10316 | return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name); |
| 10317 | const field_index = @intCast(u32, field_index_big); | |
| 8524 | 10318 | const field = struct_obj.fields.values()[field_index]; |
| 8525 | const ptr_field_ty = try Module.simplePtrType(arena, field.ty, true, .One); | |
| 10319 | const ptr_field_ty = try Type.ptr(arena, .{ | |
| 10320 | .pointee_type = field.ty, | |
| 10321 | .mutable = struct_ptr_ty.ptrIsMutable(), | |
| 10322 | .@"addrspace" = struct_ptr_ty.ptrAddressSpace(), | |
| 10323 | }); | |
| 8526 | 10324 | |
| 8527 | 10325 | if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { |
| 8528 | 10326 | return sema.addConstant( |
| ... | ... | @@ -8535,31 +10333,7 @@ fn structFieldPtr( |
| 8535 | 10333 | } |
| 8536 | 10334 | |
| 8537 | 10335 | try sema.requireRuntimeBlock(block, src); |
| 8538 | const tag: Air.Inst.Tag = switch (field_index) { | |
| 8539 | 0 => .struct_field_ptr_index_0, | |
| 8540 | 1 => .struct_field_ptr_index_1, | |
| 8541 | 2 => .struct_field_ptr_index_2, | |
| 8542 | 3 => .struct_field_ptr_index_3, | |
| 8543 | else => { | |
| 8544 | return block.addInst(.{ | |
| 8545 | .tag = .struct_field_ptr, | |
| 8546 | .data = .{ .ty_pl = .{ | |
| 8547 | .ty = try sema.addType(ptr_field_ty), | |
| 8548 | .payload = try sema.addExtra(Air.StructField{ | |
| 8549 | .struct_operand = struct_ptr, | |
| 8550 | .field_index = @intCast(u32, field_index), | |
| 8551 | }), | |
| 8552 | } }, | |
| 8553 | }); | |
| 8554 | }, | |
| 8555 | }; | |
| 8556 | return block.addInst(.{ | |
| 8557 | .tag = tag, | |
| 8558 | .data = .{ .ty_op = .{ | |
| 8559 | .ty = try sema.addType(ptr_field_ty), | |
| 8560 | .operand = struct_ptr, | |
| 8561 | } }, | |
| 8562 | }); | |
| 10336 | return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty); | |
| 8563 | 10337 | } |
| 8564 | 10338 | |
| 8565 | 10339 | fn structFieldVal( |
| ... | ... | @@ -8609,18 +10383,23 @@ fn unionFieldPtr( |
| 8609 | 10383 | field_name_src: LazySrcLoc, |
| 8610 | 10384 | unresolved_union_ty: Type, |
| 8611 | 10385 | ) CompileError!Air.Inst.Ref { |
| 8612 | const mod = sema.mod; | |
| 8613 | 10386 | const arena = sema.arena; |
| 8614 | 10387 | assert(unresolved_union_ty.zigTypeTag() == .Union); |
| 8615 | 10388 | |
| 10389 | const union_ptr_ty = sema.typeOf(union_ptr); | |
| 8616 | 10390 | const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty); |
| 8617 | 10391 | const union_obj = union_ty.cast(Type.Payload.Union).?.data; |
| 8618 | 10392 | |
| 8619 | const field_index = union_obj.fields.getIndex(field_name) orelse | |
| 10393 | const field_index_big = union_obj.fields.getIndex(field_name) orelse | |
| 8620 | 10394 | return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name); |
| 10395 | const field_index = @intCast(u32, field_index_big); | |
| 8621 | 10396 | |
| 8622 | 10397 | const field = union_obj.fields.values()[field_index]; |
| 8623 | const ptr_field_ty = try Module.simplePtrType(arena, field.ty, true, .One); | |
| 10398 | const ptr_field_ty = try Type.ptr(arena, .{ | |
| 10399 | .pointee_type = field.ty, | |
| 10400 | .mutable = union_ptr_ty.ptrIsMutable(), | |
| 10401 | .@"addrspace" = union_ptr_ty.ptrAddressSpace(), | |
| 10402 | }); | |
| 8624 | 10403 | |
| 8625 | 10404 | if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| { |
| 8626 | 10405 | // TODO detect inactive union field and emit compile error |
| ... | ... | @@ -8634,7 +10413,7 @@ fn unionFieldPtr( |
| 8634 | 10413 | } |
| 8635 | 10414 | |
| 8636 | 10415 | try sema.requireRuntimeBlock(block, src); |
| 8637 | return mod.fail(&block.base, src, "TODO implement runtime union field access", .{}); | |
| 10416 | return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); | |
| 8638 | 10417 | } |
| 8639 | 10418 | |
| 8640 | 10419 | fn unionFieldVal( |
| ... | ... | @@ -8796,10 +10575,11 @@ fn elemPtrArray( |
| 8796 | 10575 | ) CompileError!Air.Inst.Ref { |
| 8797 | 10576 | const array_ptr_ty = sema.typeOf(array_ptr); |
| 8798 | 10577 | const pointee_type = array_ptr_ty.elemType().elemType(); |
| 8799 | const result_ty = if (array_ptr_ty.ptrIsMutable()) | |
| 8800 | try Type.Tag.single_mut_pointer.create(sema.arena, pointee_type) | |
| 8801 | else | |
| 8802 | try Type.Tag.single_const_pointer.create(sema.arena, pointee_type); | |
| 10578 | const result_ty = try Type.ptr(sema.arena, .{ | |
| 10579 | .pointee_type = pointee_type, | |
| 10580 | .mutable = array_ptr_ty.ptrIsMutable(), | |
| 10581 | .@"addrspace" = array_ptr_ty.ptrAddressSpace(), | |
| 10582 | }); | |
| 8803 | 10583 | |
| 8804 | 10584 | if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| { |
| 8805 | 10585 | if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| { |
| ... | ... | @@ -8845,14 +10625,14 @@ fn coerce( |
| 8845 | 10625 | if (dest_type.eql(inst_ty)) |
| 8846 | 10626 | return inst; |
| 8847 | 10627 | |
| 8848 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst_ty); | |
| 10628 | const mod = sema.mod; | |
| 10629 | const arena = sema.arena; | |
| 10630 | ||
| 10631 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst_ty, false, mod.getTarget()); | |
| 8849 | 10632 | if (in_memory_result == .ok) { |
| 8850 | 10633 | return sema.bitcast(block, dest_type, inst, inst_src); |
| 8851 | 10634 | } |
| 8852 | 10635 | |
| 8853 | const mod = sema.mod; | |
| 8854 | const arena = sema.arena; | |
| 8855 | ||
| 8856 | 10636 | // undefined to anything |
| 8857 | 10637 | if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| { |
| 8858 | 10638 | if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) { |
| ... | ... | @@ -8887,11 +10667,13 @@ fn coerce( |
| 8887 | 10667 | const array_type = inst_ty.elemType(); |
| 8888 | 10668 | if (array_type.zigTypeTag() != .Array) break :src_array_ptr; |
| 8889 | 10669 | const array_elem_type = array_type.elemType(); |
| 8890 | if (inst_ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr; | |
| 10670 | const dest_is_mut = !dest_type.isConstPtr(); | |
| 10671 | if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr; | |
| 8891 | 10672 | if (inst_ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr; |
| 10673 | if (inst_ty.ptrAddressSpace() != dest_type.ptrAddressSpace()) break :src_array_ptr; | |
| 8892 | 10674 | |
| 8893 | 10675 | const dst_elem_type = dest_type.elemType(); |
| 8894 | switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) { | |
| 10676 | switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, mod.getTarget())) { | |
| 8895 | 10677 | .ok => {}, |
| 8896 | 10678 | .no_match => break :src_array_ptr, |
| 8897 | 10679 | } |
| ... | ... | @@ -8932,7 +10714,7 @@ fn coerce( |
| 8932 | 10714 | const src_info = inst_ty.intInfo(target); |
| 8933 | 10715 | if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or |
| 8934 | 10716 | // small enough unsigned ints can get casted to large enough signed ints |
| 8935 | (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits)) | |
| 10717 | (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) | |
| 8936 | 10718 | { |
| 8937 | 10719 | try sema.requireRuntimeBlock(block, inst_src); |
| 8938 | 10720 | return block.addTyOp(.intcast, dest_type, inst); |
| ... | ... | @@ -8948,13 +10730,13 @@ fn coerce( |
| 8948 | 10730 | const dst_bits = dest_type.floatBits(target); |
| 8949 | 10731 | if (dst_bits >= src_bits) { |
| 8950 | 10732 | try sema.requireRuntimeBlock(block, inst_src); |
| 8951 | return block.addTyOp(.floatcast, dest_type, inst); | |
| 10733 | return block.addTyOp(.fpext, dest_type, inst); | |
| 8952 | 10734 | } |
| 8953 | 10735 | } |
| 8954 | 10736 | }, |
| 8955 | .Enum => { | |
| 8956 | // enum literal to enum | |
| 8957 | if (inst_ty.zigTypeTag() == .EnumLiteral) { | |
| 10737 | .Enum => switch (inst_ty.zigTypeTag()) { | |
| 10738 | .EnumLiteral => { | |
| 10739 | // enum literal to enum | |
| 8958 | 10740 | const val = try sema.resolveConstValue(block, inst_src, inst); |
| 8959 | 10741 | const bytes = val.castTag(.enum_literal).?.data; |
| 8960 | 10742 | const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type); |
| ... | ... | @@ -8981,7 +10763,15 @@ fn coerce( |
| 8981 | 10763 | resolved_dest_type, |
| 8982 | 10764 | try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)), |
| 8983 | 10765 | ); |
| 8984 | } | |
| 10766 | }, | |
| 10767 | .Union => blk: { | |
| 10768 | // union to its own tag type | |
| 10769 | const union_tag_ty = inst_ty.unionTagType() orelse break :blk; | |
| 10770 | if (union_tag_ty.eql(dest_type)) { | |
| 10771 | return sema.unionToTag(block, dest_type, inst, inst_src); | |
| 10772 | } | |
| 10773 | }, | |
| 10774 | else => {}, | |
| 8985 | 10775 | }, |
| 8986 | 10776 | .ErrorUnion => { |
| 8987 | 10777 | // T to E!T or E to E!T |
| ... | ... | @@ -8998,10 +10788,92 @@ const InMemoryCoercionResult = enum { |
| 8998 | 10788 | no_match, |
| 8999 | 10789 | }; |
| 9000 | 10790 | |
| 9001 | fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult { | |
| 10791 | /// If pointers have the same representation in runtime memory, a bitcast AIR instruction | |
| 10792 | /// may be used for the coercion. | |
| 10793 | /// * `const` attribute can be gained | |
| 10794 | /// * `volatile` attribute can be gained | |
| 10795 | /// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if !dest_is_mut | |
| 10796 | /// * alignment can be decreased | |
| 10797 | /// * bit offset attributes must match exactly | |
| 10798 | /// * `*`/`[*]` must match exactly, but `[*c]` matches either one | |
| 10799 | /// * sentinel-terminated pointers can coerce into `[*]` | |
| 10800 | /// TODO improve this function to report recursive compile errors like it does in stage1. | |
| 10801 | /// look at the function types_match_const_cast_only | |
| 10802 | fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult { | |
| 9002 | 10803 | if (dest_type.eql(src_type)) |
| 9003 | 10804 | return .ok; |
| 9004 | 10805 | |
| 10806 | if (dest_type.zigTypeTag() == .Pointer and | |
| 10807 | src_type.zigTypeTag() == .Pointer) | |
| 10808 | { | |
| 10809 | const dest_info = dest_type.ptrInfo().data; | |
| 10810 | const src_info = src_type.ptrInfo().data; | |
| 10811 | ||
| 10812 | const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target); | |
| 10813 | if (child == .no_match) { | |
| 10814 | return child; | |
| 10815 | } | |
| 10816 | ||
| 10817 | if (dest_info.@"addrspace" != src_info.@"addrspace") { | |
| 10818 | return .no_match; | |
| 10819 | } | |
| 10820 | ||
| 10821 | const ok_sent = dest_info.sentinel == null or src_info.size == .C or | |
| 10822 | (src_info.sentinel != null and | |
| 10823 | dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type)); | |
| 10824 | if (!ok_sent) { | |
| 10825 | return .no_match; | |
| 10826 | } | |
| 10827 | ||
| 10828 | const ok_ptr_size = src_info.size == dest_info.size or | |
| 10829 | src_info.size == .C or dest_info.size == .C; | |
| 10830 | if (!ok_ptr_size) { | |
| 10831 | return .no_match; | |
| 10832 | } | |
| 10833 | ||
| 10834 | const ok_cv_qualifiers = | |
| 10835 | (src_info.mutable or !dest_info.mutable) and | |
| 10836 | (!src_info.@"volatile" or dest_info.@"volatile"); | |
| 10837 | ||
| 10838 | if (!ok_cv_qualifiers) { | |
| 10839 | return .no_match; | |
| 10840 | } | |
| 10841 | ||
| 10842 | const ok_allows_zero = (dest_info.@"allowzero" and | |
| 10843 | (src_info.@"allowzero" or !dest_is_mut)) or | |
| 10844 | (!dest_info.@"allowzero" and !src_info.@"allowzero"); | |
| 10845 | if (!ok_allows_zero) { | |
| 10846 | return .no_match; | |
| 10847 | } | |
| 10848 | ||
| 10849 | if (dest_type.hasCodeGenBits() != src_type.hasCodeGenBits()) { | |
| 10850 | return .no_match; | |
| 10851 | } | |
| 10852 | ||
| 10853 | if (src_info.host_size != dest_info.host_size or | |
| 10854 | src_info.bit_offset != dest_info.bit_offset) | |
| 10855 | { | |
| 10856 | return .no_match; | |
| 10857 | } | |
| 10858 | ||
| 10859 | // If both pointers have alignment 0, it means they both want ABI alignment. | |
| 10860 | // In this case, if they share the same child type, no need to resolve | |
| 10861 | // pointee type alignment. Otherwise both pointee types must have their alignment | |
| 10862 | // resolved and we compare the alignment numerically. | |
| 10863 | if (src_info.@"align" != 0 or dest_info.@"align" != 0 or | |
| 10864 | !dest_info.pointee_type.eql(src_info.pointee_type)) | |
| 10865 | { | |
| 10866 | const src_align = src_type.ptrAlignment(target); | |
| 10867 | const dest_align = dest_type.ptrAlignment(target); | |
| 10868 | ||
| 10869 | if (dest_align > src_align) { | |
| 10870 | return .no_match; | |
| 10871 | } | |
| 10872 | } | |
| 10873 | ||
| 10874 | return .ok; | |
| 10875 | } | |
| 10876 | ||
| 9005 | 10877 | // TODO: implement more of this function |
| 9006 | 10878 | |
| 9007 | 10879 | return .no_match; |
| ... | ... | @@ -9021,33 +10893,56 @@ fn coerceNum( |
| 9021 | 10893 | |
| 9022 | 10894 | const target = sema.mod.getTarget(); |
| 9023 | 10895 | |
| 9024 | if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { | |
| 9025 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | |
| 9026 | if (val.floatHasFraction()) { | |
| 9027 | return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty }); | |
| 9028 | } | |
| 9029 | return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{}); | |
| 9030 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | |
| 9031 | if (!val.intFitsInType(dest_type, target)) { | |
| 9032 | return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val }); | |
| 9033 | } | |
| 9034 | return try sema.addConstant(dest_type, val); | |
| 9035 | } | |
| 9036 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { | |
| 9037 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | |
| 9038 | const res = val.floatCast(sema.arena, dest_type) catch |err| switch (err) { | |
| 9039 | error.Overflow => return sema.mod.fail( | |
| 9040 | &block.base, | |
| 9041 | inst_src, | |
| 9042 | "cast of value {} to type '{}' loses information", | |
| 9043 | .{ val, dest_type }, | |
| 9044 | ), | |
| 9045 | error.OutOfMemory => return error.OutOfMemory, | |
| 9046 | }; | |
| 9047 | return try sema.addConstant(dest_type, res); | |
| 9048 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | |
| 9049 | return sema.mod.fail(&block.base, inst_src, "TODO int to float", .{}); | |
| 9050 | } | |
| 10896 | switch (dst_zig_tag) { | |
| 10897 | .ComptimeInt, .Int => switch (src_zig_tag) { | |
| 10898 | .Float, .ComptimeFloat => { | |
| 10899 | if (val.floatHasFraction()) { | |
| 10900 | return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_type }); | |
| 10901 | } | |
| 10902 | return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{}); | |
| 10903 | }, | |
| 10904 | .Int, .ComptimeInt => { | |
| 10905 | if (!val.intFitsInType(dest_type, target)) { | |
| 10906 | return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val }); | |
| 10907 | } | |
| 10908 | return try sema.addConstant(dest_type, val); | |
| 10909 | }, | |
| 10910 | else => {}, | |
| 10911 | }, | |
| 10912 | .ComptimeFloat, .Float => switch (src_zig_tag) { | |
| 10913 | .ComptimeFloat => { | |
| 10914 | const result_val = try val.floatCast(sema.arena, dest_type); | |
| 10915 | return try sema.addConstant(dest_type, result_val); | |
| 10916 | }, | |
| 10917 | .Float => { | |
| 10918 | const result_val = try val.floatCast(sema.arena, dest_type); | |
| 10919 | if (!val.eql(result_val, dest_type)) { | |
| 10920 | return sema.mod.fail( | |
| 10921 | &block.base, | |
| 10922 | inst_src, | |
| 10923 | "type {} cannot represent float value {}", | |
| 10924 | .{ dest_type, val }, | |
| 10925 | ); | |
| 10926 | } | |
| 10927 | return try sema.addConstant(dest_type, result_val); | |
| 10928 | }, | |
| 10929 | .Int, .ComptimeInt => { | |
| 10930 | const result_val = try val.intToFloat(sema.arena, dest_type, target); | |
| 10931 | // TODO implement this compile error | |
| 10932 | //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty); | |
| 10933 | //if (!int_again_val.eql(val, inst_ty)) { | |
| 10934 | // return sema.mod.fail( | |
| 10935 | // &block.base, | |
| 10936 | // inst_src, | |
| 10937 | // "type {} cannot represent integer value {}", | |
| 10938 | // .{ dest_type, val }, | |
| 10939 | // ); | |
| 10940 | //} | |
| 10941 | return try sema.addConstant(dest_type, result_val); | |
| 10942 | }, | |
| 10943 | else => {}, | |
| 10944 | }, | |
| 10945 | else => {}, | |
| 9051 | 10946 | } |
| 9052 | 10947 | return null; |
| 9053 | 10948 | } |
| ... | ... | @@ -9067,66 +10962,97 @@ fn coerceVarArgParam( |
| 9067 | 10962 | return inst; |
| 9068 | 10963 | } |
| 9069 | 10964 | |
| 10965 | // TODO migrate callsites to use storePtr2 instead. | |
| 9070 | 10966 | fn storePtr( |
| 9071 | 10967 | sema: *Sema, |
| 9072 | 10968 | block: *Scope.Block, |
| 9073 | 10969 | src: LazySrcLoc, |
| 9074 | 10970 | ptr: Air.Inst.Ref, |
| 9075 | uncasted_value: Air.Inst.Ref, | |
| 10971 | uncasted_operand: Air.Inst.Ref, | |
| 10972 | ) !void { | |
| 10973 | return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, .store); | |
| 10974 | } | |
| 10975 | ||
| 10976 | fn storePtr2( | |
| 10977 | sema: *Sema, | |
| 10978 | block: *Scope.Block, | |
| 10979 | src: LazySrcLoc, | |
| 10980 | ptr: Air.Inst.Ref, | |
| 10981 | ptr_src: LazySrcLoc, | |
| 10982 | uncasted_operand: Air.Inst.Ref, | |
| 10983 | operand_src: LazySrcLoc, | |
| 10984 | air_tag: Air.Inst.Tag, | |
| 9076 | 10985 | ) !void { |
| 9077 | 10986 | const ptr_ty = sema.typeOf(ptr); |
| 9078 | 10987 | if (ptr_ty.isConstPtr()) |
| 9079 | 10988 | return sema.mod.fail(&block.base, src, "cannot assign to constant", .{}); |
| 9080 | 10989 | |
| 9081 | 10990 | const elem_ty = ptr_ty.elemType(); |
| 9082 | const value = try sema.coerce(block, elem_ty, uncasted_value, src); | |
| 10991 | const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src); | |
| 9083 | 10992 | if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null) |
| 9084 | 10993 | return; |
| 9085 | 10994 | |
| 9086 | if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| { | |
| 9087 | if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| { | |
| 9088 | const const_val = (try sema.resolveMaybeUndefVal(block, src, value)) orelse | |
| 9089 | return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{}); | |
| 9090 | ||
| 9091 | if (decl_ref_mut.data.runtime_index < block.runtime_index) { | |
| 9092 | if (block.runtime_cond) |cond_src| { | |
| 9093 | const msg = msg: { | |
| 9094 | const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{}); | |
| 9095 | errdefer msg.destroy(sema.gpa); | |
| 9096 | try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{}); | |
| 9097 | break :msg msg; | |
| 9098 | }; | |
| 9099 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 9100 | } | |
| 9101 | if (block.runtime_loop) |loop_src| { | |
| 9102 | const msg = msg: { | |
| 9103 | const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{}); | |
| 9104 | errdefer msg.destroy(sema.gpa); | |
| 9105 | try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{}); | |
| 9106 | break :msg msg; | |
| 9107 | }; | |
| 9108 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 9109 | } | |
| 9110 | unreachable; | |
| 9111 | } | |
| 9112 | var new_arena = std.heap.ArenaAllocator.init(sema.gpa); | |
| 9113 | errdefer new_arena.deinit(); | |
| 9114 | const new_ty = try elem_ty.copy(&new_arena.allocator); | |
| 9115 | const new_val = try const_val.copy(&new_arena.allocator); | |
| 9116 | const decl = decl_ref_mut.data.decl; | |
| 9117 | var old_arena = decl.value_arena.?.promote(sema.gpa); | |
| 9118 | decl.value_arena = null; | |
| 9119 | try decl.finalizeNewArena(&new_arena); | |
| 9120 | decl.ty = new_ty; | |
| 9121 | decl.val = new_val; | |
| 9122 | old_arena.deinit(); | |
| 10995 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { | |
| 10996 | const operand_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse | |
| 10997 | return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{}); | |
| 10998 | if (ptr_val.tag() == .decl_ref_mut) { | |
| 10999 | try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); | |
| 9123 | 11000 | return; |
| 9124 | 11001 | } |
| 9125 | } | |
| 11002 | break :rs operand_src; | |
| 11003 | } else ptr_src; | |
| 11004 | ||
| 9126 | 11005 | // TODO handle if the element type requires comptime |
| 9127 | 11006 | |
| 9128 | try sema.requireRuntimeBlock(block, src); | |
| 9129 | _ = try block.addBinOp(.store, ptr, value); | |
| 11007 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 11008 | _ = try block.addBinOp(air_tag, ptr, operand); | |
| 11009 | } | |
| 11010 | ||
| 11011 | /// Call when you have Value objects rather than Air instructions, and you want to | |
| 11012 | /// assert the store must be done at comptime. | |
| 11013 | fn storePtrVal( | |
| 11014 | sema: *Sema, | |
| 11015 | block: *Scope.Block, | |
| 11016 | src: LazySrcLoc, | |
| 11017 | ptr_val: Value, | |
| 11018 | operand_val: Value, | |
| 11019 | operand_ty: Type, | |
| 11020 | ) !void { | |
| 11021 | if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| { | |
| 11022 | if (decl_ref_mut.data.runtime_index < block.runtime_index) { | |
| 11023 | if (block.runtime_cond) |cond_src| { | |
| 11024 | const msg = msg: { | |
| 11025 | const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{}); | |
| 11026 | errdefer msg.destroy(sema.gpa); | |
| 11027 | try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{}); | |
| 11028 | break :msg msg; | |
| 11029 | }; | |
| 11030 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 11031 | } | |
| 11032 | if (block.runtime_loop) |loop_src| { | |
| 11033 | const msg = msg: { | |
| 11034 | const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{}); | |
| 11035 | errdefer msg.destroy(sema.gpa); | |
| 11036 | try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{}); | |
| 11037 | break :msg msg; | |
| 11038 | }; | |
| 11039 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 11040 | } | |
| 11041 | unreachable; | |
| 11042 | } | |
| 11043 | var new_arena = std.heap.ArenaAllocator.init(sema.gpa); | |
| 11044 | errdefer new_arena.deinit(); | |
| 11045 | const new_ty = try operand_ty.copy(&new_arena.allocator); | |
| 11046 | const new_val = try operand_val.copy(&new_arena.allocator); | |
| 11047 | const decl = decl_ref_mut.data.decl; | |
| 11048 | var old_arena = decl.value_arena.?.promote(sema.gpa); | |
| 11049 | decl.value_arena = null; | |
| 11050 | try decl.finalizeNewArena(&new_arena); | |
| 11051 | decl.ty = new_ty; | |
| 11052 | decl.val = new_val; | |
| 11053 | old_arena.deinit(); | |
| 11054 | return; | |
| 11055 | } | |
| 9130 | 11056 | } |
| 9131 | 11057 | |
| 9132 | 11058 | fn bitcast( |
| ... | ... | @@ -9171,7 +11097,8 @@ fn coerceArrayPtrToMany( |
| 9171 | 11097 | // The comptime Value representation is compatible with both types. |
| 9172 | 11098 | return sema.addConstant(dest_type, val); |
| 9173 | 11099 | } |
| 9174 | return sema.mod.fail(&block.base, inst_src, "TODO implement coerceArrayPtrToMany runtime instruction", .{}); | |
| 11100 | try sema.requireRuntimeBlock(block, inst_src); | |
| 11101 | return sema.bitcast(block, dest_type, inst, inst_src); | |
| 9175 | 11102 | } |
| 9176 | 11103 | |
| 9177 | 11104 | fn analyzeDeclVal( |
| ... | ... | @@ -9211,11 +11138,19 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref { |
| 9211 | 11138 | const decl_tv = try decl.typedValue(); |
| 9212 | 11139 | if (decl_tv.val.castTag(.variable)) |payload| { |
| 9213 | 11140 | const variable = payload.data; |
| 9214 | const ty = try Module.simplePtrType(sema.arena, decl_tv.ty, variable.is_mutable, .One); | |
| 11141 | const ty = try Type.ptr(sema.arena, .{ | |
| 11142 | .pointee_type = decl_tv.ty, | |
| 11143 | .mutable = variable.is_mutable, | |
| 11144 | .@"addrspace" = decl.@"addrspace", | |
| 11145 | }); | |
| 9215 | 11146 | return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl)); |
| 9216 | 11147 | } |
| 9217 | 11148 | return sema.addConstant( |
| 9218 | try Module.simplePtrType(sema.arena, decl_tv.ty, false, .One), | |
| 11149 | try Type.ptr(sema.arena, .{ | |
| 11150 | .pointee_type = decl_tv.ty, | |
| 11151 | .mutable = false, | |
| 11152 | .@"addrspace" = decl.@"addrspace", | |
| 11153 | }), | |
| 9219 | 11154 | try Value.Tag.decl_ref.create(sema.arena, decl), |
| 9220 | 11155 | ); |
| 9221 | 11156 | } |
| ... | ... | @@ -9232,14 +11167,22 @@ fn analyzeRef( |
| 9232 | 11167 | var anon_decl = try block.startAnonDecl(); |
| 9233 | 11168 | defer anon_decl.deinit(); |
| 9234 | 11169 | return sema.analyzeDeclRef(try anon_decl.finish( |
| 9235 | operand_ty, | |
| 11170 | try operand_ty.copy(anon_decl.arena()), | |
| 9236 | 11171 | try val.copy(anon_decl.arena()), |
| 9237 | 11172 | )); |
| 9238 | 11173 | } |
| 9239 | 11174 | |
| 9240 | 11175 | try sema.requireRuntimeBlock(block, src); |
| 9241 | const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One); | |
| 9242 | const mut_ptr_type = try Module.simplePtrType(sema.arena, operand_ty, true, .One); | |
| 11176 | const address_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local); | |
| 11177 | const ptr_type = try Type.ptr(sema.arena, .{ | |
| 11178 | .pointee_type = operand_ty, | |
| 11179 | .mutable = false, | |
| 11180 | .@"addrspace" = address_space, | |
| 11181 | }); | |
| 11182 | const mut_ptr_type = try Type.ptr(sema.arena, .{ | |
| 11183 | .pointee_type = operand_ty, | |
| 11184 | .@"addrspace" = address_space, | |
| 11185 | }); | |
| 9243 | 11186 | const alloc = try block.addTy(.alloc, mut_ptr_type); |
| 9244 | 11187 | try sema.storePtr(block, src, alloc, operand); |
| 9245 | 11188 | |
| ... | ... | @@ -9395,18 +11338,16 @@ fn analyzeSlice( |
| 9395 | 11338 | } |
| 9396 | 11339 | } |
| 9397 | 11340 | } |
| 9398 | const return_type = try Module.ptrType( | |
| 9399 | sema.arena, | |
| 9400 | return_elem_type, | |
| 9401 | if (end_opt == .none) slice_sentinel else null, | |
| 9402 | 0, // TODO alignment | |
| 9403 | 0, | |
| 9404 | 0, | |
| 9405 | !ptr_child.isConstPtr(), | |
| 9406 | ptr_child.isAllowzeroPtr(), | |
| 9407 | ptr_child.isVolatilePtr(), | |
| 9408 | return_ptr_size, | |
| 9409 | ); | |
| 11341 | const return_type = try Type.ptr(sema.arena, .{ | |
| 11342 | .pointee_type = return_elem_type, | |
| 11343 | .sentinel = if (end_opt == .none) slice_sentinel else null, | |
| 11344 | .@"align" = 0, // TODO alignment | |
| 11345 | .@"addrspace" = if (ptr_child.zigTypeTag() == .Pointer) ptr_child.ptrAddressSpace() else .generic, | |
| 11346 | .mutable = !ptr_child.isConstPtr(), | |
| 11347 | .@"allowzero" = ptr_child.isAllowzeroPtr(), | |
| 11348 | .@"volatile" = ptr_child.isVolatilePtr(), | |
| 11349 | .size = return_ptr_size, | |
| 11350 | }); | |
| 9410 | 11351 | _ = return_type; |
| 9411 | 11352 | |
| 9412 | 11353 | return sema.mod.fail(&block.base, src, "TODO implement analysis of slice", .{}); |
| ... | ... | @@ -9508,11 +11449,11 @@ fn cmpNumeric( |
| 9508 | 11449 | const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| |
| 9509 | 11450 | lhs_val.compareWithZero(.lt) |
| 9510 | 11451 | else |
| 9511 | (lhs_ty.isFloat() or lhs_ty.isSignedInt()); | |
| 11452 | (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt()); | |
| 9512 | 11453 | const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| |
| 9513 | 11454 | rhs_val.compareWithZero(.lt) |
| 9514 | 11455 | else |
| 9515 | (rhs_ty.isFloat() or rhs_ty.isSignedInt()); | |
| 11456 | (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt()); | |
| 9516 | 11457 | const dest_int_is_signed = lhs_is_signed or rhs_is_signed; |
| 9517 | 11458 | |
| 9518 | 11459 | var dest_float_type: ?Type = null; |
| ... | ... | @@ -9692,6 +11633,20 @@ fn wrapErrorUnion( |
| 9692 | 11633 | } |
| 9693 | 11634 | } |
| 9694 | 11635 | |
| 11636 | fn unionToTag( | |
| 11637 | sema: *Sema, | |
| 11638 | block: *Scope.Block, | |
| 11639 | dest_type: Type, | |
| 11640 | un: Air.Inst.Ref, | |
| 11641 | un_src: LazySrcLoc, | |
| 11642 | ) !Air.Inst.Ref { | |
| 11643 | if (try sema.resolveMaybeUndefVal(block, un_src, un)) |un_val| { | |
| 11644 | return sema.addConstant(dest_type, un_val.unionTag()); | |
| 11645 | } | |
| 11646 | try sema.requireRuntimeBlock(block, un_src); | |
| 11647 | return block.addTyOp(.get_union_tag, dest_type, un); | |
| 11648 | } | |
| 11649 | ||
| 9695 | 11650 | fn resolvePeerTypes( |
| 9696 | 11651 | sema: *Sema, |
| 9697 | 11652 | block: *Scope.Block, |
| ... | ... | @@ -9738,7 +11693,7 @@ fn resolvePeerTypes( |
| 9738 | 11693 | } |
| 9739 | 11694 | continue; |
| 9740 | 11695 | } |
| 9741 | if (chosen_ty.isFloat() and candidate_ty.isFloat()) { | |
| 11696 | if (chosen_ty.isRuntimeFloat() and candidate_ty.isRuntimeFloat()) { | |
| 9742 | 11697 | if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) { |
| 9743 | 11698 | chosen = candidate; |
| 9744 | 11699 | chosen_i = candidate_i + 1; |
| ... | ... | @@ -9756,13 +11711,13 @@ fn resolvePeerTypes( |
| 9756 | 11711 | continue; |
| 9757 | 11712 | } |
| 9758 | 11713 | |
| 9759 | if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isFloat()) { | |
| 11714 | if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isRuntimeFloat()) { | |
| 9760 | 11715 | chosen = candidate; |
| 9761 | 11716 | chosen_i = candidate_i + 1; |
| 9762 | 11717 | continue; |
| 9763 | 11718 | } |
| 9764 | 11719 | |
| 9765 | if (chosen_ty.isFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) { | |
| 11720 | if (chosen_ty.isRuntimeFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) { | |
| 9766 | 11721 | continue; |
| 9767 | 11722 | } |
| 9768 | 11723 | |
| ... | ... | @@ -9813,9 +11768,6 @@ pub fn resolveTypeLayout( |
| 9813 | 11768 | ty: Type, |
| 9814 | 11769 | ) CompileError!void { |
| 9815 | 11770 | switch (ty.zigTypeTag()) { |
| 9816 | .Pointer => { | |
| 9817 | return sema.resolveTypeLayout(block, src, ty.elemType()); | |
| 9818 | }, | |
| 9819 | 11771 | .Struct => { |
| 9820 | 11772 | const resolved_ty = try sema.resolveTypeFields(block, src, ty); |
| 9821 | 11773 | const struct_obj = resolved_ty.castTag(.@"struct").?.data; |
| ... | ... | @@ -9863,6 +11815,10 @@ pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: |
| 9863 | 11815 | sema.namespace = &struct_obj.namespace; |
| 9864 | 11816 | defer sema.namespace = prev_namespace; |
| 9865 | 11817 | |
| 11818 | const old_src = block.src_decl; | |
| 11819 | defer block.src_decl = old_src; | |
| 11820 | block.src_decl = struct_obj.owner_decl; | |
| 11821 | ||
| 9866 | 11822 | struct_obj.status = .field_types_wip; |
| 9867 | 11823 | try sema.analyzeStructFields(block, struct_obj); |
| 9868 | 11824 | struct_obj.status = .have_field_types; |
| ... | ... | @@ -9881,6 +11837,10 @@ pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: |
| 9881 | 11837 | sema.namespace = &union_obj.namespace; |
| 9882 | 11838 | defer sema.namespace = prev_namespace; |
| 9883 | 11839 | |
| 11840 | const old_src = block.src_decl; | |
| 11841 | defer block.src_decl = old_src; | |
| 11842 | block.src_decl = union_obj.owner_decl; | |
| 11843 | ||
| 9884 | 11844 | union_obj.status = .field_types_wip; |
| 9885 | 11845 | try sema.analyzeUnionFields(block, union_obj); |
| 9886 | 11846 | union_obj.status = .have_field_types; |
| ... | ... | @@ -9907,6 +11867,7 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type |
| 9907 | 11867 | .atomic_order => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrder"), |
| 9908 | 11868 | .atomic_rmw_op => return sema.resolveBuiltinTypeFields(block, src, "AtomicRmwOp"), |
| 9909 | 11869 | .calling_convention => return sema.resolveBuiltinTypeFields(block, src, "CallingConvention"), |
| 11870 | .address_space => return sema.resolveBuiltinTypeFields(block, src, "AddressSpace"), | |
| 9910 | 11871 | .float_mode => return sema.resolveBuiltinTypeFields(block, src, "FloatMode"), |
| 9911 | 11872 | .reduce_op => return sema.resolveBuiltinTypeFields(block, src, "ReduceOp"), |
| 9912 | 11873 | .call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"), |
| ... | ... | @@ -10081,9 +12042,11 @@ fn analyzeUnionFields( |
| 10081 | 12042 | const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset }; |
| 10082 | 12043 | extra_index += @boolToInt(small.has_src_node); |
| 10083 | 12044 | |
| 10084 | if (small.has_tag_type) { | |
| 12045 | const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { | |
| 12046 | const ty_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | |
| 10085 | 12047 | extra_index += 1; |
| 10086 | } | |
| 12048 | break :blk ty_ref; | |
| 12049 | } else .none; | |
| 10087 | 12050 | |
| 10088 | 12051 | const body_len = if (small.has_body_len) blk: { |
| 10089 | 12052 | const body_len = zir.extra[extra_index]; |
| ... | ... | @@ -10118,11 +12081,33 @@ fn analyzeUnionFields( |
| 10118 | 12081 | var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa); |
| 10119 | 12082 | defer union_obj.owner_decl.value_arena.?.* = decl_arena.state; |
| 10120 | 12083 | |
| 10121 | try union_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len); | |
| 12084 | try union_obj.fields.ensureTotalCapacity(&decl_arena.allocator, fields_len); | |
| 10122 | 12085 | |
| 10123 | 12086 | if (body.len != 0) { |
| 10124 | 12087 | _ = try sema.analyzeBody(block, body); |
| 10125 | 12088 | } |
| 12089 | var int_tag_ty: Type = undefined; | |
| 12090 | var enum_field_names: ?*Module.EnumNumbered.NameMap = null; | |
| 12091 | var enum_value_map: ?*Module.EnumNumbered.ValueMap = null; | |
| 12092 | if (tag_type_ref != .none) { | |
| 12093 | const provided_ty = try sema.resolveType(block, src, tag_type_ref); | |
| 12094 | if (small.auto_enum_tag) { | |
| 12095 | // The provided type is an integer type and we must construct the enum tag type here. | |
| 12096 | int_tag_ty = provided_ty; | |
| 12097 | union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(block, fields_len, provided_ty); | |
| 12098 | enum_field_names = &union_obj.tag_ty.castTag(.enum_numbered).?.data.fields; | |
| 12099 | enum_value_map = &union_obj.tag_ty.castTag(.enum_numbered).?.data.values; | |
| 12100 | } else { | |
| 12101 | // The provided type is the enum tag type. | |
| 12102 | union_obj.tag_ty = provided_ty; | |
| 12103 | } | |
| 12104 | } else { | |
| 12105 | // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis | |
| 12106 | // purposes, we still auto-generate an enum tag type the same way. That the union is | |
| 12107 | // untagged is represented by the Type tag (union vs union_tagged). | |
| 12108 | union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, fields_len); | |
| 12109 | enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields; | |
| 12110 | } | |
| 10126 | 12111 | |
| 10127 | 12112 | const bits_per_field = 4; |
| 10128 | 12113 | const fields_per_u32 = 32 / bits_per_field; |
| ... | ... | @@ -10161,12 +12146,25 @@ fn analyzeUnionFields( |
| 10161 | 12146 | break :blk align_ref; |
| 10162 | 12147 | } else .none; |
| 10163 | 12148 | |
| 10164 | if (has_tag) { | |
| 12149 | const tag_ref: Zir.Inst.Ref = if (has_tag) blk: { | |
| 12150 | const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | |
| 10165 | 12151 | extra_index += 1; |
| 12152 | break :blk tag_ref; | |
| 12153 | } else .none; | |
| 12154 | ||
| 12155 | if (enum_value_map) |map| { | |
| 12156 | const tag_src = src; // TODO better source location | |
| 12157 | const coerced = try sema.coerce(block, int_tag_ty, tag_ref, tag_src); | |
| 12158 | const val = try sema.resolveConstValue(block, tag_src, coerced); | |
| 12159 | map.putAssumeCapacityContext(val, {}, .{ .ty = int_tag_ty }); | |
| 10166 | 12160 | } |
| 10167 | 12161 | |
| 10168 | 12162 | // This string needs to outlive the ZIR code. |
| 10169 | 12163 | const field_name = try decl_arena.allocator.dupe(u8, field_name_zir); |
| 12164 | if (enum_field_names) |set| { | |
| 12165 | set.putAssumeCapacity(field_name, {}); | |
| 12166 | } | |
| 12167 | ||
| 10170 | 12168 | const field_ty: Type = if (field_type_ref == .none) |
| 10171 | 12169 | Type.initTag(.void) |
| 10172 | 12170 | else |
| ... | ... | @@ -10188,10 +12186,84 @@ fn analyzeUnionFields( |
| 10188 | 12186 | // But only resolve the source location if we need to emit a compile error. |
| 10189 | 12187 | const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val; |
| 10190 | 12188 | gop.value_ptr.abi_align = try abi_align_val.copy(&decl_arena.allocator); |
| 12189 | } else { | |
| 12190 | gop.value_ptr.abi_align = Value.initTag(.abi_align_default); | |
| 10191 | 12191 | } |
| 10192 | 12192 | } |
| 12193 | } | |
| 12194 | ||
| 12195 | fn generateUnionTagTypeNumbered( | |
| 12196 | sema: *Sema, | |
| 12197 | block: *Scope.Block, | |
| 12198 | fields_len: u32, | |
| 12199 | int_ty: Type, | |
| 12200 | ) !Type { | |
| 12201 | const mod = sema.mod; | |
| 12202 | ||
| 12203 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | |
| 12204 | errdefer new_decl_arena.deinit(); | |
| 12205 | ||
| 12206 | const enum_obj = try new_decl_arena.allocator.create(Module.EnumNumbered); | |
| 12207 | const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumNumbered); | |
| 12208 | enum_ty_payload.* = .{ | |
| 12209 | .base = .{ .tag = .enum_numbered }, | |
| 12210 | .data = enum_obj, | |
| 12211 | }; | |
| 12212 | const enum_ty = Type.initPayload(&enum_ty_payload.base); | |
| 12213 | const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty); | |
| 12214 | // TODO better type name | |
| 12215 | const new_decl = try mod.createAnonymousDecl(&block.base, .{ | |
| 12216 | .ty = Type.initTag(.type), | |
| 12217 | .val = enum_val, | |
| 12218 | }); | |
| 12219 | new_decl.owns_tv = true; | |
| 12220 | errdefer sema.mod.deleteAnonDecl(&block.base, new_decl); | |
| 12221 | ||
| 12222 | enum_obj.* = .{ | |
| 12223 | .owner_decl = new_decl, | |
| 12224 | .tag_ty = int_ty, | |
| 12225 | .fields = .{}, | |
| 12226 | .values = .{}, | |
| 12227 | .node_offset = 0, | |
| 12228 | }; | |
| 12229 | // Here we pre-allocate the maps using the decl arena. | |
| 12230 | try enum_obj.fields.ensureTotalCapacity(&new_decl_arena.allocator, fields_len); | |
| 12231 | try enum_obj.values.ensureTotalCapacityContext(&new_decl_arena.allocator, fields_len, .{ .ty = int_ty }); | |
| 12232 | try new_decl.finalizeNewArena(&new_decl_arena); | |
| 12233 | return enum_ty; | |
| 12234 | } | |
| 12235 | ||
| 12236 | fn generateUnionTagTypeSimple(sema: *Sema, block: *Scope.Block, fields_len: u32) !Type { | |
| 12237 | const mod = sema.mod; | |
| 12238 | ||
| 12239 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | |
| 12240 | errdefer new_decl_arena.deinit(); | |
| 12241 | ||
| 12242 | const enum_obj = try new_decl_arena.allocator.create(Module.EnumSimple); | |
| 12243 | const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumSimple); | |
| 12244 | enum_ty_payload.* = .{ | |
| 12245 | .base = .{ .tag = .enum_simple }, | |
| 12246 | .data = enum_obj, | |
| 12247 | }; | |
| 12248 | const enum_ty = Type.initPayload(&enum_ty_payload.base); | |
| 12249 | const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty); | |
| 12250 | // TODO better type name | |
| 12251 | const new_decl = try mod.createAnonymousDecl(&block.base, .{ | |
| 12252 | .ty = Type.initTag(.type), | |
| 12253 | .val = enum_val, | |
| 12254 | }); | |
| 12255 | new_decl.owns_tv = true; | |
| 12256 | errdefer sema.mod.deleteAnonDecl(&block.base, new_decl); | |
| 10193 | 12257 | |
| 10194 | // TODO resolve the union tag_type_ref | |
| 12258 | enum_obj.* = .{ | |
| 12259 | .owner_decl = new_decl, | |
| 12260 | .fields = .{}, | |
| 12261 | .node_offset = 0, | |
| 12262 | }; | |
| 12263 | // Here we pre-allocate the maps using the decl arena. | |
| 12264 | try enum_obj.fields.ensureTotalCapacity(&new_decl_arena.allocator, fields_len); | |
| 12265 | try new_decl.finalizeNewArena(&new_decl_arena); | |
| 12266 | return enum_ty; | |
| 10195 | 12267 | } |
| 10196 | 12268 | |
| 10197 | 12269 | fn getBuiltin( |
| ... | ... | @@ -10231,7 +12303,7 @@ fn getBuiltinType( |
| 10231 | 12303 | } |
| 10232 | 12304 | |
| 10233 | 12305 | /// There is another implementation of this in `Type.onePossibleValue`. This one |
| 10234 | /// in `Sema` is for calling during semantic analysis, and peforms field resolution | |
| 12306 | /// in `Sema` is for calling during semantic analysis, and performs field resolution | |
| 10235 | 12307 | /// to get the answer. The one in `Type` is for calling during codegen and asserts |
| 10236 | 12308 | /// that the types are already resolved. |
| 10237 | 12309 | fn typeHasOnePossibleValue( |
| ... | ... | @@ -10301,6 +12373,7 @@ fn typeHasOnePossibleValue( |
| 10301 | 12373 | .atomic_order, |
| 10302 | 12374 | .atomic_rmw_op, |
| 10303 | 12375 | .calling_convention, |
| 12376 | .address_space, | |
| 10304 | 12377 | .float_mode, |
| 10305 | 12378 | .reduce_op, |
| 10306 | 12379 | .call_options, |
| ... | ... | @@ -10316,6 +12389,7 @@ fn typeHasOnePossibleValue( |
| 10316 | 12389 | .single_const_pointer, |
| 10317 | 12390 | .single_mut_pointer, |
| 10318 | 12391 | .pointer, |
| 12392 | .bound_fn, | |
| 10319 | 12393 | => return null, |
| 10320 | 12394 | |
| 10321 | 12395 | .@"struct" => { |
| ... | ... | @@ -10328,11 +12402,28 @@ fn typeHasOnePossibleValue( |
| 10328 | 12402 | } |
| 10329 | 12403 | return Value.initTag(.empty_struct_value); |
| 10330 | 12404 | }, |
| 12405 | .enum_numbered => { | |
| 12406 | const resolved_ty = try sema.resolveTypeFields(block, src, ty); | |
| 12407 | const enum_obj = resolved_ty.castTag(.enum_numbered).?.data; | |
| 12408 | if (enum_obj.fields.count() == 1) { | |
| 12409 | if (enum_obj.values.count() == 0) { | |
| 12410 | return Value.initTag(.zero); // auto-numbered | |
| 12411 | } else { | |
| 12412 | return enum_obj.values.keys()[0]; | |
| 12413 | } | |
| 12414 | } else { | |
| 12415 | return null; | |
| 12416 | } | |
| 12417 | }, | |
| 10331 | 12418 | .enum_full => { |
| 10332 | 12419 | const resolved_ty = try sema.resolveTypeFields(block, src, ty); |
| 10333 | const enum_full = resolved_ty.castTag(.enum_full).?.data; | |
| 10334 | if (enum_full.fields.count() == 1) { | |
| 10335 | return enum_full.values.keys()[0]; | |
| 12420 | const enum_obj = resolved_ty.castTag(.enum_full).?.data; | |
| 12421 | if (enum_obj.fields.count() == 1) { | |
| 12422 | if (enum_obj.values.count() == 0) { | |
| 12423 | return Value.initTag(.zero); // auto-numbered | |
| 12424 | } else { | |
| 12425 | return enum_obj.values.keys()[0]; | |
| 12426 | } | |
| 10336 | 12427 | } else { |
| 10337 | 12428 | return null; |
| 10338 | 12429 | } |
| ... | ... | @@ -10486,6 +12577,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { |
| 10486 | 12577 | .atomic_order => return .atomic_order_type, |
| 10487 | 12578 | .atomic_rmw_op => return .atomic_rmw_op_type, |
| 10488 | 12579 | .calling_convention => return .calling_convention_type, |
| 12580 | .address_space => return .address_space_type, | |
| 10489 | 12581 | .float_mode => return .float_mode_type, |
| 10490 | 12582 | .reduce_op => return .reduce_op_type, |
| 10491 | 12583 | .call_options => return .call_options_type, |
| ... | ... | @@ -10581,7 +12673,10 @@ fn analyzeComptimeAlloc( |
| 10581 | 12673 | block: *Scope.Block, |
| 10582 | 12674 | var_type: Type, |
| 10583 | 12675 | ) CompileError!Air.Inst.Ref { |
| 10584 | const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One); | |
| 12676 | const ptr_type = try Type.ptr(sema.arena, .{ | |
| 12677 | .pointee_type = var_type, | |
| 12678 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant), | |
| 12679 | }); | |
| 10585 | 12680 | |
| 10586 | 12681 | var anon_decl = try block.startAnonDecl(); |
| 10587 | 12682 | defer anon_decl.deinit(); |
| ... | ... | @@ -10597,3 +12692,58 @@ fn analyzeComptimeAlloc( |
| 10597 | 12692 | .decl = decl, |
| 10598 | 12693 | })); |
| 10599 | 12694 | } |
| 12695 | ||
| 12696 | /// The places where a user can specify an address space attribute | |
| 12697 | pub const AddressSpaceContext = enum { | |
| 12698 | /// A function is specified to be placed in a certain address space. | |
| 12699 | function, | |
| 12700 | ||
| 12701 | /// A (global) variable is specified to be placed in a certain address space. | |
| 12702 | /// In contrast to .constant, these values (and thus the address space they will be | |
| 12703 | /// placed in) are required to be mutable. | |
| 12704 | variable, | |
| 12705 | ||
| 12706 | /// A (global) constant value is specified to be placed in a certain address space. | |
| 12707 | /// In contrast to .variable, values placed in this address space are not required to be mutable. | |
| 12708 | constant, | |
| 12709 | ||
| 12710 | /// A pointer is ascripted to point into a certain address space. | |
| 12711 | pointer, | |
| 12712 | }; | |
| 12713 | ||
| 12714 | pub fn analyzeAddrspace( | |
| 12715 | sema: *Sema, | |
| 12716 | block: *Scope.Block, | |
| 12717 | src: LazySrcLoc, | |
| 12718 | zir_ref: Zir.Inst.Ref, | |
| 12719 | ctx: AddressSpaceContext, | |
| 12720 | ) !std.builtin.AddressSpace { | |
| 12721 | const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref); | |
| 12722 | const address_space = addrspace_tv.val.toEnum(std.builtin.AddressSpace); | |
| 12723 | const target = sema.mod.getTarget(); | |
| 12724 | const arch = target.cpu.arch; | |
| 12725 | ||
| 12726 | const supported = switch (address_space) { | |
| 12727 | .generic => true, | |
| 12728 | .gs, .fs, .ss => (arch == .i386 or arch == .x86_64) and ctx == .pointer, | |
| 12729 | }; | |
| 12730 | ||
| 12731 | if (!supported) { | |
| 12732 | // TODO error messages could be made more elaborate here | |
| 12733 | const entity = switch (ctx) { | |
| 12734 | .function => "functions", | |
| 12735 | .variable => "mutable values", | |
| 12736 | .constant => "constant values", | |
| 12737 | .pointer => "pointers", | |
| 12738 | }; | |
| 12739 | ||
| 12740 | return sema.mod.fail( | |
| 12741 | &block.base, | |
| 12742 | src, | |
| 12743 | "{s} with address space '{s}' are not supported on {s}", | |
| 12744 | .{ entity, @tagName(address_space), arch.genericName() }, | |
| 12745 | ); | |
| 12746 | } | |
| 12747 | ||
| 12748 | return address_space; | |
| 12749 | } |
src/Zir.zig+238-1993| ... | ... | @@ -49,8 +49,6 @@ pub const Header = extern struct { |
| 49 | 49 | }; |
| 50 | 50 | |
| 51 | 51 | pub const ExtraIndex = enum(u32) { |
| 52 | /// Ref. The main struct decl for this file. | |
| 53 | main_struct, | |
| 54 | 52 | /// If this is 0, no compile errors. Otherwise there is a `CompileErrors` |
| 55 | 53 | /// payload at this index. |
| 56 | 54 | compile_errors, |
| ... | ... | @@ -61,11 +59,6 @@ pub const ExtraIndex = enum(u32) { |
| 61 | 59 | _, |
| 62 | 60 | }; |
| 63 | 61 | |
| 64 | pub fn getMainStruct(zir: Zir) Inst.Index { | |
| 65 | return zir.extra[@enumToInt(ExtraIndex.main_struct)] - | |
| 66 | @intCast(u32, Inst.Ref.typed_value_map.len); | |
| 67 | } | |
| 68 | ||
| 69 | 62 | /// Returns the requested data, as well as the new index which is at the start of the |
| 70 | 63 | /// trailers for the object. |
| 71 | 64 | pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } { |
| ... | ... | @@ -77,6 +70,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en |
| 77 | 70 | u32 => code.extra[i], |
| 78 | 71 | Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]), |
| 79 | 72 | i32 => @bitCast(i32, code.extra[i]), |
| 73 | Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]), | |
| 80 | 74 | else => @compileError("bad field type"), |
| 81 | 75 | }; |
| 82 | 76 | i += 1; |
| ... | ... | @@ -112,50 +106,9 @@ pub fn deinit(code: *Zir, gpa: *Allocator) void { |
| 112 | 106 | code.* = undefined; |
| 113 | 107 | } |
| 114 | 108 | |
| 115 | /// Write human-readable, debug formatted ZIR code to a file. | |
| 116 | pub fn renderAsTextToFile( | |
| 117 | gpa: *Allocator, | |
| 118 | scope_file: *Module.Scope.File, | |
| 119 | fs_file: std.fs.File, | |
| 120 | ) !void { | |
| 121 | var arena = std.heap.ArenaAllocator.init(gpa); | |
| 122 | defer arena.deinit(); | |
| 123 | ||
| 124 | var writer: Writer = .{ | |
| 125 | .gpa = gpa, | |
| 126 | .arena = &arena.allocator, | |
| 127 | .file = scope_file, | |
| 128 | .code = scope_file.zir, | |
| 129 | .indent = 0, | |
| 130 | .parent_decl_node = 0, | |
| 131 | }; | |
| 132 | ||
| 133 | const main_struct_inst = scope_file.zir.getMainStruct(); | |
| 134 | try fs_file.writer().print("%{d} ", .{main_struct_inst}); | |
| 135 | try writer.writeInstToStream(fs_file.writer(), main_struct_inst); | |
| 136 | try fs_file.writeAll("\n"); | |
| 137 | const imports_index = scope_file.zir.extra[@enumToInt(ExtraIndex.imports)]; | |
| 138 | if (imports_index != 0) { | |
| 139 | try fs_file.writeAll("Imports:\n"); | |
| 140 | ||
| 141 | const extra = scope_file.zir.extraData(Inst.Imports, imports_index); | |
| 142 | var import_i: u32 = 0; | |
| 143 | var extra_index = extra.end; | |
| 144 | ||
| 145 | while (import_i < extra.data.imports_len) : (import_i += 1) { | |
| 146 | const item = scope_file.zir.extraData(Inst.Imports.Item, extra_index); | |
| 147 | extra_index = item.end; | |
| 148 | ||
| 149 | const src: LazySrcLoc = .{ .token_abs = item.data.token }; | |
| 150 | const import_path = scope_file.zir.nullTerminatedString(item.data.name); | |
| 151 | try fs_file.writer().print(" @import(\"{}\") ", .{ | |
| 152 | std.zig.fmtEscapes(import_path), | |
| 153 | }); | |
| 154 | try writer.writeSrc(fs_file.writer(), src); | |
| 155 | try fs_file.writer().writeAll("\n"); | |
| 156 | } | |
| 157 | } | |
| 158 | } | |
| 109 | /// ZIR is structured so that the outermost "main" struct of any file | |
| 110 | /// is always at index 0. | |
| 111 | pub const main_struct_inst: Inst.Index = 0; | |
| 159 | 112 | |
| 160 | 113 | /// These are untyped instructions generated from an Abstract Syntax Tree. |
| 161 | 114 | /// The data here is immutable because it is possible to have multiple |
| ... | ... | @@ -173,6 +126,64 @@ pub const Inst = struct { |
| 173 | 126 | /// Twos complement wrapping integer addition. |
| 174 | 127 | /// Uses the `pl_node` union field. Payload is `Bin`. |
| 175 | 128 | addwrap, |
| 129 | /// Saturating addition. | |
| 130 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 131 | add_sat, | |
| 132 | /// Arithmetic subtraction. Asserts no integer overflow. | |
| 133 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 134 | sub, | |
| 135 | /// Twos complement wrapping integer subtraction. | |
| 136 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 137 | subwrap, | |
| 138 | /// Saturating subtraction. | |
| 139 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 140 | sub_sat, | |
| 141 | /// Arithmetic multiplication. Asserts no integer overflow. | |
| 142 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 143 | mul, | |
| 144 | /// Twos complement wrapping integer multiplication. | |
| 145 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 146 | mulwrap, | |
| 147 | /// Saturating multiplication. | |
| 148 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 149 | mul_sat, | |
| 150 | /// Implements the `@divExact` builtin. | |
| 151 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 152 | div_exact, | |
| 153 | /// Implements the `@divFloor` builtin. | |
| 154 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 155 | div_floor, | |
| 156 | /// Implements the `@divTrunc` builtin. | |
| 157 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 158 | div_trunc, | |
| 159 | /// Implements the `@mod` builtin. | |
| 160 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 161 | mod, | |
| 162 | /// Implements the `@rem` builtin. | |
| 163 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 164 | rem, | |
| 165 | /// Ambiguously remainder division or modulus. If the computation would possibly have | |
| 166 | /// a different value depending on whether the operation is remainder division or modulus, | |
| 167 | /// a compile error is emitted. Otherwise the computation is performed. | |
| 168 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 169 | mod_rem, | |
| 170 | /// Integer shift-left. Zeroes are shifted in from the right hand side. | |
| 171 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 172 | shl, | |
| 173 | /// Implements the `@shlExact` builtin. | |
| 174 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 175 | shl_exact, | |
| 176 | /// Saturating shift-left. | |
| 177 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 178 | shl_sat, | |
| 179 | /// Integer shift-right. Arithmetic or logical depending on the signedness of | |
| 180 | /// the integer type. | |
| 181 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 182 | shr, | |
| 183 | /// Implements the `@shrExact` builtin. | |
| 184 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 185 | shr_exact, | |
| 186 | ||
| 176 | 187 | /// Declares a parameter of the current function. Used for: |
| 177 | 188 | /// * debug info |
| 178 | 189 | /// * checking shadowing against declarations in the current namespace |
| ... | ... | @@ -270,17 +281,9 @@ pub const Inst = struct { |
| 270 | 281 | break_inline, |
| 271 | 282 | /// Uses the `node` union field. |
| 272 | 283 | breakpoint, |
| 273 | /// Function call with modifier `.auto`. | |
| 284 | /// Function call. | |
| 274 | 285 | /// Uses `pl_node`. AST node is the function call. Payload is `Call`. |
| 275 | 286 | call, |
| 276 | /// Same as `call` but it also does `ensure_result_used` on the return value. | |
| 277 | call_chkused, | |
| 278 | /// Same as `call` but with modifier `.compile_time`. | |
| 279 | call_compile_time, | |
| 280 | /// Same as `call` but with modifier `.no_suspend`. | |
| 281 | call_nosuspend, | |
| 282 | /// Same as `call` but with modifier `.async_kw`. | |
| 283 | call_async, | |
| 284 | 287 | /// `<` |
| 285 | 288 | /// Uses the `pl_node` union field. Payload is `Bin`. |
| 286 | 289 | cmp_lt, |
| ... | ... | @@ -312,11 +315,6 @@ pub const Inst = struct { |
| 312 | 315 | /// only the taken branch is analyzed. The then block and else block must |
| 313 | 316 | /// terminate with an "inline" variant of a noreturn instruction. |
| 314 | 317 | condbr_inline, |
| 315 | /// An opaque type definition. Provides an AST node only. | |
| 316 | /// Uses the `pl_node` union field. Payload is `OpaqueDecl`. | |
| 317 | opaque_decl, | |
| 318 | opaque_decl_anon, | |
| 319 | opaque_decl_func, | |
| 320 | 318 | /// An error set type definition. Contains a list of field names. |
| 321 | 319 | /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`. |
| 322 | 320 | error_set_decl, |
| ... | ... | @@ -364,9 +362,13 @@ pub const Inst = struct { |
| 364 | 362 | /// `error.Foo` syntax. Uses the `str_tok` field of the Data union. |
| 365 | 363 | error_value, |
| 366 | 364 | /// Implements the `@export` builtin function, based on either an identifier to a Decl, |
| 367 | /// or field access of a Decl. | |
| 365 | /// or field access of a Decl. The thing being exported is the Decl. | |
| 368 | 366 | /// Uses the `pl_node` union field. Payload is `Export`. |
| 369 | 367 | @"export", |
| 368 | /// Implements the `@export` builtin function, based on a comptime-known value. | |
| 369 | /// The thing being exported is the comptime-known value which is the operand. | |
| 370 | /// Uses the `pl_node` union field. Payload is `ExportValue`. | |
| 371 | export_value, | |
| 370 | 372 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer |
| 371 | 373 | /// to the named field. The field name is stored in string_bytes. Used by a.b syntax. |
| 372 | 374 | /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. |
| ... | ... | @@ -376,6 +378,15 @@ pub const Inst = struct { |
| 376 | 378 | /// This instruction also accepts a pointer. |
| 377 | 379 | /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. |
| 378 | 380 | field_val, |
| 381 | /// Given a pointer to a struct or object that contains virtual fields, returns the | |
| 382 | /// named field. If there is no named field, searches in the type for a decl that | |
| 383 | /// matches the field name. The decl is resolved and we ensure that it's a function | |
| 384 | /// which can accept the object as the first parameter, with one pointer fixup. If | |
| 385 | /// all of that works, this instruction produces a special "bound function" value | |
| 386 | /// which contains both the function and the saved first parameter value. | |
| 387 | /// Bound functions may only be used as the function parameter to a `call` or | |
| 388 | /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler. | |
| 389 | field_call_bind, | |
| 379 | 390 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer |
| 380 | 391 | /// to the named field. The field name is a comptime instruction. Used by @field. |
| 381 | 392 | /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. |
| ... | ... | @@ -384,6 +395,15 @@ pub const Inst = struct { |
| 384 | 395 | /// The field name is a comptime instruction. Used by @field. |
| 385 | 396 | /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. |
| 386 | 397 | field_val_named, |
| 398 | /// Given a pointer to a struct or object that contains virtual fields, returns the | |
| 399 | /// named field. If there is no named field, searches in the type for a decl that | |
| 400 | /// matches the field name. The decl is resolved and we ensure that it's a function | |
| 401 | /// which can accept the object as the first parameter, with one pointer fixup. If | |
| 402 | /// all of that works, this instruction produces a special "bound function" value | |
| 403 | /// which contains both the function and the saved first parameter value. | |
| 404 | /// Bound functions may only be used as the function parameter to a `call` or | |
| 405 | /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler. | |
| 406 | field_call_bind_named, | |
| 387 | 407 | /// Returns a function type, or a function instance, depending on whether |
| 388 | 408 | /// the body_len is 0. Calling convention is auto. |
| 389 | 409 | /// Uses the `pl_node` union field. `payload_index` points to a `Func`. |
| ... | ... | @@ -433,25 +453,6 @@ pub const Inst = struct { |
| 433 | 453 | /// Merge two error sets into one, `E1 || E2`. |
| 434 | 454 | /// Uses the `pl_node` field with payload `Bin`. |
| 435 | 455 | merge_error_sets, |
| 436 | /// Ambiguously remainder division or modulus. If the computation would possibly have | |
| 437 | /// a different value depending on whether the operation is remainder division or modulus, | |
| 438 | /// a compile error is emitted. Otherwise the computation is performed. | |
| 439 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 440 | mod_rem, | |
| 441 | /// Arithmetic multiplication. Asserts no integer overflow. | |
| 442 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 443 | mul, | |
| 444 | /// Twos complement wrapping integer multiplication. | |
| 445 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 446 | mulwrap, | |
| 447 | /// Given a reference to a function and a parameter index, returns the | |
| 448 | /// type of the parameter. The only usage of this instruction is for the | |
| 449 | /// result location of parameters of function calls. In the case of a function's | |
| 450 | /// parameter type being `anytype`, it is the type coercion's job to detect this | |
| 451 | /// scenario and skip the coercion, so that semantic analysis of this instruction | |
| 452 | /// is not in a position where it must create an invalid type. | |
| 453 | /// Uses the `param_type` union field. | |
| 454 | param_type, | |
| 455 | 456 | /// Turns an R-Value into a const L-Value. In other words, it takes a value, |
| 456 | 457 | /// stores it in a memory location, and returns a const pointer to it. If the value |
| 457 | 458 | /// is `comptime`, the memory location is global static constant data. Otherwise, |
| ... | ... | @@ -488,10 +489,10 @@ pub const Inst = struct { |
| 488 | 489 | /// this instruction; a following 'ret' instruction will do the diversion. |
| 489 | 490 | /// Uses the `str_tok` union field. |
| 490 | 491 | ret_err_value_code, |
| 491 | /// Create a pointer type that does not have a sentinel, alignment, or bit range specified. | |
| 492 | /// Create a pointer type that does not have a sentinel, alignment, address space, or bit range specified. | |
| 492 | 493 | /// Uses the `ptr_type_simple` union field. |
| 493 | 494 | ptr_type_simple, |
| 494 | /// Create a pointer type which can have a sentinel, alignment, and/or bit range. | |
| 495 | /// Create a pointer type which can have a sentinel, alignment, address space, and/or bit range. | |
| 495 | 496 | /// Uses the `ptr_type` union field. |
| 496 | 497 | ptr_type, |
| 497 | 498 | /// Slice operation `lhs[rhs..]`. No sentinel and no end offset. |
| ... | ... | @@ -528,12 +529,6 @@ pub const Inst = struct { |
| 528 | 529 | /// String Literal. Makes an anonymous Decl and then takes a pointer to it. |
| 529 | 530 | /// Uses the `str` union field. |
| 530 | 531 | str, |
| 531 | /// Arithmetic subtraction. Asserts no integer overflow. | |
| 532 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 533 | sub, | |
| 534 | /// Twos complement wrapping integer subtraction. | |
| 535 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 536 | subwrap, | |
| 537 | 532 | /// Arithmetic negation. Asserts no integer overflow. |
| 538 | 533 | /// Same as sub with a lhs of 0, split into a separate instruction to save memory. |
| 539 | 534 | /// Uses `un_node`. |
| ... | ... | @@ -731,7 +726,7 @@ pub const Inst = struct { |
| 731 | 726 | size_of, |
| 732 | 727 | /// Implements the `@bitSizeOf` builtin. Uses `un_node`. |
| 733 | 728 | bit_size_of, |
| 734 | /// Implements the `@fence` builtin. Uses `node`. | |
| 729 | /// Implements the `@fence` builtin. Uses `un_node`. | |
| 735 | 730 | fence, |
| 736 | 731 | |
| 737 | 732 | /// Implement builtin `@ptrToInt`. Uses `un_node`. |
| ... | ... | @@ -859,35 +854,6 @@ pub const Inst = struct { |
| 859 | 854 | /// Implements the `@bitReverse` builtin. Uses the `un_node` union field. |
| 860 | 855 | bit_reverse, |
| 861 | 856 | |
| 862 | /// Implements the `@divExact` builtin. | |
| 863 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 864 | div_exact, | |
| 865 | /// Implements the `@divFloor` builtin. | |
| 866 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 867 | div_floor, | |
| 868 | /// Implements the `@divTrunc` builtin. | |
| 869 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 870 | div_trunc, | |
| 871 | /// Implements the `@mod` builtin. | |
| 872 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 873 | mod, | |
| 874 | /// Implements the `@rem` builtin. | |
| 875 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 876 | rem, | |
| 877 | ||
| 878 | /// Integer shift-left. Zeroes are shifted in from the right hand side. | |
| 879 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 880 | shl, | |
| 881 | /// Implements the `@shlExact` builtin. | |
| 882 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 883 | shl_exact, | |
| 884 | /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type. | |
| 885 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 886 | shr, | |
| 887 | /// Implements the `@shrExact` builtin. | |
| 888 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 889 | shr_exact, | |
| 890 | ||
| 891 | 857 | /// Implements the `@bitOffsetOf` builtin. |
| 892 | 858 | /// Uses the `pl_node` union field with payload `Bin`. |
| 893 | 859 | bit_offset_of, |
| ... | ... | @@ -982,6 +948,17 @@ pub const Inst = struct { |
| 982 | 948 | @"await", |
| 983 | 949 | await_nosuspend, |
| 984 | 950 | |
| 951 | /// When a type or function refers to a comptime value from an outer | |
| 952 | /// scope, that forms a closure over comptime value. The outer scope | |
| 953 | /// will record a capture of that value, which encodes its current state | |
| 954 | /// and marks it to persist. Uses `un_tok` field. Operand is the | |
| 955 | /// instruction value to capture. | |
| 956 | closure_capture, | |
| 957 | /// The inner scope of a closure uses closure_get to retrieve the value | |
| 958 | /// stored by the outer scope. Uses `inst_node` field. Operand is the | |
| 959 | /// closure_capture instruction ref. | |
| 960 | closure_get, | |
| 961 | ||
| 985 | 962 | /// The ZIR instruction tag is one of the `Extended` ones. |
| 986 | 963 | /// Uses the `extended` union field. |
| 987 | 964 | extended, |
| ... | ... | @@ -996,6 +973,7 @@ pub const Inst = struct { |
| 996 | 973 | .param_anytype_comptime, |
| 997 | 974 | .add, |
| 998 | 975 | .addwrap, |
| 976 | .add_sat, | |
| 999 | 977 | .alloc, |
| 1000 | 978 | .alloc_mut, |
| 1001 | 979 | .alloc_comptime, |
| ... | ... | @@ -1026,10 +1004,6 @@ pub const Inst = struct { |
| 1026 | 1004 | .breakpoint, |
| 1027 | 1005 | .fence, |
| 1028 | 1006 | .call, |
| 1029 | .call_chkused, | |
| 1030 | .call_compile_time, | |
| 1031 | .call_nosuspend, | |
| 1032 | .call_async, | |
| 1033 | 1007 | .cmp_lt, |
| 1034 | 1008 | .cmp_lte, |
| 1035 | 1009 | .cmp_eq, |
| ... | ... | @@ -1037,9 +1011,6 @@ pub const Inst = struct { |
| 1037 | 1011 | .cmp_gt, |
| 1038 | 1012 | .cmp_neq, |
| 1039 | 1013 | .coerce_result_ptr, |
| 1040 | .opaque_decl, | |
| 1041 | .opaque_decl_anon, | |
| 1042 | .opaque_decl_func, | |
| 1043 | 1014 | .error_set_decl, |
| 1044 | 1015 | .error_set_decl_anon, |
| 1045 | 1016 | .error_set_decl_func, |
| ... | ... | @@ -1055,10 +1026,13 @@ pub const Inst = struct { |
| 1055 | 1026 | .ensure_result_used, |
| 1056 | 1027 | .ensure_result_non_error, |
| 1057 | 1028 | .@"export", |
| 1029 | .export_value, | |
| 1058 | 1030 | .field_ptr, |
| 1059 | 1031 | .field_val, |
| 1032 | .field_call_bind, | |
| 1060 | 1033 | .field_ptr_named, |
| 1061 | 1034 | .field_val_named, |
| 1035 | .field_call_bind_named, | |
| 1062 | 1036 | .func, |
| 1063 | 1037 | .func_inferred, |
| 1064 | 1038 | .has_decl, |
| ... | ... | @@ -1074,9 +1048,10 @@ pub const Inst = struct { |
| 1074 | 1048 | .mod_rem, |
| 1075 | 1049 | .mul, |
| 1076 | 1050 | .mulwrap, |
| 1077 | .param_type, | |
| 1051 | .mul_sat, | |
| 1078 | 1052 | .ref, |
| 1079 | 1053 | .shl, |
| 1054 | .shl_sat, | |
| 1080 | 1055 | .shr, |
| 1081 | 1056 | .store, |
| 1082 | 1057 | .store_node, |
| ... | ... | @@ -1085,6 +1060,7 @@ pub const Inst = struct { |
| 1085 | 1060 | .str, |
| 1086 | 1061 | .sub, |
| 1087 | 1062 | .subwrap, |
| 1063 | .sub_sat, | |
| 1088 | 1064 | .negate, |
| 1089 | 1065 | .negate_wrap, |
| 1090 | 1066 | .typeof, |
| ... | ... | @@ -1231,6 +1207,8 @@ pub const Inst = struct { |
| 1231 | 1207 | .await_nosuspend, |
| 1232 | 1208 | .ret_err_value_code, |
| 1233 | 1209 | .extended, |
| 1210 | .closure_get, | |
| 1211 | .closure_capture, | |
| 1234 | 1212 | => false, |
| 1235 | 1213 | |
| 1236 | 1214 | .@"break", |
| ... | ... | @@ -1256,6 +1234,14 @@ pub const Inst = struct { |
| 1256 | 1234 | break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{ |
| 1257 | 1235 | .add = .pl_node, |
| 1258 | 1236 | .addwrap = .pl_node, |
| 1237 | .add_sat = .pl_node, | |
| 1238 | .sub = .pl_node, | |
| 1239 | .subwrap = .pl_node, | |
| 1240 | .sub_sat = .pl_node, | |
| 1241 | .mul = .pl_node, | |
| 1242 | .mulwrap = .pl_node, | |
| 1243 | .mul_sat = .pl_node, | |
| 1244 | ||
| 1259 | 1245 | .param = .pl_tok, |
| 1260 | 1246 | .param_comptime = .pl_tok, |
| 1261 | 1247 | .param_anytype = .str_tok, |
| ... | ... | @@ -1285,10 +1271,6 @@ pub const Inst = struct { |
| 1285 | 1271 | .break_inline = .@"break", |
| 1286 | 1272 | .breakpoint = .node, |
| 1287 | 1273 | .call = .pl_node, |
| 1288 | .call_chkused = .pl_node, | |
| 1289 | .call_compile_time = .pl_node, | |
| 1290 | .call_nosuspend = .pl_node, | |
| 1291 | .call_async = .pl_node, | |
| 1292 | 1274 | .cmp_lt = .pl_node, |
| 1293 | 1275 | .cmp_lte = .pl_node, |
| 1294 | 1276 | .cmp_eq = .pl_node, |
| ... | ... | @@ -1298,9 +1280,6 @@ pub const Inst = struct { |
| 1298 | 1280 | .coerce_result_ptr = .bin, |
| 1299 | 1281 | .condbr = .pl_node, |
| 1300 | 1282 | .condbr_inline = .pl_node, |
| 1301 | .opaque_decl = .pl_node, | |
| 1302 | .opaque_decl_anon = .pl_node, | |
| 1303 | .opaque_decl_func = .pl_node, | |
| 1304 | 1283 | .error_set_decl = .pl_node, |
| 1305 | 1284 | .error_set_decl_anon = .pl_node, |
| 1306 | 1285 | .error_set_decl_func = .pl_node, |
| ... | ... | @@ -1318,10 +1297,13 @@ pub const Inst = struct { |
| 1318 | 1297 | .error_union_type = .pl_node, |
| 1319 | 1298 | .error_value = .str_tok, |
| 1320 | 1299 | .@"export" = .pl_node, |
| 1300 | .export_value = .pl_node, | |
| 1321 | 1301 | .field_ptr = .pl_node, |
| 1322 | 1302 | .field_val = .pl_node, |
| 1323 | 1303 | .field_ptr_named = .pl_node, |
| 1324 | 1304 | .field_val_named = .pl_node, |
| 1305 | .field_call_bind = .pl_node, | |
| 1306 | .field_call_bind_named = .pl_node, | |
| 1325 | 1307 | .func = .pl_node, |
| 1326 | 1308 | .func_inferred = .pl_node, |
| 1327 | 1309 | .import = .str_tok, |
| ... | ... | @@ -1339,9 +1321,6 @@ pub const Inst = struct { |
| 1339 | 1321 | .repeat_inline = .node, |
| 1340 | 1322 | .merge_error_sets = .pl_node, |
| 1341 | 1323 | .mod_rem = .pl_node, |
| 1342 | .mul = .pl_node, | |
| 1343 | .mulwrap = .pl_node, | |
| 1344 | .param_type = .param_type, | |
| 1345 | 1324 | .ref = .un_tok, |
| 1346 | 1325 | .ret_node = .un_node, |
| 1347 | 1326 | .ret_load = .un_node, |
| ... | ... | @@ -1358,8 +1337,6 @@ pub const Inst = struct { |
| 1358 | 1337 | .store_to_block_ptr = .bin, |
| 1359 | 1338 | .store_to_inferred_ptr = .bin, |
| 1360 | 1339 | .str = .str, |
| 1361 | .sub = .pl_node, | |
| 1362 | .subwrap = .pl_node, | |
| 1363 | 1340 | .negate = .un_node, |
| 1364 | 1341 | .negate_wrap = .un_node, |
| 1365 | 1342 | .typeof = .un_node, |
| ... | ... | @@ -1416,7 +1393,7 @@ pub const Inst = struct { |
| 1416 | 1393 | .type_info = .un_node, |
| 1417 | 1394 | .size_of = .un_node, |
| 1418 | 1395 | .bit_size_of = .un_node, |
| 1419 | .fence = .node, | |
| 1396 | .fence = .un_node, | |
| 1420 | 1397 | |
| 1421 | 1398 | .ptr_to_int = .un_node, |
| 1422 | 1399 | .error_to_int = .un_node, |
| ... | ... | @@ -1480,6 +1457,7 @@ pub const Inst = struct { |
| 1480 | 1457 | |
| 1481 | 1458 | .shl = .pl_node, |
| 1482 | 1459 | .shl_exact = .pl_node, |
| 1460 | .shl_sat = .pl_node, | |
| 1483 | 1461 | .shr = .pl_node, |
| 1484 | 1462 | .shr_exact = .pl_node, |
| 1485 | 1463 | |
| ... | ... | @@ -1517,6 +1495,9 @@ pub const Inst = struct { |
| 1517 | 1495 | .@"await" = .un_node, |
| 1518 | 1496 | .await_nosuspend = .un_node, |
| 1519 | 1497 | |
| 1498 | .closure_capture = .un_tok, | |
| 1499 | .closure_get = .inst_node, | |
| 1500 | ||
| 1520 | 1501 | .extended = .extended, |
| 1521 | 1502 | }); |
| 1522 | 1503 | }; |
| ... | ... | @@ -1549,6 +1530,10 @@ pub const Inst = struct { |
| 1549 | 1530 | /// `operand` is payload index to `UnionDecl`. |
| 1550 | 1531 | /// `small` is `UnionDecl.Small`. |
| 1551 | 1532 | union_decl, |
| 1533 | /// An opaque type definition. Contains references to decls and captures. | |
| 1534 | /// `operand` is payload index to `OpaqueDecl`. | |
| 1535 | /// `small` is `OpaqueDecl.Small`. | |
| 1536 | opaque_decl, | |
| 1552 | 1537 | /// Obtains a pointer to the return value. |
| 1553 | 1538 | /// `operand` is `src_node: i32`. |
| 1554 | 1539 | ret_ptr, |
| ... | ... | @@ -1629,22 +1614,6 @@ pub const Inst = struct { |
| 1629 | 1614 | wasm_memory_size, |
| 1630 | 1615 | /// `operand` is payload index to `BinNode`. |
| 1631 | 1616 | wasm_memory_grow, |
| 1632 | /// Implements the `@addWithSaturation` builtin. | |
| 1633 | /// `operand` is payload index to `SaturatingArithmetic`. | |
| 1634 | /// `small` is unused. | |
| 1635 | add_with_saturation, | |
| 1636 | /// Implements the `@subWithSaturation` builtin. | |
| 1637 | /// `operand` is payload index to `SaturatingArithmetic`. | |
| 1638 | /// `small` is unused. | |
| 1639 | sub_with_saturation, | |
| 1640 | /// Implements the `@mulWithSaturation` builtin. | |
| 1641 | /// `operand` is payload index to `SaturatingArithmetic`. | |
| 1642 | /// `small` is unused. | |
| 1643 | mul_with_saturation, | |
| 1644 | /// Implements the `@shlWithSaturation` builtin. | |
| 1645 | /// `operand` is payload index to `SaturatingArithmetic`. | |
| 1646 | /// `small` is unused. | |
| 1647 | shl_with_saturation, | |
| 1648 | 1617 | |
| 1649 | 1618 | pub const InstData = struct { |
| 1650 | 1619 | opcode: Extended, |
| ... | ... | @@ -1717,6 +1686,7 @@ pub const Inst = struct { |
| 1717 | 1686 | atomic_order_type, |
| 1718 | 1687 | atomic_rmw_op_type, |
| 1719 | 1688 | calling_convention_type, |
| 1689 | address_space_type, | |
| 1720 | 1690 | float_mode_type, |
| 1721 | 1691 | reduce_op_type, |
| 1722 | 1692 | call_options_type, |
| ... | ... | @@ -1973,6 +1943,10 @@ pub const Inst = struct { |
| 1973 | 1943 | .ty = Type.initTag(.type), |
| 1974 | 1944 | .val = Value.initTag(.calling_convention_type), |
| 1975 | 1945 | }, |
| 1946 | .address_space_type = .{ | |
| 1947 | .ty = Type.initTag(.type), | |
| 1948 | .val = Value.initTag(.address_space_type), | |
| 1949 | }, | |
| 1976 | 1950 | .float_mode_type = .{ |
| 1977 | 1951 | .ty = Type.initTag(.type), |
| 1978 | 1952 | .val = Value.initTag(.float_mode_type), |
| ... | ... | @@ -2174,8 +2148,9 @@ pub const Inst = struct { |
| 2174 | 2148 | is_volatile: bool, |
| 2175 | 2149 | has_sentinel: bool, |
| 2176 | 2150 | has_align: bool, |
| 2151 | has_addrspace: bool, | |
| 2177 | 2152 | has_bit_range: bool, |
| 2178 | _: u2 = undefined, | |
| 2153 | _: u1 = undefined, | |
| 2179 | 2154 | }, |
| 2180 | 2155 | size: std.builtin.TypeInfo.Pointer.Size, |
| 2181 | 2156 | /// Index into extra. See `PtrType`. |
| ... | ... | @@ -2197,10 +2172,6 @@ pub const Inst = struct { |
| 2197 | 2172 | /// Points to a `Block`. |
| 2198 | 2173 | payload_index: u32, |
| 2199 | 2174 | }, |
| 2200 | param_type: struct { | |
| 2201 | callee: Ref, | |
| 2202 | param_index: u32, | |
| 2203 | }, | |
| 2204 | 2175 | @"unreachable": struct { |
| 2205 | 2176 | /// Offset from Decl AST node index. |
| 2206 | 2177 | /// `Tag` determines which kind of AST node this points to. |
| ... | ... | @@ -2227,6 +2198,18 @@ pub const Inst = struct { |
| 2227 | 2198 | line: u32, |
| 2228 | 2199 | column: u32, |
| 2229 | 2200 | }, |
| 2201 | /// Used for unary operators which reference an inst, | |
| 2202 | /// with an AST node source location. | |
| 2203 | inst_node: struct { | |
| 2204 | /// Offset from Decl AST node index. | |
| 2205 | src_node: i32, | |
| 2206 | /// The meaning of this operand depends on the corresponding `Tag`. | |
| 2207 | inst: Index, | |
| 2208 | ||
| 2209 | pub fn src(self: @This()) LazySrcLoc { | |
| 2210 | return .{ .node_offset = self.src_node }; | |
| 2211 | } | |
| 2212 | }, | |
| 2230 | 2213 | |
| 2231 | 2214 | // Make sure we don't accidentally add a field to make this union |
| 2232 | 2215 | // bigger than expected. Note that in Debug builds, Zig is allowed |
| ... | ... | @@ -2259,11 +2242,11 @@ pub const Inst = struct { |
| 2259 | 2242 | ptr_type, |
| 2260 | 2243 | int_type, |
| 2261 | 2244 | bool_br, |
| 2262 | param_type, | |
| 2263 | 2245 | @"unreachable", |
| 2264 | 2246 | @"break", |
| 2265 | 2247 | switch_capture, |
| 2266 | 2248 | dbg_stmt, |
| 2249 | inst_node, | |
| 2267 | 2250 | }; |
| 2268 | 2251 | }; |
| 2269 | 2252 | |
| ... | ... | @@ -2386,8 +2369,27 @@ pub const Inst = struct { |
| 2386 | 2369 | /// Stored inside extra, with trailing arguments according to `args_len`. |
| 2387 | 2370 | /// Each argument is a `Ref`. |
| 2388 | 2371 | pub const Call = struct { |
| 2372 | // Note: Flags *must* come first so that unusedResultExpr | |
| 2373 | // can find it when it goes to modify them. | |
| 2374 | flags: Flags, | |
| 2389 | 2375 | callee: Ref, |
| 2390 | args_len: u32, | |
| 2376 | ||
| 2377 | pub const Flags = packed struct { | |
| 2378 | /// std.builtin.CallOptions.Modifier in packed form | |
| 2379 | pub const PackedModifier = u3; | |
| 2380 | pub const PackedArgsLen = u28; | |
| 2381 | ||
| 2382 | packed_modifier: PackedModifier, | |
| 2383 | ensure_result_used: bool = false, | |
| 2384 | args_len: PackedArgsLen, | |
| 2385 | ||
| 2386 | comptime { | |
| 2387 | if (@sizeOf(Flags) != 4 or @bitSizeOf(Flags) != 32) | |
| 2388 | @compileError("Layout of Call.Flags needs to be updated!"); | |
| 2389 | if (@bitSizeOf(std.builtin.CallOptions.Modifier) != @bitSizeOf(PackedModifier)) | |
| 2390 | @compileError("Call.Flags.PackedModifier needs to be updated!"); | |
| 2391 | } | |
| 2392 | }; | |
| 2391 | 2393 | }; |
| 2392 | 2394 | |
| 2393 | 2395 | pub const BuiltinCall = struct { |
| ... | ... | @@ -2405,12 +2407,13 @@ pub const Inst = struct { |
| 2405 | 2407 | else_body_len: u32, |
| 2406 | 2408 | }; |
| 2407 | 2409 | |
| 2408 | /// Stored in extra. Depending on the flags in Data, there will be up to 4 | |
| 2410 | /// Stored in extra. Depending on the flags in Data, there will be up to 5 | |
| 2409 | 2411 | /// trailing Ref fields: |
| 2410 | 2412 | /// 0. sentinel: Ref // if `has_sentinel` flag is set |
| 2411 | 2413 | /// 1. align: Ref // if `has_align` flag is set |
| 2412 | /// 2. bit_start: Ref // if `has_bit_range` flag is set | |
| 2413 | /// 3. bit_end: Ref // if `has_bit_range` flag is set | |
| 2414 | /// 2. address_space: Ref // if `has_addrspace` flag is set | |
| 2415 | /// 3. bit_start: Ref // if `has_bit_range` flag is set | |
| 2416 | /// 4. bit_end: Ref // if `has_bit_range` flag is set | |
| 2414 | 2417 | pub const PtrType = struct { |
| 2415 | 2418 | elem_type: Ref, |
| 2416 | 2419 | }; |
| ... | ... | @@ -2528,7 +2531,7 @@ pub const Inst = struct { |
| 2528 | 2531 | /// 0b000X: whether corresponding decl is pub |
| 2529 | 2532 | /// 0b00X0: whether corresponding decl is exported |
| 2530 | 2533 | /// 0b0X00: whether corresponding decl has an align expression |
| 2531 | /// 0bX000: whether corresponding decl has a linksection expression | |
| 2534 | /// 0bX000: whether corresponding decl has a linksection or an address space expression | |
| 2532 | 2535 | /// 5. decl: { // for every decls_len |
| 2533 | 2536 | /// src_hash: [4]u32, // hash of source bytes |
| 2534 | 2537 | /// line: u32, // line number of decl, relative to parent |
| ... | ... | @@ -2540,7 +2543,10 @@ pub const Inst = struct { |
| 2540 | 2543 | /// this is a test decl, and the name starts at `name+1`. |
| 2541 | 2544 | /// value: Index, |
| 2542 | 2545 | /// align: Ref, // if corresponding bit is set |
| 2543 | /// link_section: Ref, // if corresponding bit is set | |
| 2546 | /// link_section_or_address_space: { // if corresponding bit is set. | |
| 2547 | /// link_section: Ref, | |
| 2548 | /// address_space: Ref, | |
| 2549 | /// } | |
| 2544 | 2550 | /// } |
| 2545 | 2551 | /// 6. inst: Index // for every body_len |
| 2546 | 2552 | /// 7. flags: u32 // for every 8 fields |
| ... | ... | @@ -2592,7 +2598,7 @@ pub const Inst = struct { |
| 2592 | 2598 | /// 0b000X: whether corresponding decl is pub |
| 2593 | 2599 | /// 0b00X0: whether corresponding decl is exported |
| 2594 | 2600 | /// 0b0X00: whether corresponding decl has an align expression |
| 2595 | /// 0bX000: whether corresponding decl has a linksection expression | |
| 2601 | /// 0bX000: whether corresponding decl has a linksection or an address space expression | |
| 2596 | 2602 | /// 6. decl: { // for every decls_len |
| 2597 | 2603 | /// src_hash: [4]u32, // hash of source bytes |
| 2598 | 2604 | /// line: u32, // line number of decl, relative to parent |
| ... | ... | @@ -2604,7 +2610,10 @@ pub const Inst = struct { |
| 2604 | 2610 | /// this is a test decl, and the name starts at `name+1`. |
| 2605 | 2611 | /// value: Index, |
| 2606 | 2612 | /// align: Ref, // if corresponding bit is set |
| 2607 | /// link_section: Ref, // if corresponding bit is set | |
| 2613 | /// link_section_or_address_space: { // if corresponding bit is set. | |
| 2614 | /// link_section: Ref, | |
| 2615 | /// address_space: Ref, | |
| 2616 | /// } | |
| 2608 | 2617 | /// } |
| 2609 | 2618 | /// 7. inst: Index // for every body_len |
| 2610 | 2619 | /// 8. has_bits: u32 // for every 32 fields |
| ... | ... | @@ -2637,7 +2646,7 @@ pub const Inst = struct { |
| 2637 | 2646 | /// 0b000X: whether corresponding decl is pub |
| 2638 | 2647 | /// 0b00X0: whether corresponding decl is exported |
| 2639 | 2648 | /// 0b0X00: whether corresponding decl has an align expression |
| 2640 | /// 0bX000: whether corresponding decl has a linksection expression | |
| 2649 | /// 0bX000: whether corresponding decl has a linksection or an address space expression | |
| 2641 | 2650 | /// 6. decl: { // for every decls_len |
| 2642 | 2651 | /// src_hash: [4]u32, // hash of source bytes |
| 2643 | 2652 | /// line: u32, // line number of decl, relative to parent |
| ... | ... | @@ -2649,7 +2658,10 @@ pub const Inst = struct { |
| 2649 | 2658 | /// this is a test decl, and the name starts at `name+1`. |
| 2650 | 2659 | /// value: Index, |
| 2651 | 2660 | /// align: Ref, // if corresponding bit is set |
| 2652 | /// link_section: Ref, // if corresponding bit is set | |
| 2661 | /// link_section_or_address_space: { // if corresponding bit is set. | |
| 2662 | /// link_section: Ref, | |
| 2663 | /// address_space: Ref, | |
| 2664 | /// } | |
| 2653 | 2665 | /// } |
| 2654 | 2666 | /// 7. inst: Index // for every body_len |
| 2655 | 2667 | /// 8. has_bits: u32 // for every 8 fields |
| ... | ... | @@ -2673,21 +2685,27 @@ pub const Inst = struct { |
| 2673 | 2685 | has_decls_len: bool, |
| 2674 | 2686 | name_strategy: NameStrategy, |
| 2675 | 2687 | layout: std.builtin.TypeInfo.ContainerLayout, |
| 2676 | /// false: union(tag_type) | |
| 2677 | /// true: union(enum(tag_type)) | |
| 2688 | /// has_tag_type | auto_enum_tag | result | |
| 2689 | /// ------------------------------------- | |
| 2690 | /// false | false | union { } | |
| 2691 | /// false | true | union(enum) { } | |
| 2692 | /// true | true | union(enum(T)) { } | |
| 2693 | /// true | false | union(T) { } | |
| 2678 | 2694 | auto_enum_tag: bool, |
| 2679 | 2695 | _: u6 = undefined, |
| 2680 | 2696 | }; |
| 2681 | 2697 | }; |
| 2682 | 2698 | |
| 2683 | 2699 | /// Trailing: |
| 2684 | /// 0. decl_bits: u32 // for every 8 decls | |
| 2700 | /// 0. src_node: i32, // if has_src_node | |
| 2701 | /// 1. decls_len: u32, // if has_decls_len | |
| 2702 | /// 2. decl_bits: u32 // for every 8 decls | |
| 2685 | 2703 | /// - sets of 4 bits: |
| 2686 | 2704 | /// 0b000X: whether corresponding decl is pub |
| 2687 | 2705 | /// 0b00X0: whether corresponding decl is exported |
| 2688 | 2706 | /// 0b0X00: whether corresponding decl has an align expression |
| 2689 | /// 0bX000: whether corresponding decl has a linksection expression | |
| 2690 | /// 1. decl: { // for every decls_len | |
| 2707 | /// 0bX000: whether corresponding decl has a linksection or an address space expression | |
| 2708 | /// 3. decl: { // for every decls_len | |
| 2691 | 2709 | /// src_hash: [4]u32, // hash of source bytes |
| 2692 | 2710 | /// line: u32, // line number of decl, relative to parent |
| 2693 | 2711 | /// name: u32, // null terminated string index |
| ... | ... | @@ -2698,10 +2716,18 @@ pub const Inst = struct { |
| 2698 | 2716 | /// this is a test decl, and the name starts at `name+1`. |
| 2699 | 2717 | /// value: Index, |
| 2700 | 2718 | /// align: Ref, // if corresponding bit is set |
| 2701 | /// link_section: Ref, // if corresponding bit is set | |
| 2719 | /// link_section_or_address_space: { // if corresponding bit is set. | |
| 2720 | /// link_section: Ref, | |
| 2721 | /// address_space: Ref, | |
| 2722 | /// } | |
| 2702 | 2723 | /// } |
| 2703 | 2724 | pub const OpaqueDecl = struct { |
| 2704 | decls_len: u32, | |
| 2725 | pub const Small = packed struct { | |
| 2726 | has_src_node: bool, | |
| 2727 | has_decls_len: bool, | |
| 2728 | name_strategy: NameStrategy, | |
| 2729 | _: u12 = undefined, | |
| 2730 | }; | |
| 2705 | 2731 | }; |
| 2706 | 2732 | |
| 2707 | 2733 | /// Trailing: field_name: u32 // for every field: null terminated string index |
| ... | ... | @@ -2767,12 +2793,6 @@ pub const Inst = struct { |
| 2767 | 2793 | ptr: Ref, |
| 2768 | 2794 | }; |
| 2769 | 2795 | |
| 2770 | pub const SaturatingArithmetic = struct { | |
| 2771 | node: i32, | |
| 2772 | lhs: Ref, | |
| 2773 | rhs: Ref, | |
| 2774 | }; | |
| 2775 | ||
| 2776 | 2796 | pub const Cmpxchg = struct { |
| 2777 | 2797 | ptr: Ref, |
| 2778 | 2798 | expected_value: Ref, |
| ... | ... | @@ -2858,6 +2878,14 @@ pub const Inst = struct { |
| 2858 | 2878 | /// 1. align_inst: Ref, // if small 0b00X0 is set |
| 2859 | 2879 | pub const AllocExtended = struct { |
| 2860 | 2880 | src_node: i32, |
| 2881 | ||
| 2882 | pub const Small = packed struct { | |
| 2883 | has_type: bool, | |
| 2884 | has_align: bool, | |
| 2885 | is_const: bool, | |
| 2886 | is_comptime: bool, | |
| 2887 | _: u12 = undefined, | |
| 2888 | }; | |
| 2861 | 2889 | }; |
| 2862 | 2890 | |
| 2863 | 2891 | pub const Export = struct { |
| ... | ... | @@ -2869,6 +2897,12 @@ pub const Inst = struct { |
| 2869 | 2897 | options: Ref, |
| 2870 | 2898 | }; |
| 2871 | 2899 | |
| 2900 | pub const ExportValue = struct { | |
| 2901 | /// The comptime value to export. | |
| 2902 | operand: Ref, | |
| 2903 | options: Ref, | |
| 2904 | }; | |
| 2905 | ||
| 2872 | 2906 | /// Trailing: `CompileErrors.Item` for each `items_len`. |
| 2873 | 2907 | pub const CompileErrors = struct { |
| 2874 | 2908 | items_len: u32, |
| ... | ... | @@ -2904,1794 +2938,6 @@ pub const Inst = struct { |
| 2904 | 2938 | |
| 2905 | 2939 | pub const SpecialProng = enum { none, @"else", under }; |
| 2906 | 2940 | |
| 2907 | const Writer = struct { | |
| 2908 | gpa: *Allocator, | |
| 2909 | arena: *Allocator, | |
| 2910 | file: *Module.Scope.File, | |
| 2911 | code: Zir, | |
| 2912 | indent: u32, | |
| 2913 | parent_decl_node: u32, | |
| 2914 | ||
| 2915 | fn relativeToNodeIndex(self: *Writer, offset: i32) Ast.Node.Index { | |
| 2916 | return @bitCast(Ast.Node.Index, offset + @bitCast(i32, self.parent_decl_node)); | |
| 2917 | } | |
| 2918 | ||
| 2919 | fn writeInstToStream( | |
| 2920 | self: *Writer, | |
| 2921 | stream: anytype, | |
| 2922 | inst: Inst.Index, | |
| 2923 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 2924 | const tags = self.code.instructions.items(.tag); | |
| 2925 | const tag = tags[inst]; | |
| 2926 | try stream.print("= {s}(", .{@tagName(tags[inst])}); | |
| 2927 | switch (tag) { | |
| 2928 | .array_type, | |
| 2929 | .as, | |
| 2930 | .coerce_result_ptr, | |
| 2931 | .elem_ptr, | |
| 2932 | .elem_val, | |
| 2933 | .store, | |
| 2934 | .store_to_block_ptr, | |
| 2935 | .store_to_inferred_ptr, | |
| 2936 | .field_ptr_type, | |
| 2937 | => try self.writeBin(stream, inst), | |
| 2938 | ||
| 2939 | .alloc, | |
| 2940 | .alloc_mut, | |
| 2941 | .alloc_comptime, | |
| 2942 | .indexable_ptr_len, | |
| 2943 | .anyframe_type, | |
| 2944 | .bit_not, | |
| 2945 | .bool_not, | |
| 2946 | .negate, | |
| 2947 | .negate_wrap, | |
| 2948 | .load, | |
| 2949 | .ensure_result_used, | |
| 2950 | .ensure_result_non_error, | |
| 2951 | .ret_node, | |
| 2952 | .ret_load, | |
| 2953 | .resolve_inferred_alloc, | |
| 2954 | .optional_type, | |
| 2955 | .optional_payload_safe, | |
| 2956 | .optional_payload_unsafe, | |
| 2957 | .optional_payload_safe_ptr, | |
| 2958 | .optional_payload_unsafe_ptr, | |
| 2959 | .err_union_payload_safe, | |
| 2960 | .err_union_payload_unsafe, | |
| 2961 | .err_union_payload_safe_ptr, | |
| 2962 | .err_union_payload_unsafe_ptr, | |
| 2963 | .err_union_code, | |
| 2964 | .err_union_code_ptr, | |
| 2965 | .is_non_null, | |
| 2966 | .is_non_null_ptr, | |
| 2967 | .is_non_err, | |
| 2968 | .is_non_err_ptr, | |
| 2969 | .typeof, | |
| 2970 | .typeof_elem, | |
| 2971 | .struct_init_empty, | |
| 2972 | .type_info, | |
| 2973 | .size_of, | |
| 2974 | .bit_size_of, | |
| 2975 | .typeof_log2_int_type, | |
| 2976 | .log2_int_type, | |
| 2977 | .ptr_to_int, | |
| 2978 | .error_to_int, | |
| 2979 | .int_to_error, | |
| 2980 | .compile_error, | |
| 2981 | .set_eval_branch_quota, | |
| 2982 | .enum_to_int, | |
| 2983 | .align_of, | |
| 2984 | .bool_to_int, | |
| 2985 | .embed_file, | |
| 2986 | .error_name, | |
| 2987 | .panic, | |
| 2988 | .set_align_stack, | |
| 2989 | .set_cold, | |
| 2990 | .set_float_mode, | |
| 2991 | .set_runtime_safety, | |
| 2992 | .sqrt, | |
| 2993 | .sin, | |
| 2994 | .cos, | |
| 2995 | .exp, | |
| 2996 | .exp2, | |
| 2997 | .log, | |
| 2998 | .log2, | |
| 2999 | .log10, | |
| 3000 | .fabs, | |
| 3001 | .floor, | |
| 3002 | .ceil, | |
| 3003 | .trunc, | |
| 3004 | .round, | |
| 3005 | .tag_name, | |
| 3006 | .reify, | |
| 3007 | .type_name, | |
| 3008 | .frame_type, | |
| 3009 | .frame_size, | |
| 3010 | .clz, | |
| 3011 | .ctz, | |
| 3012 | .pop_count, | |
| 3013 | .byte_swap, | |
| 3014 | .bit_reverse, | |
| 3015 | .elem_type, | |
| 3016 | .@"resume", | |
| 3017 | .@"await", | |
| 3018 | .await_nosuspend, | |
| 3019 | => try self.writeUnNode(stream, inst), | |
| 3020 | ||
| 3021 | .ref, | |
| 3022 | .ret_coerce, | |
| 3023 | .ensure_err_payload_void, | |
| 3024 | => try self.writeUnTok(stream, inst), | |
| 3025 | ||
| 3026 | .bool_br_and, | |
| 3027 | .bool_br_or, | |
| 3028 | => try self.writeBoolBr(stream, inst), | |
| 3029 | ||
| 3030 | .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), | |
| 3031 | .param_type => try self.writeParamType(stream, inst), | |
| 3032 | .ptr_type_simple => try self.writePtrTypeSimple(stream, inst), | |
| 3033 | .ptr_type => try self.writePtrType(stream, inst), | |
| 3034 | .int => try self.writeInt(stream, inst), | |
| 3035 | .int_big => try self.writeIntBig(stream, inst), | |
| 3036 | .float => try self.writeFloat(stream, inst), | |
| 3037 | .float128 => try self.writeFloat128(stream, inst), | |
| 3038 | .str => try self.writeStr(stream, inst), | |
| 3039 | .int_type => try self.writeIntType(stream, inst), | |
| 3040 | ||
| 3041 | .@"break", | |
| 3042 | .break_inline, | |
| 3043 | => try self.writeBreak(stream, inst), | |
| 3044 | ||
| 3045 | .elem_ptr_node, | |
| 3046 | .elem_val_node, | |
| 3047 | .field_ptr_named, | |
| 3048 | .field_val_named, | |
| 3049 | .slice_start, | |
| 3050 | .slice_end, | |
| 3051 | .slice_sentinel, | |
| 3052 | .array_init, | |
| 3053 | .array_init_anon, | |
| 3054 | .array_init_ref, | |
| 3055 | .array_init_anon_ref, | |
| 3056 | .union_init_ptr, | |
| 3057 | .shuffle, | |
| 3058 | .select, | |
| 3059 | .atomic_rmw, | |
| 3060 | .atomic_store, | |
| 3061 | .mul_add, | |
| 3062 | .builtin_call, | |
| 3063 | .field_parent_ptr, | |
| 3064 | .memcpy, | |
| 3065 | .memset, | |
| 3066 | .builtin_async_call, | |
| 3067 | => try self.writePlNode(stream, inst), | |
| 3068 | ||
| 3069 | .struct_init, | |
| 3070 | .struct_init_ref, | |
| 3071 | => try self.writeStructInit(stream, inst), | |
| 3072 | ||
| 3073 | .cmpxchg_strong, | |
| 3074 | .cmpxchg_weak, | |
| 3075 | => try self.writeCmpxchg(stream, inst), | |
| 3076 | ||
| 3077 | .struct_init_anon, | |
| 3078 | .struct_init_anon_ref, | |
| 3079 | => try self.writeStructInitAnon(stream, inst), | |
| 3080 | ||
| 3081 | .field_type => try self.writeFieldType(stream, inst), | |
| 3082 | .field_type_ref => try self.writeFieldTypeRef(stream, inst), | |
| 3083 | ||
| 3084 | .add, | |
| 3085 | .addwrap, | |
| 3086 | .array_cat, | |
| 3087 | .array_mul, | |
| 3088 | .mul, | |
| 3089 | .mulwrap, | |
| 3090 | .sub, | |
| 3091 | .subwrap, | |
| 3092 | .cmp_lt, | |
| 3093 | .cmp_lte, | |
| 3094 | .cmp_eq, | |
| 3095 | .cmp_gte, | |
| 3096 | .cmp_gt, | |
| 3097 | .cmp_neq, | |
| 3098 | .div, | |
| 3099 | .has_decl, | |
| 3100 | .has_field, | |
| 3101 | .mod_rem, | |
| 3102 | .shl, | |
| 3103 | .shl_exact, | |
| 3104 | .shr, | |
| 3105 | .shr_exact, | |
| 3106 | .xor, | |
| 3107 | .store_node, | |
| 3108 | .error_union_type, | |
| 3109 | .merge_error_sets, | |
| 3110 | .bit_and, | |
| 3111 | .bit_or, | |
| 3112 | .float_to_int, | |
| 3113 | .int_to_float, | |
| 3114 | .int_to_ptr, | |
| 3115 | .int_to_enum, | |
| 3116 | .float_cast, | |
| 3117 | .int_cast, | |
| 3118 | .err_set_cast, | |
| 3119 | .ptr_cast, | |
| 3120 | .truncate, | |
| 3121 | .align_cast, | |
| 3122 | .div_exact, | |
| 3123 | .div_floor, | |
| 3124 | .div_trunc, | |
| 3125 | .mod, | |
| 3126 | .rem, | |
| 3127 | .bit_offset_of, | |
| 3128 | .offset_of, | |
| 3129 | .splat, | |
| 3130 | .reduce, | |
| 3131 | .atomic_load, | |
| 3132 | .bitcast, | |
| 3133 | .bitcast_result_ptr, | |
| 3134 | .vector_type, | |
| 3135 | .maximum, | |
| 3136 | .minimum, | |
| 3137 | => try self.writePlNodeBin(stream, inst), | |
| 3138 | ||
| 3139 | .@"export" => try self.writePlNodeExport(stream, inst), | |
| 3140 | ||
| 3141 | .call, | |
| 3142 | .call_chkused, | |
| 3143 | .call_compile_time, | |
| 3144 | .call_nosuspend, | |
| 3145 | .call_async, | |
| 3146 | => try self.writePlNodeCall(stream, inst), | |
| 3147 | ||
| 3148 | .block, | |
| 3149 | .block_inline, | |
| 3150 | .suspend_block, | |
| 3151 | .loop, | |
| 3152 | .validate_struct_init_ptr, | |
| 3153 | .validate_array_init_ptr, | |
| 3154 | .c_import, | |
| 3155 | => try self.writePlNodeBlock(stream, inst), | |
| 3156 | ||
| 3157 | .condbr, | |
| 3158 | .condbr_inline, | |
| 3159 | => try self.writePlNodeCondBr(stream, inst), | |
| 3160 | ||
| 3161 | .opaque_decl => try self.writeOpaqueDecl(stream, inst, .parent), | |
| 3162 | .opaque_decl_anon => try self.writeOpaqueDecl(stream, inst, .anon), | |
| 3163 | .opaque_decl_func => try self.writeOpaqueDecl(stream, inst, .func), | |
| 3164 | ||
| 3165 | .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent), | |
| 3166 | .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon), | |
| 3167 | .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func), | |
| 3168 | ||
| 3169 | .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none), | |
| 3170 | .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), | |
| 3171 | .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under), | |
| 3172 | .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none), | |
| 3173 | .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), | |
| 3174 | .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under), | |
| 3175 | ||
| 3176 | .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none), | |
| 3177 | .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"), | |
| 3178 | .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under), | |
| 3179 | .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none), | |
| 3180 | .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"), | |
| 3181 | .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under), | |
| 3182 | ||
| 3183 | .field_ptr, | |
| 3184 | .field_val, | |
| 3185 | => try self.writePlNodeField(stream, inst), | |
| 3186 | ||
| 3187 | .as_node => try self.writeAs(stream, inst), | |
| 3188 | ||
| 3189 | .breakpoint, | |
| 3190 | .fence, | |
| 3191 | .repeat, | |
| 3192 | .repeat_inline, | |
| 3193 | .alloc_inferred, | |
| 3194 | .alloc_inferred_mut, | |
| 3195 | .alloc_inferred_comptime, | |
| 3196 | => try self.writeNode(stream, inst), | |
| 3197 | ||
| 3198 | .error_value, | |
| 3199 | .enum_literal, | |
| 3200 | .decl_ref, | |
| 3201 | .decl_val, | |
| 3202 | .import, | |
| 3203 | .ret_err_value, | |
| 3204 | .ret_err_value_code, | |
| 3205 | .param_anytype, | |
| 3206 | .param_anytype_comptime, | |
| 3207 | => try self.writeStrTok(stream, inst), | |
| 3208 | ||
| 3209 | .param, .param_comptime => try self.writeParam(stream, inst), | |
| 3210 | ||
| 3211 | .func => try self.writeFunc(stream, inst, false), | |
| 3212 | .func_inferred => try self.writeFunc(stream, inst, true), | |
| 3213 | ||
| 3214 | .@"unreachable" => try self.writeUnreachable(stream, inst), | |
| 3215 | ||
| 3216 | .switch_capture, | |
| 3217 | .switch_capture_ref, | |
| 3218 | .switch_capture_multi, | |
| 3219 | .switch_capture_multi_ref, | |
| 3220 | .switch_capture_else, | |
| 3221 | .switch_capture_else_ref, | |
| 3222 | => try self.writeSwitchCapture(stream, inst), | |
| 3223 | ||
| 3224 | .dbg_stmt => try self.writeDbgStmt(stream, inst), | |
| 3225 | ||
| 3226 | .extended => try self.writeExtended(stream, inst), | |
| 3227 | } | |
| 3228 | } | |
| 3229 | ||
| 3230 | fn writeExtended(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3231 | const extended = self.code.instructions.items(.data)[inst].extended; | |
| 3232 | try stream.print("{s}(", .{@tagName(extended.opcode)}); | |
| 3233 | switch (extended.opcode) { | |
| 3234 | .ret_ptr, | |
| 3235 | .ret_type, | |
| 3236 | .this, | |
| 3237 | .ret_addr, | |
| 3238 | .error_return_trace, | |
| 3239 | .frame, | |
| 3240 | .frame_address, | |
| 3241 | .builtin_src, | |
| 3242 | => try self.writeExtNode(stream, extended), | |
| 3243 | ||
| 3244 | .@"asm" => try self.writeAsm(stream, extended), | |
| 3245 | .func => try self.writeFuncExtended(stream, extended), | |
| 3246 | .variable => try self.writeVarExtended(stream, extended), | |
| 3247 | ||
| 3248 | .compile_log, | |
| 3249 | .typeof_peer, | |
| 3250 | => try self.writeNodeMultiOp(stream, extended), | |
| 3251 | ||
| 3252 | .add_with_overflow, | |
| 3253 | .sub_with_overflow, | |
| 3254 | .mul_with_overflow, | |
| 3255 | .shl_with_overflow, | |
| 3256 | => try self.writeOverflowArithmetic(stream, extended), | |
| 3257 | ||
| 3258 | .add_with_saturation, | |
| 3259 | .sub_with_saturation, | |
| 3260 | .mul_with_saturation, | |
| 3261 | .shl_with_saturation, | |
| 3262 | => try self.writeSaturatingArithmetic(stream, extended), | |
| 3263 | .struct_decl => try self.writeStructDecl(stream, extended), | |
| 3264 | .union_decl => try self.writeUnionDecl(stream, extended), | |
| 3265 | .enum_decl => try self.writeEnumDecl(stream, extended), | |
| 3266 | ||
| 3267 | .alloc, | |
| 3268 | .builtin_extern, | |
| 3269 | .c_undef, | |
| 3270 | .c_include, | |
| 3271 | .c_define, | |
| 3272 | .wasm_memory_size, | |
| 3273 | .wasm_memory_grow, | |
| 3274 | => try stream.writeAll("TODO))"), | |
| 3275 | } | |
| 3276 | } | |
| 3277 | ||
| 3278 | fn writeExtNode(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { | |
| 3279 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; | |
| 3280 | try stream.writeAll(")) "); | |
| 3281 | try self.writeSrc(stream, src); | |
| 3282 | } | |
| 3283 | ||
| 3284 | fn writeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3285 | const inst_data = self.code.instructions.items(.data)[inst].bin; | |
| 3286 | try self.writeInstRef(stream, inst_data.lhs); | |
| 3287 | try stream.writeAll(", "); | |
| 3288 | try self.writeInstRef(stream, inst_data.rhs); | |
| 3289 | try stream.writeByte(')'); | |
| 3290 | } | |
| 3291 | ||
| 3292 | fn writeUnNode( | |
| 3293 | self: *Writer, | |
| 3294 | stream: anytype, | |
| 3295 | inst: Inst.Index, | |
| 3296 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 3297 | const inst_data = self.code.instructions.items(.data)[inst].un_node; | |
| 3298 | try self.writeInstRef(stream, inst_data.operand); | |
| 3299 | try stream.writeAll(") "); | |
| 3300 | try self.writeSrc(stream, inst_data.src()); | |
| 3301 | } | |
| 3302 | ||
| 3303 | fn writeUnTok( | |
| 3304 | self: *Writer, | |
| 3305 | stream: anytype, | |
| 3306 | inst: Inst.Index, | |
| 3307 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 3308 | const inst_data = self.code.instructions.items(.data)[inst].un_tok; | |
| 3309 | try self.writeInstRef(stream, inst_data.operand); | |
| 3310 | try stream.writeAll(") "); | |
| 3311 | try self.writeSrc(stream, inst_data.src()); | |
| 3312 | } | |
| 3313 | ||
| 3314 | fn writeArrayTypeSentinel( | |
| 3315 | self: *Writer, | |
| 3316 | stream: anytype, | |
| 3317 | inst: Inst.Index, | |
| 3318 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 3319 | const inst_data = self.code.instructions.items(.data)[inst].array_type_sentinel; | |
| 3320 | _ = inst_data; | |
| 3321 | try stream.writeAll("TODO)"); | |
| 3322 | } | |
| 3323 | ||
| 3324 | fn writeParamType( | |
| 3325 | self: *Writer, | |
| 3326 | stream: anytype, | |
| 3327 | inst: Inst.Index, | |
| 3328 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 3329 | const inst_data = self.code.instructions.items(.data)[inst].param_type; | |
| 3330 | try self.writeInstRef(stream, inst_data.callee); | |
| 3331 | try stream.print(", {d})", .{inst_data.param_index}); | |
| 3332 | } | |
| 3333 | ||
| 3334 | fn writePtrTypeSimple( | |
| 3335 | self: *Writer, | |
| 3336 | stream: anytype, | |
| 3337 | inst: Inst.Index, | |
| 3338 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 3339 | const inst_data = self.code.instructions.items(.data)[inst].ptr_type_simple; | |
| 3340 | const str_allowzero = if (inst_data.is_allowzero) "allowzero, " else ""; | |
| 3341 | const str_const = if (!inst_data.is_mutable) "const, " else ""; | |
| 3342 | const str_volatile = if (inst_data.is_volatile) "volatile, " else ""; | |
| 3343 | try self.writeInstRef(stream, inst_data.elem_type); | |
| 3344 | try stream.print(", {s}{s}{s}{s})", .{ | |
| 3345 | str_allowzero, | |
| 3346 | str_const, | |
| 3347 | str_volatile, | |
| 3348 | @tagName(inst_data.size), | |
| 3349 | }); | |
| 3350 | } | |
| 3351 | ||
| 3352 | fn writePtrType( | |
| 3353 | self: *Writer, | |
| 3354 | stream: anytype, | |
| 3355 | inst: Inst.Index, | |
| 3356 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 3357 | const inst_data = self.code.instructions.items(.data)[inst].ptr_type; | |
| 3358 | _ = inst_data; | |
| 3359 | try stream.writeAll("TODO)"); | |
| 3360 | } | |
| 3361 | ||
| 3362 | fn writeInt(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3363 | const inst_data = self.code.instructions.items(.data)[inst].int; | |
| 3364 | try stream.print("{d})", .{inst_data}); | |
| 3365 | } | |
| 3366 | ||
| 3367 | fn writeIntBig(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3368 | const inst_data = self.code.instructions.items(.data)[inst].str; | |
| 3369 | const byte_count = inst_data.len * @sizeOf(std.math.big.Limb); | |
| 3370 | const limb_bytes = self.code.string_bytes[inst_data.start..][0..byte_count]; | |
| 3371 | // limb_bytes is not aligned properly; we must allocate and copy the bytes | |
| 3372 | // in order to accomplish this. | |
| 3373 | const limbs = try self.gpa.alloc(std.math.big.Limb, inst_data.len); | |
| 3374 | defer self.gpa.free(limbs); | |
| 3375 | ||
| 3376 | mem.copy(u8, mem.sliceAsBytes(limbs), limb_bytes); | |
| 3377 | const big_int: std.math.big.int.Const = .{ | |
| 3378 | .limbs = limbs, | |
| 3379 | .positive = true, | |
| 3380 | }; | |
| 3381 | const as_string = try big_int.toStringAlloc(self.gpa, 10, .lower); | |
| 3382 | defer self.gpa.free(as_string); | |
| 3383 | try stream.print("{s})", .{as_string}); | |
| 3384 | } | |
| 3385 | ||
| 3386 | fn writeFloat(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3387 | const number = self.code.instructions.items(.data)[inst].float; | |
| 3388 | try stream.print("{d})", .{number}); | |
| 3389 | } | |
| 3390 | ||
| 3391 | fn writeFloat128(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3392 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3393 | const extra = self.code.extraData(Inst.Float128, inst_data.payload_index).data; | |
| 3394 | const src = inst_data.src(); | |
| 3395 | const number = extra.get(); | |
| 3396 | // TODO improve std.format to be able to print f128 values | |
| 3397 | try stream.print("{d}) ", .{@floatCast(f64, number)}); | |
| 3398 | try self.writeSrc(stream, src); | |
| 3399 | } | |
| 3400 | ||
| 3401 | fn writeStr( | |
| 3402 | self: *Writer, | |
| 3403 | stream: anytype, | |
| 3404 | inst: Inst.Index, | |
| 3405 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 3406 | const inst_data = self.code.instructions.items(.data)[inst].str; | |
| 3407 | const str = inst_data.get(self.code); | |
| 3408 | try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)}); | |
| 3409 | } | |
| 3410 | ||
| 3411 | fn writePlNode(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3412 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3413 | try stream.writeAll("TODO) "); | |
| 3414 | try self.writeSrc(stream, inst_data.src()); | |
| 3415 | } | |
| 3416 | ||
| 3417 | fn writeParam(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3418 | const inst_data = self.code.instructions.items(.data)[inst].pl_tok; | |
| 3419 | const extra = self.code.extraData(Inst.Param, inst_data.payload_index); | |
| 3420 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; | |
| 3421 | try stream.print("\"{}\", ", .{ | |
| 3422 | std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)), | |
| 3423 | }); | |
| 3424 | try stream.writeAll("{\n"); | |
| 3425 | self.indent += 2; | |
| 3426 | try self.writeBody(stream, body); | |
| 3427 | self.indent -= 2; | |
| 3428 | try stream.writeByteNTimes(' ', self.indent); | |
| 3429 | try stream.writeAll("}) "); | |
| 3430 | try self.writeSrc(stream, inst_data.src()); | |
| 3431 | } | |
| 3432 | ||
| 3433 | fn writePlNodeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3434 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3435 | const extra = self.code.extraData(Inst.Bin, inst_data.payload_index).data; | |
| 3436 | try self.writeInstRef(stream, extra.lhs); | |
| 3437 | try stream.writeAll(", "); | |
| 3438 | try self.writeInstRef(stream, extra.rhs); | |
| 3439 | try stream.writeAll(") "); | |
| 3440 | try self.writeSrc(stream, inst_data.src()); | |
| 3441 | } | |
| 3442 | ||
| 3443 | fn writePlNodeExport(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3444 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3445 | const extra = self.code.extraData(Inst.Export, inst_data.payload_index).data; | |
| 3446 | const decl_name = self.code.nullTerminatedString(extra.decl_name); | |
| 3447 | ||
| 3448 | try self.writeInstRef(stream, extra.namespace); | |
| 3449 | try stream.print(", {}, ", .{std.zig.fmtId(decl_name)}); | |
| 3450 | try self.writeInstRef(stream, extra.options); | |
| 3451 | try stream.writeAll(") "); | |
| 3452 | try self.writeSrc(stream, inst_data.src()); | |
| 3453 | } | |
| 3454 | ||
| 3455 | fn writeStructInit(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3456 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3457 | const extra = self.code.extraData(Inst.StructInit, inst_data.payload_index); | |
| 3458 | var field_i: u32 = 0; | |
| 3459 | var extra_index = extra.end; | |
| 3460 | ||
| 3461 | while (field_i < extra.data.fields_len) : (field_i += 1) { | |
| 3462 | const item = self.code.extraData(Inst.StructInit.Item, extra_index); | |
| 3463 | extra_index = item.end; | |
| 3464 | ||
| 3465 | if (field_i != 0) { | |
| 3466 | try stream.writeAll(", ["); | |
| 3467 | } else { | |
| 3468 | try stream.writeAll("["); | |
| 3469 | } | |
| 3470 | try self.writeInstIndex(stream, item.data.field_type); | |
| 3471 | try stream.writeAll(", "); | |
| 3472 | try self.writeInstRef(stream, item.data.init); | |
| 3473 | try stream.writeAll("]"); | |
| 3474 | } | |
| 3475 | try stream.writeAll(") "); | |
| 3476 | try self.writeSrc(stream, inst_data.src()); | |
| 3477 | } | |
| 3478 | ||
| 3479 | fn writeCmpxchg(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3480 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3481 | const extra = self.code.extraData(Inst.Cmpxchg, inst_data.payload_index).data; | |
| 3482 | ||
| 3483 | try self.writeInstRef(stream, extra.ptr); | |
| 3484 | try stream.writeAll(", "); | |
| 3485 | try self.writeInstRef(stream, extra.expected_value); | |
| 3486 | try stream.writeAll(", "); | |
| 3487 | try self.writeInstRef(stream, extra.new_value); | |
| 3488 | try stream.writeAll(", "); | |
| 3489 | try self.writeInstRef(stream, extra.success_order); | |
| 3490 | try stream.writeAll(", "); | |
| 3491 | try self.writeInstRef(stream, extra.failure_order); | |
| 3492 | try stream.writeAll(") "); | |
| 3493 | try self.writeSrc(stream, inst_data.src()); | |
| 3494 | } | |
| 3495 | ||
| 3496 | fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3497 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3498 | const extra = self.code.extraData(Inst.StructInitAnon, inst_data.payload_index); | |
| 3499 | var field_i: u32 = 0; | |
| 3500 | var extra_index = extra.end; | |
| 3501 | ||
| 3502 | while (field_i < extra.data.fields_len) : (field_i += 1) { | |
| 3503 | const item = self.code.extraData(Inst.StructInitAnon.Item, extra_index); | |
| 3504 | extra_index = item.end; | |
| 3505 | ||
| 3506 | const field_name = self.code.nullTerminatedString(item.data.field_name); | |
| 3507 | ||
| 3508 | const prefix = if (field_i != 0) ", [" else "["; | |
| 3509 | try stream.print("{s}[{s}=", .{ prefix, field_name }); | |
| 3510 | try self.writeInstRef(stream, item.data.init); | |
| 3511 | try stream.writeAll("]"); | |
| 3512 | } | |
| 3513 | try stream.writeAll(") "); | |
| 3514 | try self.writeSrc(stream, inst_data.src()); | |
| 3515 | } | |
| 3516 | ||
| 3517 | fn writeFieldType(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3518 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3519 | const extra = self.code.extraData(Inst.FieldType, inst_data.payload_index).data; | |
| 3520 | try self.writeInstRef(stream, extra.container_type); | |
| 3521 | const field_name = self.code.nullTerminatedString(extra.name_start); | |
| 3522 | try stream.print(", {s}) ", .{field_name}); | |
| 3523 | try self.writeSrc(stream, inst_data.src()); | |
| 3524 | } | |
| 3525 | ||
| 3526 | fn writeFieldTypeRef(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3527 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3528 | const extra = self.code.extraData(Inst.FieldTypeRef, inst_data.payload_index).data; | |
| 3529 | try self.writeInstRef(stream, extra.container_type); | |
| 3530 | try stream.writeAll(", "); | |
| 3531 | try self.writeInstRef(stream, extra.field_name); | |
| 3532 | try stream.writeAll(") "); | |
| 3533 | try self.writeSrc(stream, inst_data.src()); | |
| 3534 | } | |
| 3535 | ||
| 3536 | fn writeNodeMultiOp(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { | |
| 3537 | const extra = self.code.extraData(Inst.NodeMultiOp, extended.operand); | |
| 3538 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; | |
| 3539 | const operands = self.code.refSlice(extra.end, extended.small); | |
| 3540 | ||
| 3541 | for (operands) |operand, i| { | |
| 3542 | if (i != 0) try stream.writeAll(", "); | |
| 3543 | try self.writeInstRef(stream, operand); | |
| 3544 | } | |
| 3545 | try stream.writeAll(")) "); | |
| 3546 | try self.writeSrc(stream, src); | |
| 3547 | } | |
| 3548 | ||
| 3549 | fn writeAsm(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { | |
| 3550 | const extra = self.code.extraData(Inst.Asm, extended.operand); | |
| 3551 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; | |
| 3552 | const outputs_len = @truncate(u5, extended.small); | |
| 3553 | const inputs_len = @truncate(u5, extended.small >> 5); | |
| 3554 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 3555 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | |
| 3556 | const asm_source = self.code.nullTerminatedString(extra.data.asm_source); | |
| 3557 | ||
| 3558 | try self.writeFlag(stream, "volatile, ", is_volatile); | |
| 3559 | try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)}); | |
| 3560 | try stream.writeAll(", "); | |
| 3561 | ||
| 3562 | var extra_i: usize = extra.end; | |
| 3563 | var output_type_bits = extra.data.output_type_bits; | |
| 3564 | { | |
| 3565 | var i: usize = 0; | |
| 3566 | while (i < outputs_len) : (i += 1) { | |
| 3567 | const output = self.code.extraData(Inst.Asm.Output, extra_i); | |
| 3568 | extra_i = output.end; | |
| 3569 | ||
| 3570 | const is_type = @truncate(u1, output_type_bits) != 0; | |
| 3571 | output_type_bits >>= 1; | |
| 3572 | ||
| 3573 | const name = self.code.nullTerminatedString(output.data.name); | |
| 3574 | const constraint = self.code.nullTerminatedString(output.data.constraint); | |
| 3575 | try stream.print("output({}, \"{}\", ", .{ | |
| 3576 | std.zig.fmtId(name), std.zig.fmtEscapes(constraint), | |
| 3577 | }); | |
| 3578 | try self.writeFlag(stream, "->", is_type); | |
| 3579 | try self.writeInstRef(stream, output.data.operand); | |
| 3580 | try stream.writeAll(")"); | |
| 3581 | if (i + 1 < outputs_len) { | |
| 3582 | try stream.writeAll("), "); | |
| 3583 | } | |
| 3584 | } | |
| 3585 | } | |
| 3586 | { | |
| 3587 | var i: usize = 0; | |
| 3588 | while (i < inputs_len) : (i += 1) { | |
| 3589 | const input = self.code.extraData(Inst.Asm.Input, extra_i); | |
| 3590 | extra_i = input.end; | |
| 3591 | ||
| 3592 | const name = self.code.nullTerminatedString(input.data.name); | |
| 3593 | const constraint = self.code.nullTerminatedString(input.data.constraint); | |
| 3594 | try stream.print("input({}, \"{}\", ", .{ | |
| 3595 | std.zig.fmtId(name), std.zig.fmtEscapes(constraint), | |
| 3596 | }); | |
| 3597 | try self.writeInstRef(stream, input.data.operand); | |
| 3598 | try stream.writeAll(")"); | |
| 3599 | if (i + 1 < inputs_len) { | |
| 3600 | try stream.writeAll(", "); | |
| 3601 | } | |
| 3602 | } | |
| 3603 | } | |
| 3604 | { | |
| 3605 | var i: usize = 0; | |
| 3606 | while (i < clobbers_len) : (i += 1) { | |
| 3607 | const str_index = self.code.extra[extra_i]; | |
| 3608 | extra_i += 1; | |
| 3609 | const clobber = self.code.nullTerminatedString(str_index); | |
| 3610 | try stream.print("{}", .{std.zig.fmtId(clobber)}); | |
| 3611 | if (i + 1 < clobbers_len) { | |
| 3612 | try stream.writeAll(", "); | |
| 3613 | } | |
| 3614 | } | |
| 3615 | } | |
| 3616 | try stream.writeAll(")) "); | |
| 3617 | try self.writeSrc(stream, src); | |
| 3618 | } | |
| 3619 | ||
| 3620 | fn writeOverflowArithmetic(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { | |
| 3621 | const extra = self.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data; | |
| 3622 | const src: LazySrcLoc = .{ .node_offset = extra.node }; | |
| 3623 | ||
| 3624 | try self.writeInstRef(stream, extra.lhs); | |
| 3625 | try stream.writeAll(", "); | |
| 3626 | try self.writeInstRef(stream, extra.rhs); | |
| 3627 | try stream.writeAll(", "); | |
| 3628 | try self.writeInstRef(stream, extra.ptr); | |
| 3629 | try stream.writeAll(")) "); | |
| 3630 | try self.writeSrc(stream, src); | |
| 3631 | } | |
| 3632 | ||
| 3633 | fn writeSaturatingArithmetic(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { | |
| 3634 | const extra = self.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data; | |
| 3635 | const src: LazySrcLoc = .{ .node_offset = extra.node }; | |
| 3636 | ||
| 3637 | try self.writeInstRef(stream, extra.lhs); | |
| 3638 | try stream.writeAll(", "); | |
| 3639 | try self.writeInstRef(stream, extra.rhs); | |
| 3640 | try stream.writeAll(", "); | |
| 3641 | try stream.writeAll(") "); | |
| 3642 | try self.writeSrc(stream, src); | |
| 3643 | } | |
| 3644 | ||
| 3645 | fn writePlNodeCall(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3646 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3647 | const extra = self.code.extraData(Inst.Call, inst_data.payload_index); | |
| 3648 | const args = self.code.refSlice(extra.end, extra.data.args_len); | |
| 3649 | ||
| 3650 | try self.writeInstRef(stream, extra.data.callee); | |
| 3651 | try stream.writeAll(", ["); | |
| 3652 | for (args) |arg, i| { | |
| 3653 | if (i != 0) try stream.writeAll(", "); | |
| 3654 | try self.writeInstRef(stream, arg); | |
| 3655 | } | |
| 3656 | try stream.writeAll("]) "); | |
| 3657 | try self.writeSrc(stream, inst_data.src()); | |
| 3658 | } | |
| 3659 | ||
| 3660 | fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3661 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3662 | try self.writePlNodeBlockWithoutSrc(stream, inst); | |
| 3663 | try self.writeSrc(stream, inst_data.src()); | |
| 3664 | } | |
| 3665 | ||
| 3666 | fn writePlNodeBlockWithoutSrc(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3667 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3668 | const extra = self.code.extraData(Inst.Block, inst_data.payload_index); | |
| 3669 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; | |
| 3670 | try stream.writeAll("{\n"); | |
| 3671 | self.indent += 2; | |
| 3672 | try self.writeBody(stream, body); | |
| 3673 | self.indent -= 2; | |
| 3674 | try stream.writeByteNTimes(' ', self.indent); | |
| 3675 | try stream.writeAll("}) "); | |
| 3676 | } | |
| 3677 | ||
| 3678 | fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 3679 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 3680 | const extra = self.code.extraData(Inst.CondBr, inst_data.payload_index); | |
| 3681 | const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; | |
| 3682 | const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | |
| 3683 | try self.writeInstRef(stream, extra.data.condition); | |
| 3684 | try stream.writeAll(", {\n"); | |
| 3685 | self.indent += 2; | |
| 3686 | try self.writeBody(stream, then_body); | |
| 3687 | self.indent -= 2; | |
| 3688 | try stream.writeByteNTimes(' ', self.indent); | |
| 3689 | try stream.writeAll("}, {\n"); | |
| 3690 | self.indent += 2; | |
| 3691 | try self.writeBody(stream, else_body); | |
| 3692 | self.indent -= 2; | |
| 3693 | try stream.writeByteNTimes(' ', self.indent); | |
| 3694 | try stream.writeAll("}) "); | |
| 3695 | try self.writeSrc(stream, inst_data.src()); | |
| 3696 | } | |
| 3697 | ||
| 3698 | fn writeStructDecl(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { | |
| 3699 | const small = @bitCast(Inst.StructDecl.Small, extended.small); | |
| 3700 | ||
| 3701 | var extra_index: usize = extended.operand; | |
| 3702 | ||
| 3703 | const src_node: ?i32 = if (small.has_src_node) blk: { | |
| 3704 | const src_node = @bitCast(i32, self.code.extra[extra_index]); | |
| 3705 | extra_index += 1; | |
| 3706 | break :blk src_node; | |
| 3707 | } else null; | |
| 3708 | ||
| 3709 | const body_len = if (small.has_body_len) blk: { | |
| 3710 | const body_len = self.code.extra[extra_index]; | |
| 3711 | extra_index += 1; | |
| 3712 | break :blk body_len; | |
| 3713 | } else 0; | |
| 3714 | ||
| 3715 | const fields_len = if (small.has_fields_len) blk: { | |
| 3716 | const fields_len = self.code.extra[extra_index]; | |
| 3717 | extra_index += 1; | |
| 3718 | break :blk fields_len; | |
| 3719 | } else 0; | |
| 3720 | ||
| 3721 | const decls_len = if (small.has_decls_len) blk: { | |
| 3722 | const decls_len = self.code.extra[extra_index]; | |
| 3723 | extra_index += 1; | |
| 3724 | break :blk decls_len; | |
| 3725 | } else 0; | |
| 3726 | ||
| 3727 | try self.writeFlag(stream, "known_has_bits, ", small.known_has_bits); | |
| 3728 | try stream.print("{s}, {s}, ", .{ | |
| 3729 | @tagName(small.name_strategy), @tagName(small.layout), | |
| 3730 | }); | |
| 3731 | ||
| 3732 | if (decls_len == 0) { | |
| 3733 | try stream.writeAll("{}, "); | |
| 3734 | } else { | |
| 3735 | try stream.writeAll("{\n"); | |
| 3736 | self.indent += 2; | |
| 3737 | extra_index = try self.writeDecls(stream, decls_len, extra_index); | |
| 3738 | self.indent -= 2; | |
| 3739 | try stream.writeByteNTimes(' ', self.indent); | |
| 3740 | try stream.writeAll("}, "); | |
| 3741 | } | |
| 3742 | ||
| 3743 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 3744 | extra_index += body.len; | |
| 3745 | ||
| 3746 | if (fields_len == 0) { | |
| 3747 | assert(body.len == 0); | |
| 3748 | try stream.writeAll("{}, {})"); | |
| 3749 | } else { | |
| 3750 | const prev_parent_decl_node = self.parent_decl_node; | |
| 3751 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); | |
| 3752 | self.indent += 2; | |
| 3753 | if (body.len == 0) { | |
| 3754 | try stream.writeAll("{}, {\n"); | |
| 3755 | } else { | |
| 3756 | try stream.writeAll("{\n"); | |
| 3757 | try self.writeBody(stream, body); | |
| 3758 | ||
| 3759 | try stream.writeByteNTimes(' ', self.indent - 2); | |
| 3760 | try stream.writeAll("}, {\n"); | |
| 3761 | } | |
| 3762 | ||
| 3763 | const bits_per_field = 4; | |
| 3764 | const fields_per_u32 = 32 / bits_per_field; | |
| 3765 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; | |
| 3766 | var bit_bag_index: usize = extra_index; | |
| 3767 | extra_index += bit_bags_count; | |
| 3768 | var cur_bit_bag: u32 = undefined; | |
| 3769 | var field_i: u32 = 0; | |
| 3770 | while (field_i < fields_len) : (field_i += 1) { | |
| 3771 | if (field_i % fields_per_u32 == 0) { | |
| 3772 | cur_bit_bag = self.code.extra[bit_bag_index]; | |
| 3773 | bit_bag_index += 1; | |
| 3774 | } | |
| 3775 | const has_align = @truncate(u1, cur_bit_bag) != 0; | |
| 3776 | cur_bit_bag >>= 1; | |
| 3777 | const has_default = @truncate(u1, cur_bit_bag) != 0; | |
| 3778 | cur_bit_bag >>= 1; | |
| 3779 | const is_comptime = @truncate(u1, cur_bit_bag) != 0; | |
| 3780 | cur_bit_bag >>= 1; | |
| 3781 | const unused = @truncate(u1, cur_bit_bag) != 0; | |
| 3782 | cur_bit_bag >>= 1; | |
| 3783 | ||
| 3784 | _ = unused; | |
| 3785 | ||
| 3786 | const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); | |
| 3787 | extra_index += 1; | |
| 3788 | const field_type = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 3789 | extra_index += 1; | |
| 3790 | ||
| 3791 | try stream.writeByteNTimes(' ', self.indent); | |
| 3792 | try self.writeFlag(stream, "comptime ", is_comptime); | |
| 3793 | try stream.print("{}: ", .{std.zig.fmtId(field_name)}); | |
| 3794 | try self.writeInstRef(stream, field_type); | |
| 3795 | ||
| 3796 | if (has_align) { | |
| 3797 | const align_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 3798 | extra_index += 1; | |
| 3799 | ||
| 3800 | try stream.writeAll(" align("); | |
| 3801 | try self.writeInstRef(stream, align_ref); | |
| 3802 | try stream.writeAll(")"); | |
| 3803 | } | |
| 3804 | if (has_default) { | |
| 3805 | const default_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 3806 | extra_index += 1; | |
| 3807 | ||
| 3808 | try stream.writeAll(" = "); | |
| 3809 | try self.writeInstRef(stream, default_ref); | |
| 3810 | } | |
| 3811 | try stream.writeAll(",\n"); | |
| 3812 | } | |
| 3813 | ||
| 3814 | self.parent_decl_node = prev_parent_decl_node; | |
| 3815 | self.indent -= 2; | |
| 3816 | try stream.writeByteNTimes(' ', self.indent); | |
| 3817 | try stream.writeAll("})"); | |
| 3818 | } | |
| 3819 | try self.writeSrcNode(stream, src_node); | |
| 3820 | } | |
| 3821 | ||
| 3822 | fn writeUnionDecl(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { | |
| 3823 | const small = @bitCast(Inst.UnionDecl.Small, extended.small); | |
| 3824 | ||
| 3825 | var extra_index: usize = extended.operand; | |
| 3826 | ||
| 3827 | const src_node: ?i32 = if (small.has_src_node) blk: { | |
| 3828 | const src_node = @bitCast(i32, self.code.extra[extra_index]); | |
| 3829 | extra_index += 1; | |
| 3830 | break :blk src_node; | |
| 3831 | } else null; | |
| 3832 | ||
| 3833 | const tag_type_ref = if (small.has_tag_type) blk: { | |
| 3834 | const tag_type_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 3835 | extra_index += 1; | |
| 3836 | break :blk tag_type_ref; | |
| 3837 | } else .none; | |
| 3838 | ||
| 3839 | const body_len = if (small.has_body_len) blk: { | |
| 3840 | const body_len = self.code.extra[extra_index]; | |
| 3841 | extra_index += 1; | |
| 3842 | break :blk body_len; | |
| 3843 | } else 0; | |
| 3844 | ||
| 3845 | const fields_len = if (small.has_fields_len) blk: { | |
| 3846 | const fields_len = self.code.extra[extra_index]; | |
| 3847 | extra_index += 1; | |
| 3848 | break :blk fields_len; | |
| 3849 | } else 0; | |
| 3850 | ||
| 3851 | const decls_len = if (small.has_decls_len) blk: { | |
| 3852 | const decls_len = self.code.extra[extra_index]; | |
| 3853 | extra_index += 1; | |
| 3854 | break :blk decls_len; | |
| 3855 | } else 0; | |
| 3856 | ||
| 3857 | try stream.print("{s}, {s}, ", .{ | |
| 3858 | @tagName(small.name_strategy), @tagName(small.layout), | |
| 3859 | }); | |
| 3860 | try self.writeFlag(stream, "autoenum, ", small.auto_enum_tag); | |
| 3861 | ||
| 3862 | if (decls_len == 0) { | |
| 3863 | try stream.writeAll("{}, "); | |
| 3864 | } else { | |
| 3865 | try stream.writeAll("{\n"); | |
| 3866 | self.indent += 2; | |
| 3867 | extra_index = try self.writeDecls(stream, decls_len, extra_index); | |
| 3868 | self.indent -= 2; | |
| 3869 | try stream.writeByteNTimes(' ', self.indent); | |
| 3870 | try stream.writeAll("}, "); | |
| 3871 | } | |
| 3872 | ||
| 3873 | assert(fields_len != 0); | |
| 3874 | ||
| 3875 | if (tag_type_ref != .none) { | |
| 3876 | try self.writeInstRef(stream, tag_type_ref); | |
| 3877 | try stream.writeAll(", "); | |
| 3878 | } | |
| 3879 | ||
| 3880 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 3881 | extra_index += body.len; | |
| 3882 | ||
| 3883 | const prev_parent_decl_node = self.parent_decl_node; | |
| 3884 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); | |
| 3885 | self.indent += 2; | |
| 3886 | if (body.len == 0) { | |
| 3887 | try stream.writeAll("{}, {\n"); | |
| 3888 | } else { | |
| 3889 | try stream.writeAll("{\n"); | |
| 3890 | try self.writeBody(stream, body); | |
| 3891 | ||
| 3892 | try stream.writeByteNTimes(' ', self.indent - 2); | |
| 3893 | try stream.writeAll("}, {\n"); | |
| 3894 | } | |
| 3895 | ||
| 3896 | const bits_per_field = 4; | |
| 3897 | const fields_per_u32 = 32 / bits_per_field; | |
| 3898 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; | |
| 3899 | const body_end = extra_index; | |
| 3900 | extra_index += bit_bags_count; | |
| 3901 | var bit_bag_index: usize = body_end; | |
| 3902 | var cur_bit_bag: u32 = undefined; | |
| 3903 | var field_i: u32 = 0; | |
| 3904 | while (field_i < fields_len) : (field_i += 1) { | |
| 3905 | if (field_i % fields_per_u32 == 0) { | |
| 3906 | cur_bit_bag = self.code.extra[bit_bag_index]; | |
| 3907 | bit_bag_index += 1; | |
| 3908 | } | |
| 3909 | const has_type = @truncate(u1, cur_bit_bag) != 0; | |
| 3910 | cur_bit_bag >>= 1; | |
| 3911 | const has_align = @truncate(u1, cur_bit_bag) != 0; | |
| 3912 | cur_bit_bag >>= 1; | |
| 3913 | const has_value = @truncate(u1, cur_bit_bag) != 0; | |
| 3914 | cur_bit_bag >>= 1; | |
| 3915 | const unused = @truncate(u1, cur_bit_bag) != 0; | |
| 3916 | cur_bit_bag >>= 1; | |
| 3917 | ||
| 3918 | _ = unused; | |
| 3919 | ||
| 3920 | const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); | |
| 3921 | extra_index += 1; | |
| 3922 | try stream.writeByteNTimes(' ', self.indent); | |
| 3923 | try stream.print("{}", .{std.zig.fmtId(field_name)}); | |
| 3924 | ||
| 3925 | if (has_type) { | |
| 3926 | const field_type = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 3927 | extra_index += 1; | |
| 3928 | ||
| 3929 | try stream.writeAll(": "); | |
| 3930 | try self.writeInstRef(stream, field_type); | |
| 3931 | } | |
| 3932 | if (has_align) { | |
| 3933 | const align_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 3934 | extra_index += 1; | |
| 3935 | ||
| 3936 | try stream.writeAll(" align("); | |
| 3937 | try self.writeInstRef(stream, align_ref); | |
| 3938 | try stream.writeAll(")"); | |
| 3939 | } | |
| 3940 | if (has_value) { | |
| 3941 | const default_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 3942 | extra_index += 1; | |
| 3943 | ||
| 3944 | try stream.writeAll(" = "); | |
| 3945 | try self.writeInstRef(stream, default_ref); | |
| 3946 | } | |
| 3947 | try stream.writeAll(",\n"); | |
| 3948 | } | |
| 3949 | ||
| 3950 | self.parent_decl_node = prev_parent_decl_node; | |
| 3951 | self.indent -= 2; | |
| 3952 | try stream.writeByteNTimes(' ', self.indent); | |
| 3953 | try stream.writeAll("})"); | |
| 3954 | try self.writeSrcNode(stream, src_node); | |
| 3955 | } | |
| 3956 | ||
| 3957 | fn writeDecls(self: *Writer, stream: anytype, decls_len: u32, extra_start: usize) !usize { | |
| 3958 | const parent_decl_node = self.parent_decl_node; | |
| 3959 | const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; | |
| 3960 | var extra_index = extra_start + bit_bags_count; | |
| 3961 | var bit_bag_index: usize = extra_start; | |
| 3962 | var cur_bit_bag: u32 = undefined; | |
| 3963 | var decl_i: u32 = 0; | |
| 3964 | while (decl_i < decls_len) : (decl_i += 1) { | |
| 3965 | if (decl_i % 8 == 0) { | |
| 3966 | cur_bit_bag = self.code.extra[bit_bag_index]; | |
| 3967 | bit_bag_index += 1; | |
| 3968 | } | |
| 3969 | const is_pub = @truncate(u1, cur_bit_bag) != 0; | |
| 3970 | cur_bit_bag >>= 1; | |
| 3971 | const is_exported = @truncate(u1, cur_bit_bag) != 0; | |
| 3972 | cur_bit_bag >>= 1; | |
| 3973 | const has_align = @truncate(u1, cur_bit_bag) != 0; | |
| 3974 | cur_bit_bag >>= 1; | |
| 3975 | const has_section = @truncate(u1, cur_bit_bag) != 0; | |
| 3976 | cur_bit_bag >>= 1; | |
| 3977 | ||
| 3978 | const sub_index = extra_index; | |
| 3979 | ||
| 3980 | const hash_u32s = self.code.extra[extra_index..][0..4]; | |
| 3981 | extra_index += 4; | |
| 3982 | const line = self.code.extra[extra_index]; | |
| 3983 | extra_index += 1; | |
| 3984 | const decl_name_index = self.code.extra[extra_index]; | |
| 3985 | extra_index += 1; | |
| 3986 | const decl_index = self.code.extra[extra_index]; | |
| 3987 | extra_index += 1; | |
| 3988 | const align_inst: Inst.Ref = if (!has_align) .none else inst: { | |
| 3989 | const inst = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 3990 | extra_index += 1; | |
| 3991 | break :inst inst; | |
| 3992 | }; | |
| 3993 | const section_inst: Inst.Ref = if (!has_section) .none else inst: { | |
| 3994 | const inst = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 3995 | extra_index += 1; | |
| 3996 | break :inst inst; | |
| 3997 | }; | |
| 3998 | ||
| 3999 | const pub_str = if (is_pub) "pub " else ""; | |
| 4000 | const hash_bytes = @bitCast([16]u8, hash_u32s.*); | |
| 4001 | try stream.writeByteNTimes(' ', self.indent); | |
| 4002 | if (decl_name_index == 0) { | |
| 4003 | const name = if (is_exported) "usingnamespace" else "comptime"; | |
| 4004 | try stream.writeAll(pub_str); | |
| 4005 | try stream.writeAll(name); | |
| 4006 | } else if (decl_name_index == 1) { | |
| 4007 | try stream.writeAll("test"); | |
| 4008 | } else { | |
| 4009 | const raw_decl_name = self.code.nullTerminatedString(decl_name_index); | |
| 4010 | const decl_name = if (raw_decl_name.len == 0) | |
| 4011 | self.code.nullTerminatedString(decl_name_index + 1) | |
| 4012 | else | |
| 4013 | raw_decl_name; | |
| 4014 | const test_str = if (raw_decl_name.len == 0) "test " else ""; | |
| 4015 | const export_str = if (is_exported) "export " else ""; | |
| 4016 | try stream.print("[{d}] {s}{s}{s}{}", .{ | |
| 4017 | sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name), | |
| 4018 | }); | |
| 4019 | if (align_inst != .none) { | |
| 4020 | try stream.writeAll(" align("); | |
| 4021 | try self.writeInstRef(stream, align_inst); | |
| 4022 | try stream.writeAll(")"); | |
| 4023 | } | |
| 4024 | if (section_inst != .none) { | |
| 4025 | try stream.writeAll(" linksection("); | |
| 4026 | try self.writeInstRef(stream, section_inst); | |
| 4027 | try stream.writeAll(")"); | |
| 4028 | } | |
| 4029 | } | |
| 4030 | const tag = self.code.instructions.items(.tag)[decl_index]; | |
| 4031 | try stream.print(" line({d}) hash({}): %{d} = {s}(", .{ | |
| 4032 | line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag), | |
| 4033 | }); | |
| 4034 | ||
| 4035 | const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node; | |
| 4036 | const sub_decl_node_off = decl_block_inst_data.src_node; | |
| 4037 | self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off); | |
| 4038 | try self.writePlNodeBlockWithoutSrc(stream, decl_index); | |
| 4039 | self.parent_decl_node = parent_decl_node; | |
| 4040 | try self.writeSrc(stream, decl_block_inst_data.src()); | |
| 4041 | try stream.writeAll("\n"); | |
| 4042 | } | |
| 4043 | return extra_index; | |
| 4044 | } | |
| 4045 | ||
| 4046 | fn writeEnumDecl(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { | |
| 4047 | const small = @bitCast(Inst.EnumDecl.Small, extended.small); | |
| 4048 | var extra_index: usize = extended.operand; | |
| 4049 | ||
| 4050 | const src_node: ?i32 = if (small.has_src_node) blk: { | |
| 4051 | const src_node = @bitCast(i32, self.code.extra[extra_index]); | |
| 4052 | extra_index += 1; | |
| 4053 | break :blk src_node; | |
| 4054 | } else null; | |
| 4055 | ||
| 4056 | const tag_type_ref = if (small.has_tag_type) blk: { | |
| 4057 | const tag_type_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 4058 | extra_index += 1; | |
| 4059 | break :blk tag_type_ref; | |
| 4060 | } else .none; | |
| 4061 | ||
| 4062 | const body_len = if (small.has_body_len) blk: { | |
| 4063 | const body_len = self.code.extra[extra_index]; | |
| 4064 | extra_index += 1; | |
| 4065 | break :blk body_len; | |
| 4066 | } else 0; | |
| 4067 | ||
| 4068 | const fields_len = if (small.has_fields_len) blk: { | |
| 4069 | const fields_len = self.code.extra[extra_index]; | |
| 4070 | extra_index += 1; | |
| 4071 | break :blk fields_len; | |
| 4072 | } else 0; | |
| 4073 | ||
| 4074 | const decls_len = if (small.has_decls_len) blk: { | |
| 4075 | const decls_len = self.code.extra[extra_index]; | |
| 4076 | extra_index += 1; | |
| 4077 | break :blk decls_len; | |
| 4078 | } else 0; | |
| 4079 | ||
| 4080 | try stream.print("{s}, ", .{@tagName(small.name_strategy)}); | |
| 4081 | try self.writeFlag(stream, "nonexhaustive, ", small.nonexhaustive); | |
| 4082 | ||
| 4083 | if (decls_len == 0) { | |
| 4084 | try stream.writeAll("{}, "); | |
| 4085 | } else { | |
| 4086 | try stream.writeAll("{\n"); | |
| 4087 | self.indent += 2; | |
| 4088 | extra_index = try self.writeDecls(stream, decls_len, extra_index); | |
| 4089 | self.indent -= 2; | |
| 4090 | try stream.writeByteNTimes(' ', self.indent); | |
| 4091 | try stream.writeAll("}, "); | |
| 4092 | } | |
| 4093 | ||
| 4094 | if (tag_type_ref != .none) { | |
| 4095 | try self.writeInstRef(stream, tag_type_ref); | |
| 4096 | try stream.writeAll(", "); | |
| 4097 | } | |
| 4098 | ||
| 4099 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 4100 | extra_index += body.len; | |
| 4101 | ||
| 4102 | if (fields_len == 0) { | |
| 4103 | assert(body.len == 0); | |
| 4104 | try stream.writeAll("{}, {})"); | |
| 4105 | } else { | |
| 4106 | const prev_parent_decl_node = self.parent_decl_node; | |
| 4107 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); | |
| 4108 | self.indent += 2; | |
| 4109 | if (body.len == 0) { | |
| 4110 | try stream.writeAll("{}, {\n"); | |
| 4111 | } else { | |
| 4112 | try stream.writeAll("{\n"); | |
| 4113 | try self.writeBody(stream, body); | |
| 4114 | ||
| 4115 | try stream.writeByteNTimes(' ', self.indent - 2); | |
| 4116 | try stream.writeAll("}, {\n"); | |
| 4117 | } | |
| 4118 | ||
| 4119 | const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; | |
| 4120 | const body_end = extra_index; | |
| 4121 | extra_index += bit_bags_count; | |
| 4122 | var bit_bag_index: usize = body_end; | |
| 4123 | var cur_bit_bag: u32 = undefined; | |
| 4124 | var field_i: u32 = 0; | |
| 4125 | while (field_i < fields_len) : (field_i += 1) { | |
| 4126 | if (field_i % 32 == 0) { | |
| 4127 | cur_bit_bag = self.code.extra[bit_bag_index]; | |
| 4128 | bit_bag_index += 1; | |
| 4129 | } | |
| 4130 | const has_tag_value = @truncate(u1, cur_bit_bag) != 0; | |
| 4131 | cur_bit_bag >>= 1; | |
| 4132 | ||
| 4133 | const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); | |
| 4134 | extra_index += 1; | |
| 4135 | ||
| 4136 | try stream.writeByteNTimes(' ', self.indent); | |
| 4137 | try stream.print("{}", .{std.zig.fmtId(field_name)}); | |
| 4138 | ||
| 4139 | if (has_tag_value) { | |
| 4140 | const tag_value_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 4141 | extra_index += 1; | |
| 4142 | ||
| 4143 | try stream.writeAll(" = "); | |
| 4144 | try self.writeInstRef(stream, tag_value_ref); | |
| 4145 | } | |
| 4146 | try stream.writeAll(",\n"); | |
| 4147 | } | |
| 4148 | self.parent_decl_node = prev_parent_decl_node; | |
| 4149 | self.indent -= 2; | |
| 4150 | try stream.writeByteNTimes(' ', self.indent); | |
| 4151 | try stream.writeAll("})"); | |
| 4152 | } | |
| 4153 | try self.writeSrcNode(stream, src_node); | |
| 4154 | } | |
| 4155 | ||
| 4156 | fn writeOpaqueDecl( | |
| 4157 | self: *Writer, | |
| 4158 | stream: anytype, | |
| 4159 | inst: Inst.Index, | |
| 4160 | name_strategy: Inst.NameStrategy, | |
| 4161 | ) !void { | |
| 4162 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 4163 | const extra = self.code.extraData(Inst.OpaqueDecl, inst_data.payload_index); | |
| 4164 | const decls_len = extra.data.decls_len; | |
| 4165 | ||
| 4166 | try stream.print("{s}, ", .{@tagName(name_strategy)}); | |
| 4167 | ||
| 4168 | if (decls_len == 0) { | |
| 4169 | try stream.writeAll("}) "); | |
| 4170 | } else { | |
| 4171 | try stream.writeAll("\n"); | |
| 4172 | self.indent += 2; | |
| 4173 | _ = try self.writeDecls(stream, decls_len, extra.end); | |
| 4174 | self.indent -= 2; | |
| 4175 | try stream.writeByteNTimes(' ', self.indent); | |
| 4176 | try stream.writeAll("}) "); | |
| 4177 | } | |
| 4178 | try self.writeSrc(stream, inst_data.src()); | |
| 4179 | } | |
| 4180 | ||
| 4181 | fn writeErrorSetDecl( | |
| 4182 | self: *Writer, | |
| 4183 | stream: anytype, | |
| 4184 | inst: Inst.Index, | |
| 4185 | name_strategy: Inst.NameStrategy, | |
| 4186 | ) !void { | |
| 4187 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 4188 | const extra = self.code.extraData(Inst.ErrorSetDecl, inst_data.payload_index); | |
| 4189 | const fields = self.code.extra[extra.end..][0..extra.data.fields_len]; | |
| 4190 | ||
| 4191 | try stream.print("{s}, ", .{@tagName(name_strategy)}); | |
| 4192 | ||
| 4193 | try stream.writeAll("{\n"); | |
| 4194 | self.indent += 2; | |
| 4195 | for (fields) |str_index| { | |
| 4196 | const name = self.code.nullTerminatedString(str_index); | |
| 4197 | try stream.writeByteNTimes(' ', self.indent); | |
| 4198 | try stream.print("{},\n", .{std.zig.fmtId(name)}); | |
| 4199 | } | |
| 4200 | self.indent -= 2; | |
| 4201 | try stream.writeByteNTimes(' ', self.indent); | |
| 4202 | try stream.writeAll("}) "); | |
| 4203 | ||
| 4204 | try self.writeSrc(stream, inst_data.src()); | |
| 4205 | } | |
| 4206 | ||
| 4207 | fn writePlNodeSwitchBr( | |
| 4208 | self: *Writer, | |
| 4209 | stream: anytype, | |
| 4210 | inst: Inst.Index, | |
| 4211 | special_prong: SpecialProng, | |
| 4212 | ) !void { | |
| 4213 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 4214 | const extra = self.code.extraData(Inst.SwitchBlock, inst_data.payload_index); | |
| 4215 | const special: struct { | |
| 4216 | body: []const Inst.Index, | |
| 4217 | end: usize, | |
| 4218 | } = switch (special_prong) { | |
| 4219 | .none => .{ .body = &.{}, .end = extra.end }, | |
| 4220 | .under, .@"else" => blk: { | |
| 4221 | const body_len = self.code.extra[extra.end]; | |
| 4222 | const extra_body_start = extra.end + 1; | |
| 4223 | break :blk .{ | |
| 4224 | .body = self.code.extra[extra_body_start..][0..body_len], | |
| 4225 | .end = extra_body_start + body_len, | |
| 4226 | }; | |
| 4227 | }, | |
| 4228 | }; | |
| 4229 | ||
| 4230 | try self.writeInstRef(stream, extra.data.operand); | |
| 4231 | ||
| 4232 | if (special.body.len != 0) { | |
| 4233 | const prong_name = switch (special_prong) { | |
| 4234 | .@"else" => "else", | |
| 4235 | .under => "_", | |
| 4236 | else => unreachable, | |
| 4237 | }; | |
| 4238 | try stream.print(", {s} => {{\n", .{prong_name}); | |
| 4239 | self.indent += 2; | |
| 4240 | try self.writeBody(stream, special.body); | |
| 4241 | self.indent -= 2; | |
| 4242 | try stream.writeByteNTimes(' ', self.indent); | |
| 4243 | try stream.writeAll("}"); | |
| 4244 | } | |
| 4245 | ||
| 4246 | var extra_index: usize = special.end; | |
| 4247 | { | |
| 4248 | var scalar_i: usize = 0; | |
| 4249 | while (scalar_i < extra.data.cases_len) : (scalar_i += 1) { | |
| 4250 | const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 4251 | extra_index += 1; | |
| 4252 | const body_len = self.code.extra[extra_index]; | |
| 4253 | extra_index += 1; | |
| 4254 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 4255 | extra_index += body_len; | |
| 4256 | ||
| 4257 | try stream.writeAll(", "); | |
| 4258 | try self.writeInstRef(stream, item_ref); | |
| 4259 | try stream.writeAll(" => {\n"); | |
| 4260 | self.indent += 2; | |
| 4261 | try self.writeBody(stream, body); | |
| 4262 | self.indent -= 2; | |
| 4263 | try stream.writeByteNTimes(' ', self.indent); | |
| 4264 | try stream.writeAll("}"); | |
| 4265 | } | |
| 4266 | } | |
| 4267 | try stream.writeAll(") "); | |
| 4268 | try self.writeSrc(stream, inst_data.src()); | |
| 4269 | } | |
| 4270 | ||
| 4271 | fn writePlNodeSwitchBlockMulti( | |
| 4272 | self: *Writer, | |
| 4273 | stream: anytype, | |
| 4274 | inst: Inst.Index, | |
| 4275 | special_prong: SpecialProng, | |
| 4276 | ) !void { | |
| 4277 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 4278 | const extra = self.code.extraData(Inst.SwitchBlockMulti, inst_data.payload_index); | |
| 4279 | const special: struct { | |
| 4280 | body: []const Inst.Index, | |
| 4281 | end: usize, | |
| 4282 | } = switch (special_prong) { | |
| 4283 | .none => .{ .body = &.{}, .end = extra.end }, | |
| 4284 | .under, .@"else" => blk: { | |
| 4285 | const body_len = self.code.extra[extra.end]; | |
| 4286 | const extra_body_start = extra.end + 1; | |
| 4287 | break :blk .{ | |
| 4288 | .body = self.code.extra[extra_body_start..][0..body_len], | |
| 4289 | .end = extra_body_start + body_len, | |
| 4290 | }; | |
| 4291 | }, | |
| 4292 | }; | |
| 4293 | ||
| 4294 | try self.writeInstRef(stream, extra.data.operand); | |
| 4295 | ||
| 4296 | if (special.body.len != 0) { | |
| 4297 | const prong_name = switch (special_prong) { | |
| 4298 | .@"else" => "else", | |
| 4299 | .under => "_", | |
| 4300 | else => unreachable, | |
| 4301 | }; | |
| 4302 | try stream.print(", {s} => {{\n", .{prong_name}); | |
| 4303 | self.indent += 2; | |
| 4304 | try self.writeBody(stream, special.body); | |
| 4305 | self.indent -= 2; | |
| 4306 | try stream.writeByteNTimes(' ', self.indent); | |
| 4307 | try stream.writeAll("}"); | |
| 4308 | } | |
| 4309 | ||
| 4310 | var extra_index: usize = special.end; | |
| 4311 | { | |
| 4312 | var scalar_i: usize = 0; | |
| 4313 | while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) { | |
| 4314 | const item_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 4315 | extra_index += 1; | |
| 4316 | const body_len = self.code.extra[extra_index]; | |
| 4317 | extra_index += 1; | |
| 4318 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 4319 | extra_index += body_len; | |
| 4320 | ||
| 4321 | try stream.writeAll(", "); | |
| 4322 | try self.writeInstRef(stream, item_ref); | |
| 4323 | try stream.writeAll(" => {\n"); | |
| 4324 | self.indent += 2; | |
| 4325 | try self.writeBody(stream, body); | |
| 4326 | self.indent -= 2; | |
| 4327 | try stream.writeByteNTimes(' ', self.indent); | |
| 4328 | try stream.writeAll("}"); | |
| 4329 | } | |
| 4330 | } | |
| 4331 | { | |
| 4332 | var multi_i: usize = 0; | |
| 4333 | while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) { | |
| 4334 | const items_len = self.code.extra[extra_index]; | |
| 4335 | extra_index += 1; | |
| 4336 | const ranges_len = self.code.extra[extra_index]; | |
| 4337 | extra_index += 1; | |
| 4338 | const body_len = self.code.extra[extra_index]; | |
| 4339 | extra_index += 1; | |
| 4340 | const items = self.code.refSlice(extra_index, items_len); | |
| 4341 | extra_index += items_len; | |
| 4342 | ||
| 4343 | for (items) |item_ref| { | |
| 4344 | try stream.writeAll(", "); | |
| 4345 | try self.writeInstRef(stream, item_ref); | |
| 4346 | } | |
| 4347 | ||
| 4348 | var range_i: usize = 0; | |
| 4349 | while (range_i < ranges_len) : (range_i += 1) { | |
| 4350 | const item_first = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 4351 | extra_index += 1; | |
| 4352 | const item_last = @intToEnum(Inst.Ref, self.code.extra[extra_index]); | |
| 4353 | extra_index += 1; | |
| 4354 | ||
| 4355 | try stream.writeAll(", "); | |
| 4356 | try self.writeInstRef(stream, item_first); | |
| 4357 | try stream.writeAll("..."); | |
| 4358 | try self.writeInstRef(stream, item_last); | |
| 4359 | } | |
| 4360 | ||
| 4361 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 4362 | extra_index += body_len; | |
| 4363 | try stream.writeAll(" => {\n"); | |
| 4364 | self.indent += 2; | |
| 4365 | try self.writeBody(stream, body); | |
| 4366 | self.indent -= 2; | |
| 4367 | try stream.writeByteNTimes(' ', self.indent); | |
| 4368 | try stream.writeAll("}"); | |
| 4369 | } | |
| 4370 | } | |
| 4371 | try stream.writeAll(") "); | |
| 4372 | try self.writeSrc(stream, inst_data.src()); | |
| 4373 | } | |
| 4374 | ||
| 4375 | fn writePlNodeField(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 4376 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 4377 | const extra = self.code.extraData(Inst.Field, inst_data.payload_index).data; | |
| 4378 | const name = self.code.nullTerminatedString(extra.field_name_start); | |
| 4379 | try self.writeInstRef(stream, extra.lhs); | |
| 4380 | try stream.print(", \"{}\") ", .{std.zig.fmtEscapes(name)}); | |
| 4381 | try self.writeSrc(stream, inst_data.src()); | |
| 4382 | } | |
| 4383 | ||
| 4384 | fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 4385 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 4386 | const extra = self.code.extraData(Inst.As, inst_data.payload_index).data; | |
| 4387 | try self.writeInstRef(stream, extra.dest_type); | |
| 4388 | try stream.writeAll(", "); | |
| 4389 | try self.writeInstRef(stream, extra.operand); | |
| 4390 | try stream.writeAll(") "); | |
| 4391 | try self.writeSrc(stream, inst_data.src()); | |
| 4392 | } | |
| 4393 | ||
| 4394 | fn writeNode( | |
| 4395 | self: *Writer, | |
| 4396 | stream: anytype, | |
| 4397 | inst: Inst.Index, | |
| 4398 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 4399 | const src_node = self.code.instructions.items(.data)[inst].node; | |
| 4400 | const src: LazySrcLoc = .{ .node_offset = src_node }; | |
| 4401 | try stream.writeAll(") "); | |
| 4402 | try self.writeSrc(stream, src); | |
| 4403 | } | |
| 4404 | ||
| 4405 | fn writeStrTok( | |
| 4406 | self: *Writer, | |
| 4407 | stream: anytype, | |
| 4408 | inst: Inst.Index, | |
| 4409 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 4410 | const inst_data = self.code.instructions.items(.data)[inst].str_tok; | |
| 4411 | const str = inst_data.get(self.code); | |
| 4412 | try stream.print("\"{}\") ", .{std.zig.fmtEscapes(str)}); | |
| 4413 | try self.writeSrc(stream, inst_data.src()); | |
| 4414 | } | |
| 4415 | ||
| 4416 | fn writeFunc( | |
| 4417 | self: *Writer, | |
| 4418 | stream: anytype, | |
| 4419 | inst: Inst.Index, | |
| 4420 | inferred_error_set: bool, | |
| 4421 | ) !void { | |
| 4422 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 4423 | const src = inst_data.src(); | |
| 4424 | const extra = self.code.extraData(Inst.Func, inst_data.payload_index); | |
| 4425 | var extra_index = extra.end; | |
| 4426 | ||
| 4427 | const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 4428 | extra_index += ret_ty_body.len; | |
| 4429 | ||
| 4430 | const body = self.code.extra[extra_index..][0..extra.data.body_len]; | |
| 4431 | extra_index += body.len; | |
| 4432 | ||
| 4433 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; | |
| 4434 | if (body.len != 0) { | |
| 4435 | src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; | |
| 4436 | } | |
| 4437 | return self.writeFuncCommon( | |
| 4438 | stream, | |
| 4439 | ret_ty_body, | |
| 4440 | inferred_error_set, | |
| 4441 | false, | |
| 4442 | false, | |
| 4443 | .none, | |
| 4444 | .none, | |
| 4445 | body, | |
| 4446 | src, | |
| 4447 | src_locs, | |
| 4448 | ); | |
| 4449 | } | |
| 4450 | ||
| 4451 | fn writeFuncExtended(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { | |
| 4452 | const extra = self.code.extraData(Inst.ExtendedFunc, extended.operand); | |
| 4453 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; | |
| 4454 | const small = @bitCast(Inst.ExtendedFunc.Small, extended.small); | |
| 4455 | ||
| 4456 | var extra_index: usize = extra.end; | |
| 4457 | if (small.has_lib_name) { | |
| 4458 | const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]); | |
| 4459 | extra_index += 1; | |
| 4460 | try stream.print("lib_name=\"{}\", ", .{std.zig.fmtEscapes(lib_name)}); | |
| 4461 | } | |
| 4462 | try self.writeFlag(stream, "test, ", small.is_test); | |
| 4463 | const cc: Inst.Ref = if (!small.has_cc) .none else blk: { | |
| 4464 | const cc = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 4465 | extra_index += 1; | |
| 4466 | break :blk cc; | |
| 4467 | }; | |
| 4468 | const align_inst: Inst.Ref = if (!small.has_align) .none else blk: { | |
| 4469 | const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 4470 | extra_index += 1; | |
| 4471 | break :blk align_inst; | |
| 4472 | }; | |
| 4473 | ||
| 4474 | const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 4475 | extra_index += ret_ty_body.len; | |
| 4476 | ||
| 4477 | const body = self.code.extra[extra_index..][0..extra.data.body_len]; | |
| 4478 | extra_index += body.len; | |
| 4479 | ||
| 4480 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; | |
| 4481 | if (body.len != 0) { | |
| 4482 | src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; | |
| 4483 | } | |
| 4484 | return self.writeFuncCommon( | |
| 4485 | stream, | |
| 4486 | ret_ty_body, | |
| 4487 | small.is_inferred_error, | |
| 4488 | small.is_var_args, | |
| 4489 | small.is_extern, | |
| 4490 | cc, | |
| 4491 | align_inst, | |
| 4492 | body, | |
| 4493 | src, | |
| 4494 | src_locs, | |
| 4495 | ); | |
| 4496 | } | |
| 4497 | ||
| 4498 | fn writeVarExtended(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { | |
| 4499 | const extra = self.code.extraData(Inst.ExtendedVar, extended.operand); | |
| 4500 | const small = @bitCast(Inst.ExtendedVar.Small, extended.small); | |
| 4501 | ||
| 4502 | try self.writeInstRef(stream, extra.data.var_type); | |
| 4503 | ||
| 4504 | var extra_index: usize = extra.end; | |
| 4505 | if (small.has_lib_name) { | |
| 4506 | const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]); | |
| 4507 | extra_index += 1; | |
| 4508 | try stream.print(", lib_name=\"{}\"", .{std.zig.fmtEscapes(lib_name)}); | |
| 4509 | } | |
| 4510 | const align_inst: Inst.Ref = if (!small.has_align) .none else blk: { | |
| 4511 | const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 4512 | extra_index += 1; | |
| 4513 | break :blk align_inst; | |
| 4514 | }; | |
| 4515 | const init_inst: Inst.Ref = if (!small.has_init) .none else blk: { | |
| 4516 | const init_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 4517 | extra_index += 1; | |
| 4518 | break :blk init_inst; | |
| 4519 | }; | |
| 4520 | try self.writeFlag(stream, ", is_extern", small.is_extern); | |
| 4521 | try self.writeOptionalInstRef(stream, ", align=", align_inst); | |
| 4522 | try self.writeOptionalInstRef(stream, ", init=", init_inst); | |
| 4523 | try stream.writeAll("))"); | |
| 4524 | } | |
| 4525 | ||
| 4526 | fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 4527 | const inst_data = self.code.instructions.items(.data)[inst].bool_br; | |
| 4528 | const extra = self.code.extraData(Inst.Block, inst_data.payload_index); | |
| 4529 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; | |
| 4530 | try self.writeInstRef(stream, inst_data.lhs); | |
| 4531 | try stream.writeAll(", {\n"); | |
| 4532 | self.indent += 2; | |
| 4533 | try self.writeBody(stream, body); | |
| 4534 | self.indent -= 2; | |
| 4535 | try stream.writeByteNTimes(' ', self.indent); | |
| 4536 | try stream.writeAll("})"); | |
| 4537 | } | |
| 4538 | ||
| 4539 | fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 4540 | const int_type = self.code.instructions.items(.data)[inst].int_type; | |
| 4541 | const prefix: u8 = switch (int_type.signedness) { | |
| 4542 | .signed => 'i', | |
| 4543 | .unsigned => 'u', | |
| 4544 | }; | |
| 4545 | try stream.print("{c}{d}) ", .{ prefix, int_type.bit_count }); | |
| 4546 | try self.writeSrc(stream, int_type.src()); | |
| 4547 | } | |
| 4548 | ||
| 4549 | fn writeBreak(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 4550 | const inst_data = self.code.instructions.items(.data)[inst].@"break"; | |
| 4551 | ||
| 4552 | try self.writeInstIndex(stream, inst_data.block_inst); | |
| 4553 | try stream.writeAll(", "); | |
| 4554 | try self.writeInstRef(stream, inst_data.operand); | |
| 4555 | try stream.writeAll(")"); | |
| 4556 | } | |
| 4557 | ||
| 4558 | fn writeUnreachable(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 4559 | const inst_data = self.code.instructions.items(.data)[inst].@"unreachable"; | |
| 4560 | const safety_str = if (inst_data.safety) "safe" else "unsafe"; | |
| 4561 | try stream.print("{s}) ", .{safety_str}); | |
| 4562 | try self.writeSrc(stream, inst_data.src()); | |
| 4563 | } | |
| 4564 | ||
| 4565 | fn writeFuncCommon( | |
| 4566 | self: *Writer, | |
| 4567 | stream: anytype, | |
| 4568 | ret_ty_body: []const Inst.Index, | |
| 4569 | inferred_error_set: bool, | |
| 4570 | var_args: bool, | |
| 4571 | is_extern: bool, | |
| 4572 | cc: Inst.Ref, | |
| 4573 | align_inst: Inst.Ref, | |
| 4574 | body: []const Inst.Index, | |
| 4575 | src: LazySrcLoc, | |
| 4576 | src_locs: Zir.Inst.Func.SrcLocs, | |
| 4577 | ) !void { | |
| 4578 | if (ret_ty_body.len == 0) { | |
| 4579 | try stream.writeAll("ret_ty=void"); | |
| 4580 | } else { | |
| 4581 | try stream.writeAll("ret_ty={\n"); | |
| 4582 | self.indent += 2; | |
| 4583 | try self.writeBody(stream, ret_ty_body); | |
| 4584 | self.indent -= 2; | |
| 4585 | try stream.writeByteNTimes(' ', self.indent); | |
| 4586 | try stream.writeAll("}"); | |
| 4587 | } | |
| 4588 | ||
| 4589 | try self.writeOptionalInstRef(stream, ", cc=", cc); | |
| 4590 | try self.writeOptionalInstRef(stream, ", align=", align_inst); | |
| 4591 | try self.writeFlag(stream, ", vargs", var_args); | |
| 4592 | try self.writeFlag(stream, ", extern", is_extern); | |
| 4593 | try self.writeFlag(stream, ", inferror", inferred_error_set); | |
| 4594 | ||
| 4595 | if (body.len == 0) { | |
| 4596 | try stream.writeAll(", body={}) "); | |
| 4597 | } else { | |
| 4598 | try stream.writeAll(", body={\n"); | |
| 4599 | self.indent += 2; | |
| 4600 | try self.writeBody(stream, body); | |
| 4601 | self.indent -= 2; | |
| 4602 | try stream.writeByteNTimes(' ', self.indent); | |
| 4603 | try stream.writeAll("}) "); | |
| 4604 | } | |
| 4605 | if (body.len != 0) { | |
| 4606 | try stream.print("(lbrace={d}:{d},rbrace={d}:{d}) ", .{ | |
| 4607 | src_locs.lbrace_line, @truncate(u16, src_locs.columns), | |
| 4608 | src_locs.rbrace_line, @truncate(u16, src_locs.columns >> 16), | |
| 4609 | }); | |
| 4610 | } | |
| 4611 | try self.writeSrc(stream, src); | |
| 4612 | } | |
| 4613 | ||
| 4614 | fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 4615 | const inst_data = self.code.instructions.items(.data)[inst].switch_capture; | |
| 4616 | try self.writeInstIndex(stream, inst_data.switch_inst); | |
| 4617 | try stream.print(", {d})", .{inst_data.prong_index}); | |
| 4618 | } | |
| 4619 | ||
| 4620 | fn writeDbgStmt(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 4621 | const inst_data = self.code.instructions.items(.data)[inst].dbg_stmt; | |
| 4622 | try stream.print("{d}, {d})", .{ inst_data.line, inst_data.column }); | |
| 4623 | } | |
| 4624 | ||
| 4625 | fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void { | |
| 4626 | var i: usize = @enumToInt(ref); | |
| 4627 | ||
| 4628 | if (i < Inst.Ref.typed_value_map.len) { | |
| 4629 | return stream.print("@{}", .{ref}); | |
| 4630 | } | |
| 4631 | i -= Inst.Ref.typed_value_map.len; | |
| 4632 | ||
| 4633 | return self.writeInstIndex(stream, @intCast(Inst.Index, i)); | |
| 4634 | } | |
| 4635 | ||
| 4636 | fn writeInstIndex(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 4637 | _ = self; | |
| 4638 | return stream.print("%{d}", .{inst}); | |
| 4639 | } | |
| 4640 | ||
| 4641 | fn writeOptionalInstRef( | |
| 4642 | self: *Writer, | |
| 4643 | stream: anytype, | |
| 4644 | prefix: []const u8, | |
| 4645 | inst: Inst.Ref, | |
| 4646 | ) !void { | |
| 4647 | if (inst == .none) return; | |
| 4648 | try stream.writeAll(prefix); | |
| 4649 | try self.writeInstRef(stream, inst); | |
| 4650 | } | |
| 4651 | ||
| 4652 | fn writeFlag( | |
| 4653 | self: *Writer, | |
| 4654 | stream: anytype, | |
| 4655 | name: []const u8, | |
| 4656 | flag: bool, | |
| 4657 | ) !void { | |
| 4658 | _ = self; | |
| 4659 | if (!flag) return; | |
| 4660 | try stream.writeAll(name); | |
| 4661 | } | |
| 4662 | ||
| 4663 | fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void { | |
| 4664 | const tree = self.file.tree; | |
| 4665 | const src_loc: Module.SrcLoc = .{ | |
| 4666 | .file_scope = self.file, | |
| 4667 | .parent_decl_node = self.parent_decl_node, | |
| 4668 | .lazy = src, | |
| 4669 | }; | |
| 4670 | // Caller must ensure AST tree is loaded. | |
| 4671 | const abs_byte_off = src_loc.byteOffset(self.gpa) catch unreachable; | |
| 4672 | const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off); | |
| 4673 | try stream.print("{s}:{d}:{d}", .{ | |
| 4674 | @tagName(src), delta_line.line + 1, delta_line.column + 1, | |
| 4675 | }); | |
| 4676 | } | |
| 4677 | ||
| 4678 | fn writeSrcNode(self: *Writer, stream: anytype, src_node: ?i32) !void { | |
| 4679 | const node_offset = src_node orelse return; | |
| 4680 | const src: LazySrcLoc = .{ .node_offset = node_offset }; | |
| 4681 | try stream.writeAll(" "); | |
| 4682 | return self.writeSrc(stream, src); | |
| 4683 | } | |
| 4684 | ||
| 4685 | fn writeBody(self: *Writer, stream: anytype, body: []const Inst.Index) !void { | |
| 4686 | for (body) |inst| { | |
| 4687 | try stream.writeByteNTimes(' ', self.indent); | |
| 4688 | try stream.print("%{d} ", .{inst}); | |
| 4689 | try self.writeInstToStream(stream, inst); | |
| 4690 | try stream.writeByte('\n'); | |
| 4691 | } | |
| 4692 | } | |
| 4693 | }; | |
| 4694 | ||
| 4695 | 2941 | pub const DeclIterator = struct { |
| 4696 | 2942 | extra_index: usize, |
| 4697 | 2943 | bit_bag_index: usize, |
| ... | ... | @@ -4735,15 +2981,6 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator { |
| 4735 | 2981 | const tags = zir.instructions.items(.tag); |
| 4736 | 2982 | const datas = zir.instructions.items(.data); |
| 4737 | 2983 | switch (tags[decl_inst]) { |
| 4738 | .opaque_decl, | |
| 4739 | .opaque_decl_anon, | |
| 4740 | .opaque_decl_func, | |
| 4741 | => { | |
| 4742 | const inst_data = datas[decl_inst].pl_node; | |
| 4743 | const extra = zir.extraData(Inst.OpaqueDecl, inst_data.payload_index); | |
| 4744 | return declIteratorInner(zir, extra.end, extra.data.decls_len); | |
| 4745 | }, | |
| 4746 | ||
| 4747 | 2984 | // Functions are allowed and yield no iterations. |
| 4748 | 2985 | // There is one case matching this in the extended instruction set below. |
| 4749 | 2986 | .func, |
| ... | ... | @@ -4798,6 +3035,18 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator { |
| 4798 | 3035 | |
| 4799 | 3036 | return declIteratorInner(zir, extra_index, decls_len); |
| 4800 | 3037 | }, |
| 3038 | .opaque_decl => { | |
| 3039 | const small = @bitCast(Inst.OpaqueDecl.Small, extended.small); | |
| 3040 | var extra_index: usize = extended.operand; | |
| 3041 | extra_index += @boolToInt(small.has_src_node); | |
| 3042 | const decls_len = if (small.has_decls_len) decls_len: { | |
| 3043 | const decls_len = zir.extra[extra_index]; | |
| 3044 | extra_index += 1; | |
| 3045 | break :decls_len decls_len; | |
| 3046 | } else 0; | |
| 3047 | ||
| 3048 | return declIteratorInner(zir, extra_index, decls_len); | |
| 3049 | }, | |
| 4801 | 3050 | else => unreachable, |
| 4802 | 3051 | } |
| 4803 | 3052 | }, |
| ... | ... | @@ -4835,13 +3084,6 @@ fn findDeclsInner( |
| 4835 | 3084 | const datas = zir.instructions.items(.data); |
| 4836 | 3085 | |
| 4837 | 3086 | switch (tags[inst]) { |
| 4838 | // Decl instructions are interesting but have no body. | |
| 4839 | // TODO yes they do have a body actually. recurse over them just like block instructions. | |
| 4840 | .opaque_decl, | |
| 4841 | .opaque_decl_anon, | |
| 4842 | .opaque_decl_func, | |
| 4843 | => return list.append(inst), | |
| 4844 | ||
| 4845 | 3087 | // Functions instructions are interesting and have a body. |
| 4846 | 3088 | .func, |
| 4847 | 3089 | .func_inferred, |
| ... | ... | @@ -4869,9 +3111,12 @@ fn findDeclsInner( |
| 4869 | 3111 | return zir.findDeclsBody(list, body); |
| 4870 | 3112 | }, |
| 4871 | 3113 | |
| 3114 | // Decl instructions are interesting but have no body. | |
| 3115 | // TODO yes they do have a body actually. recurse over them just like block instructions. | |
| 4872 | 3116 | .struct_decl, |
| 4873 | 3117 | .union_decl, |
| 4874 | 3118 | .enum_decl, |
| 3119 | .opaque_decl, | |
| 4875 | 3120 | => return list.append(inst), |
| 4876 | 3121 | |
| 4877 | 3122 | else => return, |
src/arch/aarch64/bits.zig created+1230| ... | ... | @@ -0,0 +1,1230 @@ |
| 1 | const std = @import("std"); | |
| 2 | const DW = std.dwarf; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const testing = std.testing; | |
| 5 | ||
| 6 | // zig fmt: off | |
| 7 | ||
| 8 | /// General purpose registers in the AArch64 instruction set | |
| 9 | pub const Register = enum(u6) { | |
| 10 | // 64-bit registers | |
| 11 | x0, x1, x2, x3, x4, x5, x6, x7, | |
| 12 | x8, x9, x10, x11, x12, x13, x14, x15, | |
| 13 | x16, x17, x18, x19, x20, x21, x22, x23, | |
| 14 | x24, x25, x26, x27, x28, x29, x30, xzr, | |
| 15 | ||
| 16 | // 32-bit registers | |
| 17 | w0, w1, w2, w3, w4, w5, w6, w7, | |
| 18 | w8, w9, w10, w11, w12, w13, w14, w15, | |
| 19 | w16, w17, w18, w19, w20, w21, w22, w23, | |
| 20 | w24, w25, w26, w27, w28, w29, w30, wzr, | |
| 21 | ||
| 22 | pub const sp = Register.xzr; | |
| 23 | ||
| 24 | pub fn id(self: Register) u5 { | |
| 25 | return @truncate(u5, @enumToInt(self)); | |
| 26 | } | |
| 27 | ||
| 28 | /// Returns the bit-width of the register. | |
| 29 | pub fn size(self: Register) u7 { | |
| 30 | return switch (@enumToInt(self)) { | |
| 31 | 0...31 => 64, | |
| 32 | 32...63 => 32, | |
| 33 | }; | |
| 34 | } | |
| 35 | ||
| 36 | /// Convert from any register to its 64 bit alias. | |
| 37 | pub fn to64(self: Register) Register { | |
| 38 | return @intToEnum(Register, self.id()); | |
| 39 | } | |
| 40 | ||
| 41 | /// Convert from any register to its 32 bit alias. | |
| 42 | pub fn to32(self: Register) Register { | |
| 43 | return @intToEnum(Register, @as(u6, self.id()) + 32); | |
| 44 | } | |
| 45 | ||
| 46 | /// Returns the index into `callee_preserved_regs`. | |
| 47 | pub fn allocIndex(self: Register) ?u4 { | |
| 48 | inline for (callee_preserved_regs) |cpreg, i| { | |
| 49 | if (self.id() == cpreg.id()) return i; | |
| 50 | } | |
| 51 | return null; | |
| 52 | } | |
| 53 | ||
| 54 | pub fn dwarfLocOp(self: Register) u8 { | |
| 55 | return @as(u8, self.id()) + DW.OP.reg0; | |
| 56 | } | |
| 57 | }; | |
| 58 | ||
| 59 | // zig fmt: on | |
| 60 | ||
| 61 | pub const callee_preserved_regs = [_]Register{ | |
| 62 | .x19, .x20, .x21, .x22, .x23, | |
| 63 | .x24, .x25, .x26, .x27, .x28, | |
| 64 | }; | |
| 65 | ||
| 66 | pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | |
| 67 | pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | |
| 68 | ||
| 69 | test "Register.id" { | |
| 70 | try testing.expectEqual(@as(u5, 0), Register.x0.id()); | |
| 71 | try testing.expectEqual(@as(u5, 0), Register.w0.id()); | |
| 72 | ||
| 73 | try testing.expectEqual(@as(u5, 31), Register.xzr.id()); | |
| 74 | try testing.expectEqual(@as(u5, 31), Register.wzr.id()); | |
| 75 | ||
| 76 | try testing.expectEqual(@as(u5, 31), Register.sp.id()); | |
| 77 | try testing.expectEqual(@as(u5, 31), Register.sp.id()); | |
| 78 | } | |
| 79 | ||
| 80 | test "Register.size" { | |
| 81 | try testing.expectEqual(@as(u7, 64), Register.x19.size()); | |
| 82 | try testing.expectEqual(@as(u7, 32), Register.w3.size()); | |
| 83 | } | |
| 84 | ||
| 85 | test "Register.to64/to32" { | |
| 86 | try testing.expectEqual(Register.x0, Register.w0.to64()); | |
| 87 | try testing.expectEqual(Register.x0, Register.x0.to64()); | |
| 88 | ||
| 89 | try testing.expectEqual(Register.w3, Register.w3.to32()); | |
| 90 | try testing.expectEqual(Register.w3, Register.x3.to32()); | |
| 91 | } | |
| 92 | ||
| 93 | // zig fmt: off | |
| 94 | ||
| 95 | /// Scalar floating point registers in the aarch64 instruction set | |
| 96 | pub const FloatingPointRegister = enum(u8) { | |
| 97 | // 128-bit registers | |
| 98 | q0, q1, q2, q3, q4, q5, q6, q7, | |
| 99 | q8, q9, q10, q11, q12, q13, q14, q15, | |
| 100 | q16, q17, q18, q19, q20, q21, q22, q23, | |
| 101 | q24, q25, q26, q27, q28, q29, q30, q31, | |
| 102 | ||
| 103 | // 64-bit registers | |
| 104 | d0, d1, d2, d3, d4, d5, d6, d7, | |
| 105 | d8, d9, d10, d11, d12, d13, d14, d15, | |
| 106 | d16, d17, d18, d19, d20, d21, d22, d23, | |
| 107 | d24, d25, d26, d27, d28, d29, d30, d31, | |
| 108 | ||
| 109 | // 32-bit registers | |
| 110 | s0, s1, s2, s3, s4, s5, s6, s7, | |
| 111 | s8, s9, s10, s11, s12, s13, s14, s15, | |
| 112 | s16, s17, s18, s19, s20, s21, s22, s23, | |
| 113 | s24, s25, s26, s27, s28, s29, s30, s31, | |
| 114 | ||
| 115 | // 16-bit registers | |
| 116 | h0, h1, h2, h3, h4, h5, h6, h7, | |
| 117 | h8, h9, h10, h11, h12, h13, h14, h15, | |
| 118 | h16, h17, h18, h19, h20, h21, h22, h23, | |
| 119 | h24, h25, h26, h27, h28, h29, h30, h31, | |
| 120 | ||
| 121 | // 8-bit registers | |
| 122 | b0, b1, b2, b3, b4, b5, b6, b7, | |
| 123 | b8, b9, b10, b11, b12, b13, b14, b15, | |
| 124 | b16, b17, b18, b19, b20, b21, b22, b23, | |
| 125 | b24, b25, b26, b27, b28, b29, b30, b31, | |
| 126 | ||
| 127 | pub fn id(self: FloatingPointRegister) u5 { | |
| 128 | return @truncate(u5, @enumToInt(self)); | |
| 129 | } | |
| 130 | ||
| 131 | /// Returns the bit-width of the register. | |
| 132 | pub fn size(self: FloatingPointRegister) u8 { | |
| 133 | return switch (@enumToInt(self)) { | |
| 134 | 0...31 => 128, | |
| 135 | 32...63 => 64, | |
| 136 | 64...95 => 32, | |
| 137 | 96...127 => 16, | |
| 138 | 128...159 => 8, | |
| 139 | else => unreachable, | |
| 140 | }; | |
| 141 | } | |
| 142 | ||
| 143 | /// Convert from any register to its 128 bit alias. | |
| 144 | pub fn to128(self: FloatingPointRegister) FloatingPointRegister { | |
| 145 | return @intToEnum(FloatingPointRegister, self.id()); | |
| 146 | } | |
| 147 | ||
| 148 | /// Convert from any register to its 64 bit alias. | |
| 149 | pub fn to64(self: FloatingPointRegister) FloatingPointRegister { | |
| 150 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32); | |
| 151 | } | |
| 152 | ||
| 153 | /// Convert from any register to its 32 bit alias. | |
| 154 | pub fn to32(self: FloatingPointRegister) FloatingPointRegister { | |
| 155 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64); | |
| 156 | } | |
| 157 | ||
| 158 | /// Convert from any register to its 16 bit alias. | |
| 159 | pub fn to16(self: FloatingPointRegister) FloatingPointRegister { | |
| 160 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96); | |
| 161 | } | |
| 162 | ||
| 163 | /// Convert from any register to its 8 bit alias. | |
| 164 | pub fn to8(self: FloatingPointRegister) FloatingPointRegister { | |
| 165 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128); | |
| 166 | } | |
| 167 | }; | |
| 168 | ||
| 169 | // zig fmt: on | |
| 170 | ||
| 171 | test "FloatingPointRegister.id" { | |
| 172 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id()); | |
| 173 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id()); | |
| 174 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id()); | |
| 175 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id()); | |
| 176 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id()); | |
| 177 | ||
| 178 | try testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id()); | |
| 179 | try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id()); | |
| 180 | } | |
| 181 | ||
| 182 | test "FloatingPointRegister.size" { | |
| 183 | try testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size()); | |
| 184 | try testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size()); | |
| 185 | try testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size()); | |
| 186 | try testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size()); | |
| 187 | try testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size()); | |
| 188 | } | |
| 189 | ||
| 190 | test "FloatingPointRegister.toX" { | |
| 191 | try testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128()); | |
| 192 | try testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128()); | |
| 193 | try testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128()); | |
| 194 | ||
| 195 | try testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64()); | |
| 196 | try testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32()); | |
| 197 | try testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16()); | |
| 198 | try testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8()); | |
| 199 | } | |
| 200 | ||
| 201 | /// Represents an instruction in the AArch64 instruction set | |
| 202 | pub const Instruction = union(enum) { | |
| 203 | move_wide_immediate: packed struct { | |
| 204 | rd: u5, | |
| 205 | imm16: u16, | |
| 206 | hw: u2, | |
| 207 | fixed: u6 = 0b100101, | |
| 208 | opc: u2, | |
| 209 | sf: u1, | |
| 210 | }, | |
| 211 | pc_relative_address: packed struct { | |
| 212 | rd: u5, | |
| 213 | immhi: u19, | |
| 214 | fixed: u5 = 0b10000, | |
| 215 | immlo: u2, | |
| 216 | op: u1, | |
| 217 | }, | |
| 218 | load_store_register: packed struct { | |
| 219 | rt: u5, | |
| 220 | rn: u5, | |
| 221 | offset: u12, | |
| 222 | opc: u2, | |
| 223 | op1: u2, | |
| 224 | v: u1, | |
| 225 | fixed: u3 = 0b111, | |
| 226 | size: u2, | |
| 227 | }, | |
| 228 | load_store_register_pair: packed struct { | |
| 229 | rt1: u5, | |
| 230 | rn: u5, | |
| 231 | rt2: u5, | |
| 232 | imm7: u7, | |
| 233 | load: u1, | |
| 234 | encoding: u2, | |
| 235 | fixed: u5 = 0b101_0_0, | |
| 236 | opc: u2, | |
| 237 | }, | |
| 238 | load_literal: packed struct { | |
| 239 | rt: u5, | |
| 240 | imm19: u19, | |
| 241 | fixed: u6 = 0b011_0_00, | |
| 242 | opc: u2, | |
| 243 | }, | |
| 244 | exception_generation: packed struct { | |
| 245 | ll: u2, | |
| 246 | op2: u3, | |
| 247 | imm16: u16, | |
| 248 | opc: u3, | |
| 249 | fixed: u8 = 0b1101_0100, | |
| 250 | }, | |
| 251 | unconditional_branch_register: packed struct { | |
| 252 | op4: u5, | |
| 253 | rn: u5, | |
| 254 | op3: u6, | |
| 255 | op2: u5, | |
| 256 | opc: u4, | |
| 257 | fixed: u7 = 0b1101_011, | |
| 258 | }, | |
| 259 | unconditional_branch_immediate: packed struct { | |
| 260 | imm26: u26, | |
| 261 | fixed: u5 = 0b00101, | |
| 262 | op: u1, | |
| 263 | }, | |
| 264 | no_operation: packed struct { | |
| 265 | fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, | |
| 266 | }, | |
| 267 | logical_shifted_register: packed struct { | |
| 268 | rd: u5, | |
| 269 | rn: u5, | |
| 270 | imm6: u6, | |
| 271 | rm: u5, | |
| 272 | n: u1, | |
| 273 | shift: u2, | |
| 274 | fixed: u5 = 0b01010, | |
| 275 | opc: u2, | |
| 276 | sf: u1, | |
| 277 | }, | |
| 278 | add_subtract_immediate: packed struct { | |
| 279 | rd: u5, | |
| 280 | rn: u5, | |
| 281 | imm12: u12, | |
| 282 | sh: u1, | |
| 283 | fixed: u6 = 0b100010, | |
| 284 | s: u1, | |
| 285 | op: u1, | |
| 286 | sf: u1, | |
| 287 | }, | |
| 288 | conditional_branch: struct { | |
| 289 | cond: u4, | |
| 290 | o0: u1, | |
| 291 | imm19: u19, | |
| 292 | o1: u1, | |
| 293 | fixed: u7 = 0b0101010, | |
| 294 | }, | |
| 295 | compare_and_branch: struct { | |
| 296 | rt: u5, | |
| 297 | imm19: u19, | |
| 298 | op: u1, | |
| 299 | fixed: u6 = 0b011010, | |
| 300 | sf: u1, | |
| 301 | }, | |
| 302 | ||
| 303 | pub const Shift = struct { | |
| 304 | shift: Type = .lsl, | |
| 305 | amount: u6 = 0, | |
| 306 | ||
| 307 | pub const Type = enum(u2) { | |
| 308 | lsl, | |
| 309 | lsr, | |
| 310 | asr, | |
| 311 | ror, | |
| 312 | }; | |
| 313 | ||
| 314 | pub const none = Shift{ | |
| 315 | .shift = .lsl, | |
| 316 | .amount = 0, | |
| 317 | }; | |
| 318 | }; | |
| 319 | ||
| 320 | pub const Condition = enum(u4) { | |
| 321 | /// Integer: Equal | |
| 322 | /// Floating point: Equal | |
| 323 | eq, | |
| 324 | /// Integer: Not equal | |
| 325 | /// Floating point: Not equal or unordered | |
| 326 | ne, | |
| 327 | /// Integer: Carry set | |
| 328 | /// Floating point: Greater than, equal, or unordered | |
| 329 | cs, | |
| 330 | /// Integer: Carry clear | |
| 331 | /// Floating point: Less than | |
| 332 | cc, | |
| 333 | /// Integer: Minus, negative | |
| 334 | /// Floating point: Less than | |
| 335 | mi, | |
| 336 | /// Integer: Plus, positive or zero | |
| 337 | /// Floating point: Greater than, equal, or unordered | |
| 338 | pl, | |
| 339 | /// Integer: Overflow | |
| 340 | /// Floating point: Unordered | |
| 341 | vs, | |
| 342 | /// Integer: No overflow | |
| 343 | /// Floating point: Ordered | |
| 344 | vc, | |
| 345 | /// Integer: Unsigned higher | |
| 346 | /// Floating point: Greater than, or unordered | |
| 347 | hi, | |
| 348 | /// Integer: Unsigned lower or same | |
| 349 | /// Floating point: Less than or equal | |
| 350 | ls, | |
| 351 | /// Integer: Signed greater than or equal | |
| 352 | /// Floating point: Greater than or equal | |
| 353 | ge, | |
| 354 | /// Integer: Signed less than | |
| 355 | /// Floating point: Less than, or unordered | |
| 356 | lt, | |
| 357 | /// Integer: Signed greater than | |
| 358 | /// Floating point: Greater than | |
| 359 | gt, | |
| 360 | /// Integer: Signed less than or equal | |
| 361 | /// Floating point: Less than, equal, or unordered | |
| 362 | le, | |
| 363 | /// Integer: Always | |
| 364 | /// Floating point: Always | |
| 365 | al, | |
| 366 | /// Integer: Always | |
| 367 | /// Floating point: Always | |
| 368 | nv, | |
| 369 | }; | |
| 370 | ||
| 371 | pub fn toU32(self: Instruction) u32 { | |
| 372 | return switch (self) { | |
| 373 | .move_wide_immediate => |v| @bitCast(u32, v), | |
| 374 | .pc_relative_address => |v| @bitCast(u32, v), | |
| 375 | .load_store_register => |v| @bitCast(u32, v), | |
| 376 | .load_store_register_pair => |v| @bitCast(u32, v), | |
| 377 | .load_literal => |v| @bitCast(u32, v), | |
| 378 | .exception_generation => |v| @bitCast(u32, v), | |
| 379 | .unconditional_branch_register => |v| @bitCast(u32, v), | |
| 380 | .unconditional_branch_immediate => |v| @bitCast(u32, v), | |
| 381 | .no_operation => |v| @bitCast(u32, v), | |
| 382 | .logical_shifted_register => |v| @bitCast(u32, v), | |
| 383 | .add_subtract_immediate => |v| @bitCast(u32, v), | |
| 384 | // TODO once packed structs work, this can be refactored | |
| 385 | .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), | |
| 386 | .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), | |
| 387 | }; | |
| 388 | } | |
| 389 | ||
| 390 | fn moveWideImmediate( | |
| 391 | opc: u2, | |
| 392 | rd: Register, | |
| 393 | imm16: u16, | |
| 394 | shift: u6, | |
| 395 | ) Instruction { | |
| 396 | switch (rd.size()) { | |
| 397 | 32 => { | |
| 398 | assert(shift % 16 == 0 and shift <= 16); | |
| 399 | return Instruction{ | |
| 400 | .move_wide_immediate = .{ | |
| 401 | .rd = rd.id(), | |
| 402 | .imm16 = imm16, | |
| 403 | .hw = @intCast(u2, shift / 16), | |
| 404 | .opc = opc, | |
| 405 | .sf = 0, | |
| 406 | }, | |
| 407 | }; | |
| 408 | }, | |
| 409 | 64 => { | |
| 410 | assert(shift % 16 == 0 and shift <= 48); | |
| 411 | return Instruction{ | |
| 412 | .move_wide_immediate = .{ | |
| 413 | .rd = rd.id(), | |
| 414 | .imm16 = imm16, | |
| 415 | .hw = @intCast(u2, shift / 16), | |
| 416 | .opc = opc, | |
| 417 | .sf = 1, | |
| 418 | }, | |
| 419 | }; | |
| 420 | }, | |
| 421 | else => unreachable, // unexpected register size | |
| 422 | } | |
| 423 | } | |
| 424 | ||
| 425 | fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { | |
| 426 | assert(rd.size() == 64); | |
| 427 | const imm21_u = @bitCast(u21, imm21); | |
| 428 | return Instruction{ | |
| 429 | .pc_relative_address = .{ | |
| 430 | .rd = rd.id(), | |
| 431 | .immlo = @truncate(u2, imm21_u), | |
| 432 | .immhi = @truncate(u19, imm21_u >> 2), | |
| 433 | .op = op, | |
| 434 | }, | |
| 435 | }; | |
| 436 | } | |
| 437 | ||
| 438 | /// Represents the offset operand of a load or store instruction. | |
| 439 | /// Data can be loaded from memory with either an immediate offset | |
| 440 | /// or an offset that is stored in some register. | |
| 441 | pub const LoadStoreOffset = union(enum) { | |
| 442 | Immediate: union(enum) { | |
| 443 | PostIndex: i9, | |
| 444 | PreIndex: i9, | |
| 445 | Unsigned: u12, | |
| 446 | }, | |
| 447 | Register: struct { | |
| 448 | rm: u5, | |
| 449 | shift: union(enum) { | |
| 450 | Uxtw: u2, | |
| 451 | Lsl: u2, | |
| 452 | Sxtw: u2, | |
| 453 | Sxtx: u2, | |
| 454 | }, | |
| 455 | }, | |
| 456 | ||
| 457 | pub const none = LoadStoreOffset{ | |
| 458 | .Immediate = .{ .Unsigned = 0 }, | |
| 459 | }; | |
| 460 | ||
| 461 | pub fn toU12(self: LoadStoreOffset) u12 { | |
| 462 | return switch (self) { | |
| 463 | .Immediate => |imm_type| switch (imm_type) { | |
| 464 | .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1, | |
| 465 | .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3, | |
| 466 | .Unsigned => |v| v, | |
| 467 | }, | |
| 468 | .Register => |r| switch (r.shift) { | |
| 469 | .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, | |
| 470 | .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, | |
| 471 | .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, | |
| 472 | .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, | |
| 473 | }, | |
| 474 | }; | |
| 475 | } | |
| 476 | ||
| 477 | pub fn imm(offset: u12) LoadStoreOffset { | |
| 478 | return .{ | |
| 479 | .Immediate = .{ .Unsigned = offset }, | |
| 480 | }; | |
| 481 | } | |
| 482 | ||
| 483 | pub fn imm_post_index(offset: i9) LoadStoreOffset { | |
| 484 | return .{ | |
| 485 | .Immediate = .{ .PostIndex = offset }, | |
| 486 | }; | |
| 487 | } | |
| 488 | ||
| 489 | pub fn imm_pre_index(offset: i9) LoadStoreOffset { | |
| 490 | return .{ | |
| 491 | .Immediate = .{ .PreIndex = offset }, | |
| 492 | }; | |
| 493 | } | |
| 494 | ||
| 495 | pub fn reg(rm: Register) LoadStoreOffset { | |
| 496 | return .{ | |
| 497 | .Register = .{ | |
| 498 | .rm = rm.id(), | |
| 499 | .shift = .{ | |
| 500 | .Lsl = 0, | |
| 501 | }, | |
| 502 | }, | |
| 503 | }; | |
| 504 | } | |
| 505 | ||
| 506 | pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset { | |
| 507 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); | |
| 508 | return .{ | |
| 509 | .Register = .{ | |
| 510 | .rm = rm.id(), | |
| 511 | .shift = .{ | |
| 512 | .Uxtw = shift, | |
| 513 | }, | |
| 514 | }, | |
| 515 | }; | |
| 516 | } | |
| 517 | ||
| 518 | pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset { | |
| 519 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); | |
| 520 | return .{ | |
| 521 | .Register = .{ | |
| 522 | .rm = rm.id(), | |
| 523 | .shift = .{ | |
| 524 | .Lsl = shift, | |
| 525 | }, | |
| 526 | }, | |
| 527 | }; | |
| 528 | } | |
| 529 | ||
| 530 | pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset { | |
| 531 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); | |
| 532 | return .{ | |
| 533 | .Register = .{ | |
| 534 | .rm = rm.id(), | |
| 535 | .shift = .{ | |
| 536 | .Sxtw = shift, | |
| 537 | }, | |
| 538 | }, | |
| 539 | }; | |
| 540 | } | |
| 541 | ||
| 542 | pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset { | |
| 543 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); | |
| 544 | return .{ | |
| 545 | .Register = .{ | |
| 546 | .rm = rm.id(), | |
| 547 | .shift = .{ | |
| 548 | .Sxtx = shift, | |
| 549 | }, | |
| 550 | }, | |
| 551 | }; | |
| 552 | } | |
| 553 | }; | |
| 554 | ||
| 555 | /// Which kind of load/store to perform | |
| 556 | const LoadStoreVariant = enum { | |
| 557 | /// 32-bit or 64-bit | |
| 558 | str, | |
| 559 | /// 16-bit, zero-extended | |
| 560 | strh, | |
| 561 | /// 8-bit, zero-extended | |
| 562 | strb, | |
| 563 | /// 32-bit or 64-bit | |
| 564 | ldr, | |
| 565 | /// 16-bit, zero-extended | |
| 566 | ldrh, | |
| 567 | /// 8-bit, zero-extended | |
| 568 | ldrb, | |
| 569 | }; | |
| 570 | ||
| 571 | fn loadStoreRegister( | |
| 572 | rt: Register, | |
| 573 | rn: Register, | |
| 574 | offset: LoadStoreOffset, | |
| 575 | variant: LoadStoreVariant, | |
| 576 | ) Instruction { | |
| 577 | const off = offset.toU12(); | |
| 578 | const op1: u2 = blk: { | |
| 579 | switch (offset) { | |
| 580 | .Immediate => |imm| switch (imm) { | |
| 581 | .Unsigned => break :blk 0b01, | |
| 582 | else => {}, | |
| 583 | }, | |
| 584 | else => {}, | |
| 585 | } | |
| 586 | break :blk 0b00; | |
| 587 | }; | |
| 588 | const opc: u2 = switch (variant) { | |
| 589 | .ldr, .ldrh, .ldrb => 0b01, | |
| 590 | .str, .strh, .strb => 0b00, | |
| 591 | }; | |
| 592 | return Instruction{ | |
| 593 | .load_store_register = .{ | |
| 594 | .rt = rt.id(), | |
| 595 | .rn = rn.id(), | |
| 596 | .offset = off, | |
| 597 | .opc = opc, | |
| 598 | .op1 = op1, | |
| 599 | .v = 0, | |
| 600 | .size = blk: { | |
| 601 | switch (variant) { | |
| 602 | .ldr, .str => switch (rt.size()) { | |
| 603 | 32 => break :blk 0b10, | |
| 604 | 64 => break :blk 0b11, | |
| 605 | else => unreachable, // unexpected register size | |
| 606 | }, | |
| 607 | .ldrh, .strh => break :blk 0b01, | |
| 608 | .ldrb, .strb => break :blk 0b00, | |
| 609 | } | |
| 610 | }, | |
| 611 | }, | |
| 612 | }; | |
| 613 | } | |
| 614 | ||
| 615 | fn loadStoreRegisterPair( | |
| 616 | rt1: Register, | |
| 617 | rt2: Register, | |
| 618 | rn: Register, | |
| 619 | offset: i9, | |
| 620 | encoding: u2, | |
| 621 | load: bool, | |
| 622 | ) Instruction { | |
| 623 | switch (rt1.size()) { | |
| 624 | 32 => { | |
| 625 | assert(-256 <= offset and offset <= 252); | |
| 626 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 2)); | |
| 627 | return Instruction{ | |
| 628 | .load_store_register_pair = .{ | |
| 629 | .rt1 = rt1.id(), | |
| 630 | .rn = rn.id(), | |
| 631 | .rt2 = rt2.id(), | |
| 632 | .imm7 = imm7, | |
| 633 | .load = @boolToInt(load), | |
| 634 | .encoding = encoding, | |
| 635 | .opc = 0b00, | |
| 636 | }, | |
| 637 | }; | |
| 638 | }, | |
| 639 | 64 => { | |
| 640 | assert(-512 <= offset and offset <= 504); | |
| 641 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 3)); | |
| 642 | return Instruction{ | |
| 643 | .load_store_register_pair = .{ | |
| 644 | .rt1 = rt1.id(), | |
| 645 | .rn = rn.id(), | |
| 646 | .rt2 = rt2.id(), | |
| 647 | .imm7 = imm7, | |
| 648 | .load = @boolToInt(load), | |
| 649 | .encoding = encoding, | |
| 650 | .opc = 0b10, | |
| 651 | }, | |
| 652 | }; | |
| 653 | }, | |
| 654 | else => unreachable, // unexpected register size | |
| 655 | } | |
| 656 | } | |
| 657 | ||
| 658 | fn loadLiteral(rt: Register, imm19: u19) Instruction { | |
| 659 | switch (rt.size()) { | |
| 660 | 32 => { | |
| 661 | return Instruction{ | |
| 662 | .load_literal = .{ | |
| 663 | .rt = rt.id(), | |
| 664 | .imm19 = imm19, | |
| 665 | .opc = 0b00, | |
| 666 | }, | |
| 667 | }; | |
| 668 | }, | |
| 669 | 64 => { | |
| 670 | return Instruction{ | |
| 671 | .load_literal = .{ | |
| 672 | .rt = rt.id(), | |
| 673 | .imm19 = imm19, | |
| 674 | .opc = 0b01, | |
| 675 | }, | |
| 676 | }; | |
| 677 | }, | |
| 678 | else => unreachable, // unexpected register size | |
| 679 | } | |
| 680 | } | |
| 681 | ||
| 682 | fn exceptionGeneration( | |
| 683 | opc: u3, | |
| 684 | op2: u3, | |
| 685 | ll: u2, | |
| 686 | imm16: u16, | |
| 687 | ) Instruction { | |
| 688 | return Instruction{ | |
| 689 | .exception_generation = .{ | |
| 690 | .ll = ll, | |
| 691 | .op2 = op2, | |
| 692 | .imm16 = imm16, | |
| 693 | .opc = opc, | |
| 694 | }, | |
| 695 | }; | |
| 696 | } | |
| 697 | ||
| 698 | fn unconditionalBranchRegister( | |
| 699 | opc: u4, | |
| 700 | op2: u5, | |
| 701 | op3: u6, | |
| 702 | rn: Register, | |
| 703 | op4: u5, | |
| 704 | ) Instruction { | |
| 705 | assert(rn.size() == 64); | |
| 706 | ||
| 707 | return Instruction{ | |
| 708 | .unconditional_branch_register = .{ | |
| 709 | .op4 = op4, | |
| 710 | .rn = rn.id(), | |
| 711 | .op3 = op3, | |
| 712 | .op2 = op2, | |
| 713 | .opc = opc, | |
| 714 | }, | |
| 715 | }; | |
| 716 | } | |
| 717 | ||
| 718 | fn unconditionalBranchImmediate( | |
| 719 | op: u1, | |
| 720 | offset: i28, | |
| 721 | ) Instruction { | |
| 722 | return Instruction{ | |
| 723 | .unconditional_branch_immediate = .{ | |
| 724 | .imm26 = @bitCast(u26, @intCast(i26, offset >> 2)), | |
| 725 | .op = op, | |
| 726 | }, | |
| 727 | }; | |
| 728 | } | |
| 729 | ||
| 730 | fn logicalShiftedRegister( | |
| 731 | opc: u2, | |
| 732 | n: u1, | |
| 733 | shift: Shift, | |
| 734 | rd: Register, | |
| 735 | rn: Register, | |
| 736 | rm: Register, | |
| 737 | ) Instruction { | |
| 738 | switch (rd.size()) { | |
| 739 | 32 => { | |
| 740 | assert(shift.amount < 32); | |
| 741 | return Instruction{ | |
| 742 | .logical_shifted_register = .{ | |
| 743 | .rd = rd.id(), | |
| 744 | .rn = rn.id(), | |
| 745 | .imm6 = shift.amount, | |
| 746 | .rm = rm.id(), | |
| 747 | .n = n, | |
| 748 | .shift = @enumToInt(shift.shift), | |
| 749 | .opc = opc, | |
| 750 | .sf = 0b0, | |
| 751 | }, | |
| 752 | }; | |
| 753 | }, | |
| 754 | 64 => { | |
| 755 | return Instruction{ | |
| 756 | .logical_shifted_register = .{ | |
| 757 | .rd = rd.id(), | |
| 758 | .rn = rn.id(), | |
| 759 | .imm6 = shift.amount, | |
| 760 | .rm = rm.id(), | |
| 761 | .n = n, | |
| 762 | .shift = @enumToInt(shift.shift), | |
| 763 | .opc = opc, | |
| 764 | .sf = 0b1, | |
| 765 | }, | |
| 766 | }; | |
| 767 | }, | |
| 768 | else => unreachable, // unexpected register size | |
| 769 | } | |
| 770 | } | |
| 771 | ||
| 772 | fn addSubtractImmediate( | |
| 773 | op: u1, | |
| 774 | s: u1, | |
| 775 | rd: Register, | |
| 776 | rn: Register, | |
| 777 | imm12: u12, | |
| 778 | shift: bool, | |
| 779 | ) Instruction { | |
| 780 | return Instruction{ | |
| 781 | .add_subtract_immediate = .{ | |
| 782 | .rd = rd.id(), | |
| 783 | .rn = rn.id(), | |
| 784 | .imm12 = imm12, | |
| 785 | .sh = @boolToInt(shift), | |
| 786 | .s = s, | |
| 787 | .op = op, | |
| 788 | .sf = switch (rd.size()) { | |
| 789 | 32 => 0b0, | |
| 790 | 64 => 0b1, | |
| 791 | else => unreachable, // unexpected register size | |
| 792 | }, | |
| 793 | }, | |
| 794 | }; | |
| 795 | } | |
| 796 | ||
| 797 | fn conditionalBranch( | |
| 798 | o0: u1, | |
| 799 | o1: u1, | |
| 800 | cond: Condition, | |
| 801 | offset: i21, | |
| 802 | ) Instruction { | |
| 803 | assert(offset & 0b11 == 0b00); | |
| 804 | return Instruction{ | |
| 805 | .conditional_branch = .{ | |
| 806 | .cond = @enumToInt(cond), | |
| 807 | .o0 = o0, | |
| 808 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), | |
| 809 | .o1 = o1, | |
| 810 | }, | |
| 811 | }; | |
| 812 | } | |
| 813 | ||
| 814 | fn compareAndBranch( | |
| 815 | op: u1, | |
| 816 | rt: Register, | |
| 817 | offset: i21, | |
| 818 | ) Instruction { | |
| 819 | assert(offset & 0b11 == 0b00); | |
| 820 | return Instruction{ | |
| 821 | .compare_and_branch = .{ | |
| 822 | .rt = rt.id(), | |
| 823 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), | |
| 824 | .op = op, | |
| 825 | .sf = switch (rt.size()) { | |
| 826 | 32 => 0b0, | |
| 827 | 64 => 0b1, | |
| 828 | else => unreachable, // unexpected register size | |
| 829 | }, | |
| 830 | }, | |
| 831 | }; | |
| 832 | } | |
| 833 | ||
| 834 | // Helper functions for assembly syntax functions | |
| 835 | ||
| 836 | // Move wide (immediate) | |
| 837 | ||
| 838 | pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction { | |
| 839 | return moveWideImmediate(0b00, rd, imm16, shift); | |
| 840 | } | |
| 841 | ||
| 842 | pub fn movz(rd: Register, imm16: u16, shift: u6) Instruction { | |
| 843 | return moveWideImmediate(0b10, rd, imm16, shift); | |
| 844 | } | |
| 845 | ||
| 846 | pub fn movk(rd: Register, imm16: u16, shift: u6) Instruction { | |
| 847 | return moveWideImmediate(0b11, rd, imm16, shift); | |
| 848 | } | |
| 849 | ||
| 850 | // PC relative address | |
| 851 | ||
| 852 | pub fn adr(rd: Register, imm21: i21) Instruction { | |
| 853 | return pcRelativeAddress(rd, imm21, 0b0); | |
| 854 | } | |
| 855 | ||
| 856 | pub fn adrp(rd: Register, imm21: i21) Instruction { | |
| 857 | return pcRelativeAddress(rd, imm21, 0b1); | |
| 858 | } | |
| 859 | ||
| 860 | // Load or store register | |
| 861 | ||
| 862 | pub const LdrArgs = union(enum) { | |
| 863 | register: struct { | |
| 864 | rn: Register, | |
| 865 | offset: LoadStoreOffset = LoadStoreOffset.none, | |
| 866 | }, | |
| 867 | literal: u19, | |
| 868 | }; | |
| 869 | ||
| 870 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { | |
| 871 | switch (args) { | |
| 872 | .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .ldr), | |
| 873 | .literal => |literal| return loadLiteral(rt, literal), | |
| 874 | } | |
| 875 | } | |
| 876 | ||
| 877 | pub fn ldrh(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 878 | return loadStoreRegister(rt, rn, args.offset, .ldrh); | |
| 879 | } | |
| 880 | ||
| 881 | pub fn ldrb(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 882 | return loadStoreRegister(rt, rn, args.offset, .ldrb); | |
| 883 | } | |
| 884 | ||
| 885 | pub const StrArgs = struct { | |
| 886 | offset: LoadStoreOffset = LoadStoreOffset.none, | |
| 887 | }; | |
| 888 | ||
| 889 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 890 | return loadStoreRegister(rt, rn, args.offset, .str); | |
| 891 | } | |
| 892 | ||
| 893 | pub fn strh(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 894 | return loadStoreRegister(rt, rn, args.offset, .strh); | |
| 895 | } | |
| 896 | ||
| 897 | pub fn strb(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 898 | return loadStoreRegister(rt, rn, args.offset, .strb); | |
| 899 | } | |
| 900 | ||
| 901 | // Load or store pair of registers | |
| 902 | ||
| 903 | pub const LoadStorePairOffset = struct { | |
| 904 | encoding: enum(u2) { | |
| 905 | PostIndex = 0b01, | |
| 906 | Signed = 0b10, | |
| 907 | PreIndex = 0b11, | |
| 908 | }, | |
| 909 | offset: i9, | |
| 910 | ||
| 911 | pub fn none() LoadStorePairOffset { | |
| 912 | return .{ .encoding = .Signed, .offset = 0 }; | |
| 913 | } | |
| 914 | ||
| 915 | pub fn post_index(imm: i9) LoadStorePairOffset { | |
| 916 | return .{ .encoding = .PostIndex, .offset = imm }; | |
| 917 | } | |
| 918 | ||
| 919 | pub fn pre_index(imm: i9) LoadStorePairOffset { | |
| 920 | return .{ .encoding = .PreIndex, .offset = imm }; | |
| 921 | } | |
| 922 | ||
| 923 | pub fn signed(imm: i9) LoadStorePairOffset { | |
| 924 | return .{ .encoding = .Signed, .offset = imm }; | |
| 925 | } | |
| 926 | }; | |
| 927 | ||
| 928 | pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { | |
| 929 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); | |
| 930 | } | |
| 931 | ||
| 932 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { | |
| 933 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true); | |
| 934 | } | |
| 935 | ||
| 936 | pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { | |
| 937 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); | |
| 938 | } | |
| 939 | ||
| 940 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { | |
| 941 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false); | |
| 942 | } | |
| 943 | ||
| 944 | // Exception generation | |
| 945 | ||
| 946 | pub fn svc(imm16: u16) Instruction { | |
| 947 | return exceptionGeneration(0b000, 0b000, 0b01, imm16); | |
| 948 | } | |
| 949 | ||
| 950 | pub fn hvc(imm16: u16) Instruction { | |
| 951 | return exceptionGeneration(0b000, 0b000, 0b10, imm16); | |
| 952 | } | |
| 953 | ||
| 954 | pub fn smc(imm16: u16) Instruction { | |
| 955 | return exceptionGeneration(0b000, 0b000, 0b11, imm16); | |
| 956 | } | |
| 957 | ||
| 958 | pub fn brk(imm16: u16) Instruction { | |
| 959 | return exceptionGeneration(0b001, 0b000, 0b00, imm16); | |
| 960 | } | |
| 961 | ||
| 962 | pub fn hlt(imm16: u16) Instruction { | |
| 963 | return exceptionGeneration(0b010, 0b000, 0b00, imm16); | |
| 964 | } | |
| 965 | ||
| 966 | // Unconditional branch (register) | |
| 967 | ||
| 968 | pub fn br(rn: Register) Instruction { | |
| 969 | return unconditionalBranchRegister(0b0000, 0b11111, 0b000000, rn, 0b00000); | |
| 970 | } | |
| 971 | ||
| 972 | pub fn blr(rn: Register) Instruction { | |
| 973 | return unconditionalBranchRegister(0b0001, 0b11111, 0b000000, rn, 0b00000); | |
| 974 | } | |
| 975 | ||
| 976 | pub fn ret(rn: ?Register) Instruction { | |
| 977 | return unconditionalBranchRegister(0b0010, 0b11111, 0b000000, rn orelse .x30, 0b00000); | |
| 978 | } | |
| 979 | ||
| 980 | // Unconditional branch (immediate) | |
| 981 | ||
| 982 | pub fn b(offset: i28) Instruction { | |
| 983 | return unconditionalBranchImmediate(0, offset); | |
| 984 | } | |
| 985 | ||
| 986 | pub fn bl(offset: i28) Instruction { | |
| 987 | return unconditionalBranchImmediate(1, offset); | |
| 988 | } | |
| 989 | ||
| 990 | // Nop | |
| 991 | ||
| 992 | pub fn nop() Instruction { | |
| 993 | return Instruction{ .no_operation = .{} }; | |
| 994 | } | |
| 995 | ||
| 996 | // Logical (shifted register) | |
| 997 | ||
| 998 | pub fn @"and"(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 999 | return logicalShiftedRegister(0b00, 0b0, shift, rd, rn, rm); | |
| 1000 | } | |
| 1001 | ||
| 1002 | pub fn bic(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1003 | return logicalShiftedRegister(0b00, 0b1, shift, rd, rn, rm); | |
| 1004 | } | |
| 1005 | ||
| 1006 | pub fn orr(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1007 | return logicalShiftedRegister(0b01, 0b0, shift, rd, rn, rm); | |
| 1008 | } | |
| 1009 | ||
| 1010 | pub fn orn(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1011 | return logicalShiftedRegister(0b01, 0b1, shift, rd, rn, rm); | |
| 1012 | } | |
| 1013 | ||
| 1014 | pub fn eor(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1015 | return logicalShiftedRegister(0b10, 0b0, shift, rd, rn, rm); | |
| 1016 | } | |
| 1017 | ||
| 1018 | pub fn eon(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1019 | return logicalShiftedRegister(0b10, 0b1, shift, rd, rn, rm); | |
| 1020 | } | |
| 1021 | ||
| 1022 | pub fn ands(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1023 | return logicalShiftedRegister(0b11, 0b0, shift, rd, rn, rm); | |
| 1024 | } | |
| 1025 | ||
| 1026 | pub fn bics(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1027 | return logicalShiftedRegister(0b11, 0b1, shift, rd, rn, rm); | |
| 1028 | } | |
| 1029 | ||
| 1030 | // Add/subtract (immediate) | |
| 1031 | ||
| 1032 | pub fn add(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | |
| 1033 | return addSubtractImmediate(0b0, 0b0, rd, rn, imm, shift); | |
| 1034 | } | |
| 1035 | ||
| 1036 | pub fn adds(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | |
| 1037 | return addSubtractImmediate(0b0, 0b1, rd, rn, imm, shift); | |
| 1038 | } | |
| 1039 | ||
| 1040 | pub fn sub(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | |
| 1041 | return addSubtractImmediate(0b1, 0b0, rd, rn, imm, shift); | |
| 1042 | } | |
| 1043 | ||
| 1044 | pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | |
| 1045 | return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift); | |
| 1046 | } | |
| 1047 | ||
| 1048 | // Conditional branch | |
| 1049 | ||
| 1050 | pub fn bCond(cond: Condition, offset: i21) Instruction { | |
| 1051 | return conditionalBranch(0b0, 0b0, cond, offset); | |
| 1052 | } | |
| 1053 | ||
| 1054 | // Compare and branch | |
| 1055 | ||
| 1056 | pub fn cbz(rt: Register, offset: i21) Instruction { | |
| 1057 | return compareAndBranch(0b0, rt, offset); | |
| 1058 | } | |
| 1059 | ||
| 1060 | pub fn cbnz(rt: Register, offset: i21) Instruction { | |
| 1061 | return compareAndBranch(0b1, rt, offset); | |
| 1062 | } | |
| 1063 | }; | |
| 1064 | ||
| 1065 | test { | |
| 1066 | testing.refAllDecls(@This()); | |
| 1067 | } | |
| 1068 | ||
| 1069 | test "serialize instructions" { | |
| 1070 | const Testcase = struct { | |
| 1071 | inst: Instruction, | |
| 1072 | expected: u32, | |
| 1073 | }; | |
| 1074 | ||
| 1075 | const testcases = [_]Testcase{ | |
| 1076 | .{ // orr x0, xzr, x1 | |
| 1077 | .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none), | |
| 1078 | .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, | |
| 1079 | }, | |
| 1080 | .{ // orn x0, xzr, x1 | |
| 1081 | .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none), | |
| 1082 | .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, | |
| 1083 | }, | |
| 1084 | .{ // movz x1, #4 | |
| 1085 | .inst = Instruction.movz(.x1, 4, 0), | |
| 1086 | .expected = 0b1_10_100101_00_0000000000000100_00001, | |
| 1087 | }, | |
| 1088 | .{ // movz x1, #4, lsl 16 | |
| 1089 | .inst = Instruction.movz(.x1, 4, 16), | |
| 1090 | .expected = 0b1_10_100101_01_0000000000000100_00001, | |
| 1091 | }, | |
| 1092 | .{ // movz x1, #4, lsl 32 | |
| 1093 | .inst = Instruction.movz(.x1, 4, 32), | |
| 1094 | .expected = 0b1_10_100101_10_0000000000000100_00001, | |
| 1095 | }, | |
| 1096 | .{ // movz x1, #4, lsl 48 | |
| 1097 | .inst = Instruction.movz(.x1, 4, 48), | |
| 1098 | .expected = 0b1_10_100101_11_0000000000000100_00001, | |
| 1099 | }, | |
| 1100 | .{ // movz w1, #4 | |
| 1101 | .inst = Instruction.movz(.w1, 4, 0), | |
| 1102 | .expected = 0b0_10_100101_00_0000000000000100_00001, | |
| 1103 | }, | |
| 1104 | .{ // movz w1, #4, lsl 16 | |
| 1105 | .inst = Instruction.movz(.w1, 4, 16), | |
| 1106 | .expected = 0b0_10_100101_01_0000000000000100_00001, | |
| 1107 | }, | |
| 1108 | .{ // svc #0 | |
| 1109 | .inst = Instruction.svc(0), | |
| 1110 | .expected = 0b1101_0100_000_0000000000000000_00001, | |
| 1111 | }, | |
| 1112 | .{ // svc #0x80 ; typical on Darwin | |
| 1113 | .inst = Instruction.svc(0x80), | |
| 1114 | .expected = 0b1101_0100_000_0000000010000000_00001, | |
| 1115 | }, | |
| 1116 | .{ // ret | |
| 1117 | .inst = Instruction.ret(null), | |
| 1118 | .expected = 0b1101_011_00_10_11111_0000_00_11110_00000, | |
| 1119 | }, | |
| 1120 | .{ // bl #0x10 | |
| 1121 | .inst = Instruction.bl(0x10), | |
| 1122 | .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100, | |
| 1123 | }, | |
| 1124 | .{ // ldr x2, [x1] | |
| 1125 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1 } }), | |
| 1126 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, | |
| 1127 | }, | |
| 1128 | .{ // ldr x2, [x1, #1]! | |
| 1129 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_pre_index(1) } }), | |
| 1130 | .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, | |
| 1131 | }, | |
| 1132 | .{ // ldr x2, [x1], #-1 | |
| 1133 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_post_index(-1) } }), | |
| 1134 | .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, | |
| 1135 | }, | |
| 1136 | .{ // ldr x2, [x1], (x3) | |
| 1137 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) } }), | |
| 1138 | .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, | |
| 1139 | }, | |
| 1140 | .{ // ldr x2, label | |
| 1141 | .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }), | |
| 1142 | .expected = 0b01_011_0_00_0000000000000000001_00010, | |
| 1143 | }, | |
| 1144 | .{ // ldrh x7, [x4], #0xaa | |
| 1145 | .inst = Instruction.ldrh(.x7, .x4, .{ .offset = Instruction.LoadStoreOffset.imm_post_index(0xaa) }), | |
| 1146 | .expected = 0b01_111_0_00_01_0_010101010_01_00100_00111, | |
| 1147 | }, | |
| 1148 | .{ // ldrb x9, [x15, #0xff]! | |
| 1149 | .inst = Instruction.ldrb(.x9, .x15, .{ .offset = Instruction.LoadStoreOffset.imm_pre_index(0xff) }), | |
| 1150 | .expected = 0b00_111_0_00_01_0_011111111_11_01111_01001, | |
| 1151 | }, | |
| 1152 | .{ // str x2, [x1] | |
| 1153 | .inst = Instruction.str(.x2, .x1, .{}), | |
| 1154 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, | |
| 1155 | }, | |
| 1156 | .{ // str x2, [x1], (x3) | |
| 1157 | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), | |
| 1158 | .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, | |
| 1159 | }, | |
| 1160 | .{ // strh w0, [x1] | |
| 1161 | .inst = Instruction.strh(.w0, .x1, .{}), | |
| 1162 | .expected = 0b01_111_0_01_00_000000000000_00001_00000, | |
| 1163 | }, | |
| 1164 | .{ // strb w8, [x9] | |
| 1165 | .inst = Instruction.strb(.w8, .x9, .{}), | |
| 1166 | .expected = 0b00_111_0_01_00_000000000000_01001_01000, | |
| 1167 | }, | |
| 1168 | .{ // adr x2, #0x8 | |
| 1169 | .inst = Instruction.adr(.x2, 0x8), | |
| 1170 | .expected = 0b0_00_10000_0000000000000000010_00010, | |
| 1171 | }, | |
| 1172 | .{ // adr x2, -#0x8 | |
| 1173 | .inst = Instruction.adr(.x2, -0x8), | |
| 1174 | .expected = 0b0_00_10000_1111111111111111110_00010, | |
| 1175 | }, | |
| 1176 | .{ // adrp x2, #0x8 | |
| 1177 | .inst = Instruction.adrp(.x2, 0x8), | |
| 1178 | .expected = 0b1_00_10000_0000000000000000010_00010, | |
| 1179 | }, | |
| 1180 | .{ // adrp x2, -#0x8 | |
| 1181 | .inst = Instruction.adrp(.x2, -0x8), | |
| 1182 | .expected = 0b1_00_10000_1111111111111111110_00010, | |
| 1183 | }, | |
| 1184 | .{ // stp x1, x2, [sp, #8] | |
| 1185 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), | |
| 1186 | .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, | |
| 1187 | }, | |
| 1188 | .{ // ldp x1, x2, [sp, #8] | |
| 1189 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), | |
| 1190 | .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, | |
| 1191 | }, | |
| 1192 | .{ // stp x1, x2, [sp, #-16]! | |
| 1193 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.pre_index(-16)), | |
| 1194 | .expected = 0b10_101_0_011_0_1111110_00010_11111_00001, | |
| 1195 | }, | |
| 1196 | .{ // ldp x1, x2, [sp], #16 | |
| 1197 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)), | |
| 1198 | .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, | |
| 1199 | }, | |
| 1200 | .{ // and x0, x4, x2 | |
| 1201 | .inst = Instruction.@"and"(.x0, .x4, .x2, .{}), | |
| 1202 | .expected = 0b1_00_01010_00_0_00010_000000_00100_00000, | |
| 1203 | }, | |
| 1204 | .{ // and x0, x4, x2, lsl #0x8 | |
| 1205 | .inst = Instruction.@"and"(.x0, .x4, .x2, .{ .shift = .lsl, .amount = 0x8 }), | |
| 1206 | .expected = 0b1_00_01010_00_0_00010_001000_00100_00000, | |
| 1207 | }, | |
| 1208 | .{ // add x0, x10, #10 | |
| 1209 | .inst = Instruction.add(.x0, .x10, 10, false), | |
| 1210 | .expected = 0b1_0_0_100010_0_0000_0000_1010_01010_00000, | |
| 1211 | }, | |
| 1212 | .{ // subs x0, x5, #11, lsl #12 | |
| 1213 | .inst = Instruction.subs(.x0, .x5, 11, true), | |
| 1214 | .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000, | |
| 1215 | }, | |
| 1216 | .{ // b.hi #-4 | |
| 1217 | .inst = Instruction.bCond(.hi, -4), | |
| 1218 | .expected = 0b0101010_0_1111111111111111111_0_1000, | |
| 1219 | }, | |
| 1220 | .{ // cbz x10, #40 | |
| 1221 | .inst = Instruction.cbz(.x10, 40), | |
| 1222 | .expected = 0b1_011010_0_0000000000000001010_01010, | |
| 1223 | }, | |
| 1224 | }; | |
| 1225 | ||
| 1226 | for (testcases) |case| { | |
| 1227 | const actual = case.inst.toU32(); | |
| 1228 | try testing.expectEqual(case.expected, actual); | |
| 1229 | } | |
| 1230 | } |
src/arch/arm/bits.zig created+1408| ... | ... | @@ -0,0 +1,1408 @@ |
| 1 | const std = @import("std"); | |
| 2 | const DW = std.dwarf; | |
| 3 | const testing = std.testing; | |
| 4 | ||
| 5 | /// The condition field specifies the flags necessary for an | |
| 6 | /// Instruction to be executed | |
| 7 | pub const Condition = enum(u4) { | |
| 8 | /// equal | |
| 9 | eq, | |
| 10 | /// not equal | |
| 11 | ne, | |
| 12 | /// unsigned higher or same | |
| 13 | cs, | |
| 14 | /// unsigned lower | |
| 15 | cc, | |
| 16 | /// negative | |
| 17 | mi, | |
| 18 | /// positive or zero | |
| 19 | pl, | |
| 20 | /// overflow | |
| 21 | vs, | |
| 22 | /// no overflow | |
| 23 | vc, | |
| 24 | /// unsigned higer | |
| 25 | hi, | |
| 26 | /// unsigned lower or same | |
| 27 | ls, | |
| 28 | /// greater or equal | |
| 29 | ge, | |
| 30 | /// less than | |
| 31 | lt, | |
| 32 | /// greater than | |
| 33 | gt, | |
| 34 | /// less than or equal | |
| 35 | le, | |
| 36 | /// always | |
| 37 | al, | |
| 38 | ||
| 39 | /// Converts a std.math.CompareOperator into a condition flag, | |
| 40 | /// i.e. returns the condition that is true iff the result of the | |
| 41 | /// comparison is true. Assumes signed comparison | |
| 42 | pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition { | |
| 43 | return switch (op) { | |
| 44 | .gte => .ge, | |
| 45 | .gt => .gt, | |
| 46 | .neq => .ne, | |
| 47 | .lt => .lt, | |
| 48 | .lte => .le, | |
| 49 | .eq => .eq, | |
| 50 | }; | |
| 51 | } | |
| 52 | ||
| 53 | /// Converts a std.math.CompareOperator into a condition flag, | |
| 54 | /// i.e. returns the condition that is true iff the result of the | |
| 55 | /// comparison is true. Assumes unsigned comparison | |
| 56 | pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition { | |
| 57 | return switch (op) { | |
| 58 | .gte => .cs, | |
| 59 | .gt => .hi, | |
| 60 | .neq => .ne, | |
| 61 | .lt => .cc, | |
| 62 | .lte => .ls, | |
| 63 | .eq => .eq, | |
| 64 | }; | |
| 65 | } | |
| 66 | ||
| 67 | /// Returns the condition which is true iff the given condition is | |
| 68 | /// false (if such a condition exists) | |
| 69 | pub fn negate(cond: Condition) Condition { | |
| 70 | return switch (cond) { | |
| 71 | .eq => .ne, | |
| 72 | .ne => .eq, | |
| 73 | .cs => .cc, | |
| 74 | .cc => .cs, | |
| 75 | .mi => .pl, | |
| 76 | .pl => .mi, | |
| 77 | .vs => .vc, | |
| 78 | .vc => .vs, | |
| 79 | .hi => .ls, | |
| 80 | .ls => .hi, | |
| 81 | .ge => .lt, | |
| 82 | .lt => .ge, | |
| 83 | .gt => .le, | |
| 84 | .le => .gt, | |
| 85 | .al => unreachable, | |
| 86 | }; | |
| 87 | } | |
| 88 | }; | |
| 89 | ||
| 90 | test "condition from CompareOperator" { | |
| 91 | try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq)); | |
| 92 | try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq)); | |
| 93 | ||
| 94 | try testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt)); | |
| 95 | try testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt)); | |
| 96 | ||
| 97 | try testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte)); | |
| 98 | try testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte)); | |
| 99 | } | |
| 100 | ||
| 101 | test "negate condition" { | |
| 102 | try testing.expectEqual(@as(Condition, .eq), Condition.ne.negate()); | |
| 103 | try testing.expectEqual(@as(Condition, .ne), Condition.eq.negate()); | |
| 104 | } | |
| 105 | ||
| 106 | /// Represents a register in the ARM instruction set architecture | |
| 107 | pub const Register = enum(u5) { | |
| 108 | r0, | |
| 109 | r1, | |
| 110 | r2, | |
| 111 | r3, | |
| 112 | r4, | |
| 113 | r5, | |
| 114 | r6, | |
| 115 | r7, | |
| 116 | r8, | |
| 117 | r9, | |
| 118 | r10, | |
| 119 | r11, | |
| 120 | r12, | |
| 121 | r13, | |
| 122 | r14, | |
| 123 | r15, | |
| 124 | ||
| 125 | /// Argument / result / scratch register 1 | |
| 126 | a1, | |
| 127 | /// Argument / result / scratch register 2 | |
| 128 | a2, | |
| 129 | /// Argument / scratch register 3 | |
| 130 | a3, | |
| 131 | /// Argument / scratch register 4 | |
| 132 | a4, | |
| 133 | /// Variable-register 1 | |
| 134 | v1, | |
| 135 | /// Variable-register 2 | |
| 136 | v2, | |
| 137 | /// Variable-register 3 | |
| 138 | v3, | |
| 139 | /// Variable-register 4 | |
| 140 | v4, | |
| 141 | /// Variable-register 5 | |
| 142 | v5, | |
| 143 | /// Platform register | |
| 144 | v6, | |
| 145 | /// Variable-register 7 | |
| 146 | v7, | |
| 147 | /// Frame pointer or Variable-register 8 | |
| 148 | fp, | |
| 149 | /// Intra-Procedure-call scratch register | |
| 150 | ip, | |
| 151 | /// Stack pointer | |
| 152 | sp, | |
| 153 | /// Link register | |
| 154 | lr, | |
| 155 | /// Program counter | |
| 156 | pc, | |
| 157 | ||
| 158 | /// Returns the unique 4-bit ID of this register which is used in | |
| 159 | /// the machine code | |
| 160 | pub fn id(self: Register) u4 { | |
| 161 | return @truncate(u4, @enumToInt(self)); | |
| 162 | } | |
| 163 | ||
| 164 | /// Returns the index into `callee_preserved_regs`. | |
| 165 | pub fn allocIndex(self: Register) ?u4 { | |
| 166 | inline for (callee_preserved_regs) |cpreg, i| { | |
| 167 | if (self.id() == cpreg.id()) return i; | |
| 168 | } | |
| 169 | return null; | |
| 170 | } | |
| 171 | ||
| 172 | pub fn dwarfLocOp(self: Register) u8 { | |
| 173 | return @as(u8, self.id()) + DW.OP.reg0; | |
| 174 | } | |
| 175 | }; | |
| 176 | ||
| 177 | test "Register.id" { | |
| 178 | try testing.expectEqual(@as(u4, 15), Register.r15.id()); | |
| 179 | try testing.expectEqual(@as(u4, 15), Register.pc.id()); | |
| 180 | } | |
| 181 | ||
| 182 | /// Program status registers containing flags, mode bits and other | |
| 183 | /// vital information | |
| 184 | pub const Psr = enum { | |
| 185 | cpsr, | |
| 186 | spsr, | |
| 187 | }; | |
| 188 | ||
| 189 | pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; | |
| 190 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | |
| 191 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | |
| 192 | ||
| 193 | /// Represents an instruction in the ARM instruction set architecture | |
| 194 | pub const Instruction = union(enum) { | |
| 195 | data_processing: packed struct { | |
| 196 | // Note to self: The order of the fields top-to-bottom is | |
| 197 | // right-to-left in the actual 32-bit int representation | |
| 198 | op2: u12, | |
| 199 | rd: u4, | |
| 200 | rn: u4, | |
| 201 | s: u1, | |
| 202 | opcode: u4, | |
| 203 | i: u1, | |
| 204 | fixed: u2 = 0b00, | |
| 205 | cond: u4, | |
| 206 | }, | |
| 207 | multiply: packed struct { | |
| 208 | rn: u4, | |
| 209 | fixed_1: u4 = 0b1001, | |
| 210 | rm: u4, | |
| 211 | ra: u4, | |
| 212 | rd: u4, | |
| 213 | set_cond: u1, | |
| 214 | accumulate: u1, | |
| 215 | fixed_2: u6 = 0b000000, | |
| 216 | cond: u4, | |
| 217 | }, | |
| 218 | multiply_long: packed struct { | |
| 219 | rn: u4, | |
| 220 | fixed_1: u4 = 0b1001, | |
| 221 | rm: u4, | |
| 222 | rdlo: u4, | |
| 223 | rdhi: u4, | |
| 224 | set_cond: u1, | |
| 225 | accumulate: u1, | |
| 226 | unsigned: u1, | |
| 227 | fixed_2: u5 = 0b00001, | |
| 228 | cond: u4, | |
| 229 | }, | |
| 230 | integer_saturating_arithmetic: packed struct { | |
| 231 | rm: u4, | |
| 232 | fixed_1: u8 = 0b0000_0101, | |
| 233 | rd: u4, | |
| 234 | rn: u4, | |
| 235 | fixed_2: u1 = 0b0, | |
| 236 | opc: u2, | |
| 237 | fixed_3: u5 = 0b00010, | |
| 238 | cond: u4, | |
| 239 | }, | |
| 240 | single_data_transfer: packed struct { | |
| 241 | offset: u12, | |
| 242 | rd: u4, | |
| 243 | rn: u4, | |
| 244 | load_store: u1, | |
| 245 | write_back: u1, | |
| 246 | byte_word: u1, | |
| 247 | up_down: u1, | |
| 248 | pre_post: u1, | |
| 249 | imm: u1, | |
| 250 | fixed: u2 = 0b01, | |
| 251 | cond: u4, | |
| 252 | }, | |
| 253 | extra_load_store: packed struct { | |
| 254 | imm4l: u4, | |
| 255 | fixed_1: u1 = 0b1, | |
| 256 | op2: u2, | |
| 257 | fixed_2: u1 = 0b1, | |
| 258 | imm4h: u4, | |
| 259 | rt: u4, | |
| 260 | rn: u4, | |
| 261 | o1: u1, | |
| 262 | write_back: u1, | |
| 263 | imm: u1, | |
| 264 | up_down: u1, | |
| 265 | pre_index: u1, | |
| 266 | fixed_3: u3 = 0b000, | |
| 267 | cond: u4, | |
| 268 | }, | |
| 269 | block_data_transfer: packed struct { | |
| 270 | register_list: u16, | |
| 271 | rn: u4, | |
| 272 | load_store: u1, | |
| 273 | write_back: u1, | |
| 274 | psr_or_user: u1, | |
| 275 | up_down: u1, | |
| 276 | pre_post: u1, | |
| 277 | fixed: u3 = 0b100, | |
| 278 | cond: u4, | |
| 279 | }, | |
| 280 | branch: packed struct { | |
| 281 | offset: u24, | |
| 282 | link: u1, | |
| 283 | fixed: u3 = 0b101, | |
| 284 | cond: u4, | |
| 285 | }, | |
| 286 | branch_exchange: packed struct { | |
| 287 | rn: u4, | |
| 288 | fixed_1: u1 = 0b1, | |
| 289 | link: u1, | |
| 290 | fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, | |
| 291 | cond: u4, | |
| 292 | }, | |
| 293 | supervisor_call: packed struct { | |
| 294 | comment: u24, | |
| 295 | fixed: u4 = 0b1111, | |
| 296 | cond: u4, | |
| 297 | }, | |
| 298 | breakpoint: packed struct { | |
| 299 | imm4: u4, | |
| 300 | fixed_1: u4 = 0b0111, | |
| 301 | imm12: u12, | |
| 302 | fixed_2_and_cond: u12 = 0b1110_0001_0010, | |
| 303 | }, | |
| 304 | ||
| 305 | /// Represents the possible operations which can be performed by a | |
| 306 | /// Data Processing instruction | |
| 307 | const Opcode = enum(u4) { | |
| 308 | // Rd := Op1 AND Op2 | |
| 309 | @"and", | |
| 310 | // Rd := Op1 EOR Op2 | |
| 311 | eor, | |
| 312 | // Rd := Op1 - Op2 | |
| 313 | sub, | |
| 314 | // Rd := Op2 - Op1 | |
| 315 | rsb, | |
| 316 | // Rd := Op1 + Op2 | |
| 317 | add, | |
| 318 | // Rd := Op1 + Op2 + C | |
| 319 | adc, | |
| 320 | // Rd := Op1 - Op2 + C - 1 | |
| 321 | sbc, | |
| 322 | // Rd := Op2 - Op1 + C - 1 | |
| 323 | rsc, | |
| 324 | // set condition codes on Op1 AND Op2 | |
| 325 | tst, | |
| 326 | // set condition codes on Op1 EOR Op2 | |
| 327 | teq, | |
| 328 | // set condition codes on Op1 - Op2 | |
| 329 | cmp, | |
| 330 | // set condition codes on Op1 + Op2 | |
| 331 | cmn, | |
| 332 | // Rd := Op1 OR Op2 | |
| 333 | orr, | |
| 334 | // Rd := Op2 | |
| 335 | mov, | |
| 336 | // Rd := Op1 AND NOT Op2 | |
| 337 | bic, | |
| 338 | // Rd := NOT Op2 | |
| 339 | mvn, | |
| 340 | }; | |
| 341 | ||
| 342 | /// Represents the second operand to a data processing instruction | |
| 343 | /// which can either be content from a register or an immediate | |
| 344 | /// value | |
| 345 | pub const Operand = union(enum) { | |
| 346 | Register: packed struct { | |
| 347 | rm: u4, | |
| 348 | shift: u8, | |
| 349 | }, | |
| 350 | Immediate: packed struct { | |
| 351 | imm: u8, | |
| 352 | rotate: u4, | |
| 353 | }, | |
| 354 | ||
| 355 | /// Represents multiple ways a register can be shifted. A | |
| 356 | /// register can be shifted by a specific immediate value or | |
| 357 | /// by the contents of another register | |
| 358 | pub const Shift = union(enum) { | |
| 359 | Immediate: packed struct { | |
| 360 | fixed: u1 = 0b0, | |
| 361 | typ: u2, | |
| 362 | amount: u5, | |
| 363 | }, | |
| 364 | Register: packed struct { | |
| 365 | fixed_1: u1 = 0b1, | |
| 366 | typ: u2, | |
| 367 | fixed_2: u1 = 0b0, | |
| 368 | rs: u4, | |
| 369 | }, | |
| 370 | ||
| 371 | pub const Type = enum(u2) { | |
| 372 | logical_left, | |
| 373 | logical_right, | |
| 374 | arithmetic_right, | |
| 375 | rotate_right, | |
| 376 | }; | |
| 377 | ||
| 378 | pub const none = Shift{ | |
| 379 | .Immediate = .{ | |
| 380 | .amount = 0, | |
| 381 | .typ = 0, | |
| 382 | }, | |
| 383 | }; | |
| 384 | ||
| 385 | pub fn toU8(self: Shift) u8 { | |
| 386 | return switch (self) { | |
| 387 | .Register => |v| @bitCast(u8, v), | |
| 388 | .Immediate => |v| @bitCast(u8, v), | |
| 389 | }; | |
| 390 | } | |
| 391 | ||
| 392 | pub fn reg(rs: Register, typ: Type) Shift { | |
| 393 | return Shift{ | |
| 394 | .Register = .{ | |
| 395 | .rs = rs.id(), | |
| 396 | .typ = @enumToInt(typ), | |
| 397 | }, | |
| 398 | }; | |
| 399 | } | |
| 400 | ||
| 401 | pub fn imm(amount: u5, typ: Type) Shift { | |
| 402 | return Shift{ | |
| 403 | .Immediate = .{ | |
| 404 | .amount = amount, | |
| 405 | .typ = @enumToInt(typ), | |
| 406 | }, | |
| 407 | }; | |
| 408 | } | |
| 409 | }; | |
| 410 | ||
| 411 | pub fn toU12(self: Operand) u12 { | |
| 412 | return switch (self) { | |
| 413 | .Register => |v| @bitCast(u12, v), | |
| 414 | .Immediate => |v| @bitCast(u12, v), | |
| 415 | }; | |
| 416 | } | |
| 417 | ||
| 418 | pub fn reg(rm: Register, shift: Shift) Operand { | |
| 419 | return Operand{ | |
| 420 | .Register = .{ | |
| 421 | .rm = rm.id(), | |
| 422 | .shift = shift.toU8(), | |
| 423 | }, | |
| 424 | }; | |
| 425 | } | |
| 426 | ||
| 427 | pub fn imm(immediate: u8, rotate: u4) Operand { | |
| 428 | return Operand{ | |
| 429 | .Immediate = .{ | |
| 430 | .imm = immediate, | |
| 431 | .rotate = rotate, | |
| 432 | }, | |
| 433 | }; | |
| 434 | } | |
| 435 | ||
| 436 | /// Tries to convert an unsigned 32 bit integer into an | |
| 437 | /// immediate operand using rotation. Returns null when there | |
| 438 | /// is no conversion | |
| 439 | pub fn fromU32(x: u32) ?Operand { | |
| 440 | const masks = comptime blk: { | |
| 441 | const base_mask: u32 = std.math.maxInt(u8); | |
| 442 | var result = [_]u32{0} ** 16; | |
| 443 | for (result) |*mask, i| mask.* = std.math.rotr(u32, base_mask, 2 * i); | |
| 444 | break :blk result; | |
| 445 | }; | |
| 446 | ||
| 447 | return for (masks) |mask, i| { | |
| 448 | if (x & mask == x) { | |
| 449 | break Operand{ | |
| 450 | .Immediate = .{ | |
| 451 | .imm = @intCast(u8, std.math.rotl(u32, x, 2 * i)), | |
| 452 | .rotate = @intCast(u4, i), | |
| 453 | }, | |
| 454 | }; | |
| 455 | } | |
| 456 | } else null; | |
| 457 | } | |
| 458 | }; | |
| 459 | ||
| 460 | /// Represents the offset operand of a load or store | |
| 461 | /// instruction. Data can be loaded from memory with either an | |
| 462 | /// immediate offset or an offset that is stored in some register. | |
| 463 | pub const Offset = union(enum) { | |
| 464 | Immediate: u12, | |
| 465 | Register: packed struct { | |
| 466 | rm: u4, | |
| 467 | shift: u8, | |
| 468 | }, | |
| 469 | ||
| 470 | pub const none = Offset{ | |
| 471 | .Immediate = 0, | |
| 472 | }; | |
| 473 | ||
| 474 | pub fn toU12(self: Offset) u12 { | |
| 475 | return switch (self) { | |
| 476 | .Register => |v| @bitCast(u12, v), | |
| 477 | .Immediate => |v| v, | |
| 478 | }; | |
| 479 | } | |
| 480 | ||
| 481 | pub fn reg(rm: Register, shift: u8) Offset { | |
| 482 | return Offset{ | |
| 483 | .Register = .{ | |
| 484 | .rm = rm.id(), | |
| 485 | .shift = shift, | |
| 486 | }, | |
| 487 | }; | |
| 488 | } | |
| 489 | ||
| 490 | pub fn imm(immediate: u12) Offset { | |
| 491 | return Offset{ | |
| 492 | .Immediate = immediate, | |
| 493 | }; | |
| 494 | } | |
| 495 | }; | |
| 496 | ||
| 497 | /// Represents the offset operand of an extra load or store | |
| 498 | /// instruction. | |
| 499 | pub const ExtraLoadStoreOffset = union(enum) { | |
| 500 | immediate: u8, | |
| 501 | register: u4, | |
| 502 | ||
| 503 | pub const none = ExtraLoadStoreOffset{ | |
| 504 | .immediate = 0, | |
| 505 | }; | |
| 506 | ||
| 507 | pub fn reg(register: Register) ExtraLoadStoreOffset { | |
| 508 | return ExtraLoadStoreOffset{ | |
| 509 | .register = register.id(), | |
| 510 | }; | |
| 511 | } | |
| 512 | ||
| 513 | pub fn imm(immediate: u8) ExtraLoadStoreOffset { | |
| 514 | return ExtraLoadStoreOffset{ | |
| 515 | .immediate = immediate, | |
| 516 | }; | |
| 517 | } | |
| 518 | }; | |
| 519 | ||
| 520 | /// Represents the register list operand to a block data transfer | |
| 521 | /// instruction | |
| 522 | pub const RegisterList = packed struct { | |
| 523 | r0: bool = false, | |
| 524 | r1: bool = false, | |
| 525 | r2: bool = false, | |
| 526 | r3: bool = false, | |
| 527 | r4: bool = false, | |
| 528 | r5: bool = false, | |
| 529 | r6: bool = false, | |
| 530 | r7: bool = false, | |
| 531 | r8: bool = false, | |
| 532 | r9: bool = false, | |
| 533 | r10: bool = false, | |
| 534 | r11: bool = false, | |
| 535 | r12: bool = false, | |
| 536 | r13: bool = false, | |
| 537 | r14: bool = false, | |
| 538 | r15: bool = false, | |
| 539 | }; | |
| 540 | ||
| 541 | pub fn toU32(self: Instruction) u32 { | |
| 542 | return switch (self) { | |
| 543 | .data_processing => |v| @bitCast(u32, v), | |
| 544 | .multiply => |v| @bitCast(u32, v), | |
| 545 | .multiply_long => |v| @bitCast(u32, v), | |
| 546 | .integer_saturating_arithmetic => |v| @bitCast(u32, v), | |
| 547 | .single_data_transfer => |v| @bitCast(u32, v), | |
| 548 | .extra_load_store => |v| @bitCast(u32, v), | |
| 549 | .block_data_transfer => |v| @bitCast(u32, v), | |
| 550 | .branch => |v| @bitCast(u32, v), | |
| 551 | .branch_exchange => |v| @bitCast(u32, v), | |
| 552 | .supervisor_call => |v| @bitCast(u32, v), | |
| 553 | .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), | |
| 554 | }; | |
| 555 | } | |
| 556 | ||
| 557 | // Helper functions for the "real" functions below | |
| 558 | ||
| 559 | fn dataProcessing( | |
| 560 | cond: Condition, | |
| 561 | opcode: Opcode, | |
| 562 | s: u1, | |
| 563 | rd: Register, | |
| 564 | rn: Register, | |
| 565 | op2: Operand, | |
| 566 | ) Instruction { | |
| 567 | return Instruction{ | |
| 568 | .data_processing = .{ | |
| 569 | .cond = @enumToInt(cond), | |
| 570 | .i = @boolToInt(op2 == .Immediate), | |
| 571 | .opcode = @enumToInt(opcode), | |
| 572 | .s = s, | |
| 573 | .rn = rn.id(), | |
| 574 | .rd = rd.id(), | |
| 575 | .op2 = op2.toU12(), | |
| 576 | }, | |
| 577 | }; | |
| 578 | } | |
| 579 | ||
| 580 | fn specialMov( | |
| 581 | cond: Condition, | |
| 582 | rd: Register, | |
| 583 | imm: u16, | |
| 584 | top: bool, | |
| 585 | ) Instruction { | |
| 586 | return Instruction{ | |
| 587 | .data_processing = .{ | |
| 588 | .cond = @enumToInt(cond), | |
| 589 | .i = 1, | |
| 590 | .opcode = if (top) 0b1010 else 0b1000, | |
| 591 | .s = 0, | |
| 592 | .rn = @truncate(u4, imm >> 12), | |
| 593 | .rd = rd.id(), | |
| 594 | .op2 = @truncate(u12, imm), | |
| 595 | }, | |
| 596 | }; | |
| 597 | } | |
| 598 | ||
| 599 | fn multiply( | |
| 600 | cond: Condition, | |
| 601 | set_cond: u1, | |
| 602 | rd: Register, | |
| 603 | rn: Register, | |
| 604 | rm: Register, | |
| 605 | ra: ?Register, | |
| 606 | ) Instruction { | |
| 607 | return Instruction{ | |
| 608 | .multiply = .{ | |
| 609 | .cond = @enumToInt(cond), | |
| 610 | .accumulate = @boolToInt(ra != null), | |
| 611 | .set_cond = set_cond, | |
| 612 | .rd = rd.id(), | |
| 613 | .rn = rn.id(), | |
| 614 | .ra = if (ra) |reg| reg.id() else 0b0000, | |
| 615 | .rm = rm.id(), | |
| 616 | }, | |
| 617 | }; | |
| 618 | } | |
| 619 | ||
| 620 | fn multiplyLong( | |
| 621 | cond: Condition, | |
| 622 | signed: u1, | |
| 623 | accumulate: u1, | |
| 624 | set_cond: u1, | |
| 625 | rdhi: Register, | |
| 626 | rdlo: Register, | |
| 627 | rm: Register, | |
| 628 | rn: Register, | |
| 629 | ) Instruction { | |
| 630 | return Instruction{ | |
| 631 | .multiply_long = .{ | |
| 632 | .cond = @enumToInt(cond), | |
| 633 | .unsigned = signed, | |
| 634 | .accumulate = accumulate, | |
| 635 | .set_cond = set_cond, | |
| 636 | .rdlo = rdlo.id(), | |
| 637 | .rdhi = rdhi.id(), | |
| 638 | .rn = rn.id(), | |
| 639 | .rm = rm.id(), | |
| 640 | }, | |
| 641 | }; | |
| 642 | } | |
| 643 | ||
| 644 | fn integerSaturationArithmetic( | |
| 645 | cond: Condition, | |
| 646 | rd: Register, | |
| 647 | rm: Register, | |
| 648 | rn: Register, | |
| 649 | opc: u2, | |
| 650 | ) Instruction { | |
| 651 | return Instruction{ | |
| 652 | .integer_saturating_arithmetic = .{ | |
| 653 | .rm = rm.id(), | |
| 654 | .rd = rd.id(), | |
| 655 | .rn = rn.id(), | |
| 656 | .opc = opc, | |
| 657 | .cond = @enumToInt(cond), | |
| 658 | }, | |
| 659 | }; | |
| 660 | } | |
| 661 | ||
| 662 | fn singleDataTransfer( | |
| 663 | cond: Condition, | |
| 664 | rd: Register, | |
| 665 | rn: Register, | |
| 666 | offset: Offset, | |
| 667 | pre_index: bool, | |
| 668 | positive: bool, | |
| 669 | byte_word: u1, | |
| 670 | write_back: bool, | |
| 671 | load_store: u1, | |
| 672 | ) Instruction { | |
| 673 | return Instruction{ | |
| 674 | .single_data_transfer = .{ | |
| 675 | .cond = @enumToInt(cond), | |
| 676 | .rn = rn.id(), | |
| 677 | .rd = rd.id(), | |
| 678 | .offset = offset.toU12(), | |
| 679 | .load_store = load_store, | |
| 680 | .write_back = @boolToInt(write_back), | |
| 681 | .byte_word = byte_word, | |
| 682 | .up_down = @boolToInt(positive), | |
| 683 | .pre_post = @boolToInt(pre_index), | |
| 684 | .imm = @boolToInt(offset != .Immediate), | |
| 685 | }, | |
| 686 | }; | |
| 687 | } | |
| 688 | ||
| 689 | fn extraLoadStore( | |
| 690 | cond: Condition, | |
| 691 | pre_index: bool, | |
| 692 | positive: bool, | |
| 693 | write_back: bool, | |
| 694 | o1: u1, | |
| 695 | op2: u2, | |
| 696 | rn: Register, | |
| 697 | rt: Register, | |
| 698 | offset: ExtraLoadStoreOffset, | |
| 699 | ) Instruction { | |
| 700 | const imm4l: u4 = switch (offset) { | |
| 701 | .immediate => |imm| @truncate(u4, imm), | |
| 702 | .register => |reg| reg, | |
| 703 | }; | |
| 704 | const imm4h: u4 = switch (offset) { | |
| 705 | .immediate => |imm| @truncate(u4, imm >> 4), | |
| 706 | .register => 0b0000, | |
| 707 | }; | |
| 708 | ||
| 709 | return Instruction{ | |
| 710 | .extra_load_store = .{ | |
| 711 | .imm4l = imm4l, | |
| 712 | .op2 = op2, | |
| 713 | .imm4h = imm4h, | |
| 714 | .rt = rt.id(), | |
| 715 | .rn = rn.id(), | |
| 716 | .o1 = o1, | |
| 717 | .write_back = @boolToInt(write_back), | |
| 718 | .imm = @boolToInt(offset == .immediate), | |
| 719 | .up_down = @boolToInt(positive), | |
| 720 | .pre_index = @boolToInt(pre_index), | |
| 721 | .cond = @enumToInt(cond), | |
| 722 | }, | |
| 723 | }; | |
| 724 | } | |
| 725 | ||
| 726 | fn blockDataTransfer( | |
| 727 | cond: Condition, | |
| 728 | rn: Register, | |
| 729 | reg_list: RegisterList, | |
| 730 | pre_post: u1, | |
| 731 | up_down: u1, | |
| 732 | psr_or_user: u1, | |
| 733 | write_back: bool, | |
| 734 | load_store: u1, | |
| 735 | ) Instruction { | |
| 736 | return Instruction{ | |
| 737 | .block_data_transfer = .{ | |
| 738 | .register_list = @bitCast(u16, reg_list), | |
| 739 | .rn = rn.id(), | |
| 740 | .load_store = load_store, | |
| 741 | .write_back = @boolToInt(write_back), | |
| 742 | .psr_or_user = psr_or_user, | |
| 743 | .up_down = up_down, | |
| 744 | .pre_post = pre_post, | |
| 745 | .cond = @enumToInt(cond), | |
| 746 | }, | |
| 747 | }; | |
| 748 | } | |
| 749 | ||
| 750 | fn branch(cond: Condition, offset: i26, link: u1) Instruction { | |
| 751 | return Instruction{ | |
| 752 | .branch = .{ | |
| 753 | .cond = @enumToInt(cond), | |
| 754 | .link = link, | |
| 755 | .offset = @bitCast(u24, @intCast(i24, offset >> 2)), | |
| 756 | }, | |
| 757 | }; | |
| 758 | } | |
| 759 | ||
| 760 | fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction { | |
| 761 | return Instruction{ | |
| 762 | .branch_exchange = .{ | |
| 763 | .cond = @enumToInt(cond), | |
| 764 | .link = link, | |
| 765 | .rn = rn.id(), | |
| 766 | }, | |
| 767 | }; | |
| 768 | } | |
| 769 | ||
| 770 | fn supervisorCall(cond: Condition, comment: u24) Instruction { | |
| 771 | return Instruction{ | |
| 772 | .supervisor_call = .{ | |
| 773 | .cond = @enumToInt(cond), | |
| 774 | .comment = comment, | |
| 775 | }, | |
| 776 | }; | |
| 777 | } | |
| 778 | ||
| 779 | fn breakpoint(imm: u16) Instruction { | |
| 780 | return Instruction{ | |
| 781 | .breakpoint = .{ | |
| 782 | .imm12 = @truncate(u12, imm >> 4), | |
| 783 | .imm4 = @truncate(u4, imm), | |
| 784 | }, | |
| 785 | }; | |
| 786 | } | |
| 787 | ||
| 788 | // Public functions replicating assembler syntax as closely as | |
| 789 | // possible | |
| 790 | ||
| 791 | // Data processing | |
| 792 | ||
| 793 | pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 794 | return dataProcessing(cond, .@"and", 0, rd, rn, op2); | |
| 795 | } | |
| 796 | ||
| 797 | pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 798 | return dataProcessing(cond, .@"and", 1, rd, rn, op2); | |
| 799 | } | |
| 800 | ||
| 801 | pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 802 | return dataProcessing(cond, .eor, 0, rd, rn, op2); | |
| 803 | } | |
| 804 | ||
| 805 | pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 806 | return dataProcessing(cond, .eor, 1, rd, rn, op2); | |
| 807 | } | |
| 808 | ||
| 809 | pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 810 | return dataProcessing(cond, .sub, 0, rd, rn, op2); | |
| 811 | } | |
| 812 | ||
| 813 | pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 814 | return dataProcessing(cond, .sub, 1, rd, rn, op2); | |
| 815 | } | |
| 816 | ||
| 817 | pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 818 | return dataProcessing(cond, .rsb, 0, rd, rn, op2); | |
| 819 | } | |
| 820 | ||
| 821 | pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 822 | return dataProcessing(cond, .rsb, 1, rd, rn, op2); | |
| 823 | } | |
| 824 | ||
| 825 | pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 826 | return dataProcessing(cond, .add, 0, rd, rn, op2); | |
| 827 | } | |
| 828 | ||
| 829 | pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 830 | return dataProcessing(cond, .add, 1, rd, rn, op2); | |
| 831 | } | |
| 832 | ||
| 833 | pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 834 | return dataProcessing(cond, .adc, 0, rd, rn, op2); | |
| 835 | } | |
| 836 | ||
| 837 | pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 838 | return dataProcessing(cond, .adc, 1, rd, rn, op2); | |
| 839 | } | |
| 840 | ||
| 841 | pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 842 | return dataProcessing(cond, .sbc, 0, rd, rn, op2); | |
| 843 | } | |
| 844 | ||
| 845 | pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 846 | return dataProcessing(cond, .sbc, 1, rd, rn, op2); | |
| 847 | } | |
| 848 | ||
| 849 | pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 850 | return dataProcessing(cond, .rsc, 0, rd, rn, op2); | |
| 851 | } | |
| 852 | ||
| 853 | pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 854 | return dataProcessing(cond, .rsc, 1, rd, rn, op2); | |
| 855 | } | |
| 856 | ||
| 857 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { | |
| 858 | return dataProcessing(cond, .tst, 1, .r0, rn, op2); | |
| 859 | } | |
| 860 | ||
| 861 | pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction { | |
| 862 | return dataProcessing(cond, .teq, 1, .r0, rn, op2); | |
| 863 | } | |
| 864 | ||
| 865 | pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction { | |
| 866 | return dataProcessing(cond, .cmp, 1, .r0, rn, op2); | |
| 867 | } | |
| 868 | ||
| 869 | pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction { | |
| 870 | return dataProcessing(cond, .cmn, 1, .r0, rn, op2); | |
| 871 | } | |
| 872 | ||
| 873 | pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 874 | return dataProcessing(cond, .orr, 0, rd, rn, op2); | |
| 875 | } | |
| 876 | ||
| 877 | pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 878 | return dataProcessing(cond, .orr, 1, rd, rn, op2); | |
| 879 | } | |
| 880 | ||
| 881 | pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 882 | return dataProcessing(cond, .mov, 0, rd, .r0, op2); | |
| 883 | } | |
| 884 | ||
| 885 | pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 886 | return dataProcessing(cond, .mov, 1, rd, .r0, op2); | |
| 887 | } | |
| 888 | ||
| 889 | pub fn bic(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 890 | return dataProcessing(cond, .bic, 0, rd, rn, op2); | |
| 891 | } | |
| 892 | ||
| 893 | pub fn bics(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 894 | return dataProcessing(cond, .bic, 1, rd, rn, op2); | |
| 895 | } | |
| 896 | ||
| 897 | pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 898 | return dataProcessing(cond, .mvn, 0, rd, .r0, op2); | |
| 899 | } | |
| 900 | ||
| 901 | pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 902 | return dataProcessing(cond, .mvn, 1, rd, .r0, op2); | |
| 903 | } | |
| 904 | ||
| 905 | // Integer Saturating Arithmetic | |
| 906 | ||
| 907 | pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | |
| 908 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b00); | |
| 909 | } | |
| 910 | ||
| 911 | pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | |
| 912 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b01); | |
| 913 | } | |
| 914 | ||
| 915 | pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | |
| 916 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b10); | |
| 917 | } | |
| 918 | ||
| 919 | pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | |
| 920 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b11); | |
| 921 | } | |
| 922 | ||
| 923 | // movw and movt | |
| 924 | ||
| 925 | pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction { | |
| 926 | return specialMov(cond, rd, imm, false); | |
| 927 | } | |
| 928 | ||
| 929 | pub fn movt(cond: Condition, rd: Register, imm: u16) Instruction { | |
| 930 | return specialMov(cond, rd, imm, true); | |
| 931 | } | |
| 932 | ||
| 933 | // PSR transfer | |
| 934 | ||
| 935 | pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { | |
| 936 | return Instruction{ | |
| 937 | .data_processing = .{ | |
| 938 | .cond = @enumToInt(cond), | |
| 939 | .i = 0, | |
| 940 | .opcode = if (psr == .spsr) 0b1010 else 0b1000, | |
| 941 | .s = 0, | |
| 942 | .rn = 0b1111, | |
| 943 | .rd = rd.id(), | |
| 944 | .op2 = 0b0000_0000_0000, | |
| 945 | }, | |
| 946 | }; | |
| 947 | } | |
| 948 | ||
| 949 | pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction { | |
| 950 | return Instruction{ | |
| 951 | .data_processing = .{ | |
| 952 | .cond = @enumToInt(cond), | |
| 953 | .i = 0, | |
| 954 | .opcode = if (psr == .spsr) 0b1011 else 0b1001, | |
| 955 | .s = 0, | |
| 956 | .rn = 0b1111, | |
| 957 | .rd = 0b1111, | |
| 958 | .op2 = op.toU12(), | |
| 959 | }, | |
| 960 | }; | |
| 961 | } | |
| 962 | ||
| 963 | // Multiply | |
| 964 | ||
| 965 | pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { | |
| 966 | return multiply(cond, 0, rd, rn, rm, null); | |
| 967 | } | |
| 968 | ||
| 969 | pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { | |
| 970 | return multiply(cond, 1, rd, rn, rm, null); | |
| 971 | } | |
| 972 | ||
| 973 | pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { | |
| 974 | return multiply(cond, 0, rd, rn, rm, ra); | |
| 975 | } | |
| 976 | ||
| 977 | pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { | |
| 978 | return multiply(cond, 1, rd, rn, rm, ra); | |
| 979 | } | |
| 980 | ||
| 981 | // Multiply long | |
| 982 | ||
| 983 | pub fn umull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 984 | return multiplyLong(cond, 0, 0, 0, rdhi, rdlo, rm, rn); | |
| 985 | } | |
| 986 | ||
| 987 | pub fn umulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 988 | return multiplyLong(cond, 0, 0, 1, rdhi, rdlo, rm, rn); | |
| 989 | } | |
| 990 | ||
| 991 | pub fn umlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 992 | return multiplyLong(cond, 0, 1, 0, rdhi, rdlo, rm, rn); | |
| 993 | } | |
| 994 | ||
| 995 | pub fn umlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 996 | return multiplyLong(cond, 0, 1, 1, rdhi, rdlo, rm, rn); | |
| 997 | } | |
| 998 | ||
| 999 | pub fn smull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 1000 | return multiplyLong(cond, 1, 0, 0, rdhi, rdlo, rm, rn); | |
| 1001 | } | |
| 1002 | ||
| 1003 | pub fn smulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 1004 | return multiplyLong(cond, 1, 0, 1, rdhi, rdlo, rm, rn); | |
| 1005 | } | |
| 1006 | ||
| 1007 | pub fn smlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 1008 | return multiplyLong(cond, 1, 1, 0, rdhi, rdlo, rm, rn); | |
| 1009 | } | |
| 1010 | ||
| 1011 | pub fn smlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 1012 | return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn); | |
| 1013 | } | |
| 1014 | ||
| 1015 | // Single data transfer | |
| 1016 | ||
| 1017 | pub const OffsetArgs = struct { | |
| 1018 | pre_index: bool = true, | |
| 1019 | positive: bool = true, | |
| 1020 | offset: Offset, | |
| 1021 | write_back: bool = false, | |
| 1022 | }; | |
| 1023 | ||
| 1024 | pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | |
| 1025 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1); | |
| 1026 | } | |
| 1027 | ||
| 1028 | pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | |
| 1029 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1); | |
| 1030 | } | |
| 1031 | ||
| 1032 | pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | |
| 1033 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0); | |
| 1034 | } | |
| 1035 | ||
| 1036 | pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | |
| 1037 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0); | |
| 1038 | } | |
| 1039 | ||
| 1040 | // Extra load/store | |
| 1041 | ||
| 1042 | pub const ExtraLoadStoreOffsetArgs = struct { | |
| 1043 | pre_index: bool = true, | |
| 1044 | positive: bool = true, | |
| 1045 | offset: ExtraLoadStoreOffset, | |
| 1046 | write_back: bool = false, | |
| 1047 | }; | |
| 1048 | ||
| 1049 | pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | |
| 1050 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0, 0b01, rn, rt, args.offset); | |
| 1051 | } | |
| 1052 | ||
| 1053 | pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | |
| 1054 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 1, 0b01, rn, rt, args.offset); | |
| 1055 | } | |
| 1056 | ||
| 1057 | // Block data transfer | |
| 1058 | ||
| 1059 | pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1060 | return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1); | |
| 1061 | } | |
| 1062 | ||
| 1063 | pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1064 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1); | |
| 1065 | } | |
| 1066 | ||
| 1067 | pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1068 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1); | |
| 1069 | } | |
| 1070 | ||
| 1071 | pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1072 | return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1); | |
| 1073 | } | |
| 1074 | ||
| 1075 | pub const ldmfa = ldmda; | |
| 1076 | pub const ldmea = ldmdb; | |
| 1077 | pub const ldmed = ldmib; | |
| 1078 | pub const ldmfd = ldmia; | |
| 1079 | pub const ldm = ldmia; | |
| 1080 | ||
| 1081 | pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1082 | return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0); | |
| 1083 | } | |
| 1084 | ||
| 1085 | pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1086 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0); | |
| 1087 | } | |
| 1088 | ||
| 1089 | pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1090 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0); | |
| 1091 | } | |
| 1092 | ||
| 1093 | pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1094 | return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0); | |
| 1095 | } | |
| 1096 | ||
| 1097 | pub const stmed = stmda; | |
| 1098 | pub const stmfd = stmdb; | |
| 1099 | pub const stmfa = stmib; | |
| 1100 | pub const stmea = stmia; | |
| 1101 | pub const stm = stmia; | |
| 1102 | ||
| 1103 | // Branch | |
| 1104 | ||
| 1105 | pub fn b(cond: Condition, offset: i26) Instruction { | |
| 1106 | return branch(cond, offset, 0); | |
| 1107 | } | |
| 1108 | ||
| 1109 | pub fn bl(cond: Condition, offset: i26) Instruction { | |
| 1110 | return branch(cond, offset, 1); | |
| 1111 | } | |
| 1112 | ||
| 1113 | // Branch and exchange | |
| 1114 | ||
| 1115 | pub fn bx(cond: Condition, rn: Register) Instruction { | |
| 1116 | return branchExchange(cond, rn, 0); | |
| 1117 | } | |
| 1118 | ||
| 1119 | pub fn blx(cond: Condition, rn: Register) Instruction { | |
| 1120 | return branchExchange(cond, rn, 1); | |
| 1121 | } | |
| 1122 | ||
| 1123 | // Supervisor Call | |
| 1124 | ||
| 1125 | pub const swi = svc; | |
| 1126 | ||
| 1127 | pub fn svc(cond: Condition, comment: u24) Instruction { | |
| 1128 | return supervisorCall(cond, comment); | |
| 1129 | } | |
| 1130 | ||
| 1131 | // Breakpoint | |
| 1132 | ||
| 1133 | pub fn bkpt(imm: u16) Instruction { | |
| 1134 | return breakpoint(imm); | |
| 1135 | } | |
| 1136 | ||
| 1137 | // Aliases | |
| 1138 | ||
| 1139 | pub fn nop() Instruction { | |
| 1140 | return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)); | |
| 1141 | } | |
| 1142 | ||
| 1143 | pub fn pop(cond: Condition, args: anytype) Instruction { | |
| 1144 | if (@typeInfo(@TypeOf(args)) != .Struct) { | |
| 1145 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); | |
| 1146 | } | |
| 1147 | ||
| 1148 | if (args.len < 1) { | |
| 1149 | @compileError("Expected at least one register"); | |
| 1150 | } else if (args.len == 1) { | |
| 1151 | const reg = args[0]; | |
| 1152 | return ldr(cond, reg, .sp, .{ | |
| 1153 | .pre_index = false, | |
| 1154 | .positive = true, | |
| 1155 | .offset = Offset.imm(4), | |
| 1156 | .write_back = false, | |
| 1157 | }); | |
| 1158 | } else { | |
| 1159 | var register_list: u16 = 0; | |
| 1160 | inline for (args) |arg| { | |
| 1161 | const reg = @as(Register, arg); | |
| 1162 | register_list |= @as(u16, 1) << reg.id(); | |
| 1163 | } | |
| 1164 | return ldm(cond, .sp, true, @bitCast(RegisterList, register_list)); | |
| 1165 | } | |
| 1166 | } | |
| 1167 | ||
| 1168 | pub fn push(cond: Condition, args: anytype) Instruction { | |
| 1169 | if (@typeInfo(@TypeOf(args)) != .Struct) { | |
| 1170 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); | |
| 1171 | } | |
| 1172 | ||
| 1173 | if (args.len < 1) { | |
| 1174 | @compileError("Expected at least one register"); | |
| 1175 | } else if (args.len == 1) { | |
| 1176 | const reg = args[0]; | |
| 1177 | return str(cond, reg, .sp, .{ | |
| 1178 | .pre_index = true, | |
| 1179 | .positive = false, | |
| 1180 | .offset = Offset.imm(4), | |
| 1181 | .write_back = true, | |
| 1182 | }); | |
| 1183 | } else { | |
| 1184 | var register_list: u16 = 0; | |
| 1185 | inline for (args) |arg| { | |
| 1186 | const reg = @as(Register, arg); | |
| 1187 | register_list |= @as(u16, 1) << reg.id(); | |
| 1188 | } | |
| 1189 | return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list)); | |
| 1190 | } | |
| 1191 | } | |
| 1192 | ||
| 1193 | pub const ShiftAmount = union(enum) { | |
| 1194 | immediate: u5, | |
| 1195 | register: Register, | |
| 1196 | ||
| 1197 | pub fn imm(immediate: u5) ShiftAmount { | |
| 1198 | return .{ | |
| 1199 | .immediate = immediate, | |
| 1200 | }; | |
| 1201 | } | |
| 1202 | ||
| 1203 | pub fn reg(register: Register) ShiftAmount { | |
| 1204 | return .{ | |
| 1205 | .register = register, | |
| 1206 | }; | |
| 1207 | } | |
| 1208 | }; | |
| 1209 | ||
| 1210 | pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1211 | return switch (shift) { | |
| 1212 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), | |
| 1213 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), | |
| 1214 | }; | |
| 1215 | } | |
| 1216 | ||
| 1217 | pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1218 | return switch (shift) { | |
| 1219 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), | |
| 1220 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), | |
| 1221 | }; | |
| 1222 | } | |
| 1223 | ||
| 1224 | pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1225 | return switch (shift) { | |
| 1226 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), | |
| 1227 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), | |
| 1228 | }; | |
| 1229 | } | |
| 1230 | ||
| 1231 | pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1232 | return switch (shift) { | |
| 1233 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), | |
| 1234 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), | |
| 1235 | }; | |
| 1236 | } | |
| 1237 | ||
| 1238 | pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1239 | return switch (shift) { | |
| 1240 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), | |
| 1241 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), | |
| 1242 | }; | |
| 1243 | } | |
| 1244 | ||
| 1245 | pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1246 | return switch (shift) { | |
| 1247 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), | |
| 1248 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), | |
| 1249 | }; | |
| 1250 | } | |
| 1251 | ||
| 1252 | pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1253 | return switch (shift) { | |
| 1254 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), | |
| 1255 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), | |
| 1256 | }; | |
| 1257 | } | |
| 1258 | ||
| 1259 | pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1260 | return switch (shift) { | |
| 1261 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), | |
| 1262 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), | |
| 1263 | }; | |
| 1264 | } | |
| 1265 | }; | |
| 1266 | ||
| 1267 | test "serialize instructions" { | |
| 1268 | const Testcase = struct { | |
| 1269 | inst: Instruction, | |
| 1270 | expected: u32, | |
| 1271 | }; | |
| 1272 | ||
| 1273 | const testcases = [_]Testcase{ | |
| 1274 | .{ // add r0, r0, r0 | |
| 1275 | .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), | |
| 1276 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, | |
| 1277 | }, | |
| 1278 | .{ // mov r4, r2 | |
| 1279 | .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), | |
| 1280 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, | |
| 1281 | }, | |
| 1282 | .{ // mov r0, #42 | |
| 1283 | .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)), | |
| 1284 | .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, | |
| 1285 | }, | |
| 1286 | .{ // mrs r5, cpsr | |
| 1287 | .inst = Instruction.mrs(.al, .r5, .cpsr), | |
| 1288 | .expected = 0b1110_00010_0_001111_0101_000000000000, | |
| 1289 | }, | |
| 1290 | .{ // mul r0, r1, r2 | |
| 1291 | .inst = Instruction.mul(.al, .r0, .r1, .r2), | |
| 1292 | .expected = 0b1110_000000_0_0_0000_0000_0010_1001_0001, | |
| 1293 | }, | |
| 1294 | .{ // umlal r0, r1, r5, r6 | |
| 1295 | .inst = Instruction.umlal(.al, .r0, .r1, .r5, .r6), | |
| 1296 | .expected = 0b1110_00001_0_1_0_0001_0000_0110_1001_0101, | |
| 1297 | }, | |
| 1298 | .{ // ldr r0, [r2, #42] | |
| 1299 | .inst = Instruction.ldr(.al, .r0, .r2, .{ | |
| 1300 | .offset = Instruction.Offset.imm(42), | |
| 1301 | }), | |
| 1302 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, | |
| 1303 | }, | |
| 1304 | .{ // str r0, [r3] | |
| 1305 | .inst = Instruction.str(.al, .r0, .r3, .{ | |
| 1306 | .offset = Instruction.Offset.none, | |
| 1307 | }), | |
| 1308 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, | |
| 1309 | }, | |
| 1310 | .{ // strh r1, [r5] | |
| 1311 | .inst = Instruction.strh(.al, .r1, .r5, .{ | |
| 1312 | .offset = Instruction.ExtraLoadStoreOffset.none, | |
| 1313 | }), | |
| 1314 | .expected = 0b1110_000_1_1_1_0_0_0101_0001_0000_1011_0000, | |
| 1315 | }, | |
| 1316 | .{ // b #12 | |
| 1317 | .inst = Instruction.b(.al, 12), | |
| 1318 | .expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011, | |
| 1319 | }, | |
| 1320 | .{ // bl #-4 | |
| 1321 | .inst = Instruction.bl(.al, -4), | |
| 1322 | .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1111, | |
| 1323 | }, | |
| 1324 | .{ // bx lr | |
| 1325 | .inst = Instruction.bx(.al, .lr), | |
| 1326 | .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110, | |
| 1327 | }, | |
| 1328 | .{ // svc #0 | |
| 1329 | .inst = Instruction.svc(.al, 0), | |
| 1330 | .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000, | |
| 1331 | }, | |
| 1332 | .{ // bkpt #42 | |
| 1333 | .inst = Instruction.bkpt(42), | |
| 1334 | .expected = 0b1110_0001_0010_000000000010_0111_1010, | |
| 1335 | }, | |
| 1336 | .{ // stmdb r9, {r0} | |
| 1337 | .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }), | |
| 1338 | .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001, | |
| 1339 | }, | |
| 1340 | .{ // ldmea r4!, {r2, r5} | |
| 1341 | .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }), | |
| 1342 | .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100, | |
| 1343 | }, | |
| 1344 | .{ // qadd r0, r7, r8 | |
| 1345 | .inst = Instruction.qadd(.al, .r0, .r7, .r8), | |
| 1346 | .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111, | |
| 1347 | }, | |
| 1348 | }; | |
| 1349 | ||
| 1350 | for (testcases) |case| { | |
| 1351 | const actual = case.inst.toU32(); | |
| 1352 | try testing.expectEqual(case.expected, actual); | |
| 1353 | } | |
| 1354 | } | |
| 1355 | ||
| 1356 | test "aliases" { | |
| 1357 | const Testcase = struct { | |
| 1358 | expected: Instruction, | |
| 1359 | actual: Instruction, | |
| 1360 | }; | |
| 1361 | ||
| 1362 | const testcases = [_]Testcase{ | |
| 1363 | .{ // pop { r6 } | |
| 1364 | .actual = Instruction.pop(.al, .{.r6}), | |
| 1365 | .expected = Instruction.ldr(.al, .r6, .sp, .{ | |
| 1366 | .pre_index = false, | |
| 1367 | .positive = true, | |
| 1368 | .offset = Instruction.Offset.imm(4), | |
| 1369 | .write_back = false, | |
| 1370 | }), | |
| 1371 | }, | |
| 1372 | .{ // pop { r1, r5 } | |
| 1373 | .actual = Instruction.pop(.al, .{ .r1, .r5 }), | |
| 1374 | .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }), | |
| 1375 | }, | |
| 1376 | .{ // push { r3 } | |
| 1377 | .actual = Instruction.push(.al, .{.r3}), | |
| 1378 | .expected = Instruction.str(.al, .r3, .sp, .{ | |
| 1379 | .pre_index = true, | |
| 1380 | .positive = false, | |
| 1381 | .offset = Instruction.Offset.imm(4), | |
| 1382 | .write_back = true, | |
| 1383 | }), | |
| 1384 | }, | |
| 1385 | .{ // push { r0, r2 } | |
| 1386 | .actual = Instruction.push(.al, .{ .r0, .r2 }), | |
| 1387 | .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }), | |
| 1388 | }, | |
| 1389 | .{ // lsl r4, r5, #5 | |
| 1390 | .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)), | |
| 1391 | .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg( | |
| 1392 | .r5, | |
| 1393 | Instruction.Operand.Shift.imm(5, .logical_left), | |
| 1394 | )), | |
| 1395 | }, | |
| 1396 | .{ // asrs r1, r1, r3 | |
| 1397 | .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)), | |
| 1398 | .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg( | |
| 1399 | .r1, | |
| 1400 | Instruction.Operand.Shift.reg(.r3, .arithmetic_right), | |
| 1401 | )), | |
| 1402 | }, | |
| 1403 | }; | |
| 1404 | ||
| 1405 | for (testcases) |case| { | |
| 1406 | try testing.expectEqual(case.expected.toU32(), case.actual.toU32()); | |
| 1407 | } | |
| 1408 | } |
src/arch/riscv64/bits.zig created+470| ... | ... | @@ -0,0 +1,470 @@ |
| 1 | const std = @import("std"); | |
| 2 | const DW = std.dwarf; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const testing = std.testing; | |
| 5 | ||
| 6 | // TODO: this is only tagged to facilitate the monstrosity. | |
| 7 | // Once packed structs work make it packed. | |
| 8 | pub const Instruction = union(enum) { | |
| 9 | R: packed struct { | |
| 10 | opcode: u7, | |
| 11 | rd: u5, | |
| 12 | funct3: u3, | |
| 13 | rs1: u5, | |
| 14 | rs2: u5, | |
| 15 | funct7: u7, | |
| 16 | }, | |
| 17 | I: packed struct { | |
| 18 | opcode: u7, | |
| 19 | rd: u5, | |
| 20 | funct3: u3, | |
| 21 | rs1: u5, | |
| 22 | imm0_11: u12, | |
| 23 | }, | |
| 24 | S: packed struct { | |
| 25 | opcode: u7, | |
| 26 | imm0_4: u5, | |
| 27 | funct3: u3, | |
| 28 | rs1: u5, | |
| 29 | rs2: u5, | |
| 30 | imm5_11: u7, | |
| 31 | }, | |
| 32 | B: packed struct { | |
| 33 | opcode: u7, | |
| 34 | imm11: u1, | |
| 35 | imm1_4: u4, | |
| 36 | funct3: u3, | |
| 37 | rs1: u5, | |
| 38 | rs2: u5, | |
| 39 | imm5_10: u6, | |
| 40 | imm12: u1, | |
| 41 | }, | |
| 42 | U: packed struct { | |
| 43 | opcode: u7, | |
| 44 | rd: u5, | |
| 45 | imm12_31: u20, | |
| 46 | }, | |
| 47 | J: packed struct { | |
| 48 | opcode: u7, | |
| 49 | rd: u5, | |
| 50 | imm12_19: u8, | |
| 51 | imm11: u1, | |
| 52 | imm1_10: u10, | |
| 53 | imm20: u1, | |
| 54 | }, | |
| 55 | ||
| 56 | // TODO: once packed structs work we can remove this monstrosity. | |
| 57 | pub fn toU32(self: Instruction) u32 { | |
| 58 | return switch (self) { | |
| 59 | .R => |v| @bitCast(u32, v), | |
| 60 | .I => |v| @bitCast(u32, v), | |
| 61 | .S => |v| @bitCast(u32, v), | |
| 62 | .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31), | |
| 63 | .U => |v| @bitCast(u32, v), | |
| 64 | .J => |v| @bitCast(u32, v), | |
| 65 | }; | |
| 66 | } | |
| 67 | ||
| 68 | fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction { | |
| 69 | return Instruction{ | |
| 70 | .R = .{ | |
| 71 | .opcode = op, | |
| 72 | .funct3 = fn3, | |
| 73 | .funct7 = fn7, | |
| 74 | .rd = @enumToInt(rd), | |
| 75 | .rs1 = @enumToInt(r1), | |
| 76 | .rs2 = @enumToInt(r2), | |
| 77 | }, | |
| 78 | }; | |
| 79 | } | |
| 80 | ||
| 81 | // RISC-V is all signed all the time -- convert immediates to unsigned for processing | |
| 82 | fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction { | |
| 83 | const umm = @bitCast(u12, imm); | |
| 84 | ||
| 85 | return Instruction{ | |
| 86 | .I = .{ | |
| 87 | .opcode = op, | |
| 88 | .funct3 = fn3, | |
| 89 | .rd = @enumToInt(rd), | |
| 90 | .rs1 = @enumToInt(r1), | |
| 91 | .imm0_11 = umm, | |
| 92 | }, | |
| 93 | }; | |
| 94 | } | |
| 95 | ||
| 96 | fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction { | |
| 97 | const umm = @bitCast(u12, imm); | |
| 98 | ||
| 99 | return Instruction{ | |
| 100 | .S = .{ | |
| 101 | .opcode = op, | |
| 102 | .funct3 = fn3, | |
| 103 | .rs1 = @enumToInt(r1), | |
| 104 | .rs2 = @enumToInt(r2), | |
| 105 | .imm0_4 = @truncate(u5, umm), | |
| 106 | .imm5_11 = @truncate(u7, umm >> 5), | |
| 107 | }, | |
| 108 | }; | |
| 109 | } | |
| 110 | ||
| 111 | // Use significance value rather than bit value, same for J-type | |
| 112 | // -- less burden on callsite, bonus semantic checking | |
| 113 | fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction { | |
| 114 | const umm = @bitCast(u13, imm); | |
| 115 | assert(umm % 2 == 0); // misaligned branch target | |
| 116 | ||
| 117 | return Instruction{ | |
| 118 | .B = .{ | |
| 119 | .opcode = op, | |
| 120 | .funct3 = fn3, | |
| 121 | .rs1 = @enumToInt(r1), | |
| 122 | .rs2 = @enumToInt(r2), | |
| 123 | .imm1_4 = @truncate(u4, umm >> 1), | |
| 124 | .imm5_10 = @truncate(u6, umm >> 5), | |
| 125 | .imm11 = @truncate(u1, umm >> 11), | |
| 126 | .imm12 = @truncate(u1, umm >> 12), | |
| 127 | }, | |
| 128 | }; | |
| 129 | } | |
| 130 | ||
| 131 | // We have to extract the 20 bits anyway -- let's not make it more painful | |
| 132 | fn uType(op: u7, rd: Register, imm: i20) Instruction { | |
| 133 | const umm = @bitCast(u20, imm); | |
| 134 | ||
| 135 | return Instruction{ | |
| 136 | .U = .{ | |
| 137 | .opcode = op, | |
| 138 | .rd = @enumToInt(rd), | |
| 139 | .imm12_31 = umm, | |
| 140 | }, | |
| 141 | }; | |
| 142 | } | |
| 143 | ||
| 144 | fn jType(op: u7, rd: Register, imm: i21) Instruction { | |
| 145 | const umm = @bitCast(u21, imm); | |
| 146 | assert(umm % 2 == 0); // misaligned jump target | |
| 147 | ||
| 148 | return Instruction{ | |
| 149 | .J = .{ | |
| 150 | .opcode = op, | |
| 151 | .rd = @enumToInt(rd), | |
| 152 | .imm1_10 = @truncate(u10, umm >> 1), | |
| 153 | .imm11 = @truncate(u1, umm >> 11), | |
| 154 | .imm12_19 = @truncate(u8, umm >> 12), | |
| 155 | .imm20 = @truncate(u1, umm >> 20), | |
| 156 | }, | |
| 157 | }; | |
| 158 | } | |
| 159 | ||
| 160 | // The meat and potatoes. Arguments are in the order in which they would appear in assembly code. | |
| 161 | ||
| 162 | // Arithmetic/Logical, Register-Register | |
| 163 | ||
| 164 | pub fn add(rd: Register, r1: Register, r2: Register) Instruction { | |
| 165 | return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2); | |
| 166 | } | |
| 167 | ||
| 168 | pub fn sub(rd: Register, r1: Register, r2: Register) Instruction { | |
| 169 | return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2); | |
| 170 | } | |
| 171 | ||
| 172 | pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction { | |
| 173 | return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2); | |
| 174 | } | |
| 175 | ||
| 176 | pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction { | |
| 177 | return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2); | |
| 178 | } | |
| 179 | ||
| 180 | pub fn xor(rd: Register, r1: Register, r2: Register) Instruction { | |
| 181 | return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2); | |
| 182 | } | |
| 183 | ||
| 184 | pub fn sll(rd: Register, r1: Register, r2: Register) Instruction { | |
| 185 | return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2); | |
| 186 | } | |
| 187 | ||
| 188 | pub fn srl(rd: Register, r1: Register, r2: Register) Instruction { | |
| 189 | return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2); | |
| 190 | } | |
| 191 | ||
| 192 | pub fn sra(rd: Register, r1: Register, r2: Register) Instruction { | |
| 193 | return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2); | |
| 194 | } | |
| 195 | ||
| 196 | pub fn slt(rd: Register, r1: Register, r2: Register) Instruction { | |
| 197 | return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2); | |
| 198 | } | |
| 199 | ||
| 200 | pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction { | |
| 201 | return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2); | |
| 202 | } | |
| 203 | ||
| 204 | // Arithmetic/Logical, Register-Register (32-bit) | |
| 205 | ||
| 206 | pub fn addw(rd: Register, r1: Register, r2: Register) Instruction { | |
| 207 | return rType(0b0111011, 0b000, rd, r1, r2); | |
| 208 | } | |
| 209 | ||
| 210 | pub fn subw(rd: Register, r1: Register, r2: Register) Instruction { | |
| 211 | return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2); | |
| 212 | } | |
| 213 | ||
| 214 | pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction { | |
| 215 | return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2); | |
| 216 | } | |
| 217 | ||
| 218 | pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction { | |
| 219 | return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2); | |
| 220 | } | |
| 221 | ||
| 222 | pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction { | |
| 223 | return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2); | |
| 224 | } | |
| 225 | ||
| 226 | // Arithmetic/Logical, Register-Immediate | |
| 227 | ||
| 228 | pub fn addi(rd: Register, r1: Register, imm: i12) Instruction { | |
| 229 | return iType(0b0010011, 0b000, rd, r1, imm); | |
| 230 | } | |
| 231 | ||
| 232 | pub fn andi(rd: Register, r1: Register, imm: i12) Instruction { | |
| 233 | return iType(0b0010011, 0b111, rd, r1, imm); | |
| 234 | } | |
| 235 | ||
| 236 | pub fn ori(rd: Register, r1: Register, imm: i12) Instruction { | |
| 237 | return iType(0b0010011, 0b110, rd, r1, imm); | |
| 238 | } | |
| 239 | ||
| 240 | pub fn xori(rd: Register, r1: Register, imm: i12) Instruction { | |
| 241 | return iType(0b0010011, 0b100, rd, r1, imm); | |
| 242 | } | |
| 243 | ||
| 244 | pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction { | |
| 245 | return iType(0b0010011, 0b001, rd, r1, shamt); | |
| 246 | } | |
| 247 | ||
| 248 | pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction { | |
| 249 | return iType(0b0010011, 0b101, rd, r1, shamt); | |
| 250 | } | |
| 251 | ||
| 252 | pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction { | |
| 253 | return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt); | |
| 254 | } | |
| 255 | ||
| 256 | pub fn slti(rd: Register, r1: Register, imm: i12) Instruction { | |
| 257 | return iType(0b0010011, 0b010, rd, r1, imm); | |
| 258 | } | |
| 259 | ||
| 260 | pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction { | |
| 261 | return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm)); | |
| 262 | } | |
| 263 | ||
| 264 | // Arithmetic/Logical, Register-Immediate (32-bit) | |
| 265 | ||
| 266 | pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction { | |
| 267 | return iType(0b0011011, 0b000, rd, r1, imm); | |
| 268 | } | |
| 269 | ||
| 270 | pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction { | |
| 271 | return iType(0b0011011, 0b001, rd, r1, shamt); | |
| 272 | } | |
| 273 | ||
| 274 | pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction { | |
| 275 | return iType(0b0011011, 0b101, rd, r1, shamt); | |
| 276 | } | |
| 277 | ||
| 278 | pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction { | |
| 279 | return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt); | |
| 280 | } | |
| 281 | ||
| 282 | // Upper Immediate | |
| 283 | ||
| 284 | pub fn lui(rd: Register, imm: i20) Instruction { | |
| 285 | return uType(0b0110111, rd, imm); | |
| 286 | } | |
| 287 | ||
| 288 | pub fn auipc(rd: Register, imm: i20) Instruction { | |
| 289 | return uType(0b0010111, rd, imm); | |
| 290 | } | |
| 291 | ||
| 292 | // Load | |
| 293 | ||
| 294 | pub fn ld(rd: Register, offset: i12, base: Register) Instruction { | |
| 295 | return iType(0b0000011, 0b011, rd, base, offset); | |
| 296 | } | |
| 297 | ||
| 298 | pub fn lw(rd: Register, offset: i12, base: Register) Instruction { | |
| 299 | return iType(0b0000011, 0b010, rd, base, offset); | |
| 300 | } | |
| 301 | ||
| 302 | pub fn lwu(rd: Register, offset: i12, base: Register) Instruction { | |
| 303 | return iType(0b0000011, 0b110, rd, base, offset); | |
| 304 | } | |
| 305 | ||
| 306 | pub fn lh(rd: Register, offset: i12, base: Register) Instruction { | |
| 307 | return iType(0b0000011, 0b001, rd, base, offset); | |
| 308 | } | |
| 309 | ||
| 310 | pub fn lhu(rd: Register, offset: i12, base: Register) Instruction { | |
| 311 | return iType(0b0000011, 0b101, rd, base, offset); | |
| 312 | } | |
| 313 | ||
| 314 | pub fn lb(rd: Register, offset: i12, base: Register) Instruction { | |
| 315 | return iType(0b0000011, 0b000, rd, base, offset); | |
| 316 | } | |
| 317 | ||
| 318 | pub fn lbu(rd: Register, offset: i12, base: Register) Instruction { | |
| 319 | return iType(0b0000011, 0b100, rd, base, offset); | |
| 320 | } | |
| 321 | ||
| 322 | // Store | |
| 323 | ||
| 324 | pub fn sd(rs: Register, offset: i12, base: Register) Instruction { | |
| 325 | return sType(0b0100011, 0b011, base, rs, offset); | |
| 326 | } | |
| 327 | ||
| 328 | pub fn sw(rs: Register, offset: i12, base: Register) Instruction { | |
| 329 | return sType(0b0100011, 0b010, base, rs, offset); | |
| 330 | } | |
| 331 | ||
| 332 | pub fn sh(rs: Register, offset: i12, base: Register) Instruction { | |
| 333 | return sType(0b0100011, 0b001, base, rs, offset); | |
| 334 | } | |
| 335 | ||
| 336 | pub fn sb(rs: Register, offset: i12, base: Register) Instruction { | |
| 337 | return sType(0b0100011, 0b000, base, rs, offset); | |
| 338 | } | |
| 339 | ||
| 340 | // Fence | |
| 341 | // TODO: implement fence | |
| 342 | ||
| 343 | // Branch | |
| 344 | ||
| 345 | pub fn beq(r1: Register, r2: Register, offset: i13) Instruction { | |
| 346 | return bType(0b1100011, 0b000, r1, r2, offset); | |
| 347 | } | |
| 348 | ||
| 349 | pub fn bne(r1: Register, r2: Register, offset: i13) Instruction { | |
| 350 | return bType(0b1100011, 0b001, r1, r2, offset); | |
| 351 | } | |
| 352 | ||
| 353 | pub fn blt(r1: Register, r2: Register, offset: i13) Instruction { | |
| 354 | return bType(0b1100011, 0b100, r1, r2, offset); | |
| 355 | } | |
| 356 | ||
| 357 | pub fn bge(r1: Register, r2: Register, offset: i13) Instruction { | |
| 358 | return bType(0b1100011, 0b101, r1, r2, offset); | |
| 359 | } | |
| 360 | ||
| 361 | pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction { | |
| 362 | return bType(0b1100011, 0b110, r1, r2, offset); | |
| 363 | } | |
| 364 | ||
| 365 | pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction { | |
| 366 | return bType(0b1100011, 0b111, r1, r2, offset); | |
| 367 | } | |
| 368 | ||
| 369 | // Jump | |
| 370 | ||
| 371 | pub fn jal(link: Register, offset: i21) Instruction { | |
| 372 | return jType(0b1101111, link, offset); | |
| 373 | } | |
| 374 | ||
| 375 | pub fn jalr(link: Register, offset: i12, base: Register) Instruction { | |
| 376 | return iType(0b1100111, 0b000, link, base, offset); | |
| 377 | } | |
| 378 | ||
| 379 | // System | |
| 380 | ||
| 381 | pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000); | |
| 382 | pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001); | |
| 383 | }; | |
| 384 | ||
| 385 | // zig fmt: off | |
| 386 | pub const RawRegister = enum(u5) { | |
| 387 | x0, x1, x2, x3, x4, x5, x6, x7, | |
| 388 | x8, x9, x10, x11, x12, x13, x14, x15, | |
| 389 | x16, x17, x18, x19, x20, x21, x22, x23, | |
| 390 | x24, x25, x26, x27, x28, x29, x30, x31, | |
| 391 | ||
| 392 | pub fn dwarfLocOp(reg: RawRegister) u8 { | |
| 393 | return @enumToInt(reg) + DW.OP.reg0; | |
| 394 | } | |
| 395 | }; | |
| 396 | ||
| 397 | pub const Register = enum(u5) { | |
| 398 | // 64 bit registers | |
| 399 | zero, // zero | |
| 400 | ra, // return address. caller saved | |
| 401 | sp, // stack pointer. callee saved. | |
| 402 | gp, // global pointer | |
| 403 | tp, // thread pointer | |
| 404 | t0, t1, t2, // temporaries. caller saved. | |
| 405 | s0, // s0/fp, callee saved. | |
| 406 | s1, // callee saved. | |
| 407 | a0, a1, // fn args/return values. caller saved. | |
| 408 | a2, a3, a4, a5, a6, a7, // fn args. caller saved. | |
| 409 | s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved. | |
| 410 | t3, t4, t5, t6, // caller saved | |
| 411 | ||
| 412 | pub fn parseRegName(name: []const u8) ?Register { | |
| 413 | if(std.meta.stringToEnum(Register, name)) |reg| return reg; | |
| 414 | if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg)); | |
| 415 | return null; | |
| 416 | } | |
| 417 | ||
| 418 | /// Returns the index into `callee_preserved_regs`. | |
| 419 | pub fn allocIndex(self: Register) ?u4 { | |
| 420 | inline for(callee_preserved_regs) |cpreg, i| { | |
| 421 | if(self == cpreg) return i; | |
| 422 | } | |
| 423 | return null; | |
| 424 | } | |
| 425 | ||
| 426 | pub fn dwarfLocOp(reg: Register) u8 { | |
| 427 | return @as(u8, @enumToInt(reg)) + DW.OP.reg0; | |
| 428 | } | |
| 429 | }; | |
| 430 | ||
| 431 | // zig fmt: on | |
| 432 | ||
| 433 | pub const callee_preserved_regs = [_]Register{ | |
| 434 | .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, | |
| 435 | }; | |
| 436 | ||
| 437 | test "serialize instructions" { | |
| 438 | const Testcase = struct { | |
| 439 | inst: Instruction, | |
| 440 | expected: u32, | |
| 441 | }; | |
| 442 | ||
| 443 | const testcases = [_]Testcase{ | |
| 444 | .{ // add t6, zero, zero | |
| 445 | .inst = Instruction.add(.t6, .zero, .zero), | |
| 446 | .expected = 0b0000000_00000_00000_000_11111_0110011, | |
| 447 | }, | |
| 448 | .{ // sd s0, 0x7f(s0) | |
| 449 | .inst = Instruction.sd(.s0, 0x7f, .s0), | |
| 450 | .expected = 0b0000011_01000_01000_011_11111_0100011, | |
| 451 | }, | |
| 452 | .{ // bne s0, s1, 0x42 | |
| 453 | .inst = Instruction.bne(.s0, .s1, 0x42), | |
| 454 | .expected = 0b0_000010_01001_01000_001_0001_0_1100011, | |
| 455 | }, | |
| 456 | .{ // j 0x1a | |
| 457 | .inst = Instruction.jal(.zero, 0x1a), | |
| 458 | .expected = 0b0_0000001101_0_00000000_00000_1101111, | |
| 459 | }, | |
| 460 | .{ // ebreak | |
| 461 | .inst = Instruction.ebreak, | |
| 462 | .expected = 0b000000000001_00000_000_00000_1110011, | |
| 463 | }, | |
| 464 | }; | |
| 465 | ||
| 466 | for (testcases) |case| { | |
| 467 | const actual = case.inst.toU32(); | |
| 468 | try testing.expectEqual(case.expected, actual); | |
| 469 | } | |
| 470 | } |
src/arch/x86/bits.zig created+123| ... | ... | @@ -0,0 +1,123 @@ |
| 1 | const std = @import("std"); | |
| 2 | const DW = std.dwarf; | |
| 3 | ||
| 4 | // zig fmt: off | |
| 5 | pub const Register = enum(u8) { | |
| 6 | // 0 through 7, 32-bit registers. id is int value | |
| 7 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | |
| 8 | ||
| 9 | // 8-15, 16-bit registers. id is int value - 8. | |
| 10 | ax, cx, dx, bx, sp, bp, si, di, | |
| 11 | ||
| 12 | // 16-23, 8-bit registers. id is int value - 16. | |
| 13 | al, cl, dl, bl, ah, ch, dh, bh, | |
| 14 | ||
| 15 | /// Returns the bit-width of the register. | |
| 16 | pub fn size(self: @This()) u7 { | |
| 17 | return switch (@enumToInt(self)) { | |
| 18 | 0...7 => 32, | |
| 19 | 8...15 => 16, | |
| 20 | 16...23 => 8, | |
| 21 | else => unreachable, | |
| 22 | }; | |
| 23 | } | |
| 24 | ||
| 25 | /// Returns the register's id. This is used in practically every opcode the | |
| 26 | /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move | |
| 27 | /// instruction, and is used in the R/M byte. | |
| 28 | pub fn id(self: @This()) u3 { | |
| 29 | return @truncate(u3, @enumToInt(self)); | |
| 30 | } | |
| 31 | ||
| 32 | /// Returns the index into `callee_preserved_regs`. | |
| 33 | pub fn allocIndex(self: Register) ?u4 { | |
| 34 | return switch (self) { | |
| 35 | .eax, .ax, .al => 0, | |
| 36 | .ecx, .cx, .cl => 1, | |
| 37 | .edx, .dx, .dl => 2, | |
| 38 | .esi, .si => 3, | |
| 39 | .edi, .di => 4, | |
| 40 | else => null, | |
| 41 | }; | |
| 42 | } | |
| 43 | ||
| 44 | /// Convert from any register to its 32 bit alias. | |
| 45 | pub fn to32(self: Register) Register { | |
| 46 | return @intToEnum(Register, @as(u8, self.id())); | |
| 47 | } | |
| 48 | ||
| 49 | /// Convert from any register to its 16 bit alias. | |
| 50 | pub fn to16(self: Register) Register { | |
| 51 | return @intToEnum(Register, @as(u8, self.id()) + 8); | |
| 52 | } | |
| 53 | ||
| 54 | /// Convert from any register to its 8 bit alias. | |
| 55 | pub fn to8(self: Register) Register { | |
| 56 | return @intToEnum(Register, @as(u8, self.id()) + 16); | |
| 57 | } | |
| 58 | ||
| 59 | ||
| 60 | pub fn dwarfLocOp(reg: Register) u8 { | |
| 61 | return switch (reg.to32()) { | |
| 62 | .eax => DW.OP.reg0, | |
| 63 | .ecx => DW.OP.reg1, | |
| 64 | .edx => DW.OP.reg2, | |
| 65 | .ebx => DW.OP.reg3, | |
| 66 | .esp => DW.OP.reg4, | |
| 67 | .ebp => DW.OP.reg5, | |
| 68 | .esi => DW.OP.reg6, | |
| 69 | .edi => DW.OP.reg7, | |
| 70 | else => unreachable, | |
| 71 | }; | |
| 72 | } | |
| 73 | }; | |
| 74 | ||
| 75 | // zig fmt: on | |
| 76 | ||
| 77 | pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi }; | |
| 78 | ||
| 79 | // TODO add these to Register enum and corresponding dwarfLocOp | |
| 80 | // // Return Address register. This is stored in `0(%esp, "")` and is not a physical register. | |
| 81 | // RA = (8, "RA"), | |
| 82 | // | |
| 83 | // ST0 = (11, "st0"), | |
| 84 | // ST1 = (12, "st1"), | |
| 85 | // ST2 = (13, "st2"), | |
| 86 | // ST3 = (14, "st3"), | |
| 87 | // ST4 = (15, "st4"), | |
| 88 | // ST5 = (16, "st5"), | |
| 89 | // ST6 = (17, "st6"), | |
| 90 | // ST7 = (18, "st7"), | |
| 91 | // | |
| 92 | // XMM0 = (21, "xmm0"), | |
| 93 | // XMM1 = (22, "xmm1"), | |
| 94 | // XMM2 = (23, "xmm2"), | |
| 95 | // XMM3 = (24, "xmm3"), | |
| 96 | // XMM4 = (25, "xmm4"), | |
| 97 | // XMM5 = (26, "xmm5"), | |
| 98 | // XMM6 = (27, "xmm6"), | |
| 99 | // XMM7 = (28, "xmm7"), | |
| 100 | // | |
| 101 | // MM0 = (29, "mm0"), | |
| 102 | // MM1 = (30, "mm1"), | |
| 103 | // MM2 = (31, "mm2"), | |
| 104 | // MM3 = (32, "mm3"), | |
| 105 | // MM4 = (33, "mm4"), | |
| 106 | // MM5 = (34, "mm5"), | |
| 107 | // MM6 = (35, "mm6"), | |
| 108 | // MM7 = (36, "mm7"), | |
| 109 | // | |
| 110 | // MXCSR = (39, "mxcsr"), | |
| 111 | // | |
| 112 | // ES = (40, "es"), | |
| 113 | // CS = (41, "cs"), | |
| 114 | // SS = (42, "ss"), | |
| 115 | // DS = (43, "ds"), | |
| 116 | // FS = (44, "fs"), | |
| 117 | // GS = (45, "gs"), | |
| 118 | // | |
| 119 | // TR = (48, "tr"), | |
| 120 | // LDTR = (49, "ldtr"), | |
| 121 | // | |
| 122 | // FS_BASE = (93, "fs.base"), | |
| 123 | // GS_BASE = (94, "gs.base"), |
src/arch/x86_64/bits.zig created+716| ... | ... | @@ -0,0 +1,716 @@ |
| 1 | const std = @import("std"); | |
| 2 | const testing = std.testing; | |
| 3 | const mem = std.mem; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const ArrayList = std.ArrayList; | |
| 6 | const Allocator = std.mem.Allocator; | |
| 7 | const DW = std.dwarf; | |
| 8 | ||
| 9 | // zig fmt: off | |
| 10 | ||
| 11 | /// Definitions of all of the x64 registers. The order is semantically meaningful. | |
| 12 | /// The registers are defined such that IDs go in descending order of 64-bit, | |
| 13 | /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen | |
| 14 | /// registers. This results in some useful properties: | |
| 15 | /// | |
| 16 | /// Any 64-bit register can be turned into its 32-bit form by adding 16, and | |
| 17 | /// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it | |
| 18 | /// works for all except for sp, bp, si, and di, which do *not* have an 8-bit | |
| 19 | /// form. | |
| 20 | /// | |
| 21 | /// If (register & 8) is set, the register is extended. | |
| 22 | /// | |
| 23 | /// The ID can be easily determined by figuring out what range the register is | |
| 24 | /// in, and then subtracting the base. | |
| 25 | pub const Register = enum(u8) { | |
| 26 | // 0 through 15, 64-bit registers. 8-15 are extended. | |
| 27 | // id is just the int value. | |
| 28 | rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, | |
| 29 | r8, r9, r10, r11, r12, r13, r14, r15, | |
| 30 | ||
| 31 | // 16 through 31, 32-bit registers. 24-31 are extended. | |
| 32 | // id is int value - 16. | |
| 33 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | |
| 34 | r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, | |
| 35 | ||
| 36 | // 32-47, 16-bit registers. 40-47 are extended. | |
| 37 | // id is int value - 32. | |
| 38 | ax, cx, dx, bx, sp, bp, si, di, | |
| 39 | r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, | |
| 40 | ||
| 41 | // 48-63, 8-bit registers. 56-63 are extended. | |
| 42 | // id is int value - 48. | |
| 43 | al, cl, dl, bl, ah, ch, dh, bh, | |
| 44 | r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b, | |
| 45 | ||
| 46 | /// Returns the bit-width of the register. | |
| 47 | pub fn size(self: Register) u7 { | |
| 48 | return switch (@enumToInt(self)) { | |
| 49 | 0...15 => 64, | |
| 50 | 16...31 => 32, | |
| 51 | 32...47 => 16, | |
| 52 | 48...64 => 8, | |
| 53 | else => unreachable, | |
| 54 | }; | |
| 55 | } | |
| 56 | ||
| 57 | /// Returns whether the register is *extended*. Extended registers are the | |
| 58 | /// new registers added with amd64, r8 through r15. This also includes any | |
| 59 | /// other variant of access to those registers, such as r8b, r15d, and so | |
| 60 | /// on. This is needed because access to these registers requires special | |
| 61 | /// handling via the REX prefix, via the B or R bits, depending on context. | |
| 62 | pub fn isExtended(self: Register) bool { | |
| 63 | return @enumToInt(self) & 0x08 != 0; | |
| 64 | } | |
| 65 | ||
| 66 | /// This returns the 4-bit register ID, which is used in practically every | |
| 67 | /// opcode. Note that bit 3 (the highest bit) is *never* used directly in | |
| 68 | /// an instruction (@see isExtended), and requires special handling. The | |
| 69 | /// lower three bits are often embedded directly in instructions (such as | |
| 70 | /// the B8 variant of moves), or used in R/M bytes. | |
| 71 | pub fn id(self: Register) u4 { | |
| 72 | return @truncate(u4, @enumToInt(self)); | |
| 73 | } | |
| 74 | ||
| 75 | /// Like id, but only returns the lower 3 bits. | |
| 76 | pub fn low_id(self: Register) u3 { | |
| 77 | return @truncate(u3, @enumToInt(self)); | |
| 78 | } | |
| 79 | ||
| 80 | /// Returns the index into `callee_preserved_regs`. | |
| 81 | pub fn allocIndex(self: Register) ?u4 { | |
| 82 | return switch (self) { | |
| 83 | .rax, .eax, .ax, .al => 0, | |
| 84 | .rcx, .ecx, .cx, .cl => 1, | |
| 85 | .rdx, .edx, .dx, .dl => 2, | |
| 86 | .rsi, .esi, .si => 3, | |
| 87 | .rdi, .edi, .di => 4, | |
| 88 | .r8, .r8d, .r8w, .r8b => 5, | |
| 89 | .r9, .r9d, .r9w, .r9b => 6, | |
| 90 | .r10, .r10d, .r10w, .r10b => 7, | |
| 91 | .r11, .r11d, .r11w, .r11b => 8, | |
| 92 | else => null, | |
| 93 | }; | |
| 94 | } | |
| 95 | ||
| 96 | /// Convert from any register to its 64 bit alias. | |
| 97 | pub fn to64(self: Register) Register { | |
| 98 | return @intToEnum(Register, self.id()); | |
| 99 | } | |
| 100 | ||
| 101 | /// Convert from any register to its 32 bit alias. | |
| 102 | pub fn to32(self: Register) Register { | |
| 103 | return @intToEnum(Register, @as(u8, self.id()) + 16); | |
| 104 | } | |
| 105 | ||
| 106 | /// Convert from any register to its 16 bit alias. | |
| 107 | pub fn to16(self: Register) Register { | |
| 108 | return @intToEnum(Register, @as(u8, self.id()) + 32); | |
| 109 | } | |
| 110 | ||
| 111 | /// Convert from any register to its 8 bit alias. | |
| 112 | pub fn to8(self: Register) Register { | |
| 113 | return @intToEnum(Register, @as(u8, self.id()) + 48); | |
| 114 | } | |
| 115 | ||
| 116 | pub fn dwarfLocOp(self: Register) u8 { | |
| 117 | return switch (self.to64()) { | |
| 118 | .rax => DW.OP.reg0, | |
| 119 | .rdx => DW.OP.reg1, | |
| 120 | .rcx => DW.OP.reg2, | |
| 121 | .rbx => DW.OP.reg3, | |
| 122 | .rsi => DW.OP.reg4, | |
| 123 | .rdi => DW.OP.reg5, | |
| 124 | .rbp => DW.OP.reg6, | |
| 125 | .rsp => DW.OP.reg7, | |
| 126 | ||
| 127 | .r8 => DW.OP.reg8, | |
| 128 | .r9 => DW.OP.reg9, | |
| 129 | .r10 => DW.OP.reg10, | |
| 130 | .r11 => DW.OP.reg11, | |
| 131 | .r12 => DW.OP.reg12, | |
| 132 | .r13 => DW.OP.reg13, | |
| 133 | .r14 => DW.OP.reg14, | |
| 134 | .r15 => DW.OP.reg15, | |
| 135 | ||
| 136 | else => unreachable, | |
| 137 | }; | |
| 138 | } | |
| 139 | }; | |
| 140 | ||
| 141 | // zig fmt: on | |
| 142 | ||
| 143 | /// These registers belong to the called function. | |
| 144 | pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | |
| 145 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; | |
| 146 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; | |
| 147 | ||
| 148 | /// Encoding helper functions for x86_64 instructions | |
| 149 | /// | |
| 150 | /// Many of these helpers do very little, but they can help make things | |
| 151 | /// slightly more readable with more descriptive field names / function names. | |
| 152 | /// | |
| 153 | /// Some of them also have asserts to ensure that we aren't doing dumb things. | |
| 154 | /// For example, trying to use register 4 (esp) in an indirect modr/m byte is illegal, | |
| 155 | /// you need to encode it with an SIB byte. | |
| 156 | /// | |
| 157 | /// Note that ALL of these helper functions will assume capacity, | |
| 158 | /// so ensure that the `code` has sufficient capacity before using them. | |
| 159 | /// The `init` method is the recommended way to ensure capacity. | |
| 160 | pub const Encoder = struct { | |
| 161 | /// Non-owning reference to the code array | |
| 162 | code: *ArrayList(u8), | |
| 163 | ||
| 164 | const Self = @This(); | |
| 165 | ||
| 166 | /// Wrap `code` in Encoder to make it easier to call these helper functions | |
| 167 | /// | |
| 168 | /// maximum_inst_size should contain the maximum number of bytes | |
| 169 | /// that the encoded instruction will take. | |
| 170 | /// This is because the helper functions will assume capacity | |
| 171 | /// in order to avoid bounds checking. | |
| 172 | pub fn init(code: *ArrayList(u8), maximum_inst_size: u8) !Self { | |
| 173 | try code.ensureUnusedCapacity(maximum_inst_size); | |
| 174 | return Self{ .code = code }; | |
| 175 | } | |
| 176 | ||
| 177 | /// Directly write a number to the code array with big endianness | |
| 178 | pub fn writeIntBig(self: Self, comptime T: type, value: T) void { | |
| 179 | mem.writeIntBig( | |
| 180 | T, | |
| 181 | self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)), | |
| 182 | value, | |
| 183 | ); | |
| 184 | } | |
| 185 | ||
| 186 | /// Directly write a number to the code array with little endianness | |
| 187 | pub fn writeIntLittle(self: Self, comptime T: type, value: T) void { | |
| 188 | mem.writeIntLittle( | |
| 189 | T, | |
| 190 | self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)), | |
| 191 | value, | |
| 192 | ); | |
| 193 | } | |
| 194 | ||
| 195 | // -------- | |
| 196 | // Prefixes | |
| 197 | // -------- | |
| 198 | ||
| 199 | pub const LegacyPrefixes = packed struct { | |
| 200 | /// LOCK | |
| 201 | prefix_f0: bool = false, | |
| 202 | /// REPNZ, REPNE, REP, Scalar Double-precision | |
| 203 | prefix_f2: bool = false, | |
| 204 | /// REPZ, REPE, REP, Scalar Single-precision | |
| 205 | prefix_f3: bool = false, | |
| 206 | ||
| 207 | /// CS segment override or Branch not taken | |
| 208 | prefix_2e: bool = false, | |
| 209 | /// DS segment override | |
| 210 | prefix_36: bool = false, | |
| 211 | /// ES segment override | |
| 212 | prefix_26: bool = false, | |
| 213 | /// FS segment override | |
| 214 | prefix_64: bool = false, | |
| 215 | /// GS segment override | |
| 216 | prefix_65: bool = false, | |
| 217 | ||
| 218 | /// Branch taken | |
| 219 | prefix_3e: bool = false, | |
| 220 | ||
| 221 | /// Operand size override (enables 16 bit operation) | |
| 222 | prefix_66: bool = false, | |
| 223 | ||
| 224 | /// Address size override (enables 16 bit address size) | |
| 225 | prefix_67: bool = false, | |
| 226 | ||
| 227 | padding: u5 = 0, | |
| 228 | }; | |
| 229 | ||
| 230 | /// Encodes legacy prefixes | |
| 231 | pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) void { | |
| 232 | if (@bitCast(u16, prefixes) != 0) { | |
| 233 | // Hopefully this path isn't taken very often, so we'll do it the slow way for now | |
| 234 | ||
| 235 | // LOCK | |
| 236 | if (prefixes.prefix_f0) self.code.appendAssumeCapacity(0xf0); | |
| 237 | // REPNZ, REPNE, REP, Scalar Double-precision | |
| 238 | if (prefixes.prefix_f2) self.code.appendAssumeCapacity(0xf2); | |
| 239 | // REPZ, REPE, REP, Scalar Single-precision | |
| 240 | if (prefixes.prefix_f3) self.code.appendAssumeCapacity(0xf3); | |
| 241 | ||
| 242 | // CS segment override or Branch not taken | |
| 243 | if (prefixes.prefix_2e) self.code.appendAssumeCapacity(0x2e); | |
| 244 | // DS segment override | |
| 245 | if (prefixes.prefix_36) self.code.appendAssumeCapacity(0x36); | |
| 246 | // ES segment override | |
| 247 | if (prefixes.prefix_26) self.code.appendAssumeCapacity(0x26); | |
| 248 | // FS segment override | |
| 249 | if (prefixes.prefix_64) self.code.appendAssumeCapacity(0x64); | |
| 250 | // GS segment override | |
| 251 | if (prefixes.prefix_65) self.code.appendAssumeCapacity(0x65); | |
| 252 | ||
| 253 | // Branch taken | |
| 254 | if (prefixes.prefix_3e) self.code.appendAssumeCapacity(0x3e); | |
| 255 | ||
| 256 | // Operand size override | |
| 257 | if (prefixes.prefix_66) self.code.appendAssumeCapacity(0x66); | |
| 258 | ||
| 259 | // Address size override | |
| 260 | if (prefixes.prefix_67) self.code.appendAssumeCapacity(0x67); | |
| 261 | } | |
| 262 | } | |
| 263 | ||
| 264 | /// Use 16 bit operand size | |
| 265 | /// | |
| 266 | /// Note that this flag is overridden by REX.W, if both are present. | |
| 267 | pub fn prefix16BitMode(self: Self) void { | |
| 268 | self.code.appendAssumeCapacity(0x66); | |
| 269 | } | |
| 270 | ||
| 271 | /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB | |
| 272 | pub const Rex = struct { | |
| 273 | /// Wide, enables 64-bit operation | |
| 274 | w: bool = false, | |
| 275 | /// Extends the reg field in the ModR/M byte | |
| 276 | r: bool = false, | |
| 277 | /// Extends the index field in the SIB byte | |
| 278 | x: bool = false, | |
| 279 | /// Extends the r/m field in the ModR/M byte, | |
| 280 | /// or the base field in the SIB byte, | |
| 281 | /// or the reg field in the Opcode byte | |
| 282 | b: bool = false, | |
| 283 | }; | |
| 284 | ||
| 285 | /// Encodes a REX prefix byte given all the fields | |
| 286 | /// | |
| 287 | /// Use this byte whenever you need 64 bit operation, | |
| 288 | /// or one of reg, index, r/m, base, or opcode-reg might be extended. | |
| 289 | /// | |
| 290 | /// See struct `Rex` for a description of each field. | |
| 291 | /// | |
| 292 | /// Does not add a prefix byte if none of the fields are set! | |
| 293 | pub fn rex(self: Self, byte: Rex) void { | |
| 294 | var value: u8 = 0b0100_0000; | |
| 295 | ||
| 296 | if (byte.w) value |= 0b1000; | |
| 297 | if (byte.r) value |= 0b0100; | |
| 298 | if (byte.x) value |= 0b0010; | |
| 299 | if (byte.b) value |= 0b0001; | |
| 300 | ||
| 301 | if (value != 0b0100_0000) { | |
| 302 | self.code.appendAssumeCapacity(value); | |
| 303 | } | |
| 304 | } | |
| 305 | ||
| 306 | // ------ | |
| 307 | // Opcode | |
| 308 | // ------ | |
| 309 | ||
| 310 | /// Encodes a 1 byte opcode | |
| 311 | pub fn opcode_1byte(self: Self, opcode: u8) void { | |
| 312 | self.code.appendAssumeCapacity(opcode); | |
| 313 | } | |
| 314 | ||
| 315 | /// Encodes a 2 byte opcode | |
| 316 | /// | |
| 317 | /// e.g. IMUL has the opcode 0x0f 0xaf, so you use | |
| 318 | /// | |
| 319 | /// encoder.opcode_2byte(0x0f, 0xaf); | |
| 320 | pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) void { | |
| 321 | self.code.appendAssumeCapacity(prefix); | |
| 322 | self.code.appendAssumeCapacity(opcode); | |
| 323 | } | |
| 324 | ||
| 325 | /// Encodes a 1 byte opcode with a reg field | |
| 326 | /// | |
| 327 | /// Remember to add a REX prefix byte if reg is extended! | |
| 328 | pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) void { | |
| 329 | assert(opcode & 0b111 == 0); | |
| 330 | self.code.appendAssumeCapacity(opcode | reg); | |
| 331 | } | |
| 332 | ||
| 333 | // ------ | |
| 334 | // ModR/M | |
| 335 | // ------ | |
| 336 | ||
| 337 | /// Construct a ModR/M byte given all the fields | |
| 338 | /// | |
| 339 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 340 | pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) void { | |
| 341 | self.code.appendAssumeCapacity( | |
| 342 | @as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm, | |
| 343 | ); | |
| 344 | } | |
| 345 | ||
| 346 | /// Construct a ModR/M byte using direct r/m addressing | |
| 347 | /// r/m effective address: r/m | |
| 348 | /// | |
| 349 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 350 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 351 | pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) void { | |
| 352 | self.modRm(0b11, reg_or_opx, rm); | |
| 353 | } | |
| 354 | ||
| 355 | /// Construct a ModR/M byte using indirect r/m addressing | |
| 356 | /// r/m effective address: [r/m] | |
| 357 | /// | |
| 358 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 359 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 360 | pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) void { | |
| 361 | assert(rm != 4 and rm != 5); | |
| 362 | self.modRm(0b00, reg_or_opx, rm); | |
| 363 | } | |
| 364 | ||
| 365 | /// Construct a ModR/M byte using indirect SIB addressing | |
| 366 | /// r/m effective address: [SIB] | |
| 367 | /// | |
| 368 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 369 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 370 | pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) void { | |
| 371 | self.modRm(0b00, reg_or_opx, 0b100); | |
| 372 | } | |
| 373 | ||
| 374 | /// Construct a ModR/M byte using RIP-relative addressing | |
| 375 | /// r/m effective address: [RIP + disp32] | |
| 376 | /// | |
| 377 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 378 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 379 | pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) void { | |
| 380 | self.modRm(0b00, reg_or_opx, 0b101); | |
| 381 | } | |
| 382 | ||
| 383 | /// Construct a ModR/M byte using indirect r/m with a 8bit displacement | |
| 384 | /// r/m effective address: [r/m + disp8] | |
| 385 | /// | |
| 386 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 387 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 388 | pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) void { | |
| 389 | assert(rm != 4); | |
| 390 | self.modRm(0b01, reg_or_opx, rm); | |
| 391 | } | |
| 392 | ||
| 393 | /// Construct a ModR/M byte using indirect SIB with a 8bit displacement | |
| 394 | /// r/m effective address: [SIB + disp8] | |
| 395 | /// | |
| 396 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 397 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 398 | pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) void { | |
| 399 | self.modRm(0b01, reg_or_opx, 0b100); | |
| 400 | } | |
| 401 | ||
| 402 | /// Construct a ModR/M byte using indirect r/m with a 32bit displacement | |
| 403 | /// r/m effective address: [r/m + disp32] | |
| 404 | /// | |
| 405 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 406 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 407 | pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) void { | |
| 408 | assert(rm != 4); | |
| 409 | self.modRm(0b10, reg_or_opx, rm); | |
| 410 | } | |
| 411 | ||
| 412 | /// Construct a ModR/M byte using indirect SIB with a 32bit displacement | |
| 413 | /// r/m effective address: [SIB + disp32] | |
| 414 | /// | |
| 415 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 416 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 417 | pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) void { | |
| 418 | self.modRm(0b10, reg_or_opx, 0b100); | |
| 419 | } | |
| 420 | ||
| 421 | // --- | |
| 422 | // SIB | |
| 423 | // --- | |
| 424 | ||
| 425 | /// Construct a SIB byte given all the fields | |
| 426 | /// | |
| 427 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 428 | pub fn sib(self: Self, scale: u2, index: u3, base: u3) void { | |
| 429 | self.code.appendAssumeCapacity( | |
| 430 | @as(u8, scale) << 6 | @as(u8, index) << 3 | base, | |
| 431 | ); | |
| 432 | } | |
| 433 | ||
| 434 | /// Construct a SIB byte with scale * index + base, no frills. | |
| 435 | /// r/m effective address: [base + scale * index] | |
| 436 | /// | |
| 437 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 438 | pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) void { | |
| 439 | assert(base != 5); | |
| 440 | ||
| 441 | self.sib(scale, index, base); | |
| 442 | } | |
| 443 | ||
| 444 | /// Construct a SIB byte with scale * index + disp32 | |
| 445 | /// r/m effective address: [scale * index + disp32] | |
| 446 | /// | |
| 447 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 448 | pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) void { | |
| 449 | assert(index != 4); | |
| 450 | ||
| 451 | // scale is actually ignored | |
| 452 | // index = 4 means no index | |
| 453 | // base = 5 means no base, if mod == 0. | |
| 454 | self.sib(scale, index, 5); | |
| 455 | } | |
| 456 | ||
| 457 | /// Construct a SIB byte with just base | |
| 458 | /// r/m effective address: [base] | |
| 459 | /// | |
| 460 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 461 | pub fn sib_base(self: Self, base: u3) void { | |
| 462 | assert(base != 5); | |
| 463 | ||
| 464 | // scale is actually ignored | |
| 465 | // index = 4 means no index | |
| 466 | self.sib(0, 4, base); | |
| 467 | } | |
| 468 | ||
| 469 | /// Construct a SIB byte with just disp32 | |
| 470 | /// r/m effective address: [disp32] | |
| 471 | /// | |
| 472 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 473 | pub fn sib_disp32(self: Self) void { | |
| 474 | // scale is actually ignored | |
| 475 | // index = 4 means no index | |
| 476 | // base = 5 means no base, if mod == 0. | |
| 477 | self.sib(0, 4, 5); | |
| 478 | } | |
| 479 | ||
| 480 | /// Construct a SIB byte with scale * index + base + disp8 | |
| 481 | /// r/m effective address: [base + scale * index + disp8] | |
| 482 | /// | |
| 483 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 484 | pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) void { | |
| 485 | self.sib(scale, index, base); | |
| 486 | } | |
| 487 | ||
| 488 | /// Construct a SIB byte with base + disp8, no index | |
| 489 | /// r/m effective address: [base + disp8] | |
| 490 | /// | |
| 491 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 492 | pub fn sib_baseDisp8(self: Self, base: u3) void { | |
| 493 | // scale is ignored | |
| 494 | // index = 4 means no index | |
| 495 | self.sib(0, 4, base); | |
| 496 | } | |
| 497 | ||
| 498 | /// Construct a SIB byte with scale * index + base + disp32 | |
| 499 | /// r/m effective address: [base + scale * index + disp32] | |
| 500 | /// | |
| 501 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 502 | pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) void { | |
| 503 | self.sib(scale, index, base); | |
| 504 | } | |
| 505 | ||
| 506 | /// Construct a SIB byte with base + disp32, no index | |
| 507 | /// r/m effective address: [base + disp32] | |
| 508 | /// | |
| 509 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 510 | pub fn sib_baseDisp32(self: Self, base: u3) void { | |
| 511 | // scale is ignored | |
| 512 | // index = 4 means no index | |
| 513 | self.sib(0, 4, base); | |
| 514 | } | |
| 515 | ||
| 516 | // ------------------------- | |
| 517 | // Trivial (no bit fiddling) | |
| 518 | // ------------------------- | |
| 519 | ||
| 520 | /// Encode an 8 bit immediate | |
| 521 | /// | |
| 522 | /// It is sign-extended to 64 bits by the cpu. | |
| 523 | pub fn imm8(self: Self, imm: i8) void { | |
| 524 | self.code.appendAssumeCapacity(@bitCast(u8, imm)); | |
| 525 | } | |
| 526 | ||
| 527 | /// Encode an 8 bit displacement | |
| 528 | /// | |
| 529 | /// It is sign-extended to 64 bits by the cpu. | |
| 530 | pub fn disp8(self: Self, disp: i8) void { | |
| 531 | self.code.appendAssumeCapacity(@bitCast(u8, disp)); | |
| 532 | } | |
| 533 | ||
| 534 | /// Encode an 16 bit immediate | |
| 535 | /// | |
| 536 | /// It is sign-extended to 64 bits by the cpu. | |
| 537 | pub fn imm16(self: Self, imm: i16) void { | |
| 538 | self.writeIntLittle(i16, imm); | |
| 539 | } | |
| 540 | ||
| 541 | /// Encode an 32 bit immediate | |
| 542 | /// | |
| 543 | /// It is sign-extended to 64 bits by the cpu. | |
| 544 | pub fn imm32(self: Self, imm: i32) void { | |
| 545 | self.writeIntLittle(i32, imm); | |
| 546 | } | |
| 547 | ||
| 548 | /// Encode an 32 bit displacement | |
| 549 | /// | |
| 550 | /// It is sign-extended to 64 bits by the cpu. | |
| 551 | pub fn disp32(self: Self, disp: i32) void { | |
| 552 | self.writeIntLittle(i32, disp); | |
| 553 | } | |
| 554 | ||
| 555 | /// Encode an 64 bit immediate | |
| 556 | /// | |
| 557 | /// It is sign-extended to 64 bits by the cpu. | |
| 558 | pub fn imm64(self: Self, imm: u64) void { | |
| 559 | self.writeIntLittle(u64, imm); | |
| 560 | } | |
| 561 | }; | |
| 562 | ||
| 563 | test "x86_64 Encoder helpers" { | |
| 564 | var code = ArrayList(u8).init(testing.allocator); | |
| 565 | defer code.deinit(); | |
| 566 | ||
| 567 | // simple integer multiplication | |
| 568 | ||
| 569 | // imul eax,edi | |
| 570 | // 0faf c7 | |
| 571 | { | |
| 572 | try code.resize(0); | |
| 573 | const encoder = try Encoder.init(&code, 4); | |
| 574 | encoder.rex(.{ | |
| 575 | .r = Register.eax.isExtended(), | |
| 576 | .b = Register.edi.isExtended(), | |
| 577 | }); | |
| 578 | encoder.opcode_2byte(0x0f, 0xaf); | |
| 579 | encoder.modRm_direct( | |
| 580 | Register.eax.low_id(), | |
| 581 | Register.edi.low_id(), | |
| 582 | ); | |
| 583 | ||
| 584 | try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items); | |
| 585 | } | |
| 586 | ||
| 587 | // simple mov | |
| 588 | ||
| 589 | // mov eax,edi | |
| 590 | // 89 f8 | |
| 591 | { | |
| 592 | try code.resize(0); | |
| 593 | const encoder = try Encoder.init(&code, 3); | |
| 594 | encoder.rex(.{ | |
| 595 | .r = Register.edi.isExtended(), | |
| 596 | .b = Register.eax.isExtended(), | |
| 597 | }); | |
| 598 | encoder.opcode_1byte(0x89); | |
| 599 | encoder.modRm_direct( | |
| 600 | Register.edi.low_id(), | |
| 601 | Register.eax.low_id(), | |
| 602 | ); | |
| 603 | ||
| 604 | try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items); | |
| 605 | } | |
| 606 | ||
| 607 | // signed integer addition of 32-bit sign extended immediate to 64 bit register | |
| 608 | ||
| 609 | // add rcx, 2147483647 | |
| 610 | // | |
| 611 | // Using the following opcode: REX.W + 81 /0 id, we expect the following encoding | |
| 612 | // | |
| 613 | // 48 : REX.W set for 64 bit operand (*r*cx) | |
| 614 | // 81 : opcode for "<arithmetic> with immediate" | |
| 615 | // c1 : id = rcx, | |
| 616 | // : c1 = 11 <-- mod = 11 indicates r/m is register (rcx) | |
| 617 | // : 000 <-- opcode_extension = 0 because opcode extension is /0. /0 specifies ADD | |
| 618 | // : 001 <-- 001 is rcx | |
| 619 | // ffffff7f : 2147483647 | |
| 620 | { | |
| 621 | try code.resize(0); | |
| 622 | const encoder = try Encoder.init(&code, 7); | |
| 623 | encoder.rex(.{ .w = true }); // use 64 bit operation | |
| 624 | encoder.opcode_1byte(0x81); | |
| 625 | encoder.modRm_direct( | |
| 626 | 0, | |
| 627 | Register.rcx.low_id(), | |
| 628 | ); | |
| 629 | encoder.imm32(2147483647); | |
| 630 | ||
| 631 | try testing.expectEqualSlices(u8, &[_]u8{ 0x48, 0x81, 0xc1, 0xff, 0xff, 0xff, 0x7f }, code.items); | |
| 632 | } | |
| 633 | } | |
| 634 | ||
| 635 | // TODO add these registers to the enum and populate dwarfLocOp | |
| 636 | // // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. | |
| 637 | // RA = (16, "RA"), | |
| 638 | // | |
| 639 | // XMM0 = (17, "xmm0"), | |
| 640 | // XMM1 = (18, "xmm1"), | |
| 641 | // XMM2 = (19, "xmm2"), | |
| 642 | // XMM3 = (20, "xmm3"), | |
| 643 | // XMM4 = (21, "xmm4"), | |
| 644 | // XMM5 = (22, "xmm5"), | |
| 645 | // XMM6 = (23, "xmm6"), | |
| 646 | // XMM7 = (24, "xmm7"), | |
| 647 | // | |
| 648 | // XMM8 = (25, "xmm8"), | |
| 649 | // XMM9 = (26, "xmm9"), | |
| 650 | // XMM10 = (27, "xmm10"), | |
| 651 | // XMM11 = (28, "xmm11"), | |
| 652 | // XMM12 = (29, "xmm12"), | |
| 653 | // XMM13 = (30, "xmm13"), | |
| 654 | // XMM14 = (31, "xmm14"), | |
| 655 | // XMM15 = (32, "xmm15"), | |
| 656 | // | |
| 657 | // ST0 = (33, "st0"), | |
| 658 | // ST1 = (34, "st1"), | |
| 659 | // ST2 = (35, "st2"), | |
| 660 | // ST3 = (36, "st3"), | |
| 661 | // ST4 = (37, "st4"), | |
| 662 | // ST5 = (38, "st5"), | |
| 663 | // ST6 = (39, "st6"), | |
| 664 | // ST7 = (40, "st7"), | |
| 665 | // | |
| 666 | // MM0 = (41, "mm0"), | |
| 667 | // MM1 = (42, "mm1"), | |
| 668 | // MM2 = (43, "mm2"), | |
| 669 | // MM3 = (44, "mm3"), | |
| 670 | // MM4 = (45, "mm4"), | |
| 671 | // MM5 = (46, "mm5"), | |
| 672 | // MM6 = (47, "mm6"), | |
| 673 | // MM7 = (48, "mm7"), | |
| 674 | // | |
| 675 | // RFLAGS = (49, "rFLAGS"), | |
| 676 | // ES = (50, "es"), | |
| 677 | // CS = (51, "cs"), | |
| 678 | // SS = (52, "ss"), | |
| 679 | // DS = (53, "ds"), | |
| 680 | // FS = (54, "fs"), | |
| 681 | // GS = (55, "gs"), | |
| 682 | // | |
| 683 | // FS_BASE = (58, "fs.base"), | |
| 684 | // GS_BASE = (59, "gs.base"), | |
| 685 | // | |
| 686 | // TR = (62, "tr"), | |
| 687 | // LDTR = (63, "ldtr"), | |
| 688 | // MXCSR = (64, "mxcsr"), | |
| 689 | // FCW = (65, "fcw"), | |
| 690 | // FSW = (66, "fsw"), | |
| 691 | // | |
| 692 | // XMM16 = (67, "xmm16"), | |
| 693 | // XMM17 = (68, "xmm17"), | |
| 694 | // XMM18 = (69, "xmm18"), | |
| 695 | // XMM19 = (70, "xmm19"), | |
| 696 | // XMM20 = (71, "xmm20"), | |
| 697 | // XMM21 = (72, "xmm21"), | |
| 698 | // XMM22 = (73, "xmm22"), | |
| 699 | // XMM23 = (74, "xmm23"), | |
| 700 | // XMM24 = (75, "xmm24"), | |
| 701 | // XMM25 = (76, "xmm25"), | |
| 702 | // XMM26 = (77, "xmm26"), | |
| 703 | // XMM27 = (78, "xmm27"), | |
| 704 | // XMM28 = (79, "xmm28"), | |
| 705 | // XMM29 = (80, "xmm29"), | |
| 706 | // XMM30 = (81, "xmm30"), | |
| 707 | // XMM31 = (82, "xmm31"), | |
| 708 | // | |
| 709 | // K0 = (118, "k0"), | |
| 710 | // K1 = (119, "k1"), | |
| 711 | // K2 = (120, "k2"), | |
| 712 | // K3 = (121, "k3"), | |
| 713 | // K4 = (122, "k4"), | |
| 714 | // K5 = (123, "k5"), | |
| 715 | // K6 = (124, "k6"), | |
| 716 | // K7 = (125, "k7"), |
src/clang_options_data.zig+8-1| ... | ... | @@ -2434,7 +2434,14 @@ flagpd1("emit-codegen-only"), |
| 2434 | 2434 | flagpd1("emit-header-module"), |
| 2435 | 2435 | flagpd1("emit-html"), |
| 2436 | 2436 | flagpd1("emit-interface-stubs"), |
| 2437 | flagpd1("emit-llvm"), | |
| 2437 | .{ | |
| 2438 | .name = "emit-llvm", | |
| 2439 | .syntax = .flag, | |
| 2440 | .zig_equivalent = .emit_llvm, | |
| 2441 | .pd1 = true, | |
| 2442 | .pd2 = false, | |
| 2443 | .psl = false, | |
| 2444 | }, | |
| 2438 | 2445 | flagpd1("emit-llvm-bc"), |
| 2439 | 2446 | flagpd1("emit-llvm-only"), |
| 2440 | 2447 | flagpd1("emit-llvm-uselists"), |
src/codegen.zig+281-68| ... | ... | @@ -21,7 +21,7 @@ const log = std.log.scoped(.codegen); |
| 21 | 21 | const build_options = @import("build_options"); |
| 22 | 22 | const RegisterManager = @import("register_manager.zig").RegisterManager; |
| 23 | 23 | |
| 24 | const X8664Encoder = @import("codegen/x86_64.zig").Encoder; | |
| 24 | const X8664Encoder = @import("arch/x86_64/bits.zig").Encoder; | |
| 25 | 25 | |
| 26 | 26 | pub const FnResult = union(enum) { |
| 27 | 27 | /// The `code` parameter passed to `generateSymbol` has the value appended. |
| ... | ... | @@ -48,6 +48,28 @@ pub const DebugInfoOutput = union(enum) { |
| 48 | 48 | dbg_info: *std.ArrayList(u8), |
| 49 | 49 | dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable, |
| 50 | 50 | }, |
| 51 | /// the plan9 debuginfo output is a bytecode with 4 opcodes | |
| 52 | /// assume all numbers/variables are bytes | |
| 53 | /// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset | |
| 54 | /// x when x < 65 -> add x to line offset | |
| 55 | /// x when x < 129 -> subtract 64 from x and subtract it from the line offset | |
| 56 | /// x -> subtract 129 from x, multiply it by the quanta of the instruction size | |
| 57 | /// (1 on x86_64), and add it to the pc | |
| 58 | /// after every opcode, add the quanta of the instruction size to the pc | |
| 59 | plan9: struct { | |
| 60 | /// the actual opcodes | |
| 61 | dbg_line: *std.ArrayList(u8), | |
| 62 | /// what line the debuginfo starts on | |
| 63 | /// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl | |
| 64 | start_line: *?u32, | |
| 65 | /// what the line count ends on after codegen | |
| 66 | /// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl | |
| 67 | end_line: *u32, | |
| 68 | /// the last pc change op | |
| 69 | /// This is very useful for adding quanta | |
| 70 | /// to it if its not actually the last one. | |
| 71 | pcop_change_index: *?u32, | |
| 72 | }, | |
| 51 | 73 | none, |
| 52 | 74 | }; |
| 53 | 75 | |
| ... | ... | @@ -141,7 +163,7 @@ pub fn generateSymbol( |
| 141 | 163 | // TODO populate .debug_info for the array |
| 142 | 164 | if (typed_value.val.castTag(.bytes)) |payload| { |
| 143 | 165 | if (typed_value.ty.sentinel()) |sentinel| { |
| 144 | try code.ensureCapacity(code.items.len + payload.data.len + 1); | |
| 166 | try code.ensureUnusedCapacity(payload.data.len + 1); | |
| 145 | 167 | code.appendSliceAssumeCapacity(payload.data); |
| 146 | 168 | switch (try generateSymbol(bin_file, src_loc, .{ |
| 147 | 169 | .ty = typed_value.ty.elemType(), |
| ... | ... | @@ -448,7 +470,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 448 | 470 | /// A branch in the ARM instruction set |
| 449 | 471 | arm_branch: struct { |
| 450 | 472 | pos: usize, |
| 451 | cond: @import("codegen/arm.zig").Condition, | |
| 473 | cond: @import("arch/arm/bits.zig").Condition, | |
| 452 | 474 | }, |
| 453 | 475 | }; |
| 454 | 476 | |
| ... | ... | @@ -568,7 +590,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 568 | 590 | fn gen(self: *Self) !void { |
| 569 | 591 | switch (arch) { |
| 570 | 592 | .x86_64 => { |
| 571 | try self.code.ensureCapacity(self.code.items.len + 11); | |
| 593 | try self.code.ensureUnusedCapacity(11); | |
| 572 | 594 | |
| 573 | 595 | const cc = self.fn_type.fnCallingConvention(); |
| 574 | 596 | if (cc != .Naked) { |
| ... | ... | @@ -607,7 +629,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 607 | 629 | // Important to be after the possible self.code.items.len -= 5 above. |
| 608 | 630 | try self.dbgSetEpilogueBegin(); |
| 609 | 631 | |
| 610 | try self.code.ensureCapacity(self.code.items.len + 9); | |
| 632 | try self.code.ensureUnusedCapacity(9); | |
| 611 | 633 | // add rsp, x |
| 612 | 634 | if (aligned_stack_end > math.maxInt(i8)) { |
| 613 | 635 | // example: 48 81 c4 ff ff ff 7f add rsp,0x7fffffff |
| ... | ... | @@ -802,14 +824,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 802 | 824 | |
| 803 | 825 | switch (air_tags[inst]) { |
| 804 | 826 | // zig fmt: off |
| 805 | .add, .ptr_add => try self.airAdd(inst), | |
| 806 | .addwrap => try self.airAddWrap(inst), | |
| 807 | .sub, .ptr_sub => try self.airSub(inst), | |
| 808 | .subwrap => try self.airSubWrap(inst), | |
| 809 | .mul => try self.airMul(inst), | |
| 810 | .mulwrap => try self.airMulWrap(inst), | |
| 811 | .div => try self.airDiv(inst), | |
| 812 | .rem => try self.airRem(inst), | |
| 827 | .add, .ptr_add => try self.airAdd(inst), | |
| 828 | .addwrap => try self.airAddWrap(inst), | |
| 829 | .add_sat => try self.airAddSat(inst), | |
| 830 | .sub, .ptr_sub => try self.airSub(inst), | |
| 831 | .subwrap => try self.airSubWrap(inst), | |
| 832 | .sub_sat => try self.airSubSat(inst), | |
| 833 | .mul => try self.airMul(inst), | |
| 834 | .mulwrap => try self.airMulWrap(inst), | |
| 835 | .mul_sat => try self.airMulSat(inst), | |
| 836 | .div => try self.airDiv(inst), | |
| 837 | .rem => try self.airRem(inst), | |
| 838 | .mod => try self.airMod(inst), | |
| 839 | .shl, .shl_exact => try self.airShl(inst), | |
| 840 | .shl_sat => try self.airShlSat(inst), | |
| 813 | 841 | |
| 814 | 842 | .cmp_lt => try self.airCmp(inst, .lt), |
| 815 | 843 | .cmp_lte => try self.airCmp(inst, .lte), |
| ... | ... | @@ -824,7 +852,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 824 | 852 | .bit_or => try self.airBitOr(inst), |
| 825 | 853 | .xor => try self.airXor(inst), |
| 826 | 854 | .shr => try self.airShr(inst), |
| 827 | .shl => try self.airShl(inst), | |
| 828 | 855 | |
| 829 | 856 | .alloc => try self.airAlloc(inst), |
| 830 | 857 | .arg => try self.airArg(inst), |
| ... | ... | @@ -833,10 +860,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 833 | 860 | .block => try self.airBlock(inst), |
| 834 | 861 | .br => try self.airBr(inst), |
| 835 | 862 | .breakpoint => try self.airBreakpoint(), |
| 863 | .fence => try self.airFence(), | |
| 836 | 864 | .call => try self.airCall(inst), |
| 837 | 865 | .cond_br => try self.airCondBr(inst), |
| 838 | 866 | .dbg_stmt => try self.airDbgStmt(inst), |
| 839 | .floatcast => try self.airFloatCast(inst), | |
| 867 | .fptrunc => try self.airFptrunc(inst), | |
| 868 | .fpext => try self.airFpext(inst), | |
| 840 | 869 | .intcast => try self.airIntCast(inst), |
| 841 | 870 | .trunc => try self.airTrunc(inst), |
| 842 | 871 | .bool_to_int => try self.airBoolToInt(inst), |
| ... | ... | @@ -857,8 +886,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 857 | 886 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 858 | 887 | .struct_field_val=> try self.airStructFieldVal(inst), |
| 859 | 888 | .array_to_slice => try self.airArrayToSlice(inst), |
| 889 | .int_to_float => try self.airIntToFloat(inst), | |
| 890 | .float_to_int => try self.airFloatToInt(inst), | |
| 860 | 891 | .cmpxchg_strong => try self.airCmpxchg(inst), |
| 861 | 892 | .cmpxchg_weak => try self.airCmpxchg(inst), |
| 893 | .atomic_rmw => try self.airAtomicRmw(inst), | |
| 894 | .atomic_load => try self.airAtomicLoad(inst), | |
| 895 | .memcpy => try self.airMemcpy(inst), | |
| 896 | .memset => try self.airMemset(inst), | |
| 897 | .set_union_tag => try self.airSetUnionTag(inst), | |
| 898 | .get_union_tag => try self.airGetUnionTag(inst), | |
| 899 | .clz => try self.airClz(inst), | |
| 900 | .ctz => try self.airCtz(inst), | |
| 901 | ||
| 902 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), | |
| 903 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), | |
| 904 | .atomic_store_release => try self.airAtomicStore(inst, .Release), | |
| 905 | .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), | |
| 862 | 906 | |
| 863 | 907 | .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), |
| 864 | 908 | .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), |
| ... | ... | @@ -905,6 +949,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 905 | 949 | try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); |
| 906 | 950 | try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); |
| 907 | 951 | }, |
| 952 | .plan9 => {}, | |
| 908 | 953 | .none => {}, |
| 909 | 954 | } |
| 910 | 955 | } |
| ... | ... | @@ -915,15 +960,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 915 | 960 | try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); |
| 916 | 961 | try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); |
| 917 | 962 | }, |
| 963 | .plan9 => {}, | |
| 918 | 964 | .none => {}, |
| 919 | 965 | } |
| 920 | 966 | } |
| 921 | 967 | |
| 922 | 968 | fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void { |
| 969 | const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); | |
| 970 | const delta_pc: usize = self.code.items.len - self.prev_di_pc; | |
| 923 | 971 | switch (self.debug_output) { |
| 924 | 972 | .dwarf => |dbg_out| { |
| 925 | const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); | |
| 926 | const delta_pc = self.code.items.len - self.prev_di_pc; | |
| 927 | 973 | // TODO Look into using the DWARF special opcodes to compress this data. |
| 928 | 974 | // It lets you emit single-byte opcodes that add different numbers to |
| 929 | 975 | // both the PC and the line number at the same time. |
| ... | ... | @@ -935,12 +981,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 935 | 981 | leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; |
| 936 | 982 | } |
| 937 | 983 | dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); |
| 984 | self.prev_di_pc = self.code.items.len; | |
| 985 | self.prev_di_line = line; | |
| 986 | self.prev_di_column = column; | |
| 987 | self.prev_di_pc = self.code.items.len; | |
| 988 | }, | |
| 989 | .plan9 => |dbg_out| { | |
| 990 | if (delta_pc <= 0) return; // only do this when the pc changes | |
| 991 | // we have already checked the target in the linker to make sure it is compatable | |
| 992 | const quant = @import("link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable; | |
| 993 | ||
| 994 | // increasing the line number | |
| 995 | try @import("link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line); | |
| 996 | // increasing the pc | |
| 997 | const d_pc_p9 = @intCast(i64, delta_pc) - quant; | |
| 998 | if (d_pc_p9 > 0) { | |
| 999 | // minus one because if its the last one, we want to leave space to change the line which is one quanta | |
| 1000 | try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant); | |
| 1001 | if (dbg_out.pcop_change_index.*) |pci| | |
| 1002 | dbg_out.dbg_line.items[pci] += 1; | |
| 1003 | dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1); | |
| 1004 | } else if (d_pc_p9 == 0) { | |
| 1005 | // we don't need to do anything, because adding the quant does it for us | |
| 1006 | } else unreachable; | |
| 1007 | if (dbg_out.start_line.* == null) | |
| 1008 | dbg_out.start_line.* = self.prev_di_line; | |
| 1009 | dbg_out.end_line.* = line; | |
| 1010 | // only do this if the pc changed | |
| 1011 | self.prev_di_line = line; | |
| 1012 | self.prev_di_column = column; | |
| 1013 | self.prev_di_pc = self.code.items.len; | |
| 938 | 1014 | }, |
| 939 | 1015 | .none => {}, |
| 940 | 1016 | } |
| 941 | self.prev_di_line = line; | |
| 942 | self.prev_di_column = column; | |
| 943 | self.prev_di_pc = self.code.items.len; | |
| 944 | 1017 | } |
| 945 | 1018 | |
| 946 | 1019 | /// Asserts there is already capacity to insert into top branch inst_table. |
| ... | ... | @@ -1024,6 +1097,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1024 | 1097 | } |
| 1025 | 1098 | try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index)); |
| 1026 | 1099 | }, |
| 1100 | .plan9 => {}, | |
| 1027 | 1101 | .none => {}, |
| 1028 | 1102 | } |
| 1029 | 1103 | } |
| ... | ... | @@ -1110,10 +1184,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1110 | 1184 | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); |
| 1111 | 1185 | } |
| 1112 | 1186 | |
| 1113 | fn airFloatCast(self: *Self, inst: Air.Inst.Index) !void { | |
| 1187 | fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { | |
| 1188 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1189 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1190 | else => return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch}), | |
| 1191 | }; | |
| 1192 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 1193 | } | |
| 1194 | ||
| 1195 | fn airFpext(self: *Self, inst: Air.Inst.Index) !void { | |
| 1114 | 1196 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1115 | 1197 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
| 1116 | else => return self.fail("TODO implement floatCast for {}", .{self.target.cpu.arch}), | |
| 1198 | else => return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch}), | |
| 1117 | 1199 | }; |
| 1118 | 1200 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1119 | 1201 | } |
| ... | ... | @@ -1226,6 +1308,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1226 | 1308 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1227 | 1309 | } |
| 1228 | 1310 | |
| 1311 | fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { | |
| 1312 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1313 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1314 | else => return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}), | |
| 1315 | }; | |
| 1316 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1317 | } | |
| 1318 | ||
| 1229 | 1319 | fn airSub(self: *Self, inst: Air.Inst.Index) !void { |
| 1230 | 1320 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1231 | 1321 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
| ... | ... | @@ -1244,6 +1334,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1244 | 1334 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1245 | 1335 | } |
| 1246 | 1336 | |
| 1337 | fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { | |
| 1338 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1339 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1340 | else => return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}), | |
| 1341 | }; | |
| 1342 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1343 | } | |
| 1344 | ||
| 1247 | 1345 | fn airMul(self: *Self, inst: Air.Inst.Index) !void { |
| 1248 | 1346 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1249 | 1347 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
| ... | ... | @@ -1262,6 +1360,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1262 | 1360 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1263 | 1361 | } |
| 1264 | 1362 | |
| 1363 | fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { | |
| 1364 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1365 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1366 | else => return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}), | |
| 1367 | }; | |
| 1368 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1369 | } | |
| 1370 | ||
| 1265 | 1371 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| 1266 | 1372 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1267 | 1373 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
| ... | ... | @@ -1278,6 +1384,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1278 | 1384 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1279 | 1385 | } |
| 1280 | 1386 | |
| 1387 | fn airMod(self: *Self, inst: Air.Inst.Index) !void { | |
| 1388 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1389 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1390 | else => return self.fail("TODO implement mod for {}", .{self.target.cpu.arch}), | |
| 1391 | }; | |
| 1392 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1393 | } | |
| 1394 | ||
| 1281 | 1395 | fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void { |
| 1282 | 1396 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1283 | 1397 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
| ... | ... | @@ -1316,6 +1430,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1316 | 1430 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1317 | 1431 | } |
| 1318 | 1432 | |
| 1433 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { | |
| 1434 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1435 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1436 | else => return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}), | |
| 1437 | }; | |
| 1438 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1439 | } | |
| 1440 | ||
| 1319 | 1441 | fn airShr(self: *Self, inst: Air.Inst.Index) !void { |
| 1320 | 1442 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1321 | 1443 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
| ... | ... | @@ -1470,6 +1592,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1470 | 1592 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1471 | 1593 | } |
| 1472 | 1594 | |
| 1595 | fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | |
| 1596 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1597 | const result: MCValue = switch (arch) { | |
| 1598 | else => return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}), | |
| 1599 | }; | |
| 1600 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1601 | } | |
| 1602 | ||
| 1603 | fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | |
| 1604 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1605 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1606 | else => return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}), | |
| 1607 | }; | |
| 1608 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 1609 | } | |
| 1610 | ||
| 1611 | fn airClz(self: *Self, inst: Air.Inst.Index) !void { | |
| 1612 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1613 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1614 | else => return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch}), | |
| 1615 | }; | |
| 1616 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 1617 | } | |
| 1618 | ||
| 1619 | fn airCtz(self: *Self, inst: Air.Inst.Index) !void { | |
| 1620 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1621 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 1622 | else => return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}), | |
| 1623 | }; | |
| 1624 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 1625 | } | |
| 1626 | ||
| 1473 | 1627 | fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { |
| 1474 | 1628 | if (!self.liveness.operandDies(inst, op_index)) |
| 1475 | 1629 | return false; |
| ... | ... | @@ -1848,15 +2002,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1848 | 2002 | }, |
| 1849 | 2003 | .shl => { |
| 1850 | 2004 | assert(!swap_lhs_and_rhs); |
| 1851 | const shift_amout = switch (operand) { | |
| 2005 | const shift_amount = switch (operand) { | |
| 1852 | 2006 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), |
| 1853 | 2007 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), |
| 1854 | 2008 | }; |
| 1855 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amout).toU32()); | |
| 2009 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amount).toU32()); | |
| 1856 | 2010 | }, |
| 1857 | 2011 | .shr => { |
| 1858 | 2012 | assert(!swap_lhs_and_rhs); |
| 1859 | const shift_amout = switch (operand) { | |
| 2013 | const shift_amount = switch (operand) { | |
| 1860 | 2014 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), |
| 1861 | 2015 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), |
| 1862 | 2016 | }; |
| ... | ... | @@ -1865,7 +2019,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1865 | 2019 | .signed => Instruction.asr, |
| 1866 | 2020 | .unsigned => Instruction.lsr, |
| 1867 | 2021 | }; |
| 1868 | writeInt(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amout).toU32()); | |
| 2022 | writeInt(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amount).toU32()); | |
| 1869 | 2023 | }, |
| 1870 | 2024 | else => unreachable, // not a binary instruction |
| 1871 | 2025 | } |
| ... | ... | @@ -1952,7 +2106,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1952 | 2106 | // |
| 1953 | 2107 | // TODO: make this algorithm less bad |
| 1954 | 2108 | |
| 1955 | try self.code.ensureCapacity(self.code.items.len + 8); | |
| 2109 | try self.code.ensureUnusedCapacity(8); | |
| 1956 | 2110 | |
| 1957 | 2111 | const lhs = try self.resolveInst(op_lhs); |
| 1958 | 2112 | const rhs = try self.resolveInst(op_rhs); |
| ... | ... | @@ -2439,16 +2593,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2439 | 2593 | .register => |reg| { |
| 2440 | 2594 | switch (self.debug_output) { |
| 2441 | 2595 | .dwarf => |dbg_out| { |
| 2442 | try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 3); | |
| 2596 | try dbg_out.dbg_info.ensureUnusedCapacity(3); | |
| 2443 | 2597 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter); |
| 2444 | 2598 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc |
| 2445 | 2599 | 1, // ULEB128 dwarf expression length |
| 2446 | 2600 | reg.dwarfLocOp(), |
| 2447 | 2601 | }); |
| 2448 | try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len); | |
| 2602 | try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); | |
| 2449 | 2603 | try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 |
| 2450 | 2604 | dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string |
| 2451 | 2605 | }, |
| 2606 | .plan9 => {}, | |
| 2452 | 2607 | .none => {}, |
| 2453 | 2608 | } |
| 2454 | 2609 | }, |
| ... | ... | @@ -2476,13 +2631,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2476 | 2631 | try dbg_out.dbg_info.append(DW.OP.breg11); |
| 2477 | 2632 | try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset); |
| 2478 | 2633 | |
| 2479 | try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len); | |
| 2634 | try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); | |
| 2480 | 2635 | try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 |
| 2481 | 2636 | dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string |
| 2482 | 2637 | }, |
| 2483 | 2638 | else => {}, |
| 2484 | 2639 | } |
| 2485 | 2640 | }, |
| 2641 | .plan9 => {}, | |
| 2486 | 2642 | .none => {}, |
| 2487 | 2643 | } |
| 2488 | 2644 | }, |
| ... | ... | @@ -2549,6 +2705,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2549 | 2705 | return self.finishAirBookkeeping(); |
| 2550 | 2706 | } |
| 2551 | 2707 | |
| 2708 | fn airFence(self: *Self) !void { | |
| 2709 | return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch}); | |
| 2710 | //return self.finishAirBookkeeping(); | |
| 2711 | } | |
| 2712 | ||
| 2552 | 2713 | fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2553 | 2714 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 2554 | 2715 | const fn_ty = self.air.typeOf(pl_op.operand); |
| ... | ... | @@ -2613,7 +2774,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2613 | 2774 | unreachable; |
| 2614 | 2775 | |
| 2615 | 2776 | // ff 14 25 xx xx xx xx call [addr] |
| 2616 | try self.code.ensureCapacity(self.code.items.len + 7); | |
| 2777 | try self.code.ensureUnusedCapacity(7); | |
| 2617 | 2778 | self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 }); |
| 2618 | 2779 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr); |
| 2619 | 2780 | } else if (func_value.castTag(.extern_fn)) |_| { |
| ... | ... | @@ -2826,7 +2987,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2826 | 2987 | .memory = func.owner_decl.link.macho.local_sym_index, |
| 2827 | 2988 | }); |
| 2828 | 2989 | // callq *%rax |
| 2829 | try self.code.ensureCapacity(self.code.items.len + 2); | |
| 2990 | try self.code.ensureUnusedCapacity(2); | |
| 2830 | 2991 | self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 }); |
| 2831 | 2992 | }, |
| 2832 | 2993 | .aarch64 => { |
| ... | ... | @@ -2840,12 +3001,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2840 | 3001 | } |
| 2841 | 3002 | } else if (func_value.castTag(.extern_fn)) |func_payload| { |
| 2842 | 3003 | const decl = func_payload.data; |
| 2843 | const where_index = try macho_file.addExternFn(mem.spanZ(decl.name)); | |
| 3004 | const resolv = try macho_file.addExternFn(mem.spanZ(decl.name)); | |
| 2844 | 3005 | const offset = blk: { |
| 2845 | 3006 | switch (arch) { |
| 2846 | 3007 | .x86_64 => { |
| 2847 | 3008 | // callq |
| 2848 | try self.code.ensureCapacity(self.code.items.len + 5); | |
| 3009 | try self.code.ensureUnusedCapacity(5); | |
| 2849 | 3010 | self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 }); |
| 2850 | 3011 | break :blk @intCast(u32, self.code.items.len) - 4; |
| 2851 | 3012 | }, |
| ... | ... | @@ -2861,8 +3022,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2861 | 3022 | // Add relocation to the decl. |
| 2862 | 3023 | try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{ |
| 2863 | 3024 | .offset = offset, |
| 2864 | .where = .undef, | |
| 2865 | .where_index = where_index, | |
| 3025 | .where = switch (resolv.where) { | |
| 3026 | .local => .local, | |
| 3027 | .undef => .undef, | |
| 3028 | }, | |
| 3029 | .where_index = resolv.where_index, | |
| 2866 | 3030 | .payload = .{ .branch = .{ |
| 2867 | 3031 | .arch = arch, |
| 2868 | 3032 | } }, |
| ... | ... | @@ -2911,12 +3075,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2911 | 3075 | } |
| 2912 | 3076 | if (self.air.value(callee)) |func_value| { |
| 2913 | 3077 | if (func_value.castTag(.function)) |func_payload| { |
| 3078 | try p9.seeDecl(func_payload.data.owner_decl); | |
| 2914 | 3079 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 2915 | 3080 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 2916 | 3081 | const got_addr = p9.bases.data; |
| 2917 | 3082 | const got_index = func_payload.data.owner_decl.link.plan9.got_index.?; |
| 2918 | 3083 | // ff 14 25 xx xx xx xx call [addr] |
| 2919 | try self.code.ensureCapacity(self.code.items.len + 7); | |
| 3084 | try self.code.ensureUnusedCapacity(7); | |
| 2920 | 3085 | self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 }); |
| 2921 | 3086 | const fn_got_addr = got_addr + got_index * ptr_bytes; |
| 2922 | 3087 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr)); |
| ... | ... | @@ -2958,6 +3123,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2958 | 3123 | } |
| 2959 | 3124 | if (self.air.value(callee)) |func_value| { |
| 2960 | 3125 | if (func_value.castTag(.function)) |func_payload| { |
| 3126 | try p9.seeDecl(func_payload.data.owner_decl); | |
| 2961 | 3127 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 2962 | 3128 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 2963 | 3129 | const got_addr = p9.bases.data; |
| ... | ... | @@ -3059,7 +3225,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3059 | 3225 | const rhs = try self.resolveInst(bin_op.rhs); |
| 3060 | 3226 | const result: MCValue = switch (arch) { |
| 3061 | 3227 | .x86_64 => result: { |
| 3062 | try self.code.ensureCapacity(self.code.items.len + 8); | |
| 3228 | try self.code.ensureUnusedCapacity(8); | |
| 3063 | 3229 | |
| 3064 | 3230 | // There are 2 operands, destination and source. |
| 3065 | 3231 | // Either one, but not both, can be a memory operand. |
| ... | ... | @@ -3143,7 +3309,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3143 | 3309 | |
| 3144 | 3310 | const reloc: Reloc = switch (arch) { |
| 3145 | 3311 | .i386, .x86_64 => reloc: { |
| 3146 | try self.code.ensureCapacity(self.code.items.len + 6); | |
| 3312 | try self.code.ensureUnusedCapacity(6); | |
| 3147 | 3313 | |
| 3148 | 3314 | const opcode: u8 = switch (cond) { |
| 3149 | 3315 | .compare_flags_signed => |cmp_op| blk: { |
| ... | ... | @@ -3503,7 +3669,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3503 | 3669 | fn jump(self: *Self, index: usize) !void { |
| 3504 | 3670 | switch (arch) { |
| 3505 | 3671 | .i386, .x86_64 => { |
| 3506 | try self.code.ensureCapacity(self.code.items.len + 5); | |
| 3672 | try self.code.ensureUnusedCapacity(5); | |
| 3507 | 3673 | if (math.cast(i8, @intCast(i32, index) - (@intCast(i32, self.code.items.len + 2)))) |delta| { |
| 3508 | 3674 | self.code.appendAssumeCapacity(0xeb); // jmp rel8 |
| 3509 | 3675 | self.code.appendAssumeCapacity(@bitCast(u8, delta)); |
| ... | ... | @@ -3535,7 +3701,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3535 | 3701 | try self.blocks.putNoClobber(self.gpa, inst, .{ |
| 3536 | 3702 | // A block is a setup to be able to jump to the end. |
| 3537 | 3703 | .relocs = .{}, |
| 3538 | // It also acts as a receptical for break operands. | |
| 3704 | // It also acts as a receptacle for break operands. | |
| 3539 | 3705 | // Here we use `MCValue.none` to represent a null value so that the first |
| 3540 | 3706 | // break instruction will choose a MCValue for the block result and overwrite |
| 3541 | 3707 | // this field. Following break instructions will use that MCValue to put their |
| ... | ... | @@ -3589,7 +3755,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3589 | 3755 | return self.fail("TODO: enable larger branch offset", .{}); |
| 3590 | 3756 | } |
| 3591 | 3757 | }, |
| 3592 | else => unreachable, // attempting to perfrom an ARM relocation on a non-ARM target arch | |
| 3758 | else => unreachable, // attempting to perform an ARM relocation on a non-ARM target arch | |
| 3593 | 3759 | } |
| 3594 | 3760 | }, |
| 3595 | 3761 | } |
| ... | ... | @@ -3641,7 +3807,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3641 | 3807 | const block_data = self.blocks.getPtr(block).?; |
| 3642 | 3808 | |
| 3643 | 3809 | // Emit a jump with a relocation. It will be patched up after the block ends. |
| 3644 | try block_data.relocs.ensureCapacity(self.gpa, block_data.relocs.items.len + 1); | |
| 3810 | try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); | |
| 3645 | 3811 | |
| 3646 | 3812 | switch (arch) { |
| 3647 | 3813 | .i386, .x86_64 => { |
| ... | ... | @@ -4025,7 +4191,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4025 | 4191 | if (adj_off > 128) { |
| 4026 | 4192 | return self.fail("TODO implement set stack variable with large stack offset", .{}); |
| 4027 | 4193 | } |
| 4028 | try self.code.ensureCapacity(self.code.items.len + 8); | |
| 4194 | try self.code.ensureUnusedCapacity(8); | |
| 4029 | 4195 | switch (abi_size) { |
| 4030 | 4196 | 1 => { |
| 4031 | 4197 | return self.fail("TODO implement set abi_size=1 stack variable with immediate", .{}); |
| ... | ... | @@ -4051,7 +4217,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4051 | 4217 | |
| 4052 | 4218 | // 64 bit write to memory would take two mov's anyways so we |
| 4053 | 4219 | // insted just use two 32 bit writes to avoid register allocation |
| 4054 | try self.code.ensureCapacity(self.code.items.len + 14); | |
| 4220 | try self.code.ensureUnusedCapacity(14); | |
| 4055 | 4221 | var buf: [8]u8 = undefined; |
| 4056 | 4222 | mem.writeIntLittle(u64, &buf, x_big); |
| 4057 | 4223 | |
| ... | ... | @@ -4753,6 +4919,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4753 | 4919 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 4754 | 4920 | } |
| 4755 | 4921 | |
| 4922 | fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { | |
| 4923 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 4924 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 4925 | else => return self.fail("TODO implement airIntToFloat for {}", .{ | |
| 4926 | self.target.cpu.arch, | |
| 4927 | }), | |
| 4928 | }; | |
| 4929 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 4930 | } | |
| 4931 | ||
| 4932 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { | |
| 4933 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 4934 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | |
| 4935 | else => return self.fail("TODO implement airFloatToInt for {}", .{ | |
| 4936 | self.target.cpu.arch, | |
| 4937 | }), | |
| 4938 | }; | |
| 4939 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | |
| 4940 | } | |
| 4941 | ||
| 4756 | 4942 | fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { |
| 4757 | 4943 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 4758 | 4944 | const extra = self.air.extraData(Air.Block, ty_pl.payload); |
| ... | ... | @@ -4764,6 +4950,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4764 | 4950 | return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); |
| 4765 | 4951 | } |
| 4766 | 4952 | |
| 4953 | fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { | |
| 4954 | _ = inst; | |
| 4955 | return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); | |
| 4956 | } | |
| 4957 | ||
| 4958 | fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { | |
| 4959 | _ = inst; | |
| 4960 | return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch}); | |
| 4961 | } | |
| 4962 | ||
| 4963 | fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void { | |
| 4964 | _ = inst; | |
| 4965 | _ = order; | |
| 4966 | return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); | |
| 4967 | } | |
| 4968 | ||
| 4969 | fn airMemset(self: *Self, inst: Air.Inst.Index) !void { | |
| 4970 | _ = inst; | |
| 4971 | return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); | |
| 4972 | } | |
| 4973 | ||
| 4974 | fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { | |
| 4975 | _ = inst; | |
| 4976 | return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); | |
| 4977 | } | |
| 4978 | ||
| 4767 | 4979 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 4768 | 4980 | // First section of indexes correspond to a set number of constant values. |
| 4769 | 4981 | const ref_int = @enumToInt(inst); |
| ... | ... | @@ -4841,7 +5053,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4841 | 5053 | switch (typed_value.ty.zigTypeTag()) { |
| 4842 | 5054 | .Pointer => switch (typed_value.ty.ptrSize()) { |
| 4843 | 5055 | .Slice => { |
| 4844 | var buf: Type.Payload.ElemType = undefined; | |
| 5056 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 4845 | 5057 | const ptr_type = typed_value.ty.slicePtrFieldType(&buf); |
| 4846 | 5058 | const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val }); |
| 4847 | 5059 | const slice_len = typed_value.val.sliceLen(); |
| ... | ... | @@ -4869,6 +5081,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4869 | 5081 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; |
| 4870 | 5082 | return MCValue{ .memory = got_addr }; |
| 4871 | 5083 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 5084 | try p9.seeDecl(decl); | |
| 4872 | 5085 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; |
| 4873 | 5086 | return MCValue{ .memory = got_addr }; |
| 4874 | 5087 | } else { |
| ... | ... | @@ -5216,11 +5429,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 5216 | 5429 | } |
| 5217 | 5430 | |
| 5218 | 5431 | const Register = switch (arch) { |
| 5219 | .i386 => @import("codegen/x86.zig").Register, | |
| 5220 | .x86_64 => @import("codegen/x86_64.zig").Register, | |
| 5221 | .riscv64 => @import("codegen/riscv64.zig").Register, | |
| 5222 | .arm, .armeb => @import("codegen/arm.zig").Register, | |
| 5223 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Register, | |
| 5432 | .i386 => @import("arch/x86/bits.zig").Register, | |
| 5433 | .x86_64 => @import("arch/x86_64/bits.zig").Register, | |
| 5434 | .riscv64 => @import("arch/riscv64/bits.zig").Register, | |
| 5435 | .arm, .armeb => @import("arch/arm/bits.zig").Register, | |
| 5436 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Register, | |
| 5224 | 5437 | else => enum { |
| 5225 | 5438 | dummy, |
| 5226 | 5439 | |
| ... | ... | @@ -5232,39 +5445,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 5232 | 5445 | }; |
| 5233 | 5446 | |
| 5234 | 5447 | const Instruction = switch (arch) { |
| 5235 | .riscv64 => @import("codegen/riscv64.zig").Instruction, | |
| 5236 | .arm, .armeb => @import("codegen/arm.zig").Instruction, | |
| 5237 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Instruction, | |
| 5448 | .riscv64 => @import("arch/riscv64/bits.zig").Instruction, | |
| 5449 | .arm, .armeb => @import("arch/arm/bits.zig").Instruction, | |
| 5450 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Instruction, | |
| 5238 | 5451 | else => void, |
| 5239 | 5452 | }; |
| 5240 | 5453 | |
| 5241 | 5454 | const Condition = switch (arch) { |
| 5242 | .arm, .armeb => @import("codegen/arm.zig").Condition, | |
| 5455 | .arm, .armeb => @import("arch/arm/bits.zig").Condition, | |
| 5243 | 5456 | else => void, |
| 5244 | 5457 | }; |
| 5245 | 5458 | |
| 5246 | 5459 | const callee_preserved_regs = switch (arch) { |
| 5247 | .i386 => @import("codegen/x86.zig").callee_preserved_regs, | |
| 5248 | .x86_64 => @import("codegen/x86_64.zig").callee_preserved_regs, | |
| 5249 | .riscv64 => @import("codegen/riscv64.zig").callee_preserved_regs, | |
| 5250 | .arm, .armeb => @import("codegen/arm.zig").callee_preserved_regs, | |
| 5251 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").callee_preserved_regs, | |
| 5460 | .i386 => @import("arch/x86/bits.zig").callee_preserved_regs, | |
| 5461 | .x86_64 => @import("arch/x86_64/bits.zig").callee_preserved_regs, | |
| 5462 | .riscv64 => @import("arch/riscv64/bits.zig").callee_preserved_regs, | |
| 5463 | .arm, .armeb => @import("arch/arm/bits.zig").callee_preserved_regs, | |
| 5464 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").callee_preserved_regs, | |
| 5252 | 5465 | else => [_]Register{}, |
| 5253 | 5466 | }; |
| 5254 | 5467 | |
| 5255 | 5468 | const c_abi_int_param_regs = switch (arch) { |
| 5256 | .i386 => @import("codegen/x86.zig").c_abi_int_param_regs, | |
| 5257 | .x86_64 => @import("codegen/x86_64.zig").c_abi_int_param_regs, | |
| 5258 | .arm, .armeb => @import("codegen/arm.zig").c_abi_int_param_regs, | |
| 5259 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_param_regs, | |
| 5469 | .i386 => @import("arch/x86/bits.zig").c_abi_int_param_regs, | |
| 5470 | .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_param_regs, | |
| 5471 | .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_param_regs, | |
| 5472 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_param_regs, | |
| 5260 | 5473 | else => [_]Register{}, |
| 5261 | 5474 | }; |
| 5262 | 5475 | |
| 5263 | 5476 | const c_abi_int_return_regs = switch (arch) { |
| 5264 | .i386 => @import("codegen/x86.zig").c_abi_int_return_regs, | |
| 5265 | .x86_64 => @import("codegen/x86_64.zig").c_abi_int_return_regs, | |
| 5266 | .arm, .armeb => @import("codegen/arm.zig").c_abi_int_return_regs, | |
| 5267 | .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_return_regs, | |
| 5477 | .i386 => @import("arch/x86/bits.zig").c_abi_int_return_regs, | |
| 5478 | .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_return_regs, | |
| 5479 | .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_return_regs, | |
| 5480 | .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_return_regs, | |
| 5268 | 5481 | else => [_]Register{}, |
| 5269 | 5482 | }; |
| 5270 | 5483 |
src/codegen/aarch64.zig deleted-1230| ... | ... | @@ -1,1230 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const DW = std.dwarf; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const testing = std.testing; | |
| 5 | ||
| 6 | // zig fmt: off | |
| 7 | ||
| 8 | /// General purpose registers in the AArch64 instruction set | |
| 9 | pub const Register = enum(u6) { | |
| 10 | // 64-bit registers | |
| 11 | x0, x1, x2, x3, x4, x5, x6, x7, | |
| 12 | x8, x9, x10, x11, x12, x13, x14, x15, | |
| 13 | x16, x17, x18, x19, x20, x21, x22, x23, | |
| 14 | x24, x25, x26, x27, x28, x29, x30, xzr, | |
| 15 | ||
| 16 | // 32-bit registers | |
| 17 | w0, w1, w2, w3, w4, w5, w6, w7, | |
| 18 | w8, w9, w10, w11, w12, w13, w14, w15, | |
| 19 | w16, w17, w18, w19, w20, w21, w22, w23, | |
| 20 | w24, w25, w26, w27, w28, w29, w30, wzr, | |
| 21 | ||
| 22 | pub const sp = Register.xzr; | |
| 23 | ||
| 24 | pub fn id(self: Register) u5 { | |
| 25 | return @truncate(u5, @enumToInt(self)); | |
| 26 | } | |
| 27 | ||
| 28 | /// Returns the bit-width of the register. | |
| 29 | pub fn size(self: Register) u7 { | |
| 30 | return switch (@enumToInt(self)) { | |
| 31 | 0...31 => 64, | |
| 32 | 32...63 => 32, | |
| 33 | }; | |
| 34 | } | |
| 35 | ||
| 36 | /// Convert from any register to its 64 bit alias. | |
| 37 | pub fn to64(self: Register) Register { | |
| 38 | return @intToEnum(Register, self.id()); | |
| 39 | } | |
| 40 | ||
| 41 | /// Convert from any register to its 32 bit alias. | |
| 42 | pub fn to32(self: Register) Register { | |
| 43 | return @intToEnum(Register, @as(u6, self.id()) + 32); | |
| 44 | } | |
| 45 | ||
| 46 | /// Returns the index into `callee_preserved_regs`. | |
| 47 | pub fn allocIndex(self: Register) ?u4 { | |
| 48 | inline for (callee_preserved_regs) |cpreg, i| { | |
| 49 | if (self.id() == cpreg.id()) return i; | |
| 50 | } | |
| 51 | return null; | |
| 52 | } | |
| 53 | ||
| 54 | pub fn dwarfLocOp(self: Register) u8 { | |
| 55 | return @as(u8, self.id()) + DW.OP.reg0; | |
| 56 | } | |
| 57 | }; | |
| 58 | ||
| 59 | // zig fmt: on | |
| 60 | ||
| 61 | pub const callee_preserved_regs = [_]Register{ | |
| 62 | .x19, .x20, .x21, .x22, .x23, | |
| 63 | .x24, .x25, .x26, .x27, .x28, | |
| 64 | }; | |
| 65 | ||
| 66 | pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | |
| 67 | pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | |
| 68 | ||
| 69 | test "Register.id" { | |
| 70 | try testing.expectEqual(@as(u5, 0), Register.x0.id()); | |
| 71 | try testing.expectEqual(@as(u5, 0), Register.w0.id()); | |
| 72 | ||
| 73 | try testing.expectEqual(@as(u5, 31), Register.xzr.id()); | |
| 74 | try testing.expectEqual(@as(u5, 31), Register.wzr.id()); | |
| 75 | ||
| 76 | try testing.expectEqual(@as(u5, 31), Register.sp.id()); | |
| 77 | try testing.expectEqual(@as(u5, 31), Register.sp.id()); | |
| 78 | } | |
| 79 | ||
| 80 | test "Register.size" { | |
| 81 | try testing.expectEqual(@as(u7, 64), Register.x19.size()); | |
| 82 | try testing.expectEqual(@as(u7, 32), Register.w3.size()); | |
| 83 | } | |
| 84 | ||
| 85 | test "Register.to64/to32" { | |
| 86 | try testing.expectEqual(Register.x0, Register.w0.to64()); | |
| 87 | try testing.expectEqual(Register.x0, Register.x0.to64()); | |
| 88 | ||
| 89 | try testing.expectEqual(Register.w3, Register.w3.to32()); | |
| 90 | try testing.expectEqual(Register.w3, Register.x3.to32()); | |
| 91 | } | |
| 92 | ||
| 93 | // zig fmt: off | |
| 94 | ||
| 95 | /// Scalar floating point registers in the aarch64 instruction set | |
| 96 | pub const FloatingPointRegister = enum(u8) { | |
| 97 | // 128-bit registers | |
| 98 | q0, q1, q2, q3, q4, q5, q6, q7, | |
| 99 | q8, q9, q10, q11, q12, q13, q14, q15, | |
| 100 | q16, q17, q18, q19, q20, q21, q22, q23, | |
| 101 | q24, q25, q26, q27, q28, q29, q30, q31, | |
| 102 | ||
| 103 | // 64-bit registers | |
| 104 | d0, d1, d2, d3, d4, d5, d6, d7, | |
| 105 | d8, d9, d10, d11, d12, d13, d14, d15, | |
| 106 | d16, d17, d18, d19, d20, d21, d22, d23, | |
| 107 | d24, d25, d26, d27, d28, d29, d30, d31, | |
| 108 | ||
| 109 | // 32-bit registers | |
| 110 | s0, s1, s2, s3, s4, s5, s6, s7, | |
| 111 | s8, s9, s10, s11, s12, s13, s14, s15, | |
| 112 | s16, s17, s18, s19, s20, s21, s22, s23, | |
| 113 | s24, s25, s26, s27, s28, s29, s30, s31, | |
| 114 | ||
| 115 | // 16-bit registers | |
| 116 | h0, h1, h2, h3, h4, h5, h6, h7, | |
| 117 | h8, h9, h10, h11, h12, h13, h14, h15, | |
| 118 | h16, h17, h18, h19, h20, h21, h22, h23, | |
| 119 | h24, h25, h26, h27, h28, h29, h30, h31, | |
| 120 | ||
| 121 | // 8-bit registers | |
| 122 | b0, b1, b2, b3, b4, b5, b6, b7, | |
| 123 | b8, b9, b10, b11, b12, b13, b14, b15, | |
| 124 | b16, b17, b18, b19, b20, b21, b22, b23, | |
| 125 | b24, b25, b26, b27, b28, b29, b30, b31, | |
| 126 | ||
| 127 | pub fn id(self: FloatingPointRegister) u5 { | |
| 128 | return @truncate(u5, @enumToInt(self)); | |
| 129 | } | |
| 130 | ||
| 131 | /// Returns the bit-width of the register. | |
| 132 | pub fn size(self: FloatingPointRegister) u8 { | |
| 133 | return switch (@enumToInt(self)) { | |
| 134 | 0...31 => 128, | |
| 135 | 32...63 => 64, | |
| 136 | 64...95 => 32, | |
| 137 | 96...127 => 16, | |
| 138 | 128...159 => 8, | |
| 139 | else => unreachable, | |
| 140 | }; | |
| 141 | } | |
| 142 | ||
| 143 | /// Convert from any register to its 128 bit alias. | |
| 144 | pub fn to128(self: FloatingPointRegister) FloatingPointRegister { | |
| 145 | return @intToEnum(FloatingPointRegister, self.id()); | |
| 146 | } | |
| 147 | ||
| 148 | /// Convert from any register to its 64 bit alias. | |
| 149 | pub fn to64(self: FloatingPointRegister) FloatingPointRegister { | |
| 150 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32); | |
| 151 | } | |
| 152 | ||
| 153 | /// Convert from any register to its 32 bit alias. | |
| 154 | pub fn to32(self: FloatingPointRegister) FloatingPointRegister { | |
| 155 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64); | |
| 156 | } | |
| 157 | ||
| 158 | /// Convert from any register to its 16 bit alias. | |
| 159 | pub fn to16(self: FloatingPointRegister) FloatingPointRegister { | |
| 160 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96); | |
| 161 | } | |
| 162 | ||
| 163 | /// Convert from any register to its 8 bit alias. | |
| 164 | pub fn to8(self: FloatingPointRegister) FloatingPointRegister { | |
| 165 | return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128); | |
| 166 | } | |
| 167 | }; | |
| 168 | ||
| 169 | // zig fmt: on | |
| 170 | ||
| 171 | test "FloatingPointRegister.id" { | |
| 172 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id()); | |
| 173 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id()); | |
| 174 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id()); | |
| 175 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id()); | |
| 176 | try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id()); | |
| 177 | ||
| 178 | try testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id()); | |
| 179 | try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id()); | |
| 180 | } | |
| 181 | ||
| 182 | test "FloatingPointRegister.size" { | |
| 183 | try testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size()); | |
| 184 | try testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size()); | |
| 185 | try testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size()); | |
| 186 | try testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size()); | |
| 187 | try testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size()); | |
| 188 | } | |
| 189 | ||
| 190 | test "FloatingPointRegister.toX" { | |
| 191 | try testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128()); | |
| 192 | try testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128()); | |
| 193 | try testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128()); | |
| 194 | ||
| 195 | try testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64()); | |
| 196 | try testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32()); | |
| 197 | try testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16()); | |
| 198 | try testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8()); | |
| 199 | } | |
| 200 | ||
| 201 | /// Represents an instruction in the AArch64 instruction set | |
| 202 | pub const Instruction = union(enum) { | |
| 203 | move_wide_immediate: packed struct { | |
| 204 | rd: u5, | |
| 205 | imm16: u16, | |
| 206 | hw: u2, | |
| 207 | fixed: u6 = 0b100101, | |
| 208 | opc: u2, | |
| 209 | sf: u1, | |
| 210 | }, | |
| 211 | pc_relative_address: packed struct { | |
| 212 | rd: u5, | |
| 213 | immhi: u19, | |
| 214 | fixed: u5 = 0b10000, | |
| 215 | immlo: u2, | |
| 216 | op: u1, | |
| 217 | }, | |
| 218 | load_store_register: packed struct { | |
| 219 | rt: u5, | |
| 220 | rn: u5, | |
| 221 | offset: u12, | |
| 222 | opc: u2, | |
| 223 | op1: u2, | |
| 224 | v: u1, | |
| 225 | fixed: u3 = 0b111, | |
| 226 | size: u2, | |
| 227 | }, | |
| 228 | load_store_register_pair: packed struct { | |
| 229 | rt1: u5, | |
| 230 | rn: u5, | |
| 231 | rt2: u5, | |
| 232 | imm7: u7, | |
| 233 | load: u1, | |
| 234 | encoding: u2, | |
| 235 | fixed: u5 = 0b101_0_0, | |
| 236 | opc: u2, | |
| 237 | }, | |
| 238 | load_literal: packed struct { | |
| 239 | rt: u5, | |
| 240 | imm19: u19, | |
| 241 | fixed: u6 = 0b011_0_00, | |
| 242 | opc: u2, | |
| 243 | }, | |
| 244 | exception_generation: packed struct { | |
| 245 | ll: u2, | |
| 246 | op2: u3, | |
| 247 | imm16: u16, | |
| 248 | opc: u3, | |
| 249 | fixed: u8 = 0b1101_0100, | |
| 250 | }, | |
| 251 | unconditional_branch_register: packed struct { | |
| 252 | op4: u5, | |
| 253 | rn: u5, | |
| 254 | op3: u6, | |
| 255 | op2: u5, | |
| 256 | opc: u4, | |
| 257 | fixed: u7 = 0b1101_011, | |
| 258 | }, | |
| 259 | unconditional_branch_immediate: packed struct { | |
| 260 | imm26: u26, | |
| 261 | fixed: u5 = 0b00101, | |
| 262 | op: u1, | |
| 263 | }, | |
| 264 | no_operation: packed struct { | |
| 265 | fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, | |
| 266 | }, | |
| 267 | logical_shifted_register: packed struct { | |
| 268 | rd: u5, | |
| 269 | rn: u5, | |
| 270 | imm6: u6, | |
| 271 | rm: u5, | |
| 272 | n: u1, | |
| 273 | shift: u2, | |
| 274 | fixed: u5 = 0b01010, | |
| 275 | opc: u2, | |
| 276 | sf: u1, | |
| 277 | }, | |
| 278 | add_subtract_immediate: packed struct { | |
| 279 | rd: u5, | |
| 280 | rn: u5, | |
| 281 | imm12: u12, | |
| 282 | sh: u1, | |
| 283 | fixed: u6 = 0b100010, | |
| 284 | s: u1, | |
| 285 | op: u1, | |
| 286 | sf: u1, | |
| 287 | }, | |
| 288 | conditional_branch: struct { | |
| 289 | cond: u4, | |
| 290 | o0: u1, | |
| 291 | imm19: u19, | |
| 292 | o1: u1, | |
| 293 | fixed: u7 = 0b0101010, | |
| 294 | }, | |
| 295 | compare_and_branch: struct { | |
| 296 | rt: u5, | |
| 297 | imm19: u19, | |
| 298 | op: u1, | |
| 299 | fixed: u6 = 0b011010, | |
| 300 | sf: u1, | |
| 301 | }, | |
| 302 | ||
| 303 | pub const Shift = struct { | |
| 304 | shift: Type = .lsl, | |
| 305 | amount: u6 = 0, | |
| 306 | ||
| 307 | pub const Type = enum(u2) { | |
| 308 | lsl, | |
| 309 | lsr, | |
| 310 | asr, | |
| 311 | ror, | |
| 312 | }; | |
| 313 | ||
| 314 | pub const none = Shift{ | |
| 315 | .shift = .lsl, | |
| 316 | .amount = 0, | |
| 317 | }; | |
| 318 | }; | |
| 319 | ||
| 320 | pub const Condition = enum(u4) { | |
| 321 | /// Integer: Equal | |
| 322 | /// Floating point: Equal | |
| 323 | eq, | |
| 324 | /// Integer: Not equal | |
| 325 | /// Floating point: Not equal or unordered | |
| 326 | ne, | |
| 327 | /// Integer: Carry set | |
| 328 | /// Floating point: Greater than, equal, or unordered | |
| 329 | cs, | |
| 330 | /// Integer: Carry clear | |
| 331 | /// Floating point: Less than | |
| 332 | cc, | |
| 333 | /// Integer: Minus, negative | |
| 334 | /// Floating point: Less than | |
| 335 | mi, | |
| 336 | /// Integer: Plus, positive or zero | |
| 337 | /// Floating point: Greater than, equal, or unordered | |
| 338 | pl, | |
| 339 | /// Integer: Overflow | |
| 340 | /// Floating point: Unordered | |
| 341 | vs, | |
| 342 | /// Integer: No overflow | |
| 343 | /// Floating point: Ordered | |
| 344 | vc, | |
| 345 | /// Integer: Unsigned higher | |
| 346 | /// Floating point: Greater than, or unordered | |
| 347 | hi, | |
| 348 | /// Integer: Unsigned lower or same | |
| 349 | /// Floating point: Less than or equal | |
| 350 | ls, | |
| 351 | /// Integer: Signed greater than or equal | |
| 352 | /// Floating point: Greater than or equal | |
| 353 | ge, | |
| 354 | /// Integer: Signed less than | |
| 355 | /// Floating point: Less than, or unordered | |
| 356 | lt, | |
| 357 | /// Integer: Signed greater than | |
| 358 | /// Floating point: Greater than | |
| 359 | gt, | |
| 360 | /// Integer: Signed less than or equal | |
| 361 | /// Floating point: Less than, equal, or unordered | |
| 362 | le, | |
| 363 | /// Integer: Always | |
| 364 | /// Floating point: Always | |
| 365 | al, | |
| 366 | /// Integer: Always | |
| 367 | /// Floating point: Always | |
| 368 | nv, | |
| 369 | }; | |
| 370 | ||
| 371 | pub fn toU32(self: Instruction) u32 { | |
| 372 | return switch (self) { | |
| 373 | .move_wide_immediate => |v| @bitCast(u32, v), | |
| 374 | .pc_relative_address => |v| @bitCast(u32, v), | |
| 375 | .load_store_register => |v| @bitCast(u32, v), | |
| 376 | .load_store_register_pair => |v| @bitCast(u32, v), | |
| 377 | .load_literal => |v| @bitCast(u32, v), | |
| 378 | .exception_generation => |v| @bitCast(u32, v), | |
| 379 | .unconditional_branch_register => |v| @bitCast(u32, v), | |
| 380 | .unconditional_branch_immediate => |v| @bitCast(u32, v), | |
| 381 | .no_operation => |v| @bitCast(u32, v), | |
| 382 | .logical_shifted_register => |v| @bitCast(u32, v), | |
| 383 | .add_subtract_immediate => |v| @bitCast(u32, v), | |
| 384 | // TODO once packed structs work, this can be refactored | |
| 385 | .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), | |
| 386 | .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), | |
| 387 | }; | |
| 388 | } | |
| 389 | ||
| 390 | fn moveWideImmediate( | |
| 391 | opc: u2, | |
| 392 | rd: Register, | |
| 393 | imm16: u16, | |
| 394 | shift: u6, | |
| 395 | ) Instruction { | |
| 396 | switch (rd.size()) { | |
| 397 | 32 => { | |
| 398 | assert(shift % 16 == 0 and shift <= 16); | |
| 399 | return Instruction{ | |
| 400 | .move_wide_immediate = .{ | |
| 401 | .rd = rd.id(), | |
| 402 | .imm16 = imm16, | |
| 403 | .hw = @intCast(u2, shift / 16), | |
| 404 | .opc = opc, | |
| 405 | .sf = 0, | |
| 406 | }, | |
| 407 | }; | |
| 408 | }, | |
| 409 | 64 => { | |
| 410 | assert(shift % 16 == 0 and shift <= 48); | |
| 411 | return Instruction{ | |
| 412 | .move_wide_immediate = .{ | |
| 413 | .rd = rd.id(), | |
| 414 | .imm16 = imm16, | |
| 415 | .hw = @intCast(u2, shift / 16), | |
| 416 | .opc = opc, | |
| 417 | .sf = 1, | |
| 418 | }, | |
| 419 | }; | |
| 420 | }, | |
| 421 | else => unreachable, // unexpected register size | |
| 422 | } | |
| 423 | } | |
| 424 | ||
| 425 | fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { | |
| 426 | assert(rd.size() == 64); | |
| 427 | const imm21_u = @bitCast(u21, imm21); | |
| 428 | return Instruction{ | |
| 429 | .pc_relative_address = .{ | |
| 430 | .rd = rd.id(), | |
| 431 | .immlo = @truncate(u2, imm21_u), | |
| 432 | .immhi = @truncate(u19, imm21_u >> 2), | |
| 433 | .op = op, | |
| 434 | }, | |
| 435 | }; | |
| 436 | } | |
| 437 | ||
| 438 | /// Represents the offset operand of a load or store instruction. | |
| 439 | /// Data can be loaded from memory with either an immediate offset | |
| 440 | /// or an offset that is stored in some register. | |
| 441 | pub const LoadStoreOffset = union(enum) { | |
| 442 | Immediate: union(enum) { | |
| 443 | PostIndex: i9, | |
| 444 | PreIndex: i9, | |
| 445 | Unsigned: u12, | |
| 446 | }, | |
| 447 | Register: struct { | |
| 448 | rm: u5, | |
| 449 | shift: union(enum) { | |
| 450 | Uxtw: u2, | |
| 451 | Lsl: u2, | |
| 452 | Sxtw: u2, | |
| 453 | Sxtx: u2, | |
| 454 | }, | |
| 455 | }, | |
| 456 | ||
| 457 | pub const none = LoadStoreOffset{ | |
| 458 | .Immediate = .{ .Unsigned = 0 }, | |
| 459 | }; | |
| 460 | ||
| 461 | pub fn toU12(self: LoadStoreOffset) u12 { | |
| 462 | return switch (self) { | |
| 463 | .Immediate => |imm_type| switch (imm_type) { | |
| 464 | .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1, | |
| 465 | .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3, | |
| 466 | .Unsigned => |v| v, | |
| 467 | }, | |
| 468 | .Register => |r| switch (r.shift) { | |
| 469 | .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, | |
| 470 | .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, | |
| 471 | .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, | |
| 472 | .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, | |
| 473 | }, | |
| 474 | }; | |
| 475 | } | |
| 476 | ||
| 477 | pub fn imm(offset: u12) LoadStoreOffset { | |
| 478 | return .{ | |
| 479 | .Immediate = .{ .Unsigned = offset }, | |
| 480 | }; | |
| 481 | } | |
| 482 | ||
| 483 | pub fn imm_post_index(offset: i9) LoadStoreOffset { | |
| 484 | return .{ | |
| 485 | .Immediate = .{ .PostIndex = offset }, | |
| 486 | }; | |
| 487 | } | |
| 488 | ||
| 489 | pub fn imm_pre_index(offset: i9) LoadStoreOffset { | |
| 490 | return .{ | |
| 491 | .Immediate = .{ .PreIndex = offset }, | |
| 492 | }; | |
| 493 | } | |
| 494 | ||
| 495 | pub fn reg(rm: Register) LoadStoreOffset { | |
| 496 | return .{ | |
| 497 | .Register = .{ | |
| 498 | .rm = rm.id(), | |
| 499 | .shift = .{ | |
| 500 | .Lsl = 0, | |
| 501 | }, | |
| 502 | }, | |
| 503 | }; | |
| 504 | } | |
| 505 | ||
| 506 | pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset { | |
| 507 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); | |
| 508 | return .{ | |
| 509 | .Register = .{ | |
| 510 | .rm = rm.id(), | |
| 511 | .shift = .{ | |
| 512 | .Uxtw = shift, | |
| 513 | }, | |
| 514 | }, | |
| 515 | }; | |
| 516 | } | |
| 517 | ||
| 518 | pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset { | |
| 519 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); | |
| 520 | return .{ | |
| 521 | .Register = .{ | |
| 522 | .rm = rm.id(), | |
| 523 | .shift = .{ | |
| 524 | .Lsl = shift, | |
| 525 | }, | |
| 526 | }, | |
| 527 | }; | |
| 528 | } | |
| 529 | ||
| 530 | pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset { | |
| 531 | assert(rm.size() == 32 and (shift == 0 or shift == 2)); | |
| 532 | return .{ | |
| 533 | .Register = .{ | |
| 534 | .rm = rm.id(), | |
| 535 | .shift = .{ | |
| 536 | .Sxtw = shift, | |
| 537 | }, | |
| 538 | }, | |
| 539 | }; | |
| 540 | } | |
| 541 | ||
| 542 | pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset { | |
| 543 | assert(rm.size() == 64 and (shift == 0 or shift == 3)); | |
| 544 | return .{ | |
| 545 | .Register = .{ | |
| 546 | .rm = rm.id(), | |
| 547 | .shift = .{ | |
| 548 | .Sxtx = shift, | |
| 549 | }, | |
| 550 | }, | |
| 551 | }; | |
| 552 | } | |
| 553 | }; | |
| 554 | ||
| 555 | /// Which kind of load/store to perform | |
| 556 | const LoadStoreVariant = enum { | |
| 557 | /// 32-bit or 64-bit | |
| 558 | str, | |
| 559 | /// 16-bit, zero-extended | |
| 560 | strh, | |
| 561 | /// 8-bit, zero-extended | |
| 562 | strb, | |
| 563 | /// 32-bit or 64-bit | |
| 564 | ldr, | |
| 565 | /// 16-bit, zero-extended | |
| 566 | ldrh, | |
| 567 | /// 8-bit, zero-extended | |
| 568 | ldrb, | |
| 569 | }; | |
| 570 | ||
| 571 | fn loadStoreRegister( | |
| 572 | rt: Register, | |
| 573 | rn: Register, | |
| 574 | offset: LoadStoreOffset, | |
| 575 | variant: LoadStoreVariant, | |
| 576 | ) Instruction { | |
| 577 | const off = offset.toU12(); | |
| 578 | const op1: u2 = blk: { | |
| 579 | switch (offset) { | |
| 580 | .Immediate => |imm| switch (imm) { | |
| 581 | .Unsigned => break :blk 0b01, | |
| 582 | else => {}, | |
| 583 | }, | |
| 584 | else => {}, | |
| 585 | } | |
| 586 | break :blk 0b00; | |
| 587 | }; | |
| 588 | const opc: u2 = switch (variant) { | |
| 589 | .ldr, .ldrh, .ldrb => 0b01, | |
| 590 | .str, .strh, .strb => 0b00, | |
| 591 | }; | |
| 592 | return Instruction{ | |
| 593 | .load_store_register = .{ | |
| 594 | .rt = rt.id(), | |
| 595 | .rn = rn.id(), | |
| 596 | .offset = off, | |
| 597 | .opc = opc, | |
| 598 | .op1 = op1, | |
| 599 | .v = 0, | |
| 600 | .size = blk: { | |
| 601 | switch (variant) { | |
| 602 | .ldr, .str => switch (rt.size()) { | |
| 603 | 32 => break :blk 0b10, | |
| 604 | 64 => break :blk 0b11, | |
| 605 | else => unreachable, // unexpected register size | |
| 606 | }, | |
| 607 | .ldrh, .strh => break :blk 0b01, | |
| 608 | .ldrb, .strb => break :blk 0b00, | |
| 609 | } | |
| 610 | }, | |
| 611 | }, | |
| 612 | }; | |
| 613 | } | |
| 614 | ||
| 615 | fn loadStoreRegisterPair( | |
| 616 | rt1: Register, | |
| 617 | rt2: Register, | |
| 618 | rn: Register, | |
| 619 | offset: i9, | |
| 620 | encoding: u2, | |
| 621 | load: bool, | |
| 622 | ) Instruction { | |
| 623 | switch (rt1.size()) { | |
| 624 | 32 => { | |
| 625 | assert(-256 <= offset and offset <= 252); | |
| 626 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 2)); | |
| 627 | return Instruction{ | |
| 628 | .load_store_register_pair = .{ | |
| 629 | .rt1 = rt1.id(), | |
| 630 | .rn = rn.id(), | |
| 631 | .rt2 = rt2.id(), | |
| 632 | .imm7 = imm7, | |
| 633 | .load = @boolToInt(load), | |
| 634 | .encoding = encoding, | |
| 635 | .opc = 0b00, | |
| 636 | }, | |
| 637 | }; | |
| 638 | }, | |
| 639 | 64 => { | |
| 640 | assert(-512 <= offset and offset <= 504); | |
| 641 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 3)); | |
| 642 | return Instruction{ | |
| 643 | .load_store_register_pair = .{ | |
| 644 | .rt1 = rt1.id(), | |
| 645 | .rn = rn.id(), | |
| 646 | .rt2 = rt2.id(), | |
| 647 | .imm7 = imm7, | |
| 648 | .load = @boolToInt(load), | |
| 649 | .encoding = encoding, | |
| 650 | .opc = 0b10, | |
| 651 | }, | |
| 652 | }; | |
| 653 | }, | |
| 654 | else => unreachable, // unexpected register size | |
| 655 | } | |
| 656 | } | |
| 657 | ||
| 658 | fn loadLiteral(rt: Register, imm19: u19) Instruction { | |
| 659 | switch (rt.size()) { | |
| 660 | 32 => { | |
| 661 | return Instruction{ | |
| 662 | .load_literal = .{ | |
| 663 | .rt = rt.id(), | |
| 664 | .imm19 = imm19, | |
| 665 | .opc = 0b00, | |
| 666 | }, | |
| 667 | }; | |
| 668 | }, | |
| 669 | 64 => { | |
| 670 | return Instruction{ | |
| 671 | .load_literal = .{ | |
| 672 | .rt = rt.id(), | |
| 673 | .imm19 = imm19, | |
| 674 | .opc = 0b01, | |
| 675 | }, | |
| 676 | }; | |
| 677 | }, | |
| 678 | else => unreachable, // unexpected register size | |
| 679 | } | |
| 680 | } | |
| 681 | ||
| 682 | fn exceptionGeneration( | |
| 683 | opc: u3, | |
| 684 | op2: u3, | |
| 685 | ll: u2, | |
| 686 | imm16: u16, | |
| 687 | ) Instruction { | |
| 688 | return Instruction{ | |
| 689 | .exception_generation = .{ | |
| 690 | .ll = ll, | |
| 691 | .op2 = op2, | |
| 692 | .imm16 = imm16, | |
| 693 | .opc = opc, | |
| 694 | }, | |
| 695 | }; | |
| 696 | } | |
| 697 | ||
| 698 | fn unconditionalBranchRegister( | |
| 699 | opc: u4, | |
| 700 | op2: u5, | |
| 701 | op3: u6, | |
| 702 | rn: Register, | |
| 703 | op4: u5, | |
| 704 | ) Instruction { | |
| 705 | assert(rn.size() == 64); | |
| 706 | ||
| 707 | return Instruction{ | |
| 708 | .unconditional_branch_register = .{ | |
| 709 | .op4 = op4, | |
| 710 | .rn = rn.id(), | |
| 711 | .op3 = op3, | |
| 712 | .op2 = op2, | |
| 713 | .opc = opc, | |
| 714 | }, | |
| 715 | }; | |
| 716 | } | |
| 717 | ||
| 718 | fn unconditionalBranchImmediate( | |
| 719 | op: u1, | |
| 720 | offset: i28, | |
| 721 | ) Instruction { | |
| 722 | return Instruction{ | |
| 723 | .unconditional_branch_immediate = .{ | |
| 724 | .imm26 = @bitCast(u26, @intCast(i26, offset >> 2)), | |
| 725 | .op = op, | |
| 726 | }, | |
| 727 | }; | |
| 728 | } | |
| 729 | ||
| 730 | fn logicalShiftedRegister( | |
| 731 | opc: u2, | |
| 732 | n: u1, | |
| 733 | shift: Shift, | |
| 734 | rd: Register, | |
| 735 | rn: Register, | |
| 736 | rm: Register, | |
| 737 | ) Instruction { | |
| 738 | switch (rd.size()) { | |
| 739 | 32 => { | |
| 740 | assert(shift.amount < 32); | |
| 741 | return Instruction{ | |
| 742 | .logical_shifted_register = .{ | |
| 743 | .rd = rd.id(), | |
| 744 | .rn = rn.id(), | |
| 745 | .imm6 = shift.amount, | |
| 746 | .rm = rm.id(), | |
| 747 | .n = n, | |
| 748 | .shift = @enumToInt(shift.shift), | |
| 749 | .opc = opc, | |
| 750 | .sf = 0b0, | |
| 751 | }, | |
| 752 | }; | |
| 753 | }, | |
| 754 | 64 => { | |
| 755 | return Instruction{ | |
| 756 | .logical_shifted_register = .{ | |
| 757 | .rd = rd.id(), | |
| 758 | .rn = rn.id(), | |
| 759 | .imm6 = shift.amount, | |
| 760 | .rm = rm.id(), | |
| 761 | .n = n, | |
| 762 | .shift = @enumToInt(shift.shift), | |
| 763 | .opc = opc, | |
| 764 | .sf = 0b1, | |
| 765 | }, | |
| 766 | }; | |
| 767 | }, | |
| 768 | else => unreachable, // unexpected register size | |
| 769 | } | |
| 770 | } | |
| 771 | ||
| 772 | fn addSubtractImmediate( | |
| 773 | op: u1, | |
| 774 | s: u1, | |
| 775 | rd: Register, | |
| 776 | rn: Register, | |
| 777 | imm12: u12, | |
| 778 | shift: bool, | |
| 779 | ) Instruction { | |
| 780 | return Instruction{ | |
| 781 | .add_subtract_immediate = .{ | |
| 782 | .rd = rd.id(), | |
| 783 | .rn = rn.id(), | |
| 784 | .imm12 = imm12, | |
| 785 | .sh = @boolToInt(shift), | |
| 786 | .s = s, | |
| 787 | .op = op, | |
| 788 | .sf = switch (rd.size()) { | |
| 789 | 32 => 0b0, | |
| 790 | 64 => 0b1, | |
| 791 | else => unreachable, // unexpected register size | |
| 792 | }, | |
| 793 | }, | |
| 794 | }; | |
| 795 | } | |
| 796 | ||
| 797 | fn conditionalBranch( | |
| 798 | o0: u1, | |
| 799 | o1: u1, | |
| 800 | cond: Condition, | |
| 801 | offset: i21, | |
| 802 | ) Instruction { | |
| 803 | assert(offset & 0b11 == 0b00); | |
| 804 | return Instruction{ | |
| 805 | .conditional_branch = .{ | |
| 806 | .cond = @enumToInt(cond), | |
| 807 | .o0 = o0, | |
| 808 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), | |
| 809 | .o1 = o1, | |
| 810 | }, | |
| 811 | }; | |
| 812 | } | |
| 813 | ||
| 814 | fn compareAndBranch( | |
| 815 | op: u1, | |
| 816 | rt: Register, | |
| 817 | offset: i21, | |
| 818 | ) Instruction { | |
| 819 | assert(offset & 0b11 == 0b00); | |
| 820 | return Instruction{ | |
| 821 | .compare_and_branch = .{ | |
| 822 | .rt = rt.id(), | |
| 823 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), | |
| 824 | .op = op, | |
| 825 | .sf = switch (rt.size()) { | |
| 826 | 32 => 0b0, | |
| 827 | 64 => 0b1, | |
| 828 | else => unreachable, // unexpected register size | |
| 829 | }, | |
| 830 | }, | |
| 831 | }; | |
| 832 | } | |
| 833 | ||
| 834 | // Helper functions for assembly syntax functions | |
| 835 | ||
| 836 | // Move wide (immediate) | |
| 837 | ||
| 838 | pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction { | |
| 839 | return moveWideImmediate(0b00, rd, imm16, shift); | |
| 840 | } | |
| 841 | ||
| 842 | pub fn movz(rd: Register, imm16: u16, shift: u6) Instruction { | |
| 843 | return moveWideImmediate(0b10, rd, imm16, shift); | |
| 844 | } | |
| 845 | ||
| 846 | pub fn movk(rd: Register, imm16: u16, shift: u6) Instruction { | |
| 847 | return moveWideImmediate(0b11, rd, imm16, shift); | |
| 848 | } | |
| 849 | ||
| 850 | // PC relative address | |
| 851 | ||
| 852 | pub fn adr(rd: Register, imm21: i21) Instruction { | |
| 853 | return pcRelativeAddress(rd, imm21, 0b0); | |
| 854 | } | |
| 855 | ||
| 856 | pub fn adrp(rd: Register, imm21: i21) Instruction { | |
| 857 | return pcRelativeAddress(rd, imm21, 0b1); | |
| 858 | } | |
| 859 | ||
| 860 | // Load or store register | |
| 861 | ||
| 862 | pub const LdrArgs = union(enum) { | |
| 863 | register: struct { | |
| 864 | rn: Register, | |
| 865 | offset: LoadStoreOffset = LoadStoreOffset.none, | |
| 866 | }, | |
| 867 | literal: u19, | |
| 868 | }; | |
| 869 | ||
| 870 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { | |
| 871 | switch (args) { | |
| 872 | .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .ldr), | |
| 873 | .literal => |literal| return loadLiteral(rt, literal), | |
| 874 | } | |
| 875 | } | |
| 876 | ||
| 877 | pub fn ldrh(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 878 | return loadStoreRegister(rt, rn, args.offset, .ldrh); | |
| 879 | } | |
| 880 | ||
| 881 | pub fn ldrb(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 882 | return loadStoreRegister(rt, rn, args.offset, .ldrb); | |
| 883 | } | |
| 884 | ||
| 885 | pub const StrArgs = struct { | |
| 886 | offset: LoadStoreOffset = LoadStoreOffset.none, | |
| 887 | }; | |
| 888 | ||
| 889 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 890 | return loadStoreRegister(rt, rn, args.offset, .str); | |
| 891 | } | |
| 892 | ||
| 893 | pub fn strh(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 894 | return loadStoreRegister(rt, rn, args.offset, .strh); | |
| 895 | } | |
| 896 | ||
| 897 | pub fn strb(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 898 | return loadStoreRegister(rt, rn, args.offset, .strb); | |
| 899 | } | |
| 900 | ||
| 901 | // Load or store pair of registers | |
| 902 | ||
| 903 | pub const LoadStorePairOffset = struct { | |
| 904 | encoding: enum(u2) { | |
| 905 | PostIndex = 0b01, | |
| 906 | Signed = 0b10, | |
| 907 | PreIndex = 0b11, | |
| 908 | }, | |
| 909 | offset: i9, | |
| 910 | ||
| 911 | pub fn none() LoadStorePairOffset { | |
| 912 | return .{ .encoding = .Signed, .offset = 0 }; | |
| 913 | } | |
| 914 | ||
| 915 | pub fn post_index(imm: i9) LoadStorePairOffset { | |
| 916 | return .{ .encoding = .PostIndex, .offset = imm }; | |
| 917 | } | |
| 918 | ||
| 919 | pub fn pre_index(imm: i9) LoadStorePairOffset { | |
| 920 | return .{ .encoding = .PreIndex, .offset = imm }; | |
| 921 | } | |
| 922 | ||
| 923 | pub fn signed(imm: i9) LoadStorePairOffset { | |
| 924 | return .{ .encoding = .Signed, .offset = imm }; | |
| 925 | } | |
| 926 | }; | |
| 927 | ||
| 928 | pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { | |
| 929 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); | |
| 930 | } | |
| 931 | ||
| 932 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { | |
| 933 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true); | |
| 934 | } | |
| 935 | ||
| 936 | pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { | |
| 937 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); | |
| 938 | } | |
| 939 | ||
| 940 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { | |
| 941 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false); | |
| 942 | } | |
| 943 | ||
| 944 | // Exception generation | |
| 945 | ||
| 946 | pub fn svc(imm16: u16) Instruction { | |
| 947 | return exceptionGeneration(0b000, 0b000, 0b01, imm16); | |
| 948 | } | |
| 949 | ||
| 950 | pub fn hvc(imm16: u16) Instruction { | |
| 951 | return exceptionGeneration(0b000, 0b000, 0b10, imm16); | |
| 952 | } | |
| 953 | ||
| 954 | pub fn smc(imm16: u16) Instruction { | |
| 955 | return exceptionGeneration(0b000, 0b000, 0b11, imm16); | |
| 956 | } | |
| 957 | ||
| 958 | pub fn brk(imm16: u16) Instruction { | |
| 959 | return exceptionGeneration(0b001, 0b000, 0b00, imm16); | |
| 960 | } | |
| 961 | ||
| 962 | pub fn hlt(imm16: u16) Instruction { | |
| 963 | return exceptionGeneration(0b010, 0b000, 0b00, imm16); | |
| 964 | } | |
| 965 | ||
| 966 | // Unconditional branch (register) | |
| 967 | ||
| 968 | pub fn br(rn: Register) Instruction { | |
| 969 | return unconditionalBranchRegister(0b0000, 0b11111, 0b000000, rn, 0b00000); | |
| 970 | } | |
| 971 | ||
| 972 | pub fn blr(rn: Register) Instruction { | |
| 973 | return unconditionalBranchRegister(0b0001, 0b11111, 0b000000, rn, 0b00000); | |
| 974 | } | |
| 975 | ||
| 976 | pub fn ret(rn: ?Register) Instruction { | |
| 977 | return unconditionalBranchRegister(0b0010, 0b11111, 0b000000, rn orelse .x30, 0b00000); | |
| 978 | } | |
| 979 | ||
| 980 | // Unconditional branch (immediate) | |
| 981 | ||
| 982 | pub fn b(offset: i28) Instruction { | |
| 983 | return unconditionalBranchImmediate(0, offset); | |
| 984 | } | |
| 985 | ||
| 986 | pub fn bl(offset: i28) Instruction { | |
| 987 | return unconditionalBranchImmediate(1, offset); | |
| 988 | } | |
| 989 | ||
| 990 | // Nop | |
| 991 | ||
| 992 | pub fn nop() Instruction { | |
| 993 | return Instruction{ .no_operation = .{} }; | |
| 994 | } | |
| 995 | ||
| 996 | // Logical (shifted register) | |
| 997 | ||
| 998 | pub fn @"and"(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 999 | return logicalShiftedRegister(0b00, 0b0, shift, rd, rn, rm); | |
| 1000 | } | |
| 1001 | ||
| 1002 | pub fn bic(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1003 | return logicalShiftedRegister(0b00, 0b1, shift, rd, rn, rm); | |
| 1004 | } | |
| 1005 | ||
| 1006 | pub fn orr(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1007 | return logicalShiftedRegister(0b01, 0b0, shift, rd, rn, rm); | |
| 1008 | } | |
| 1009 | ||
| 1010 | pub fn orn(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1011 | return logicalShiftedRegister(0b01, 0b1, shift, rd, rn, rm); | |
| 1012 | } | |
| 1013 | ||
| 1014 | pub fn eor(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1015 | return logicalShiftedRegister(0b10, 0b0, shift, rd, rn, rm); | |
| 1016 | } | |
| 1017 | ||
| 1018 | pub fn eon(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1019 | return logicalShiftedRegister(0b10, 0b1, shift, rd, rn, rm); | |
| 1020 | } | |
| 1021 | ||
| 1022 | pub fn ands(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1023 | return logicalShiftedRegister(0b11, 0b0, shift, rd, rn, rm); | |
| 1024 | } | |
| 1025 | ||
| 1026 | pub fn bics(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { | |
| 1027 | return logicalShiftedRegister(0b11, 0b1, shift, rd, rn, rm); | |
| 1028 | } | |
| 1029 | ||
| 1030 | // Add/subtract (immediate) | |
| 1031 | ||
| 1032 | pub fn add(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | |
| 1033 | return addSubtractImmediate(0b0, 0b0, rd, rn, imm, shift); | |
| 1034 | } | |
| 1035 | ||
| 1036 | pub fn adds(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | |
| 1037 | return addSubtractImmediate(0b0, 0b1, rd, rn, imm, shift); | |
| 1038 | } | |
| 1039 | ||
| 1040 | pub fn sub(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | |
| 1041 | return addSubtractImmediate(0b1, 0b0, rd, rn, imm, shift); | |
| 1042 | } | |
| 1043 | ||
| 1044 | pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { | |
| 1045 | return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift); | |
| 1046 | } | |
| 1047 | ||
| 1048 | // Conditional branch | |
| 1049 | ||
| 1050 | pub fn bCond(cond: Condition, offset: i21) Instruction { | |
| 1051 | return conditionalBranch(0b0, 0b0, cond, offset); | |
| 1052 | } | |
| 1053 | ||
| 1054 | // Compare and branch | |
| 1055 | ||
| 1056 | pub fn cbz(rt: Register, offset: i21) Instruction { | |
| 1057 | return compareAndBranch(0b0, rt, offset); | |
| 1058 | } | |
| 1059 | ||
| 1060 | pub fn cbnz(rt: Register, offset: i21) Instruction { | |
| 1061 | return compareAndBranch(0b1, rt, offset); | |
| 1062 | } | |
| 1063 | }; | |
| 1064 | ||
| 1065 | test { | |
| 1066 | testing.refAllDecls(@This()); | |
| 1067 | } | |
| 1068 | ||
| 1069 | test "serialize instructions" { | |
| 1070 | const Testcase = struct { | |
| 1071 | inst: Instruction, | |
| 1072 | expected: u32, | |
| 1073 | }; | |
| 1074 | ||
| 1075 | const testcases = [_]Testcase{ | |
| 1076 | .{ // orr x0, xzr, x1 | |
| 1077 | .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none), | |
| 1078 | .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, | |
| 1079 | }, | |
| 1080 | .{ // orn x0, xzr, x1 | |
| 1081 | .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none), | |
| 1082 | .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, | |
| 1083 | }, | |
| 1084 | .{ // movz x1, #4 | |
| 1085 | .inst = Instruction.movz(.x1, 4, 0), | |
| 1086 | .expected = 0b1_10_100101_00_0000000000000100_00001, | |
| 1087 | }, | |
| 1088 | .{ // movz x1, #4, lsl 16 | |
| 1089 | .inst = Instruction.movz(.x1, 4, 16), | |
| 1090 | .expected = 0b1_10_100101_01_0000000000000100_00001, | |
| 1091 | }, | |
| 1092 | .{ // movz x1, #4, lsl 32 | |
| 1093 | .inst = Instruction.movz(.x1, 4, 32), | |
| 1094 | .expected = 0b1_10_100101_10_0000000000000100_00001, | |
| 1095 | }, | |
| 1096 | .{ // movz x1, #4, lsl 48 | |
| 1097 | .inst = Instruction.movz(.x1, 4, 48), | |
| 1098 | .expected = 0b1_10_100101_11_0000000000000100_00001, | |
| 1099 | }, | |
| 1100 | .{ // movz w1, #4 | |
| 1101 | .inst = Instruction.movz(.w1, 4, 0), | |
| 1102 | .expected = 0b0_10_100101_00_0000000000000100_00001, | |
| 1103 | }, | |
| 1104 | .{ // movz w1, #4, lsl 16 | |
| 1105 | .inst = Instruction.movz(.w1, 4, 16), | |
| 1106 | .expected = 0b0_10_100101_01_0000000000000100_00001, | |
| 1107 | }, | |
| 1108 | .{ // svc #0 | |
| 1109 | .inst = Instruction.svc(0), | |
| 1110 | .expected = 0b1101_0100_000_0000000000000000_00001, | |
| 1111 | }, | |
| 1112 | .{ // svc #0x80 ; typical on Darwin | |
| 1113 | .inst = Instruction.svc(0x80), | |
| 1114 | .expected = 0b1101_0100_000_0000000010000000_00001, | |
| 1115 | }, | |
| 1116 | .{ // ret | |
| 1117 | .inst = Instruction.ret(null), | |
| 1118 | .expected = 0b1101_011_00_10_11111_0000_00_11110_00000, | |
| 1119 | }, | |
| 1120 | .{ // bl #0x10 | |
| 1121 | .inst = Instruction.bl(0x10), | |
| 1122 | .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100, | |
| 1123 | }, | |
| 1124 | .{ // ldr x2, [x1] | |
| 1125 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1 } }), | |
| 1126 | .expected = 0b11_111_0_01_01_000000000000_00001_00010, | |
| 1127 | }, | |
| 1128 | .{ // ldr x2, [x1, #1]! | |
| 1129 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_pre_index(1) } }), | |
| 1130 | .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, | |
| 1131 | }, | |
| 1132 | .{ // ldr x2, [x1], #-1 | |
| 1133 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_post_index(-1) } }), | |
| 1134 | .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, | |
| 1135 | }, | |
| 1136 | .{ // ldr x2, [x1], (x3) | |
| 1137 | .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) } }), | |
| 1138 | .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, | |
| 1139 | }, | |
| 1140 | .{ // ldr x2, label | |
| 1141 | .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }), | |
| 1142 | .expected = 0b01_011_0_00_0000000000000000001_00010, | |
| 1143 | }, | |
| 1144 | .{ // ldrh x7, [x4], #0xaa | |
| 1145 | .inst = Instruction.ldrh(.x7, .x4, .{ .offset = Instruction.LoadStoreOffset.imm_post_index(0xaa) }), | |
| 1146 | .expected = 0b01_111_0_00_01_0_010101010_01_00100_00111, | |
| 1147 | }, | |
| 1148 | .{ // ldrb x9, [x15, #0xff]! | |
| 1149 | .inst = Instruction.ldrb(.x9, .x15, .{ .offset = Instruction.LoadStoreOffset.imm_pre_index(0xff) }), | |
| 1150 | .expected = 0b00_111_0_00_01_0_011111111_11_01111_01001, | |
| 1151 | }, | |
| 1152 | .{ // str x2, [x1] | |
| 1153 | .inst = Instruction.str(.x2, .x1, .{}), | |
| 1154 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, | |
| 1155 | }, | |
| 1156 | .{ // str x2, [x1], (x3) | |
| 1157 | .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), | |
| 1158 | .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, | |
| 1159 | }, | |
| 1160 | .{ // strh w0, [x1] | |
| 1161 | .inst = Instruction.strh(.w0, .x1, .{}), | |
| 1162 | .expected = 0b01_111_0_01_00_000000000000_00001_00000, | |
| 1163 | }, | |
| 1164 | .{ // strb w8, [x9] | |
| 1165 | .inst = Instruction.strb(.w8, .x9, .{}), | |
| 1166 | .expected = 0b00_111_0_01_00_000000000000_01001_01000, | |
| 1167 | }, | |
| 1168 | .{ // adr x2, #0x8 | |
| 1169 | .inst = Instruction.adr(.x2, 0x8), | |
| 1170 | .expected = 0b0_00_10000_0000000000000000010_00010, | |
| 1171 | }, | |
| 1172 | .{ // adr x2, -#0x8 | |
| 1173 | .inst = Instruction.adr(.x2, -0x8), | |
| 1174 | .expected = 0b0_00_10000_1111111111111111110_00010, | |
| 1175 | }, | |
| 1176 | .{ // adrp x2, #0x8 | |
| 1177 | .inst = Instruction.adrp(.x2, 0x8), | |
| 1178 | .expected = 0b1_00_10000_0000000000000000010_00010, | |
| 1179 | }, | |
| 1180 | .{ // adrp x2, -#0x8 | |
| 1181 | .inst = Instruction.adrp(.x2, -0x8), | |
| 1182 | .expected = 0b1_00_10000_1111111111111111110_00010, | |
| 1183 | }, | |
| 1184 | .{ // stp x1, x2, [sp, #8] | |
| 1185 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), | |
| 1186 | .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, | |
| 1187 | }, | |
| 1188 | .{ // ldp x1, x2, [sp, #8] | |
| 1189 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), | |
| 1190 | .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, | |
| 1191 | }, | |
| 1192 | .{ // stp x1, x2, [sp, #-16]! | |
| 1193 | .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.pre_index(-16)), | |
| 1194 | .expected = 0b10_101_0_011_0_1111110_00010_11111_00001, | |
| 1195 | }, | |
| 1196 | .{ // ldp x1, x2, [sp], #16 | |
| 1197 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)), | |
| 1198 | .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, | |
| 1199 | }, | |
| 1200 | .{ // and x0, x4, x2 | |
| 1201 | .inst = Instruction.@"and"(.x0, .x4, .x2, .{}), | |
| 1202 | .expected = 0b1_00_01010_00_0_00010_000000_00100_00000, | |
| 1203 | }, | |
| 1204 | .{ // and x0, x4, x2, lsl #0x8 | |
| 1205 | .inst = Instruction.@"and"(.x0, .x4, .x2, .{ .shift = .lsl, .amount = 0x8 }), | |
| 1206 | .expected = 0b1_00_01010_00_0_00010_001000_00100_00000, | |
| 1207 | }, | |
| 1208 | .{ // add x0, x10, #10 | |
| 1209 | .inst = Instruction.add(.x0, .x10, 10, false), | |
| 1210 | .expected = 0b1_0_0_100010_0_0000_0000_1010_01010_00000, | |
| 1211 | }, | |
| 1212 | .{ // subs x0, x5, #11, lsl #12 | |
| 1213 | .inst = Instruction.subs(.x0, .x5, 11, true), | |
| 1214 | .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000, | |
| 1215 | }, | |
| 1216 | .{ // b.hi #-4 | |
| 1217 | .inst = Instruction.bCond(.hi, -4), | |
| 1218 | .expected = 0b0101010_0_1111111111111111111_0_1000, | |
| 1219 | }, | |
| 1220 | .{ // cbz x10, #40 | |
| 1221 | .inst = Instruction.cbz(.x10, 40), | |
| 1222 | .expected = 0b1_011010_0_0000000000000001010_01010, | |
| 1223 | }, | |
| 1224 | }; | |
| 1225 | ||
| 1226 | for (testcases) |case| { | |
| 1227 | const actual = case.inst.toU32(); | |
| 1228 | try testing.expectEqual(case.expected, actual); | |
| 1229 | } | |
| 1230 | } |
src/codegen/arm.zig deleted-1408| ... | ... | @@ -1,1408 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const DW = std.dwarf; | |
| 3 | const testing = std.testing; | |
| 4 | ||
| 5 | /// The condition field specifies the flags neccessary for an | |
| 6 | /// Instruction to be executed | |
| 7 | pub const Condition = enum(u4) { | |
| 8 | /// equal | |
| 9 | eq, | |
| 10 | /// not equal | |
| 11 | ne, | |
| 12 | /// unsigned higher or same | |
| 13 | cs, | |
| 14 | /// unsigned lower | |
| 15 | cc, | |
| 16 | /// negative | |
| 17 | mi, | |
| 18 | /// positive or zero | |
| 19 | pl, | |
| 20 | /// overflow | |
| 21 | vs, | |
| 22 | /// no overflow | |
| 23 | vc, | |
| 24 | /// unsigned higer | |
| 25 | hi, | |
| 26 | /// unsigned lower or same | |
| 27 | ls, | |
| 28 | /// greater or equal | |
| 29 | ge, | |
| 30 | /// less than | |
| 31 | lt, | |
| 32 | /// greater than | |
| 33 | gt, | |
| 34 | /// less than or equal | |
| 35 | le, | |
| 36 | /// always | |
| 37 | al, | |
| 38 | ||
| 39 | /// Converts a std.math.CompareOperator into a condition flag, | |
| 40 | /// i.e. returns the condition that is true iff the result of the | |
| 41 | /// comparison is true. Assumes signed comparison | |
| 42 | pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition { | |
| 43 | return switch (op) { | |
| 44 | .gte => .ge, | |
| 45 | .gt => .gt, | |
| 46 | .neq => .ne, | |
| 47 | .lt => .lt, | |
| 48 | .lte => .le, | |
| 49 | .eq => .eq, | |
| 50 | }; | |
| 51 | } | |
| 52 | ||
| 53 | /// Converts a std.math.CompareOperator into a condition flag, | |
| 54 | /// i.e. returns the condition that is true iff the result of the | |
| 55 | /// comparison is true. Assumes unsigned comparison | |
| 56 | pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition { | |
| 57 | return switch (op) { | |
| 58 | .gte => .cs, | |
| 59 | .gt => .hi, | |
| 60 | .neq => .ne, | |
| 61 | .lt => .cc, | |
| 62 | .lte => .ls, | |
| 63 | .eq => .eq, | |
| 64 | }; | |
| 65 | } | |
| 66 | ||
| 67 | /// Returns the condition which is true iff the given condition is | |
| 68 | /// false (if such a condition exists) | |
| 69 | pub fn negate(cond: Condition) Condition { | |
| 70 | return switch (cond) { | |
| 71 | .eq => .ne, | |
| 72 | .ne => .eq, | |
| 73 | .cs => .cc, | |
| 74 | .cc => .cs, | |
| 75 | .mi => .pl, | |
| 76 | .pl => .mi, | |
| 77 | .vs => .vc, | |
| 78 | .vc => .vs, | |
| 79 | .hi => .ls, | |
| 80 | .ls => .hi, | |
| 81 | .ge => .lt, | |
| 82 | .lt => .ge, | |
| 83 | .gt => .le, | |
| 84 | .le => .gt, | |
| 85 | .al => unreachable, | |
| 86 | }; | |
| 87 | } | |
| 88 | }; | |
| 89 | ||
| 90 | test "condition from CompareOperator" { | |
| 91 | try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq)); | |
| 92 | try testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq)); | |
| 93 | ||
| 94 | try testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt)); | |
| 95 | try testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt)); | |
| 96 | ||
| 97 | try testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte)); | |
| 98 | try testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte)); | |
| 99 | } | |
| 100 | ||
| 101 | test "negate condition" { | |
| 102 | try testing.expectEqual(@as(Condition, .eq), Condition.ne.negate()); | |
| 103 | try testing.expectEqual(@as(Condition, .ne), Condition.eq.negate()); | |
| 104 | } | |
| 105 | ||
| 106 | /// Represents a register in the ARM instruction set architecture | |
| 107 | pub const Register = enum(u5) { | |
| 108 | r0, | |
| 109 | r1, | |
| 110 | r2, | |
| 111 | r3, | |
| 112 | r4, | |
| 113 | r5, | |
| 114 | r6, | |
| 115 | r7, | |
| 116 | r8, | |
| 117 | r9, | |
| 118 | r10, | |
| 119 | r11, | |
| 120 | r12, | |
| 121 | r13, | |
| 122 | r14, | |
| 123 | r15, | |
| 124 | ||
| 125 | /// Argument / result / scratch register 1 | |
| 126 | a1, | |
| 127 | /// Argument / result / scratch register 2 | |
| 128 | a2, | |
| 129 | /// Argument / scratch register 3 | |
| 130 | a3, | |
| 131 | /// Argument / scratch register 4 | |
| 132 | a4, | |
| 133 | /// Variable-register 1 | |
| 134 | v1, | |
| 135 | /// Variable-register 2 | |
| 136 | v2, | |
| 137 | /// Variable-register 3 | |
| 138 | v3, | |
| 139 | /// Variable-register 4 | |
| 140 | v4, | |
| 141 | /// Variable-register 5 | |
| 142 | v5, | |
| 143 | /// Platform register | |
| 144 | v6, | |
| 145 | /// Variable-register 7 | |
| 146 | v7, | |
| 147 | /// Frame pointer or Variable-register 8 | |
| 148 | fp, | |
| 149 | /// Intra-Procedure-call scratch register | |
| 150 | ip, | |
| 151 | /// Stack pointer | |
| 152 | sp, | |
| 153 | /// Link register | |
| 154 | lr, | |
| 155 | /// Program counter | |
| 156 | pc, | |
| 157 | ||
| 158 | /// Returns the unique 4-bit ID of this register which is used in | |
| 159 | /// the machine code | |
| 160 | pub fn id(self: Register) u4 { | |
| 161 | return @truncate(u4, @enumToInt(self)); | |
| 162 | } | |
| 163 | ||
| 164 | /// Returns the index into `callee_preserved_regs`. | |
| 165 | pub fn allocIndex(self: Register) ?u4 { | |
| 166 | inline for (callee_preserved_regs) |cpreg, i| { | |
| 167 | if (self.id() == cpreg.id()) return i; | |
| 168 | } | |
| 169 | return null; | |
| 170 | } | |
| 171 | ||
| 172 | pub fn dwarfLocOp(self: Register) u8 { | |
| 173 | return @as(u8, self.id()) + DW.OP.reg0; | |
| 174 | } | |
| 175 | }; | |
| 176 | ||
| 177 | test "Register.id" { | |
| 178 | try testing.expectEqual(@as(u4, 15), Register.r15.id()); | |
| 179 | try testing.expectEqual(@as(u4, 15), Register.pc.id()); | |
| 180 | } | |
| 181 | ||
| 182 | /// Program status registers containing flags, mode bits and other | |
| 183 | /// vital information | |
| 184 | pub const Psr = enum { | |
| 185 | cpsr, | |
| 186 | spsr, | |
| 187 | }; | |
| 188 | ||
| 189 | pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; | |
| 190 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | |
| 191 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | |
| 192 | ||
| 193 | /// Represents an instruction in the ARM instruction set architecture | |
| 194 | pub const Instruction = union(enum) { | |
| 195 | data_processing: packed struct { | |
| 196 | // Note to self: The order of the fields top-to-bottom is | |
| 197 | // right-to-left in the actual 32-bit int representation | |
| 198 | op2: u12, | |
| 199 | rd: u4, | |
| 200 | rn: u4, | |
| 201 | s: u1, | |
| 202 | opcode: u4, | |
| 203 | i: u1, | |
| 204 | fixed: u2 = 0b00, | |
| 205 | cond: u4, | |
| 206 | }, | |
| 207 | multiply: packed struct { | |
| 208 | rn: u4, | |
| 209 | fixed_1: u4 = 0b1001, | |
| 210 | rm: u4, | |
| 211 | ra: u4, | |
| 212 | rd: u4, | |
| 213 | set_cond: u1, | |
| 214 | accumulate: u1, | |
| 215 | fixed_2: u6 = 0b000000, | |
| 216 | cond: u4, | |
| 217 | }, | |
| 218 | multiply_long: packed struct { | |
| 219 | rn: u4, | |
| 220 | fixed_1: u4 = 0b1001, | |
| 221 | rm: u4, | |
| 222 | rdlo: u4, | |
| 223 | rdhi: u4, | |
| 224 | set_cond: u1, | |
| 225 | accumulate: u1, | |
| 226 | unsigned: u1, | |
| 227 | fixed_2: u5 = 0b00001, | |
| 228 | cond: u4, | |
| 229 | }, | |
| 230 | integer_saturating_arithmetic: packed struct { | |
| 231 | rm: u4, | |
| 232 | fixed_1: u8 = 0b0000_0101, | |
| 233 | rd: u4, | |
| 234 | rn: u4, | |
| 235 | fixed_2: u1 = 0b0, | |
| 236 | opc: u2, | |
| 237 | fixed_3: u5 = 0b00010, | |
| 238 | cond: u4, | |
| 239 | }, | |
| 240 | single_data_transfer: packed struct { | |
| 241 | offset: u12, | |
| 242 | rd: u4, | |
| 243 | rn: u4, | |
| 244 | load_store: u1, | |
| 245 | write_back: u1, | |
| 246 | byte_word: u1, | |
| 247 | up_down: u1, | |
| 248 | pre_post: u1, | |
| 249 | imm: u1, | |
| 250 | fixed: u2 = 0b01, | |
| 251 | cond: u4, | |
| 252 | }, | |
| 253 | extra_load_store: packed struct { | |
| 254 | imm4l: u4, | |
| 255 | fixed_1: u1 = 0b1, | |
| 256 | op2: u2, | |
| 257 | fixed_2: u1 = 0b1, | |
| 258 | imm4h: u4, | |
| 259 | rt: u4, | |
| 260 | rn: u4, | |
| 261 | o1: u1, | |
| 262 | write_back: u1, | |
| 263 | imm: u1, | |
| 264 | up_down: u1, | |
| 265 | pre_index: u1, | |
| 266 | fixed_3: u3 = 0b000, | |
| 267 | cond: u4, | |
| 268 | }, | |
| 269 | block_data_transfer: packed struct { | |
| 270 | register_list: u16, | |
| 271 | rn: u4, | |
| 272 | load_store: u1, | |
| 273 | write_back: u1, | |
| 274 | psr_or_user: u1, | |
| 275 | up_down: u1, | |
| 276 | pre_post: u1, | |
| 277 | fixed: u3 = 0b100, | |
| 278 | cond: u4, | |
| 279 | }, | |
| 280 | branch: packed struct { | |
| 281 | offset: u24, | |
| 282 | link: u1, | |
| 283 | fixed: u3 = 0b101, | |
| 284 | cond: u4, | |
| 285 | }, | |
| 286 | branch_exchange: packed struct { | |
| 287 | rn: u4, | |
| 288 | fixed_1: u1 = 0b1, | |
| 289 | link: u1, | |
| 290 | fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, | |
| 291 | cond: u4, | |
| 292 | }, | |
| 293 | supervisor_call: packed struct { | |
| 294 | comment: u24, | |
| 295 | fixed: u4 = 0b1111, | |
| 296 | cond: u4, | |
| 297 | }, | |
| 298 | breakpoint: packed struct { | |
| 299 | imm4: u4, | |
| 300 | fixed_1: u4 = 0b0111, | |
| 301 | imm12: u12, | |
| 302 | fixed_2_and_cond: u12 = 0b1110_0001_0010, | |
| 303 | }, | |
| 304 | ||
| 305 | /// Represents the possible operations which can be performed by a | |
| 306 | /// Data Processing instruction | |
| 307 | const Opcode = enum(u4) { | |
| 308 | // Rd := Op1 AND Op2 | |
| 309 | @"and", | |
| 310 | // Rd := Op1 EOR Op2 | |
| 311 | eor, | |
| 312 | // Rd := Op1 - Op2 | |
| 313 | sub, | |
| 314 | // Rd := Op2 - Op1 | |
| 315 | rsb, | |
| 316 | // Rd := Op1 + Op2 | |
| 317 | add, | |
| 318 | // Rd := Op1 + Op2 + C | |
| 319 | adc, | |
| 320 | // Rd := Op1 - Op2 + C - 1 | |
| 321 | sbc, | |
| 322 | // Rd := Op2 - Op1 + C - 1 | |
| 323 | rsc, | |
| 324 | // set condition codes on Op1 AND Op2 | |
| 325 | tst, | |
| 326 | // set condition codes on Op1 EOR Op2 | |
| 327 | teq, | |
| 328 | // set condition codes on Op1 - Op2 | |
| 329 | cmp, | |
| 330 | // set condition codes on Op1 + Op2 | |
| 331 | cmn, | |
| 332 | // Rd := Op1 OR Op2 | |
| 333 | orr, | |
| 334 | // Rd := Op2 | |
| 335 | mov, | |
| 336 | // Rd := Op1 AND NOT Op2 | |
| 337 | bic, | |
| 338 | // Rd := NOT Op2 | |
| 339 | mvn, | |
| 340 | }; | |
| 341 | ||
| 342 | /// Represents the second operand to a data processing instruction | |
| 343 | /// which can either be content from a register or an immediate | |
| 344 | /// value | |
| 345 | pub const Operand = union(enum) { | |
| 346 | Register: packed struct { | |
| 347 | rm: u4, | |
| 348 | shift: u8, | |
| 349 | }, | |
| 350 | Immediate: packed struct { | |
| 351 | imm: u8, | |
| 352 | rotate: u4, | |
| 353 | }, | |
| 354 | ||
| 355 | /// Represents multiple ways a register can be shifted. A | |
| 356 | /// register can be shifted by a specific immediate value or | |
| 357 | /// by the contents of another register | |
| 358 | pub const Shift = union(enum) { | |
| 359 | Immediate: packed struct { | |
| 360 | fixed: u1 = 0b0, | |
| 361 | typ: u2, | |
| 362 | amount: u5, | |
| 363 | }, | |
| 364 | Register: packed struct { | |
| 365 | fixed_1: u1 = 0b1, | |
| 366 | typ: u2, | |
| 367 | fixed_2: u1 = 0b0, | |
| 368 | rs: u4, | |
| 369 | }, | |
| 370 | ||
| 371 | pub const Type = enum(u2) { | |
| 372 | logical_left, | |
| 373 | logical_right, | |
| 374 | arithmetic_right, | |
| 375 | rotate_right, | |
| 376 | }; | |
| 377 | ||
| 378 | pub const none = Shift{ | |
| 379 | .Immediate = .{ | |
| 380 | .amount = 0, | |
| 381 | .typ = 0, | |
| 382 | }, | |
| 383 | }; | |
| 384 | ||
| 385 | pub fn toU8(self: Shift) u8 { | |
| 386 | return switch (self) { | |
| 387 | .Register => |v| @bitCast(u8, v), | |
| 388 | .Immediate => |v| @bitCast(u8, v), | |
| 389 | }; | |
| 390 | } | |
| 391 | ||
| 392 | pub fn reg(rs: Register, typ: Type) Shift { | |
| 393 | return Shift{ | |
| 394 | .Register = .{ | |
| 395 | .rs = rs.id(), | |
| 396 | .typ = @enumToInt(typ), | |
| 397 | }, | |
| 398 | }; | |
| 399 | } | |
| 400 | ||
| 401 | pub fn imm(amount: u5, typ: Type) Shift { | |
| 402 | return Shift{ | |
| 403 | .Immediate = .{ | |
| 404 | .amount = amount, | |
| 405 | .typ = @enumToInt(typ), | |
| 406 | }, | |
| 407 | }; | |
| 408 | } | |
| 409 | }; | |
| 410 | ||
| 411 | pub fn toU12(self: Operand) u12 { | |
| 412 | return switch (self) { | |
| 413 | .Register => |v| @bitCast(u12, v), | |
| 414 | .Immediate => |v| @bitCast(u12, v), | |
| 415 | }; | |
| 416 | } | |
| 417 | ||
| 418 | pub fn reg(rm: Register, shift: Shift) Operand { | |
| 419 | return Operand{ | |
| 420 | .Register = .{ | |
| 421 | .rm = rm.id(), | |
| 422 | .shift = shift.toU8(), | |
| 423 | }, | |
| 424 | }; | |
| 425 | } | |
| 426 | ||
| 427 | pub fn imm(immediate: u8, rotate: u4) Operand { | |
| 428 | return Operand{ | |
| 429 | .Immediate = .{ | |
| 430 | .imm = immediate, | |
| 431 | .rotate = rotate, | |
| 432 | }, | |
| 433 | }; | |
| 434 | } | |
| 435 | ||
| 436 | /// Tries to convert an unsigned 32 bit integer into an | |
| 437 | /// immediate operand using rotation. Returns null when there | |
| 438 | /// is no conversion | |
| 439 | pub fn fromU32(x: u32) ?Operand { | |
| 440 | const masks = comptime blk: { | |
| 441 | const base_mask: u32 = std.math.maxInt(u8); | |
| 442 | var result = [_]u32{0} ** 16; | |
| 443 | for (result) |*mask, i| mask.* = std.math.rotr(u32, base_mask, 2 * i); | |
| 444 | break :blk result; | |
| 445 | }; | |
| 446 | ||
| 447 | return for (masks) |mask, i| { | |
| 448 | if (x & mask == x) { | |
| 449 | break Operand{ | |
| 450 | .Immediate = .{ | |
| 451 | .imm = @intCast(u8, std.math.rotl(u32, x, 2 * i)), | |
| 452 | .rotate = @intCast(u4, i), | |
| 453 | }, | |
| 454 | }; | |
| 455 | } | |
| 456 | } else null; | |
| 457 | } | |
| 458 | }; | |
| 459 | ||
| 460 | /// Represents the offset operand of a load or store | |
| 461 | /// instruction. Data can be loaded from memory with either an | |
| 462 | /// immediate offset or an offset that is stored in some register. | |
| 463 | pub const Offset = union(enum) { | |
| 464 | Immediate: u12, | |
| 465 | Register: packed struct { | |
| 466 | rm: u4, | |
| 467 | shift: u8, | |
| 468 | }, | |
| 469 | ||
| 470 | pub const none = Offset{ | |
| 471 | .Immediate = 0, | |
| 472 | }; | |
| 473 | ||
| 474 | pub fn toU12(self: Offset) u12 { | |
| 475 | return switch (self) { | |
| 476 | .Register => |v| @bitCast(u12, v), | |
| 477 | .Immediate => |v| v, | |
| 478 | }; | |
| 479 | } | |
| 480 | ||
| 481 | pub fn reg(rm: Register, shift: u8) Offset { | |
| 482 | return Offset{ | |
| 483 | .Register = .{ | |
| 484 | .rm = rm.id(), | |
| 485 | .shift = shift, | |
| 486 | }, | |
| 487 | }; | |
| 488 | } | |
| 489 | ||
| 490 | pub fn imm(immediate: u12) Offset { | |
| 491 | return Offset{ | |
| 492 | .Immediate = immediate, | |
| 493 | }; | |
| 494 | } | |
| 495 | }; | |
| 496 | ||
| 497 | /// Represents the offset operand of an extra load or store | |
| 498 | /// instruction. | |
| 499 | pub const ExtraLoadStoreOffset = union(enum) { | |
| 500 | immediate: u8, | |
| 501 | register: u4, | |
| 502 | ||
| 503 | pub const none = ExtraLoadStoreOffset{ | |
| 504 | .immediate = 0, | |
| 505 | }; | |
| 506 | ||
| 507 | pub fn reg(register: Register) ExtraLoadStoreOffset { | |
| 508 | return ExtraLoadStoreOffset{ | |
| 509 | .register = register.id(), | |
| 510 | }; | |
| 511 | } | |
| 512 | ||
| 513 | pub fn imm(immediate: u8) ExtraLoadStoreOffset { | |
| 514 | return ExtraLoadStoreOffset{ | |
| 515 | .immediate = immediate, | |
| 516 | }; | |
| 517 | } | |
| 518 | }; | |
| 519 | ||
| 520 | /// Represents the register list operand to a block data transfer | |
| 521 | /// instruction | |
| 522 | pub const RegisterList = packed struct { | |
| 523 | r0: bool = false, | |
| 524 | r1: bool = false, | |
| 525 | r2: bool = false, | |
| 526 | r3: bool = false, | |
| 527 | r4: bool = false, | |
| 528 | r5: bool = false, | |
| 529 | r6: bool = false, | |
| 530 | r7: bool = false, | |
| 531 | r8: bool = false, | |
| 532 | r9: bool = false, | |
| 533 | r10: bool = false, | |
| 534 | r11: bool = false, | |
| 535 | r12: bool = false, | |
| 536 | r13: bool = false, | |
| 537 | r14: bool = false, | |
| 538 | r15: bool = false, | |
| 539 | }; | |
| 540 | ||
| 541 | pub fn toU32(self: Instruction) u32 { | |
| 542 | return switch (self) { | |
| 543 | .data_processing => |v| @bitCast(u32, v), | |
| 544 | .multiply => |v| @bitCast(u32, v), | |
| 545 | .multiply_long => |v| @bitCast(u32, v), | |
| 546 | .integer_saturating_arithmetic => |v| @bitCast(u32, v), | |
| 547 | .single_data_transfer => |v| @bitCast(u32, v), | |
| 548 | .extra_load_store => |v| @bitCast(u32, v), | |
| 549 | .block_data_transfer => |v| @bitCast(u32, v), | |
| 550 | .branch => |v| @bitCast(u32, v), | |
| 551 | .branch_exchange => |v| @bitCast(u32, v), | |
| 552 | .supervisor_call => |v| @bitCast(u32, v), | |
| 553 | .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), | |
| 554 | }; | |
| 555 | } | |
| 556 | ||
| 557 | // Helper functions for the "real" functions below | |
| 558 | ||
| 559 | fn dataProcessing( | |
| 560 | cond: Condition, | |
| 561 | opcode: Opcode, | |
| 562 | s: u1, | |
| 563 | rd: Register, | |
| 564 | rn: Register, | |
| 565 | op2: Operand, | |
| 566 | ) Instruction { | |
| 567 | return Instruction{ | |
| 568 | .data_processing = .{ | |
| 569 | .cond = @enumToInt(cond), | |
| 570 | .i = @boolToInt(op2 == .Immediate), | |
| 571 | .opcode = @enumToInt(opcode), | |
| 572 | .s = s, | |
| 573 | .rn = rn.id(), | |
| 574 | .rd = rd.id(), | |
| 575 | .op2 = op2.toU12(), | |
| 576 | }, | |
| 577 | }; | |
| 578 | } | |
| 579 | ||
| 580 | fn specialMov( | |
| 581 | cond: Condition, | |
| 582 | rd: Register, | |
| 583 | imm: u16, | |
| 584 | top: bool, | |
| 585 | ) Instruction { | |
| 586 | return Instruction{ | |
| 587 | .data_processing = .{ | |
| 588 | .cond = @enumToInt(cond), | |
| 589 | .i = 1, | |
| 590 | .opcode = if (top) 0b1010 else 0b1000, | |
| 591 | .s = 0, | |
| 592 | .rn = @truncate(u4, imm >> 12), | |
| 593 | .rd = rd.id(), | |
| 594 | .op2 = @truncate(u12, imm), | |
| 595 | }, | |
| 596 | }; | |
| 597 | } | |
| 598 | ||
| 599 | fn multiply( | |
| 600 | cond: Condition, | |
| 601 | set_cond: u1, | |
| 602 | rd: Register, | |
| 603 | rn: Register, | |
| 604 | rm: Register, | |
| 605 | ra: ?Register, | |
| 606 | ) Instruction { | |
| 607 | return Instruction{ | |
| 608 | .multiply = .{ | |
| 609 | .cond = @enumToInt(cond), | |
| 610 | .accumulate = @boolToInt(ra != null), | |
| 611 | .set_cond = set_cond, | |
| 612 | .rd = rd.id(), | |
| 613 | .rn = rn.id(), | |
| 614 | .ra = if (ra) |reg| reg.id() else 0b0000, | |
| 615 | .rm = rm.id(), | |
| 616 | }, | |
| 617 | }; | |
| 618 | } | |
| 619 | ||
| 620 | fn multiplyLong( | |
| 621 | cond: Condition, | |
| 622 | signed: u1, | |
| 623 | accumulate: u1, | |
| 624 | set_cond: u1, | |
| 625 | rdhi: Register, | |
| 626 | rdlo: Register, | |
| 627 | rm: Register, | |
| 628 | rn: Register, | |
| 629 | ) Instruction { | |
| 630 | return Instruction{ | |
| 631 | .multiply_long = .{ | |
| 632 | .cond = @enumToInt(cond), | |
| 633 | .unsigned = signed, | |
| 634 | .accumulate = accumulate, | |
| 635 | .set_cond = set_cond, | |
| 636 | .rdlo = rdlo.id(), | |
| 637 | .rdhi = rdhi.id(), | |
| 638 | .rn = rn.id(), | |
| 639 | .rm = rm.id(), | |
| 640 | }, | |
| 641 | }; | |
| 642 | } | |
| 643 | ||
| 644 | fn integerSaturationArithmetic( | |
| 645 | cond: Condition, | |
| 646 | rd: Register, | |
| 647 | rm: Register, | |
| 648 | rn: Register, | |
| 649 | opc: u2, | |
| 650 | ) Instruction { | |
| 651 | return Instruction{ | |
| 652 | .integer_saturating_arithmetic = .{ | |
| 653 | .rm = rm.id(), | |
| 654 | .rd = rd.id(), | |
| 655 | .rn = rn.id(), | |
| 656 | .opc = opc, | |
| 657 | .cond = @enumToInt(cond), | |
| 658 | }, | |
| 659 | }; | |
| 660 | } | |
| 661 | ||
| 662 | fn singleDataTransfer( | |
| 663 | cond: Condition, | |
| 664 | rd: Register, | |
| 665 | rn: Register, | |
| 666 | offset: Offset, | |
| 667 | pre_index: bool, | |
| 668 | positive: bool, | |
| 669 | byte_word: u1, | |
| 670 | write_back: bool, | |
| 671 | load_store: u1, | |
| 672 | ) Instruction { | |
| 673 | return Instruction{ | |
| 674 | .single_data_transfer = .{ | |
| 675 | .cond = @enumToInt(cond), | |
| 676 | .rn = rn.id(), | |
| 677 | .rd = rd.id(), | |
| 678 | .offset = offset.toU12(), | |
| 679 | .load_store = load_store, | |
| 680 | .write_back = @boolToInt(write_back), | |
| 681 | .byte_word = byte_word, | |
| 682 | .up_down = @boolToInt(positive), | |
| 683 | .pre_post = @boolToInt(pre_index), | |
| 684 | .imm = @boolToInt(offset != .Immediate), | |
| 685 | }, | |
| 686 | }; | |
| 687 | } | |
| 688 | ||
| 689 | fn extraLoadStore( | |
| 690 | cond: Condition, | |
| 691 | pre_index: bool, | |
| 692 | positive: bool, | |
| 693 | write_back: bool, | |
| 694 | o1: u1, | |
| 695 | op2: u2, | |
| 696 | rn: Register, | |
| 697 | rt: Register, | |
| 698 | offset: ExtraLoadStoreOffset, | |
| 699 | ) Instruction { | |
| 700 | const imm4l: u4 = switch (offset) { | |
| 701 | .immediate => |imm| @truncate(u4, imm), | |
| 702 | .register => |reg| reg, | |
| 703 | }; | |
| 704 | const imm4h: u4 = switch (offset) { | |
| 705 | .immediate => |imm| @truncate(u4, imm >> 4), | |
| 706 | .register => 0b0000, | |
| 707 | }; | |
| 708 | ||
| 709 | return Instruction{ | |
| 710 | .extra_load_store = .{ | |
| 711 | .imm4l = imm4l, | |
| 712 | .op2 = op2, | |
| 713 | .imm4h = imm4h, | |
| 714 | .rt = rt.id(), | |
| 715 | .rn = rn.id(), | |
| 716 | .o1 = o1, | |
| 717 | .write_back = @boolToInt(write_back), | |
| 718 | .imm = @boolToInt(offset == .immediate), | |
| 719 | .up_down = @boolToInt(positive), | |
| 720 | .pre_index = @boolToInt(pre_index), | |
| 721 | .cond = @enumToInt(cond), | |
| 722 | }, | |
| 723 | }; | |
| 724 | } | |
| 725 | ||
| 726 | fn blockDataTransfer( | |
| 727 | cond: Condition, | |
| 728 | rn: Register, | |
| 729 | reg_list: RegisterList, | |
| 730 | pre_post: u1, | |
| 731 | up_down: u1, | |
| 732 | psr_or_user: u1, | |
| 733 | write_back: bool, | |
| 734 | load_store: u1, | |
| 735 | ) Instruction { | |
| 736 | return Instruction{ | |
| 737 | .block_data_transfer = .{ | |
| 738 | .register_list = @bitCast(u16, reg_list), | |
| 739 | .rn = rn.id(), | |
| 740 | .load_store = load_store, | |
| 741 | .write_back = @boolToInt(write_back), | |
| 742 | .psr_or_user = psr_or_user, | |
| 743 | .up_down = up_down, | |
| 744 | .pre_post = pre_post, | |
| 745 | .cond = @enumToInt(cond), | |
| 746 | }, | |
| 747 | }; | |
| 748 | } | |
| 749 | ||
| 750 | fn branch(cond: Condition, offset: i26, link: u1) Instruction { | |
| 751 | return Instruction{ | |
| 752 | .branch = .{ | |
| 753 | .cond = @enumToInt(cond), | |
| 754 | .link = link, | |
| 755 | .offset = @bitCast(u24, @intCast(i24, offset >> 2)), | |
| 756 | }, | |
| 757 | }; | |
| 758 | } | |
| 759 | ||
| 760 | fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction { | |
| 761 | return Instruction{ | |
| 762 | .branch_exchange = .{ | |
| 763 | .cond = @enumToInt(cond), | |
| 764 | .link = link, | |
| 765 | .rn = rn.id(), | |
| 766 | }, | |
| 767 | }; | |
| 768 | } | |
| 769 | ||
| 770 | fn supervisorCall(cond: Condition, comment: u24) Instruction { | |
| 771 | return Instruction{ | |
| 772 | .supervisor_call = .{ | |
| 773 | .cond = @enumToInt(cond), | |
| 774 | .comment = comment, | |
| 775 | }, | |
| 776 | }; | |
| 777 | } | |
| 778 | ||
| 779 | fn breakpoint(imm: u16) Instruction { | |
| 780 | return Instruction{ | |
| 781 | .breakpoint = .{ | |
| 782 | .imm12 = @truncate(u12, imm >> 4), | |
| 783 | .imm4 = @truncate(u4, imm), | |
| 784 | }, | |
| 785 | }; | |
| 786 | } | |
| 787 | ||
| 788 | // Public functions replicating assembler syntax as closely as | |
| 789 | // possible | |
| 790 | ||
| 791 | // Data processing | |
| 792 | ||
| 793 | pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 794 | return dataProcessing(cond, .@"and", 0, rd, rn, op2); | |
| 795 | } | |
| 796 | ||
| 797 | pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 798 | return dataProcessing(cond, .@"and", 1, rd, rn, op2); | |
| 799 | } | |
| 800 | ||
| 801 | pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 802 | return dataProcessing(cond, .eor, 0, rd, rn, op2); | |
| 803 | } | |
| 804 | ||
| 805 | pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 806 | return dataProcessing(cond, .eor, 1, rd, rn, op2); | |
| 807 | } | |
| 808 | ||
| 809 | pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 810 | return dataProcessing(cond, .sub, 0, rd, rn, op2); | |
| 811 | } | |
| 812 | ||
| 813 | pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 814 | return dataProcessing(cond, .sub, 1, rd, rn, op2); | |
| 815 | } | |
| 816 | ||
| 817 | pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 818 | return dataProcessing(cond, .rsb, 0, rd, rn, op2); | |
| 819 | } | |
| 820 | ||
| 821 | pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 822 | return dataProcessing(cond, .rsb, 1, rd, rn, op2); | |
| 823 | } | |
| 824 | ||
| 825 | pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 826 | return dataProcessing(cond, .add, 0, rd, rn, op2); | |
| 827 | } | |
| 828 | ||
| 829 | pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 830 | return dataProcessing(cond, .add, 1, rd, rn, op2); | |
| 831 | } | |
| 832 | ||
| 833 | pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 834 | return dataProcessing(cond, .adc, 0, rd, rn, op2); | |
| 835 | } | |
| 836 | ||
| 837 | pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 838 | return dataProcessing(cond, .adc, 1, rd, rn, op2); | |
| 839 | } | |
| 840 | ||
| 841 | pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 842 | return dataProcessing(cond, .sbc, 0, rd, rn, op2); | |
| 843 | } | |
| 844 | ||
| 845 | pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 846 | return dataProcessing(cond, .sbc, 1, rd, rn, op2); | |
| 847 | } | |
| 848 | ||
| 849 | pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 850 | return dataProcessing(cond, .rsc, 0, rd, rn, op2); | |
| 851 | } | |
| 852 | ||
| 853 | pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 854 | return dataProcessing(cond, .rsc, 1, rd, rn, op2); | |
| 855 | } | |
| 856 | ||
| 857 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { | |
| 858 | return dataProcessing(cond, .tst, 1, .r0, rn, op2); | |
| 859 | } | |
| 860 | ||
| 861 | pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction { | |
| 862 | return dataProcessing(cond, .teq, 1, .r0, rn, op2); | |
| 863 | } | |
| 864 | ||
| 865 | pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction { | |
| 866 | return dataProcessing(cond, .cmp, 1, .r0, rn, op2); | |
| 867 | } | |
| 868 | ||
| 869 | pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction { | |
| 870 | return dataProcessing(cond, .cmn, 1, .r0, rn, op2); | |
| 871 | } | |
| 872 | ||
| 873 | pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 874 | return dataProcessing(cond, .orr, 0, rd, rn, op2); | |
| 875 | } | |
| 876 | ||
| 877 | pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 878 | return dataProcessing(cond, .orr, 1, rd, rn, op2); | |
| 879 | } | |
| 880 | ||
| 881 | pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 882 | return dataProcessing(cond, .mov, 0, rd, .r0, op2); | |
| 883 | } | |
| 884 | ||
| 885 | pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 886 | return dataProcessing(cond, .mov, 1, rd, .r0, op2); | |
| 887 | } | |
| 888 | ||
| 889 | pub fn bic(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 890 | return dataProcessing(cond, .bic, 0, rd, rn, op2); | |
| 891 | } | |
| 892 | ||
| 893 | pub fn bics(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | |
| 894 | return dataProcessing(cond, .bic, 1, rd, rn, op2); | |
| 895 | } | |
| 896 | ||
| 897 | pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 898 | return dataProcessing(cond, .mvn, 0, rd, .r0, op2); | |
| 899 | } | |
| 900 | ||
| 901 | pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction { | |
| 902 | return dataProcessing(cond, .mvn, 1, rd, .r0, op2); | |
| 903 | } | |
| 904 | ||
| 905 | // Integer Saturating Arithmetic | |
| 906 | ||
| 907 | pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | |
| 908 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b00); | |
| 909 | } | |
| 910 | ||
| 911 | pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | |
| 912 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b01); | |
| 913 | } | |
| 914 | ||
| 915 | pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | |
| 916 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b10); | |
| 917 | } | |
| 918 | ||
| 919 | pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { | |
| 920 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b11); | |
| 921 | } | |
| 922 | ||
| 923 | // movw and movt | |
| 924 | ||
| 925 | pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction { | |
| 926 | return specialMov(cond, rd, imm, false); | |
| 927 | } | |
| 928 | ||
| 929 | pub fn movt(cond: Condition, rd: Register, imm: u16) Instruction { | |
| 930 | return specialMov(cond, rd, imm, true); | |
| 931 | } | |
| 932 | ||
| 933 | // PSR transfer | |
| 934 | ||
| 935 | pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { | |
| 936 | return Instruction{ | |
| 937 | .data_processing = .{ | |
| 938 | .cond = @enumToInt(cond), | |
| 939 | .i = 0, | |
| 940 | .opcode = if (psr == .spsr) 0b1010 else 0b1000, | |
| 941 | .s = 0, | |
| 942 | .rn = 0b1111, | |
| 943 | .rd = rd.id(), | |
| 944 | .op2 = 0b0000_0000_0000, | |
| 945 | }, | |
| 946 | }; | |
| 947 | } | |
| 948 | ||
| 949 | pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction { | |
| 950 | return Instruction{ | |
| 951 | .data_processing = .{ | |
| 952 | .cond = @enumToInt(cond), | |
| 953 | .i = 0, | |
| 954 | .opcode = if (psr == .spsr) 0b1011 else 0b1001, | |
| 955 | .s = 0, | |
| 956 | .rn = 0b1111, | |
| 957 | .rd = 0b1111, | |
| 958 | .op2 = op.toU12(), | |
| 959 | }, | |
| 960 | }; | |
| 961 | } | |
| 962 | ||
| 963 | // Multiply | |
| 964 | ||
| 965 | pub fn mul(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { | |
| 966 | return multiply(cond, 0, rd, rn, rm, null); | |
| 967 | } | |
| 968 | ||
| 969 | pub fn muls(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { | |
| 970 | return multiply(cond, 1, rd, rn, rm, null); | |
| 971 | } | |
| 972 | ||
| 973 | pub fn mla(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { | |
| 974 | return multiply(cond, 0, rd, rn, rm, ra); | |
| 975 | } | |
| 976 | ||
| 977 | pub fn mlas(cond: Condition, rd: Register, rn: Register, rm: Register, ra: Register) Instruction { | |
| 978 | return multiply(cond, 1, rd, rn, rm, ra); | |
| 979 | } | |
| 980 | ||
| 981 | // Multiply long | |
| 982 | ||
| 983 | pub fn umull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 984 | return multiplyLong(cond, 0, 0, 0, rdhi, rdlo, rm, rn); | |
| 985 | } | |
| 986 | ||
| 987 | pub fn umulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 988 | return multiplyLong(cond, 0, 0, 1, rdhi, rdlo, rm, rn); | |
| 989 | } | |
| 990 | ||
| 991 | pub fn umlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 992 | return multiplyLong(cond, 0, 1, 0, rdhi, rdlo, rm, rn); | |
| 993 | } | |
| 994 | ||
| 995 | pub fn umlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 996 | return multiplyLong(cond, 0, 1, 1, rdhi, rdlo, rm, rn); | |
| 997 | } | |
| 998 | ||
| 999 | pub fn smull(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 1000 | return multiplyLong(cond, 1, 0, 0, rdhi, rdlo, rm, rn); | |
| 1001 | } | |
| 1002 | ||
| 1003 | pub fn smulls(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 1004 | return multiplyLong(cond, 1, 0, 1, rdhi, rdlo, rm, rn); | |
| 1005 | } | |
| 1006 | ||
| 1007 | pub fn smlal(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 1008 | return multiplyLong(cond, 1, 1, 0, rdhi, rdlo, rm, rn); | |
| 1009 | } | |
| 1010 | ||
| 1011 | pub fn smlals(cond: Condition, rdlo: Register, rdhi: Register, rn: Register, rm: Register) Instruction { | |
| 1012 | return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn); | |
| 1013 | } | |
| 1014 | ||
| 1015 | // Single data transfer | |
| 1016 | ||
| 1017 | pub const OffsetArgs = struct { | |
| 1018 | pre_index: bool = true, | |
| 1019 | positive: bool = true, | |
| 1020 | offset: Offset, | |
| 1021 | write_back: bool = false, | |
| 1022 | }; | |
| 1023 | ||
| 1024 | pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | |
| 1025 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1); | |
| 1026 | } | |
| 1027 | ||
| 1028 | pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | |
| 1029 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1); | |
| 1030 | } | |
| 1031 | ||
| 1032 | pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | |
| 1033 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0); | |
| 1034 | } | |
| 1035 | ||
| 1036 | pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | |
| 1037 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0); | |
| 1038 | } | |
| 1039 | ||
| 1040 | // Extra load/store | |
| 1041 | ||
| 1042 | pub const ExtraLoadStoreOffsetArgs = struct { | |
| 1043 | pre_index: bool = true, | |
| 1044 | positive: bool = true, | |
| 1045 | offset: ExtraLoadStoreOffset, | |
| 1046 | write_back: bool = false, | |
| 1047 | }; | |
| 1048 | ||
| 1049 | pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | |
| 1050 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0, 0b01, rn, rt, args.offset); | |
| 1051 | } | |
| 1052 | ||
| 1053 | pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | |
| 1054 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 1, 0b01, rn, rt, args.offset); | |
| 1055 | } | |
| 1056 | ||
| 1057 | // Block data transfer | |
| 1058 | ||
| 1059 | pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1060 | return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1); | |
| 1061 | } | |
| 1062 | ||
| 1063 | pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1064 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1); | |
| 1065 | } | |
| 1066 | ||
| 1067 | pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1068 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1); | |
| 1069 | } | |
| 1070 | ||
| 1071 | pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1072 | return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1); | |
| 1073 | } | |
| 1074 | ||
| 1075 | pub const ldmfa = ldmda; | |
| 1076 | pub const ldmea = ldmdb; | |
| 1077 | pub const ldmed = ldmib; | |
| 1078 | pub const ldmfd = ldmia; | |
| 1079 | pub const ldm = ldmia; | |
| 1080 | ||
| 1081 | pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1082 | return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0); | |
| 1083 | } | |
| 1084 | ||
| 1085 | pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1086 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0); | |
| 1087 | } | |
| 1088 | ||
| 1089 | pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1090 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0); | |
| 1091 | } | |
| 1092 | ||
| 1093 | pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | |
| 1094 | return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0); | |
| 1095 | } | |
| 1096 | ||
| 1097 | pub const stmed = stmda; | |
| 1098 | pub const stmfd = stmdb; | |
| 1099 | pub const stmfa = stmib; | |
| 1100 | pub const stmea = stmia; | |
| 1101 | pub const stm = stmia; | |
| 1102 | ||
| 1103 | // Branch | |
| 1104 | ||
| 1105 | pub fn b(cond: Condition, offset: i26) Instruction { | |
| 1106 | return branch(cond, offset, 0); | |
| 1107 | } | |
| 1108 | ||
| 1109 | pub fn bl(cond: Condition, offset: i26) Instruction { | |
| 1110 | return branch(cond, offset, 1); | |
| 1111 | } | |
| 1112 | ||
| 1113 | // Branch and exchange | |
| 1114 | ||
| 1115 | pub fn bx(cond: Condition, rn: Register) Instruction { | |
| 1116 | return branchExchange(cond, rn, 0); | |
| 1117 | } | |
| 1118 | ||
| 1119 | pub fn blx(cond: Condition, rn: Register) Instruction { | |
| 1120 | return branchExchange(cond, rn, 1); | |
| 1121 | } | |
| 1122 | ||
| 1123 | // Supervisor Call | |
| 1124 | ||
| 1125 | pub const swi = svc; | |
| 1126 | ||
| 1127 | pub fn svc(cond: Condition, comment: u24) Instruction { | |
| 1128 | return supervisorCall(cond, comment); | |
| 1129 | } | |
| 1130 | ||
| 1131 | // Breakpoint | |
| 1132 | ||
| 1133 | pub fn bkpt(imm: u16) Instruction { | |
| 1134 | return breakpoint(imm); | |
| 1135 | } | |
| 1136 | ||
| 1137 | // Aliases | |
| 1138 | ||
| 1139 | pub fn nop() Instruction { | |
| 1140 | return mov(.al, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)); | |
| 1141 | } | |
| 1142 | ||
| 1143 | pub fn pop(cond: Condition, args: anytype) Instruction { | |
| 1144 | if (@typeInfo(@TypeOf(args)) != .Struct) { | |
| 1145 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); | |
| 1146 | } | |
| 1147 | ||
| 1148 | if (args.len < 1) { | |
| 1149 | @compileError("Expected at least one register"); | |
| 1150 | } else if (args.len == 1) { | |
| 1151 | const reg = args[0]; | |
| 1152 | return ldr(cond, reg, .sp, .{ | |
| 1153 | .pre_index = false, | |
| 1154 | .positive = true, | |
| 1155 | .offset = Offset.imm(4), | |
| 1156 | .write_back = false, | |
| 1157 | }); | |
| 1158 | } else { | |
| 1159 | var register_list: u16 = 0; | |
| 1160 | inline for (args) |arg| { | |
| 1161 | const reg = @as(Register, arg); | |
| 1162 | register_list |= @as(u16, 1) << reg.id(); | |
| 1163 | } | |
| 1164 | return ldm(cond, .sp, true, @bitCast(RegisterList, register_list)); | |
| 1165 | } | |
| 1166 | } | |
| 1167 | ||
| 1168 | pub fn push(cond: Condition, args: anytype) Instruction { | |
| 1169 | if (@typeInfo(@TypeOf(args)) != .Struct) { | |
| 1170 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); | |
| 1171 | } | |
| 1172 | ||
| 1173 | if (args.len < 1) { | |
| 1174 | @compileError("Expected at least one register"); | |
| 1175 | } else if (args.len == 1) { | |
| 1176 | const reg = args[0]; | |
| 1177 | return str(cond, reg, .sp, .{ | |
| 1178 | .pre_index = true, | |
| 1179 | .positive = false, | |
| 1180 | .offset = Offset.imm(4), | |
| 1181 | .write_back = true, | |
| 1182 | }); | |
| 1183 | } else { | |
| 1184 | var register_list: u16 = 0; | |
| 1185 | inline for (args) |arg| { | |
| 1186 | const reg = @as(Register, arg); | |
| 1187 | register_list |= @as(u16, 1) << reg.id(); | |
| 1188 | } | |
| 1189 | return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list)); | |
| 1190 | } | |
| 1191 | } | |
| 1192 | ||
| 1193 | pub const ShiftAmount = union(enum) { | |
| 1194 | immediate: u5, | |
| 1195 | register: Register, | |
| 1196 | ||
| 1197 | pub fn imm(immediate: u5) ShiftAmount { | |
| 1198 | return .{ | |
| 1199 | .immediate = immediate, | |
| 1200 | }; | |
| 1201 | } | |
| 1202 | ||
| 1203 | pub fn reg(register: Register) ShiftAmount { | |
| 1204 | return .{ | |
| 1205 | .register = register, | |
| 1206 | }; | |
| 1207 | } | |
| 1208 | }; | |
| 1209 | ||
| 1210 | pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1211 | return switch (shift) { | |
| 1212 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), | |
| 1213 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), | |
| 1214 | }; | |
| 1215 | } | |
| 1216 | ||
| 1217 | pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1218 | return switch (shift) { | |
| 1219 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), | |
| 1220 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), | |
| 1221 | }; | |
| 1222 | } | |
| 1223 | ||
| 1224 | pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1225 | return switch (shift) { | |
| 1226 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), | |
| 1227 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), | |
| 1228 | }; | |
| 1229 | } | |
| 1230 | ||
| 1231 | pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1232 | return switch (shift) { | |
| 1233 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), | |
| 1234 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), | |
| 1235 | }; | |
| 1236 | } | |
| 1237 | ||
| 1238 | pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1239 | return switch (shift) { | |
| 1240 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), | |
| 1241 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), | |
| 1242 | }; | |
| 1243 | } | |
| 1244 | ||
| 1245 | pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1246 | return switch (shift) { | |
| 1247 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), | |
| 1248 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), | |
| 1249 | }; | |
| 1250 | } | |
| 1251 | ||
| 1252 | pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1253 | return switch (shift) { | |
| 1254 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), | |
| 1255 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), | |
| 1256 | }; | |
| 1257 | } | |
| 1258 | ||
| 1259 | pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1260 | return switch (shift) { | |
| 1261 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), | |
| 1262 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), | |
| 1263 | }; | |
| 1264 | } | |
| 1265 | }; | |
| 1266 | ||
| 1267 | test "serialize instructions" { | |
| 1268 | const Testcase = struct { | |
| 1269 | inst: Instruction, | |
| 1270 | expected: u32, | |
| 1271 | }; | |
| 1272 | ||
| 1273 | const testcases = [_]Testcase{ | |
| 1274 | .{ // add r0, r0, r0 | |
| 1275 | .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), | |
| 1276 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, | |
| 1277 | }, | |
| 1278 | .{ // mov r4, r2 | |
| 1279 | .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), | |
| 1280 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, | |
| 1281 | }, | |
| 1282 | .{ // mov r0, #42 | |
| 1283 | .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)), | |
| 1284 | .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, | |
| 1285 | }, | |
| 1286 | .{ // mrs r5, cpsr | |
| 1287 | .inst = Instruction.mrs(.al, .r5, .cpsr), | |
| 1288 | .expected = 0b1110_00010_0_001111_0101_000000000000, | |
| 1289 | }, | |
| 1290 | .{ // mul r0, r1, r2 | |
| 1291 | .inst = Instruction.mul(.al, .r0, .r1, .r2), | |
| 1292 | .expected = 0b1110_000000_0_0_0000_0000_0010_1001_0001, | |
| 1293 | }, | |
| 1294 | .{ // umlal r0, r1, r5, r6 | |
| 1295 | .inst = Instruction.umlal(.al, .r0, .r1, .r5, .r6), | |
| 1296 | .expected = 0b1110_00001_0_1_0_0001_0000_0110_1001_0101, | |
| 1297 | }, | |
| 1298 | .{ // ldr r0, [r2, #42] | |
| 1299 | .inst = Instruction.ldr(.al, .r0, .r2, .{ | |
| 1300 | .offset = Instruction.Offset.imm(42), | |
| 1301 | }), | |
| 1302 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, | |
| 1303 | }, | |
| 1304 | .{ // str r0, [r3] | |
| 1305 | .inst = Instruction.str(.al, .r0, .r3, .{ | |
| 1306 | .offset = Instruction.Offset.none, | |
| 1307 | }), | |
| 1308 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, | |
| 1309 | }, | |
| 1310 | .{ // strh r1, [r5] | |
| 1311 | .inst = Instruction.strh(.al, .r1, .r5, .{ | |
| 1312 | .offset = Instruction.ExtraLoadStoreOffset.none, | |
| 1313 | }), | |
| 1314 | .expected = 0b1110_000_1_1_1_0_0_0101_0001_0000_1011_0000, | |
| 1315 | }, | |
| 1316 | .{ // b #12 | |
| 1317 | .inst = Instruction.b(.al, 12), | |
| 1318 | .expected = 0b1110_101_0_0000_0000_0000_0000_0000_0011, | |
| 1319 | }, | |
| 1320 | .{ // bl #-4 | |
| 1321 | .inst = Instruction.bl(.al, -4), | |
| 1322 | .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1111, | |
| 1323 | }, | |
| 1324 | .{ // bx lr | |
| 1325 | .inst = Instruction.bx(.al, .lr), | |
| 1326 | .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110, | |
| 1327 | }, | |
| 1328 | .{ // svc #0 | |
| 1329 | .inst = Instruction.svc(.al, 0), | |
| 1330 | .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000, | |
| 1331 | }, | |
| 1332 | .{ // bkpt #42 | |
| 1333 | .inst = Instruction.bkpt(42), | |
| 1334 | .expected = 0b1110_0001_0010_000000000010_0111_1010, | |
| 1335 | }, | |
| 1336 | .{ // stmdb r9, {r0} | |
| 1337 | .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }), | |
| 1338 | .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001, | |
| 1339 | }, | |
| 1340 | .{ // ldmea r4!, {r2, r5} | |
| 1341 | .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }), | |
| 1342 | .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100, | |
| 1343 | }, | |
| 1344 | .{ // qadd r0, r7, r8 | |
| 1345 | .inst = Instruction.qadd(.al, .r0, .r7, .r8), | |
| 1346 | .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111, | |
| 1347 | }, | |
| 1348 | }; | |
| 1349 | ||
| 1350 | for (testcases) |case| { | |
| 1351 | const actual = case.inst.toU32(); | |
| 1352 | try testing.expectEqual(case.expected, actual); | |
| 1353 | } | |
| 1354 | } | |
| 1355 | ||
| 1356 | test "aliases" { | |
| 1357 | const Testcase = struct { | |
| 1358 | expected: Instruction, | |
| 1359 | actual: Instruction, | |
| 1360 | }; | |
| 1361 | ||
| 1362 | const testcases = [_]Testcase{ | |
| 1363 | .{ // pop { r6 } | |
| 1364 | .actual = Instruction.pop(.al, .{.r6}), | |
| 1365 | .expected = Instruction.ldr(.al, .r6, .sp, .{ | |
| 1366 | .pre_index = false, | |
| 1367 | .positive = true, | |
| 1368 | .offset = Instruction.Offset.imm(4), | |
| 1369 | .write_back = false, | |
| 1370 | }), | |
| 1371 | }, | |
| 1372 | .{ // pop { r1, r5 } | |
| 1373 | .actual = Instruction.pop(.al, .{ .r1, .r5 }), | |
| 1374 | .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }), | |
| 1375 | }, | |
| 1376 | .{ // push { r3 } | |
| 1377 | .actual = Instruction.push(.al, .{.r3}), | |
| 1378 | .expected = Instruction.str(.al, .r3, .sp, .{ | |
| 1379 | .pre_index = true, | |
| 1380 | .positive = false, | |
| 1381 | .offset = Instruction.Offset.imm(4), | |
| 1382 | .write_back = true, | |
| 1383 | }), | |
| 1384 | }, | |
| 1385 | .{ // push { r0, r2 } | |
| 1386 | .actual = Instruction.push(.al, .{ .r0, .r2 }), | |
| 1387 | .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }), | |
| 1388 | }, | |
| 1389 | .{ // lsl r4, r5, #5 | |
| 1390 | .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)), | |
| 1391 | .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg( | |
| 1392 | .r5, | |
| 1393 | Instruction.Operand.Shift.imm(5, .logical_left), | |
| 1394 | )), | |
| 1395 | }, | |
| 1396 | .{ // asrs r1, r1, r3 | |
| 1397 | .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)), | |
| 1398 | .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg( | |
| 1399 | .r1, | |
| 1400 | Instruction.Operand.Shift.reg(.r3, .arithmetic_right), | |
| 1401 | )), | |
| 1402 | }, | |
| 1403 | }; | |
| 1404 | ||
| 1405 | for (testcases) |case| { | |
| 1406 | try testing.expectEqual(case.expected.toU32(), case.actual.toU32()); | |
| 1407 | } | |
| 1408 | } |
src/codegen/c.zig+844-487| ... | ... | @@ -91,55 +91,76 @@ pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { |
| 91 | 91 | return .{ .data = ident }; |
| 92 | 92 | } |
| 93 | 93 | |
| 94 | /// This data is available when outputting .c code for a Module. | |
| 94 | /// This data is available when outputting .c code for a `*Module.Fn`. | |
| 95 | 95 | /// It is not available when generating .h file. |
| 96 | pub const Object = struct { | |
| 97 | dg: DeclGen, | |
| 96 | pub const Function = struct { | |
| 98 | 97 | air: Air, |
| 99 | 98 | liveness: Liveness, |
| 100 | gpa: *mem.Allocator, | |
| 101 | code: std.ArrayList(u8), | |
| 102 | 99 | value_map: CValueMap, |
| 103 | 100 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 104 | 101 | next_arg_index: usize = 0, |
| 105 | 102 | next_local_index: usize = 0, |
| 106 | 103 | next_block_index: usize = 0, |
| 107 | indent_writer: IndentWriter(std.ArrayList(u8).Writer), | |
| 104 | object: Object, | |
| 105 | func: *Module.Fn, | |
| 108 | 106 | |
| 109 | fn resolveInst(o: *Object, inst: Air.Inst.Ref) !CValue { | |
| 110 | if (o.air.value(inst)) |_| { | |
| 107 | fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue { | |
| 108 | if (f.air.value(inst)) |_| { | |
| 111 | 109 | return CValue{ .constant = inst }; |
| 112 | 110 | } |
| 113 | 111 | const index = Air.refToIndex(inst).?; |
| 114 | return o.value_map.get(index).?; // Assertion means instruction does not dominate usage. | |
| 112 | return f.value_map.get(index).?; // Assertion means instruction does not dominate usage. | |
| 115 | 113 | } |
| 116 | 114 | |
| 117 | fn allocLocalValue(o: *Object) CValue { | |
| 118 | const result = o.next_local_index; | |
| 119 | o.next_local_index += 1; | |
| 115 | fn allocLocalValue(f: *Function) CValue { | |
| 116 | const result = f.next_local_index; | |
| 117 | f.next_local_index += 1; | |
| 120 | 118 | return .{ .local = result }; |
| 121 | 119 | } |
| 122 | 120 | |
| 123 | fn allocLocal(o: *Object, ty: Type, mutability: Mutability) !CValue { | |
| 124 | const local_value = o.allocLocalValue(); | |
| 125 | try o.renderTypeAndName(o.writer(), ty, local_value, mutability); | |
| 121 | fn allocLocal(f: *Function, ty: Type, mutability: Mutability) !CValue { | |
| 122 | const local_value = f.allocLocalValue(); | |
| 123 | try f.object.renderTypeAndName(f.object.writer(), ty, local_value, mutability); | |
| 126 | 124 | return local_value; |
| 127 | 125 | } |
| 128 | 126 | |
| 127 | fn writeCValue(f: *Function, w: anytype, c_value: CValue) !void { | |
| 128 | switch (c_value) { | |
| 129 | .constant => |inst| { | |
| 130 | const ty = f.air.typeOf(inst); | |
| 131 | const val = f.air.value(inst).?; | |
| 132 | return f.object.dg.renderValue(w, ty, val); | |
| 133 | }, | |
| 134 | else => return Object.writeCValue(w, c_value), | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | fn fail(f: *Function, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { | |
| 139 | return f.object.dg.fail(format, args); | |
| 140 | } | |
| 141 | ||
| 142 | fn renderType(f: *Function, w: anytype, t: Type) !void { | |
| 143 | return f.object.dg.renderType(w, t); | |
| 144 | } | |
| 145 | }; | |
| 146 | ||
| 147 | /// This data is available when outputting .c code for a `Module`. | |
| 148 | /// It is not available when generating .h file. | |
| 149 | pub const Object = struct { | |
| 150 | dg: DeclGen, | |
| 151 | code: std.ArrayList(u8), | |
| 152 | indent_writer: IndentWriter(std.ArrayList(u8).Writer), | |
| 153 | ||
| 129 | 154 | fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer { |
| 130 | 155 | return o.indent_writer.writer(); |
| 131 | 156 | } |
| 132 | 157 | |
| 133 | fn writeCValue(o: *Object, w: anytype, c_value: CValue) !void { | |
| 158 | fn writeCValue(w: anytype, c_value: CValue) !void { | |
| 134 | 159 | switch (c_value) { |
| 135 | 160 | .none => unreachable, |
| 136 | 161 | .local => |i| return w.print("t{d}", .{i}), |
| 137 | 162 | .local_ref => |i| return w.print("&t{d}", .{i}), |
| 138 | .constant => |inst| { | |
| 139 | const ty = o.air.typeOf(inst); | |
| 140 | const val = o.air.value(inst).?; | |
| 141 | return o.dg.renderValue(w, ty, val); | |
| 142 | }, | |
| 163 | .constant => unreachable, | |
| 143 | 164 | .arg => |i| return w.print("a{d}", .{i}), |
| 144 | 165 | .decl => |decl| return w.writeAll(mem.span(decl.name)), |
| 145 | 166 | .decl_ref => |decl| return w.print("&{s}", .{decl.name}), |
| ... | ... | @@ -153,7 +174,7 @@ pub const Object = struct { |
| 153 | 174 | name: CValue, |
| 154 | 175 | mutability: Mutability, |
| 155 | 176 | ) error{ OutOfMemory, AnalysisFail }!void { |
| 156 | var suffix = std.ArrayList(u8).init(o.gpa); | |
| 177 | var suffix = std.ArrayList(u8).init(o.dg.gpa); | |
| 157 | 178 | defer suffix.deinit(); |
| 158 | 179 | |
| 159 | 180 | var render_ty = ty; |
| ... | ... | @@ -177,7 +198,7 @@ pub const Object = struct { |
| 177 | 198 | .Const => try w.writeAll("const "), |
| 178 | 199 | .Mut => {}, |
| 179 | 200 | } |
| 180 | try o.writeCValue(w, name); | |
| 201 | try writeCValue(w, name); | |
| 181 | 202 | try w.writeAll(")("); |
| 182 | 203 | const param_len = render_ty.fnParamLen(); |
| 183 | 204 | const is_var_args = render_ty.fnIsVarArgs(); |
| ... | ... | @@ -205,7 +226,7 @@ pub const Object = struct { |
| 205 | 226 | .Mut => "", |
| 206 | 227 | }; |
| 207 | 228 | try w.print(" {s}", .{const_prefix}); |
| 208 | try o.writeCValue(w, name); | |
| 229 | try writeCValue(w, name); | |
| 209 | 230 | } |
| 210 | 231 | try w.writeAll(suffix.items); |
| 211 | 232 | } |
| ... | ... | @@ -213,11 +234,14 @@ pub const Object = struct { |
| 213 | 234 | |
| 214 | 235 | /// This data is available both when outputting .c code and when outputting an .h file. |
| 215 | 236 | pub const DeclGen = struct { |
| 237 | gpa: *std.mem.Allocator, | |
| 216 | 238 | module: *Module, |
| 217 | 239 | decl: *Decl, |
| 218 | 240 | fwd_decl: std.ArrayList(u8), |
| 219 | 241 | error_msg: ?*Module.ErrorMsg, |
| 242 | /// The key of this map is Type which has references to typedefs_arena. | |
| 220 | 243 | typedefs: TypedefMap, |
| 244 | typedefs_arena: *std.mem.Allocator, | |
| 221 | 245 | |
| 222 | 246 | fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { |
| 223 | 247 | @setCold(true); |
| ... | ... | @@ -251,7 +275,7 @@ pub const DeclGen = struct { |
| 251 | 275 | try writer.writeByte('('); |
| 252 | 276 | try dg.renderType(writer, t); |
| 253 | 277 | try writer.writeAll("){"); |
| 254 | var buf: Type.Payload.ElemType = undefined; | |
| 278 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 255 | 279 | try dg.renderValue(writer, t.slicePtrFieldType(&buf), val); |
| 256 | 280 | try writer.writeAll(", "); |
| 257 | 281 | try writer.print("{d}", .{val.sliceLen()}); |
| ... | ... | @@ -545,7 +569,10 @@ pub const DeclGen = struct { |
| 545 | 569 | |
| 546 | 570 | try dg.typedefs.ensureUnusedCapacity(1); |
| 547 | 571 | try w.writeAll(name); |
| 548 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | |
| 572 | dg.typedefs.putAssumeCapacityNoClobber( | |
| 573 | try t.copy(dg.typedefs_arena), | |
| 574 | .{ .name = name, .rendered = rendered }, | |
| 575 | ); | |
| 549 | 576 | } else { |
| 550 | 577 | try dg.renderType(w, t.elemType()); |
| 551 | 578 | try w.writeAll(" *"); |
| ... | ... | @@ -586,7 +613,10 @@ pub const DeclGen = struct { |
| 586 | 613 | |
| 587 | 614 | try dg.typedefs.ensureUnusedCapacity(1); |
| 588 | 615 | try w.writeAll(name); |
| 589 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | |
| 616 | dg.typedefs.putAssumeCapacityNoClobber( | |
| 617 | try t.copy(dg.typedefs_arena), | |
| 618 | .{ .name = name, .rendered = rendered }, | |
| 619 | ); | |
| 590 | 620 | }, |
| 591 | 621 | .ErrorSet => { |
| 592 | 622 | comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2); |
| ... | ... | @@ -626,7 +656,10 @@ pub const DeclGen = struct { |
| 626 | 656 | |
| 627 | 657 | try dg.typedefs.ensureUnusedCapacity(1); |
| 628 | 658 | try w.writeAll(name); |
| 629 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | |
| 659 | dg.typedefs.putAssumeCapacityNoClobber( | |
| 660 | try t.copy(dg.typedefs_arena), | |
| 661 | .{ .name = name, .rendered = rendered }, | |
| 662 | ); | |
| 630 | 663 | }, |
| 631 | 664 | .Struct => { |
| 632 | 665 | if (dg.typedefs.get(t)) |some| { |
| ... | ... | @@ -659,7 +692,10 @@ pub const DeclGen = struct { |
| 659 | 692 | |
| 660 | 693 | try dg.typedefs.ensureUnusedCapacity(1); |
| 661 | 694 | try w.writeAll(name); |
| 662 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | |
| 695 | dg.typedefs.putAssumeCapacityNoClobber( | |
| 696 | try t.copy(dg.typedefs_arena), | |
| 697 | .{ .name = name, .rendered = rendered }, | |
| 698 | ); | |
| 663 | 699 | }, |
| 664 | 700 | .Enum => { |
| 665 | 701 | // For enums, we simply use the integer tag type. |
| ... | ... | @@ -724,6 +760,29 @@ pub const DeclGen = struct { |
| 724 | 760 | } |
| 725 | 761 | }; |
| 726 | 762 | |
| 763 | pub fn genFunc(f: *Function) !void { | |
| 764 | const tracy = trace(@src()); | |
| 765 | defer tracy.end(); | |
| 766 | ||
| 767 | const o = &f.object; | |
| 768 | const is_global = o.dg.module.decl_exports.contains(f.func.owner_decl); | |
| 769 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | |
| 770 | if (is_global) { | |
| 771 | try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); | |
| 772 | } | |
| 773 | try o.dg.renderFunctionSignature(fwd_decl_writer, is_global); | |
| 774 | try fwd_decl_writer.writeAll(";\n"); | |
| 775 | ||
| 776 | try o.indent_writer.insertNewline(); | |
| 777 | try o.dg.renderFunctionSignature(o.writer(), is_global); | |
| 778 | ||
| 779 | try o.writer().writeByte(' '); | |
| 780 | const main_body = f.air.getMainBody(); | |
| 781 | try genBody(f, main_body); | |
| 782 | ||
| 783 | try o.indent_writer.insertNewline(); | |
| 784 | } | |
| 785 | ||
| 727 | 786 | pub fn genDecl(o: *Object) !void { |
| 728 | 787 | const tracy = trace(@src()); |
| 729 | 788 | defer tracy.end(); |
| ... | ... | @@ -732,28 +791,6 @@ pub fn genDecl(o: *Object) !void { |
| 732 | 791 | .ty = o.dg.decl.ty, |
| 733 | 792 | .val = o.dg.decl.val, |
| 734 | 793 | }; |
| 735 | if (tv.val.castTag(.function)) |func_payload| { | |
| 736 | const func: *Module.Fn = func_payload.data; | |
| 737 | if (func.owner_decl == o.dg.decl) { | |
| 738 | const is_global = o.dg.declIsGlobal(tv); | |
| 739 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | |
| 740 | if (is_global) { | |
| 741 | try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); | |
| 742 | } | |
| 743 | try o.dg.renderFunctionSignature(fwd_decl_writer, is_global); | |
| 744 | try fwd_decl_writer.writeAll(";\n"); | |
| 745 | ||
| 746 | try o.indent_writer.insertNewline(); | |
| 747 | try o.dg.renderFunctionSignature(o.writer(), is_global); | |
| 748 | ||
| 749 | try o.writer().writeByte(' '); | |
| 750 | const main_body = o.air.getMainBody(); | |
| 751 | try genBody(o, main_body); | |
| 752 | ||
| 753 | try o.indent_writer.insertNewline(); | |
| 754 | return; | |
| 755 | } | |
| 756 | } | |
| 757 | 794 | if (tv.val.tag() == .extern_fn) { |
| 758 | 795 | const writer = o.writer(); |
| 759 | 796 | try writer.writeAll("ZIG_EXTERN_C "); |
| ... | ... | @@ -821,240 +858,263 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { |
| 821 | 858 | } |
| 822 | 859 | } |
| 823 | 860 | |
| 824 | fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void { | |
| 825 | const writer = o.writer(); | |
| 861 | fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void { | |
| 862 | const writer = f.object.writer(); | |
| 826 | 863 | if (body.len == 0) { |
| 827 | 864 | try writer.writeAll("{}"); |
| 828 | 865 | return; |
| 829 | 866 | } |
| 830 | 867 | |
| 831 | 868 | try writer.writeAll("{\n"); |
| 832 | o.indent_writer.pushIndent(); | |
| 869 | f.object.indent_writer.pushIndent(); | |
| 833 | 870 | |
| 834 | const air_tags = o.air.instructions.items(.tag); | |
| 871 | const air_tags = f.air.instructions.items(.tag); | |
| 835 | 872 | |
| 836 | 873 | for (body) |inst| { |
| 837 | 874 | const result_value = switch (air_tags[inst]) { |
| 838 | 875 | // zig fmt: off |
| 839 | 876 | .constant => unreachable, // excluded from function bodies |
| 840 | 877 | .const_ty => unreachable, // excluded from function bodies |
| 841 | .arg => airArg(o), | |
| 878 | .arg => airArg(f), | |
| 842 | 879 | |
| 843 | .breakpoint => try airBreakpoint(o), | |
| 844 | .unreach => try airUnreach(o), | |
| 880 | .breakpoint => try airBreakpoint(f), | |
| 881 | .unreach => try airUnreach(f), | |
| 882 | .fence => try airFence(f, inst), | |
| 845 | 883 | |
| 846 | 884 | // TODO use a different strategy for add that communicates to the optimizer |
| 847 | 885 | // that wrapping is UB. |
| 848 | .add, .ptr_add => try airBinOp( o, inst, " + "), | |
| 849 | .addwrap => try airWrapOp(o, inst, " + ", "addw_"), | |
| 886 | .add, .ptr_add => try airBinOp (f, inst, " + "), | |
| 850 | 887 | // TODO use a different strategy for sub that communicates to the optimizer |
| 851 | 888 | // that wrapping is UB. |
| 852 | .sub, .ptr_sub => try airBinOp( o, inst, " - "), | |
| 853 | .subwrap => try airWrapOp(o, inst, " - ", "subw_"), | |
| 889 | .sub, .ptr_sub => try airBinOp (f, inst, " - "), | |
| 854 | 890 | // TODO use a different strategy for mul that communicates to the optimizer |
| 855 | 891 | // that wrapping is UB. |
| 856 | .mul => try airBinOp( o, inst, " * "), | |
| 857 | .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"), | |
| 892 | .mul => try airBinOp (f, inst, " * "), | |
| 858 | 893 | // TODO use a different strategy for div that communicates to the optimizer |
| 859 | 894 | // that wrapping is UB. |
| 860 | .div => try airBinOp( o, inst, " / "), | |
| 861 | .rem => try airBinOp( o, inst, " % "), | |
| 862 | ||
| 863 | .cmp_eq => try airBinOp(o, inst, " == "), | |
| 864 | .cmp_gt => try airBinOp(o, inst, " > "), | |
| 865 | .cmp_gte => try airBinOp(o, inst, " >= "), | |
| 866 | .cmp_lt => try airBinOp(o, inst, " < "), | |
| 867 | .cmp_lte => try airBinOp(o, inst, " <= "), | |
| 868 | .cmp_neq => try airBinOp(o, inst, " != "), | |
| 895 | .div => try airBinOp( f, inst, " / "), | |
| 896 | .rem => try airBinOp( f, inst, " % "), | |
| 897 | .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division | |
| 898 | ||
| 899 | .addwrap => try airWrapOp(f, inst, " + ", "addw_"), | |
| 900 | .subwrap => try airWrapOp(f, inst, " - ", "subw_"), | |
| 901 | .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"), | |
| 902 | ||
| 903 | .add_sat => try airSatOp(f, inst, "adds_"), | |
| 904 | .sub_sat => try airSatOp(f, inst, "subs_"), | |
| 905 | .mul_sat => try airSatOp(f, inst, "muls_"), | |
| 906 | .shl_sat => try airSatOp(f, inst, "shls_"), | |
| 907 | ||
| 908 | .cmp_eq => try airBinOp(f, inst, " == "), | |
| 909 | .cmp_gt => try airBinOp(f, inst, " > "), | |
| 910 | .cmp_gte => try airBinOp(f, inst, " >= "), | |
| 911 | .cmp_lt => try airBinOp(f, inst, " < "), | |
| 912 | .cmp_lte => try airBinOp(f, inst, " <= "), | |
| 913 | .cmp_neq => try airBinOp(f, inst, " != "), | |
| 869 | 914 | |
| 870 | 915 | // bool_and and bool_or are non-short-circuit operations |
| 871 | .bool_and => try airBinOp(o, inst, " & "), | |
| 872 | .bool_or => try airBinOp(o, inst, " | "), | |
| 873 | .bit_and => try airBinOp(o, inst, " & "), | |
| 874 | .bit_or => try airBinOp(o, inst, " | "), | |
| 875 | .xor => try airBinOp(o, inst, " ^ "), | |
| 876 | ||
| 877 | .shr => try airBinOp(o, inst, " >> "), | |
| 878 | .shl => try airBinOp(o, inst, " << "), | |
| 879 | ||
| 880 | .not => try airNot( o, inst), | |
| 881 | ||
| 882 | .optional_payload => try airOptionalPayload(o, inst), | |
| 883 | .optional_payload_ptr => try airOptionalPayload(o, inst), | |
| 884 | ||
| 885 | .is_err => try airIsErr(o, inst, "", ".", "!="), | |
| 886 | .is_non_err => try airIsErr(o, inst, "", ".", "=="), | |
| 887 | .is_err_ptr => try airIsErr(o, inst, "*", "->", "!="), | |
| 888 | .is_non_err_ptr => try airIsErr(o, inst, "*", "->", "=="), | |
| 889 | ||
| 890 | .is_null => try airIsNull(o, inst, "==", ""), | |
| 891 | .is_non_null => try airIsNull(o, inst, "!=", ""), | |
| 892 | .is_null_ptr => try airIsNull(o, inst, "==", "[0]"), | |
| 893 | .is_non_null_ptr => try airIsNull(o, inst, "!=", "[0]"), | |
| 894 | ||
| 895 | .alloc => try airAlloc(o, inst), | |
| 896 | .assembly => try airAsm(o, inst), | |
| 897 | .block => try airBlock(o, inst), | |
| 898 | .bitcast => try airBitcast(o, inst), | |
| 899 | .call => try airCall(o, inst), | |
| 900 | .dbg_stmt => try airDbgStmt(o, inst), | |
| 901 | .intcast => try airIntCast(o, inst), | |
| 902 | .trunc => try airTrunc(o, inst), | |
| 903 | .bool_to_int => try airBoolToInt(o, inst), | |
| 904 | .load => try airLoad(o, inst), | |
| 905 | .ret => try airRet(o, inst), | |
| 906 | .store => try airStore(o, inst), | |
| 907 | .loop => try airLoop(o, inst), | |
| 908 | .cond_br => try airCondBr(o, inst), | |
| 909 | .br => try airBr(o, inst), | |
| 910 | .switch_br => try airSwitchBr(o, inst), | |
| 911 | .wrap_optional => try airWrapOptional(o, inst), | |
| 912 | .struct_field_ptr => try airStructFieldPtr(o, inst), | |
| 913 | .array_to_slice => try airArrayToSlice(o, inst), | |
| 914 | .cmpxchg_weak => try airCmpxchg(o, inst, "weak"), | |
| 915 | .cmpxchg_strong => try airCmpxchg(o, inst, "strong"), | |
| 916 | ||
| 917 | .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0), | |
| 918 | .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1), | |
| 919 | .struct_field_ptr_index_2 => try airStructFieldPtrIndex(o, inst, 2), | |
| 920 | .struct_field_ptr_index_3 => try airStructFieldPtrIndex(o, inst, 3), | |
| 921 | ||
| 922 | .struct_field_val => try airStructFieldVal(o, inst), | |
| 923 | .slice_ptr => try airSliceField(o, inst, ".ptr;\n"), | |
| 924 | .slice_len => try airSliceField(o, inst, ".len;\n"), | |
| 925 | ||
| 926 | .ptr_elem_val => try airPtrElemVal(o, inst, "["), | |
| 927 | .ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["), | |
| 928 | .ptr_elem_ptr => try airPtrElemPtr(o, inst), | |
| 929 | .slice_elem_val => try airSliceElemVal(o, inst, "["), | |
| 930 | .ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["), | |
| 931 | ||
| 932 | .unwrap_errunion_payload => try airUnwrapErrUnionPay(o, inst), | |
| 933 | .unwrap_errunion_err => try airUnwrapErrUnionErr(o, inst), | |
| 934 | .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(o, inst), | |
| 935 | .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(o, inst), | |
| 936 | .wrap_errunion_payload => try airWrapErrUnionPay(o, inst), | |
| 937 | .wrap_errunion_err => try airWrapErrUnionErr(o, inst), | |
| 938 | ||
| 939 | .ptrtoint => return o.dg.fail("TODO: C backend: implement codegen for ptrtoint", .{}), | |
| 940 | .floatcast => return o.dg.fail("TODO: C backend: implement codegen for floatcast", .{}), | |
| 916 | .bool_and => try airBinOp(f, inst, " & "), | |
| 917 | .bool_or => try airBinOp(f, inst, " | "), | |
| 918 | .bit_and => try airBinOp(f, inst, " & "), | |
| 919 | .bit_or => try airBinOp(f, inst, " | "), | |
| 920 | .xor => try airBinOp(f, inst, " ^ "), | |
| 921 | .shr => try airBinOp(f, inst, " >> "), | |
| 922 | .shl, .shl_exact => try airBinOp(f, inst, " << "), | |
| 923 | .not => try airNot (f, inst), | |
| 924 | ||
| 925 | .optional_payload => try airOptionalPayload(f, inst), | |
| 926 | .optional_payload_ptr => try airOptionalPayload(f, inst), | |
| 927 | ||
| 928 | .is_err => try airIsErr(f, inst, "", ".", "!="), | |
| 929 | .is_non_err => try airIsErr(f, inst, "", ".", "=="), | |
| 930 | .is_err_ptr => try airIsErr(f, inst, "*", "->", "!="), | |
| 931 | .is_non_err_ptr => try airIsErr(f, inst, "*", "->", "=="), | |
| 932 | ||
| 933 | .is_null => try airIsNull(f, inst, "==", ""), | |
| 934 | .is_non_null => try airIsNull(f, inst, "!=", ""), | |
| 935 | .is_null_ptr => try airIsNull(f, inst, "==", "[0]"), | |
| 936 | .is_non_null_ptr => try airIsNull(f, inst, "!=", "[0]"), | |
| 937 | ||
| 938 | .alloc => try airAlloc(f, inst), | |
| 939 | .assembly => try airAsm(f, inst), | |
| 940 | .block => try airBlock(f, inst), | |
| 941 | .bitcast => try airBitcast(f, inst), | |
| 942 | .call => try airCall(f, inst), | |
| 943 | .dbg_stmt => try airDbgStmt(f, inst), | |
| 944 | .intcast => try airIntCast(f, inst), | |
| 945 | .trunc => try airTrunc(f, inst), | |
| 946 | .bool_to_int => try airBoolToInt(f, inst), | |
| 947 | .load => try airLoad(f, inst), | |
| 948 | .ret => try airRet(f, inst), | |
| 949 | .store => try airStore(f, inst), | |
| 950 | .loop => try airLoop(f, inst), | |
| 951 | .cond_br => try airCondBr(f, inst), | |
| 952 | .br => try airBr(f, inst), | |
| 953 | .switch_br => try airSwitchBr(f, inst), | |
| 954 | .wrap_optional => try airWrapOptional(f, inst), | |
| 955 | .struct_field_ptr => try airStructFieldPtr(f, inst), | |
| 956 | .array_to_slice => try airArrayToSlice(f, inst), | |
| 957 | .cmpxchg_weak => try airCmpxchg(f, inst, "weak"), | |
| 958 | .cmpxchg_strong => try airCmpxchg(f, inst, "strong"), | |
| 959 | .atomic_rmw => try airAtomicRmw(f, inst), | |
| 960 | .atomic_load => try airAtomicLoad(f, inst), | |
| 961 | .memset => try airMemset(f, inst), | |
| 962 | .memcpy => try airMemcpy(f, inst), | |
| 963 | .set_union_tag => try airSetUnionTag(f, inst), | |
| 964 | .get_union_tag => try airGetUnionTag(f, inst), | |
| 965 | .clz => try airBuiltinCall(f, inst, "clz"), | |
| 966 | .ctz => try airBuiltinCall(f, inst, "ctz"), | |
| 967 | ||
| 968 | .int_to_float, | |
| 969 | .float_to_int, | |
| 970 | .fptrunc, | |
| 971 | .fpext, | |
| 972 | .ptrtoint, | |
| 973 | => try airSimpleCast(f, inst), | |
| 974 | ||
| 975 | .atomic_store_unordered => try airAtomicStore(f, inst, toMemoryOrder(.Unordered)), | |
| 976 | .atomic_store_monotonic => try airAtomicStore(f, inst, toMemoryOrder(.Monotonic)), | |
| 977 | .atomic_store_release => try airAtomicStore(f, inst, toMemoryOrder(.Release)), | |
| 978 | .atomic_store_seq_cst => try airAtomicStore(f, inst, toMemoryOrder(.SeqCst)), | |
| 979 | ||
| 980 | .struct_field_ptr_index_0 => try airStructFieldPtrIndex(f, inst, 0), | |
| 981 | .struct_field_ptr_index_1 => try airStructFieldPtrIndex(f, inst, 1), | |
| 982 | .struct_field_ptr_index_2 => try airStructFieldPtrIndex(f, inst, 2), | |
| 983 | .struct_field_ptr_index_3 => try airStructFieldPtrIndex(f, inst, 3), | |
| 984 | ||
| 985 | .struct_field_val => try airStructFieldVal(f, inst), | |
| 986 | .slice_ptr => try airSliceField(f, inst, ".ptr;\n"), | |
| 987 | .slice_len => try airSliceField(f, inst, ".len;\n"), | |
| 988 | ||
| 989 | .ptr_elem_val => try airPtrElemVal(f, inst, "["), | |
| 990 | .ptr_ptr_elem_val => try airPtrElemVal(f, inst, "[0]["), | |
| 991 | .ptr_elem_ptr => try airPtrElemPtr(f, inst), | |
| 992 | .slice_elem_val => try airSliceElemVal(f, inst, "["), | |
| 993 | .ptr_slice_elem_val => try airSliceElemVal(f, inst, "[0]["), | |
| 994 | ||
| 995 | .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst), | |
| 996 | .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst), | |
| 997 | .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst), | |
| 998 | .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(f, inst), | |
| 999 | .wrap_errunion_payload => try airWrapErrUnionPay(f, inst), | |
| 1000 | .wrap_errunion_err => try airWrapErrUnionErr(f, inst), | |
| 941 | 1001 | // zig fmt: on |
| 942 | 1002 | }; |
| 943 | 1003 | switch (result_value) { |
| 944 | 1004 | .none => {}, |
| 945 | else => try o.value_map.putNoClobber(inst, result_value), | |
| 1005 | else => try f.value_map.putNoClobber(inst, result_value), | |
| 946 | 1006 | } |
| 947 | 1007 | } |
| 948 | 1008 | |
| 949 | o.indent_writer.popIndent(); | |
| 1009 | f.object.indent_writer.popIndent(); | |
| 950 | 1010 | try writer.writeAll("}"); |
| 951 | 1011 | } |
| 952 | 1012 | |
| 953 | fn airSliceField(o: *Object, inst: Air.Inst.Index, suffix: []const u8) !CValue { | |
| 954 | if (o.liveness.isUnused(inst)) | |
| 1013 | fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue { | |
| 1014 | if (f.liveness.isUnused(inst)) | |
| 955 | 1015 | return CValue.none; |
| 956 | 1016 | |
| 957 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 958 | const operand = try o.resolveInst(ty_op.operand); | |
| 959 | const writer = o.writer(); | |
| 960 | const local = try o.allocLocal(Type.initTag(.usize), .Const); | |
| 1017 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1018 | const operand = try f.resolveInst(ty_op.operand); | |
| 1019 | const writer = f.object.writer(); | |
| 1020 | const local = try f.allocLocal(Type.initTag(.usize), .Const); | |
| 961 | 1021 | try writer.writeAll(" = "); |
| 962 | try o.writeCValue(writer, operand); | |
| 1022 | try f.writeCValue(writer, operand); | |
| 963 | 1023 | try writer.writeAll(suffix); |
| 964 | 1024 | return local; |
| 965 | 1025 | } |
| 966 | 1026 | |
| 967 | fn airPtrElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { | |
| 1027 | fn airPtrElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue { | |
| 968 | 1028 | const is_volatile = false; // TODO |
| 969 | if (!is_volatile and o.liveness.isUnused(inst)) | |
| 1029 | if (!is_volatile and f.liveness.isUnused(inst)) | |
| 970 | 1030 | return CValue.none; |
| 971 | 1031 | |
| 972 | 1032 | _ = prefix; |
| 973 | return o.dg.fail("TODO: C backend: airPtrElemVal", .{}); | |
| 1033 | return f.fail("TODO: C backend: airPtrElemVal", .{}); | |
| 974 | 1034 | } |
| 975 | 1035 | |
| 976 | fn airPtrElemPtr(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 977 | if (o.liveness.isUnused(inst)) | |
| 1036 | fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1037 | if (f.liveness.isUnused(inst)) | |
| 978 | 1038 | return CValue.none; |
| 979 | 1039 | |
| 980 | return o.dg.fail("TODO: C backend: airPtrElemPtr", .{}); | |
| 1040 | return f.fail("TODO: C backend: airPtrElemPtr", .{}); | |
| 981 | 1041 | } |
| 982 | 1042 | |
| 983 | fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { | |
| 1043 | fn airSliceElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue { | |
| 984 | 1044 | const is_volatile = false; // TODO |
| 985 | if (!is_volatile and o.liveness.isUnused(inst)) | |
| 1045 | if (!is_volatile and f.liveness.isUnused(inst)) | |
| 986 | 1046 | return CValue.none; |
| 987 | 1047 | |
| 988 | const bin_op = o.air.instructions.items(.data)[inst].bin_op; | |
| 989 | const slice = try o.resolveInst(bin_op.lhs); | |
| 990 | const index = try o.resolveInst(bin_op.rhs); | |
| 991 | const writer = o.writer(); | |
| 992 | const local = try o.allocLocal(o.air.typeOfIndex(inst), .Const); | |
| 1048 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | |
| 1049 | const slice = try f.resolveInst(bin_op.lhs); | |
| 1050 | const index = try f.resolveInst(bin_op.rhs); | |
| 1051 | const writer = f.object.writer(); | |
| 1052 | const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); | |
| 993 | 1053 | try writer.writeAll(" = "); |
| 994 | try o.writeCValue(writer, slice); | |
| 1054 | try f.writeCValue(writer, slice); | |
| 995 | 1055 | try writer.writeAll(prefix); |
| 996 | try o.writeCValue(writer, index); | |
| 1056 | try f.writeCValue(writer, index); | |
| 997 | 1057 | try writer.writeAll("];\n"); |
| 998 | 1058 | return local; |
| 999 | 1059 | } |
| 1000 | 1060 | |
| 1001 | fn airAlloc(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1002 | const writer = o.writer(); | |
| 1003 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1061 | fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1062 | const writer = f.object.writer(); | |
| 1063 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1004 | 1064 | |
| 1005 | 1065 | // First line: the variable used as data storage. |
| 1006 | 1066 | const elem_type = inst_ty.elemType(); |
| 1007 | 1067 | const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; |
| 1008 | const local = try o.allocLocal(elem_type, mutability); | |
| 1068 | const local = try f.allocLocal(elem_type, mutability); | |
| 1009 | 1069 | try writer.writeAll(";\n"); |
| 1010 | 1070 | |
| 1011 | 1071 | return CValue{ .local_ref = local.local }; |
| 1012 | 1072 | } |
| 1013 | 1073 | |
| 1014 | fn airArg(o: *Object) CValue { | |
| 1015 | const i = o.next_arg_index; | |
| 1016 | o.next_arg_index += 1; | |
| 1074 | fn airArg(f: *Function) CValue { | |
| 1075 | const i = f.next_arg_index; | |
| 1076 | f.next_arg_index += 1; | |
| 1017 | 1077 | return .{ .arg = i }; |
| 1018 | 1078 | } |
| 1019 | 1079 | |
| 1020 | fn airLoad(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1021 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1022 | const is_volatile = o.air.typeOf(ty_op.operand).isVolatilePtr(); | |
| 1023 | if (!is_volatile and o.liveness.isUnused(inst)) | |
| 1080 | fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1081 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1082 | const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr(); | |
| 1083 | if (!is_volatile and f.liveness.isUnused(inst)) | |
| 1024 | 1084 | return CValue.none; |
| 1025 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1026 | const operand = try o.resolveInst(ty_op.operand); | |
| 1027 | const writer = o.writer(); | |
| 1028 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1085 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1086 | const operand = try f.resolveInst(ty_op.operand); | |
| 1087 | const writer = f.object.writer(); | |
| 1088 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1029 | 1089 | switch (operand) { |
| 1030 | 1090 | .local_ref => |i| { |
| 1031 | 1091 | const wrapped: CValue = .{ .local = i }; |
| 1032 | 1092 | try writer.writeAll(" = "); |
| 1033 | try o.writeCValue(writer, wrapped); | |
| 1093 | try f.writeCValue(writer, wrapped); | |
| 1034 | 1094 | try writer.writeAll(";\n"); |
| 1035 | 1095 | }, |
| 1036 | 1096 | .decl_ref => |decl| { |
| 1037 | 1097 | const wrapped: CValue = .{ .decl = decl }; |
| 1038 | 1098 | try writer.writeAll(" = "); |
| 1039 | try o.writeCValue(writer, wrapped); | |
| 1099 | try f.writeCValue(writer, wrapped); | |
| 1040 | 1100 | try writer.writeAll(";\n"); |
| 1041 | 1101 | }, |
| 1042 | 1102 | else => { |
| 1043 | 1103 | try writer.writeAll(" = *"); |
| 1044 | try o.writeCValue(writer, operand); | |
| 1104 | try f.writeCValue(writer, operand); | |
| 1045 | 1105 | try writer.writeAll(";\n"); |
| 1046 | 1106 | }, |
| 1047 | 1107 | } |
| 1048 | 1108 | return local; |
| 1049 | 1109 | } |
| 1050 | 1110 | |
| 1051 | fn airRet(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1052 | const un_op = o.air.instructions.items(.data)[inst].un_op; | |
| 1053 | const writer = o.writer(); | |
| 1054 | if (o.air.typeOf(un_op).hasCodeGenBits()) { | |
| 1055 | const operand = try o.resolveInst(un_op); | |
| 1111 | fn airRet(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1112 | const un_op = f.air.instructions.items(.data)[inst].un_op; | |
| 1113 | const writer = f.object.writer(); | |
| 1114 | if (f.air.typeOf(un_op).hasCodeGenBits()) { | |
| 1115 | const operand = try f.resolveInst(un_op); | |
| 1056 | 1116 | try writer.writeAll("return "); |
| 1057 | try o.writeCValue(writer, operand); | |
| 1117 | try f.writeCValue(writer, operand); | |
| 1058 | 1118 | try writer.writeAll(";\n"); |
| 1059 | 1119 | } else { |
| 1060 | 1120 | try writer.writeAll("return;\n"); |
| ... | ... | @@ -1062,75 +1122,75 @@ fn airRet(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1062 | 1122 | return CValue.none; |
| 1063 | 1123 | } |
| 1064 | 1124 | |
| 1065 | fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1066 | if (o.liveness.isUnused(inst)) | |
| 1125 | fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1126 | if (f.liveness.isUnused(inst)) | |
| 1067 | 1127 | return CValue.none; |
| 1068 | 1128 | |
| 1069 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1070 | const operand = try o.resolveInst(ty_op.operand); | |
| 1129 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1130 | const operand = try f.resolveInst(ty_op.operand); | |
| 1071 | 1131 | |
| 1072 | const writer = o.writer(); | |
| 1073 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1074 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1132 | const writer = f.object.writer(); | |
| 1133 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1134 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1075 | 1135 | try writer.writeAll(" = ("); |
| 1076 | try o.dg.renderType(writer, inst_ty); | |
| 1136 | try f.renderType(writer, inst_ty); | |
| 1077 | 1137 | try writer.writeAll(")"); |
| 1078 | try o.writeCValue(writer, operand); | |
| 1138 | try f.writeCValue(writer, operand); | |
| 1079 | 1139 | try writer.writeAll(";\n"); |
| 1080 | 1140 | return local; |
| 1081 | 1141 | } |
| 1082 | 1142 | |
| 1083 | fn airTrunc(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1084 | if (o.liveness.isUnused(inst)) | |
| 1143 | fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1144 | if (f.liveness.isUnused(inst)) | |
| 1085 | 1145 | return CValue.none; |
| 1086 | 1146 | |
| 1087 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1088 | const operand = try o.resolveInst(ty_op.operand); | |
| 1147 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1148 | const operand = try f.resolveInst(ty_op.operand); | |
| 1089 | 1149 | _ = operand; |
| 1090 | return o.dg.fail("TODO: C backend: airTrunc", .{}); | |
| 1150 | return f.fail("TODO: C backend: airTrunc", .{}); | |
| 1091 | 1151 | } |
| 1092 | 1152 | |
| 1093 | fn airBoolToInt(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1094 | if (o.liveness.isUnused(inst)) | |
| 1153 | fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1154 | if (f.liveness.isUnused(inst)) | |
| 1095 | 1155 | return CValue.none; |
| 1096 | const un_op = o.air.instructions.items(.data)[inst].un_op; | |
| 1097 | const writer = o.writer(); | |
| 1098 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1099 | const operand = try o.resolveInst(un_op); | |
| 1100 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1156 | const un_op = f.air.instructions.items(.data)[inst].un_op; | |
| 1157 | const writer = f.object.writer(); | |
| 1158 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1159 | const operand = try f.resolveInst(un_op); | |
| 1160 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1101 | 1161 | try writer.writeAll(" = "); |
| 1102 | try o.writeCValue(writer, operand); | |
| 1162 | try f.writeCValue(writer, operand); | |
| 1103 | 1163 | try writer.writeAll(";\n"); |
| 1104 | 1164 | return local; |
| 1105 | 1165 | } |
| 1106 | 1166 | |
| 1107 | fn airStore(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1167 | fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1108 | 1168 | // *a = b; |
| 1109 | const bin_op = o.air.instructions.items(.data)[inst].bin_op; | |
| 1110 | const dest_ptr = try o.resolveInst(bin_op.lhs); | |
| 1111 | const src_val = try o.resolveInst(bin_op.rhs); | |
| 1169 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | |
| 1170 | const dest_ptr = try f.resolveInst(bin_op.lhs); | |
| 1171 | const src_val = try f.resolveInst(bin_op.rhs); | |
| 1112 | 1172 | |
| 1113 | const writer = o.writer(); | |
| 1173 | const writer = f.object.writer(); | |
| 1114 | 1174 | switch (dest_ptr) { |
| 1115 | 1175 | .local_ref => |i| { |
| 1116 | 1176 | const dest: CValue = .{ .local = i }; |
| 1117 | try o.writeCValue(writer, dest); | |
| 1177 | try f.writeCValue(writer, dest); | |
| 1118 | 1178 | try writer.writeAll(" = "); |
| 1119 | try o.writeCValue(writer, src_val); | |
| 1179 | try f.writeCValue(writer, src_val); | |
| 1120 | 1180 | try writer.writeAll(";\n"); |
| 1121 | 1181 | }, |
| 1122 | 1182 | .decl_ref => |decl| { |
| 1123 | 1183 | const dest: CValue = .{ .decl = decl }; |
| 1124 | try o.writeCValue(writer, dest); | |
| 1184 | try f.writeCValue(writer, dest); | |
| 1125 | 1185 | try writer.writeAll(" = "); |
| 1126 | try o.writeCValue(writer, src_val); | |
| 1186 | try f.writeCValue(writer, src_val); | |
| 1127 | 1187 | try writer.writeAll(";\n"); |
| 1128 | 1188 | }, |
| 1129 | 1189 | else => { |
| 1130 | 1190 | try writer.writeAll("*"); |
| 1131 | try o.writeCValue(writer, dest_ptr); | |
| 1191 | try f.writeCValue(writer, dest_ptr); | |
| 1132 | 1192 | try writer.writeAll(" = "); |
| 1133 | try o.writeCValue(writer, src_val); | |
| 1193 | try f.writeCValue(writer, src_val); | |
| 1134 | 1194 | try writer.writeAll(";\n"); |
| 1135 | 1195 | }, |
| 1136 | 1196 | } |
| ... | ... | @@ -1138,17 +1198,17 @@ fn airStore(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1138 | 1198 | } |
| 1139 | 1199 | |
| 1140 | 1200 | fn airWrapOp( |
| 1141 | o: *Object, | |
| 1201 | f: *Function, | |
| 1142 | 1202 | inst: Air.Inst.Index, |
| 1143 | 1203 | str_op: [*:0]const u8, |
| 1144 | 1204 | fn_op: [*:0]const u8, |
| 1145 | 1205 | ) !CValue { |
| 1146 | if (o.liveness.isUnused(inst)) | |
| 1206 | if (f.liveness.isUnused(inst)) | |
| 1147 | 1207 | return CValue.none; |
| 1148 | 1208 | |
| 1149 | const bin_op = o.air.instructions.items(.data)[inst].bin_op; | |
| 1150 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1151 | const int_info = inst_ty.intInfo(o.dg.module.getTarget()); | |
| 1209 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | |
| 1210 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1211 | const int_info = inst_ty.intInfo(f.object.dg.module.getTarget()); | |
| 1152 | 1212 | const bits = int_info.bits; |
| 1153 | 1213 | |
| 1154 | 1214 | // if it's an unsigned int with non-arbitrary bit size then we can just add |
| ... | ... | @@ -1158,12 +1218,12 @@ fn airWrapOp( |
| 1158 | 1218 | else => false, |
| 1159 | 1219 | }; |
| 1160 | 1220 | if (ok_bits or inst_ty.tag() != .int_unsigned) { |
| 1161 | return try airBinOp(o, inst, str_op); | |
| 1221 | return try airBinOp(f, inst, str_op); | |
| 1162 | 1222 | } |
| 1163 | 1223 | } |
| 1164 | 1224 | |
| 1165 | 1225 | if (bits > 64) { |
| 1166 | return o.dg.fail("TODO: C backend: airWrapOp for large integers", .{}); | |
| 1226 | return f.fail("TODO: C backend: airWrapOp for large integers", .{}); | |
| 1167 | 1227 | } |
| 1168 | 1228 | |
| 1169 | 1229 | var min_buf: [80]u8 = undefined; |
| ... | ... | @@ -1210,11 +1270,11 @@ fn airWrapOp( |
| 1210 | 1270 | }, |
| 1211 | 1271 | }; |
| 1212 | 1272 | |
| 1213 | const lhs = try o.resolveInst(bin_op.lhs); | |
| 1214 | const rhs = try o.resolveInst(bin_op.rhs); | |
| 1215 | const w = o.writer(); | |
| 1273 | const lhs = try f.resolveInst(bin_op.lhs); | |
| 1274 | const rhs = try f.resolveInst(bin_op.rhs); | |
| 1275 | const w = f.object.writer(); | |
| 1216 | 1276 | |
| 1217 | const ret = try o.allocLocal(inst_ty, .Mut); | |
| 1277 | const ret = try f.allocLocal(inst_ty, .Mut); | |
| 1218 | 1278 | try w.print(" = zig_{s}", .{fn_op}); |
| 1219 | 1279 | |
| 1220 | 1280 | switch (inst_ty.tag()) { |
| ... | ... | @@ -1240,71 +1300,179 @@ fn airWrapOp( |
| 1240 | 1300 | } |
| 1241 | 1301 | |
| 1242 | 1302 | try w.writeByte('('); |
| 1243 | try o.writeCValue(w, lhs); | |
| 1303 | try f.writeCValue(w, lhs); | |
| 1244 | 1304 | try w.writeAll(", "); |
| 1245 | try o.writeCValue(w, rhs); | |
| 1305 | try f.writeCValue(w, rhs); | |
| 1246 | 1306 | |
| 1247 | 1307 | if (int_info.signedness == .signed) { |
| 1248 | 1308 | try w.print(", {s}", .{min}); |
| 1249 | 1309 | } |
| 1250 | 1310 | |
| 1251 | 1311 | try w.print(", {s});", .{max}); |
| 1252 | try o.indent_writer.insertNewline(); | |
| 1312 | try f.object.indent_writer.insertNewline(); | |
| 1253 | 1313 | |
| 1254 | 1314 | return ret; |
| 1255 | 1315 | } |
| 1256 | 1316 | |
| 1257 | fn airNot(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1258 | if (o.liveness.isUnused(inst)) | |
| 1317 | fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { | |
| 1318 | if (f.liveness.isUnused(inst)) | |
| 1259 | 1319 | return CValue.none; |
| 1260 | 1320 | |
| 1261 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1262 | const op = try o.resolveInst(ty_op.operand); | |
| 1321 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | |
| 1322 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1323 | const int_info = inst_ty.intInfo(f.object.dg.module.getTarget()); | |
| 1324 | const bits = int_info.bits; | |
| 1325 | ||
| 1326 | switch (bits) { | |
| 1327 | 8, 16, 32, 64, 128 => {}, | |
| 1328 | else => return f.object.dg.fail("TODO: C backend: airSatOp for non power of 2 integers", .{}), | |
| 1329 | } | |
| 1263 | 1330 | |
| 1264 | const writer = o.writer(); | |
| 1265 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1266 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1331 | // if it's an unsigned int with non-arbitrary bit size then we can just add | |
| 1332 | if (bits > 64) { | |
| 1333 | return f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{}); | |
| 1334 | } | |
| 1335 | ||
| 1336 | var min_buf: [80]u8 = undefined; | |
| 1337 | const min = switch (int_info.signedness) { | |
| 1338 | .unsigned => "0", | |
| 1339 | else => switch (inst_ty.tag()) { | |
| 1340 | .c_short => "SHRT_MIN", | |
| 1341 | .c_int => "INT_MIN", | |
| 1342 | .c_long => "LONG_MIN", | |
| 1343 | .c_longlong => "LLONG_MIN", | |
| 1344 | .isize => "INTPTR_MIN", | |
| 1345 | else => blk: { | |
| 1346 | // compute the type minimum based on the bitcount (bits) | |
| 1347 | const val = -1 * std.math.pow(i65, 2, @intCast(i65, bits - 1)); | |
| 1348 | break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { | |
| 1349 | error.NoSpaceLeft => unreachable, | |
| 1350 | else => |e| return e, | |
| 1351 | }; | |
| 1352 | }, | |
| 1353 | }, | |
| 1354 | }; | |
| 1355 | ||
| 1356 | var max_buf: [80]u8 = undefined; | |
| 1357 | const max = switch (inst_ty.tag()) { | |
| 1358 | .c_short => "SHRT_MAX", | |
| 1359 | .c_ushort => "USHRT_MAX", | |
| 1360 | .c_int => "INT_MAX", | |
| 1361 | .c_uint => "UINT_MAX", | |
| 1362 | .c_long => "LONG_MAX", | |
| 1363 | .c_ulong => "ULONG_MAX", | |
| 1364 | .c_longlong => "LLONG_MAX", | |
| 1365 | .c_ulonglong => "ULLONG_MAX", | |
| 1366 | .isize => "INTPTR_MAX", | |
| 1367 | .usize => "UINTPTR_MAX", | |
| 1368 | else => blk: { | |
| 1369 | const pow_bits = switch (int_info.signedness) { | |
| 1370 | .signed => bits - 1, | |
| 1371 | .unsigned => bits, | |
| 1372 | }; | |
| 1373 | const val = std.math.pow(u65, 2, pow_bits) - 1; | |
| 1374 | break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { | |
| 1375 | error.NoSpaceLeft => unreachable, | |
| 1376 | else => |e| return e, | |
| 1377 | }; | |
| 1378 | }, | |
| 1379 | }; | |
| 1380 | ||
| 1381 | const lhs = try f.resolveInst(bin_op.lhs); | |
| 1382 | const rhs = try f.resolveInst(bin_op.rhs); | |
| 1383 | const w = f.object.writer(); | |
| 1384 | ||
| 1385 | const ret = try f.allocLocal(inst_ty, .Mut); | |
| 1386 | try w.print(" = zig_{s}", .{fn_op}); | |
| 1387 | ||
| 1388 | switch (inst_ty.tag()) { | |
| 1389 | .isize => try w.writeAll("isize"), | |
| 1390 | .c_short => try w.writeAll("short"), | |
| 1391 | .c_int => try w.writeAll("int"), | |
| 1392 | .c_long => try w.writeAll("long"), | |
| 1393 | .c_longlong => try w.writeAll("longlong"), | |
| 1394 | else => { | |
| 1395 | const prefix_byte: u8 = switch (int_info.signedness) { | |
| 1396 | .signed => 'i', | |
| 1397 | .unsigned => 'u', | |
| 1398 | }; | |
| 1399 | for ([_]u8{ 8, 16, 32, 64 }) |nbits| { | |
| 1400 | if (bits <= nbits) { | |
| 1401 | try w.print("{c}{d}", .{ prefix_byte, nbits }); | |
| 1402 | break; | |
| 1403 | } | |
| 1404 | } else { | |
| 1405 | unreachable; | |
| 1406 | } | |
| 1407 | }, | |
| 1408 | } | |
| 1409 | ||
| 1410 | try w.writeByte('('); | |
| 1411 | try f.writeCValue(w, lhs); | |
| 1412 | try w.writeAll(", "); | |
| 1413 | try f.writeCValue(w, rhs); | |
| 1414 | ||
| 1415 | if (int_info.signedness == .signed) { | |
| 1416 | try w.print(", {s}", .{min}); | |
| 1417 | } | |
| 1418 | ||
| 1419 | try w.print(", {s});", .{max}); | |
| 1420 | try f.object.indent_writer.insertNewline(); | |
| 1421 | ||
| 1422 | return ret; | |
| 1423 | } | |
| 1424 | ||
| 1425 | fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1426 | if (f.liveness.isUnused(inst)) | |
| 1427 | return CValue.none; | |
| 1428 | ||
| 1429 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1430 | const op = try f.resolveInst(ty_op.operand); | |
| 1431 | ||
| 1432 | const writer = f.object.writer(); | |
| 1433 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1434 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1267 | 1435 | |
| 1268 | 1436 | try writer.writeAll(" = "); |
| 1269 | 1437 | if (inst_ty.zigTypeTag() == .Bool) |
| 1270 | 1438 | try writer.writeAll("!") |
| 1271 | 1439 | else |
| 1272 | 1440 | try writer.writeAll("~"); |
| 1273 | try o.writeCValue(writer, op); | |
| 1441 | try f.writeCValue(writer, op); | |
| 1274 | 1442 | try writer.writeAll(";\n"); |
| 1275 | 1443 | |
| 1276 | 1444 | return local; |
| 1277 | 1445 | } |
| 1278 | 1446 | |
| 1279 | fn airBinOp(o: *Object, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { | |
| 1280 | if (o.liveness.isUnused(inst)) | |
| 1447 | fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { | |
| 1448 | if (f.liveness.isUnused(inst)) | |
| 1281 | 1449 | return CValue.none; |
| 1282 | 1450 | |
| 1283 | const bin_op = o.air.instructions.items(.data)[inst].bin_op; | |
| 1284 | const lhs = try o.resolveInst(bin_op.lhs); | |
| 1285 | const rhs = try o.resolveInst(bin_op.rhs); | |
| 1451 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | |
| 1452 | const lhs = try f.resolveInst(bin_op.lhs); | |
| 1453 | const rhs = try f.resolveInst(bin_op.rhs); | |
| 1286 | 1454 | |
| 1287 | const writer = o.writer(); | |
| 1288 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1289 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1455 | const writer = f.object.writer(); | |
| 1456 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1457 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1290 | 1458 | |
| 1291 | 1459 | try writer.writeAll(" = "); |
| 1292 | try o.writeCValue(writer, lhs); | |
| 1460 | try f.writeCValue(writer, lhs); | |
| 1293 | 1461 | try writer.print("{s}", .{operator}); |
| 1294 | try o.writeCValue(writer, rhs); | |
| 1462 | try f.writeCValue(writer, rhs); | |
| 1295 | 1463 | try writer.writeAll(";\n"); |
| 1296 | 1464 | |
| 1297 | 1465 | return local; |
| 1298 | 1466 | } |
| 1299 | 1467 | |
| 1300 | fn airCall(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1301 | const pl_op = o.air.instructions.items(.data)[inst].pl_op; | |
| 1302 | const extra = o.air.extraData(Air.Call, pl_op.payload); | |
| 1303 | const args = @bitCast([]const Air.Inst.Ref, o.air.extra[extra.end..][0..extra.data.args_len]); | |
| 1304 | const fn_ty = o.air.typeOf(pl_op.operand); | |
| 1468 | fn airCall(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1469 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | |
| 1470 | const extra = f.air.extraData(Air.Call, pl_op.payload); | |
| 1471 | const args = @bitCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); | |
| 1472 | const fn_ty = f.air.typeOf(pl_op.operand); | |
| 1305 | 1473 | const ret_ty = fn_ty.fnReturnType(); |
| 1306 | const unused_result = o.liveness.isUnused(inst); | |
| 1307 | const writer = o.writer(); | |
| 1474 | const unused_result = f.liveness.isUnused(inst); | |
| 1475 | const writer = f.object.writer(); | |
| 1308 | 1476 | |
| 1309 | 1477 | var result_local: CValue = .none; |
| 1310 | 1478 | if (unused_result) { |
| ... | ... | @@ -1312,11 +1480,11 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1312 | 1480 | try writer.print("(void)", .{}); |
| 1313 | 1481 | } |
| 1314 | 1482 | } else { |
| 1315 | result_local = try o.allocLocal(ret_ty, .Const); | |
| 1483 | result_local = try f.allocLocal(ret_ty, .Const); | |
| 1316 | 1484 | try writer.writeAll(" = "); |
| 1317 | 1485 | } |
| 1318 | 1486 | |
| 1319 | if (o.air.value(pl_op.operand)) |func_val| { | |
| 1487 | if (f.air.value(pl_op.operand)) |func_val| { | |
| 1320 | 1488 | const fn_decl = if (func_val.castTag(.extern_fn)) |extern_fn| |
| 1321 | 1489 | extern_fn.data |
| 1322 | 1490 | else if (func_val.castTag(.function)) |func_payload| |
| ... | ... | @@ -1326,8 +1494,8 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1326 | 1494 | |
| 1327 | 1495 | try writer.writeAll(mem.spanZ(fn_decl.name)); |
| 1328 | 1496 | } else { |
| 1329 | const callee = try o.resolveInst(pl_op.operand); | |
| 1330 | try o.writeCValue(writer, callee); | |
| 1497 | const callee = try f.resolveInst(pl_op.operand); | |
| 1498 | try f.writeCValue(writer, callee); | |
| 1331 | 1499 | } |
| 1332 | 1500 | |
| 1333 | 1501 | try writer.writeAll("("); |
| ... | ... | @@ -1335,189 +1503,200 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1335 | 1503 | if (i != 0) { |
| 1336 | 1504 | try writer.writeAll(", "); |
| 1337 | 1505 | } |
| 1338 | if (o.air.value(arg)) |val| { | |
| 1339 | try o.dg.renderValue(writer, o.air.typeOf(arg), val); | |
| 1506 | if (f.air.value(arg)) |val| { | |
| 1507 | try f.object.dg.renderValue(writer, f.air.typeOf(arg), val); | |
| 1340 | 1508 | } else { |
| 1341 | const val = try o.resolveInst(arg); | |
| 1342 | try o.writeCValue(writer, val); | |
| 1509 | const val = try f.resolveInst(arg); | |
| 1510 | try f.writeCValue(writer, val); | |
| 1343 | 1511 | } |
| 1344 | 1512 | } |
| 1345 | 1513 | try writer.writeAll(");\n"); |
| 1346 | 1514 | return result_local; |
| 1347 | 1515 | } |
| 1348 | 1516 | |
| 1349 | fn airDbgStmt(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1350 | const dbg_stmt = o.air.instructions.items(.data)[inst].dbg_stmt; | |
| 1351 | const writer = o.writer(); | |
| 1517 | fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1518 | const dbg_stmt = f.air.instructions.items(.data)[inst].dbg_stmt; | |
| 1519 | const writer = f.object.writer(); | |
| 1352 | 1520 | try writer.print("#line {d}\n", .{dbg_stmt.line + 1}); |
| 1353 | 1521 | return CValue.none; |
| 1354 | 1522 | } |
| 1355 | 1523 | |
| 1356 | fn airBlock(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1357 | const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; | |
| 1358 | const extra = o.air.extraData(Air.Block, ty_pl.payload); | |
| 1359 | const body = o.air.extra[extra.end..][0..extra.data.body_len]; | |
| 1524 | fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1525 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; | |
| 1526 | const extra = f.air.extraData(Air.Block, ty_pl.payload); | |
| 1527 | const body = f.air.extra[extra.end..][0..extra.data.body_len]; | |
| 1360 | 1528 | |
| 1361 | const block_id: usize = o.next_block_index; | |
| 1362 | o.next_block_index += 1; | |
| 1363 | const writer = o.writer(); | |
| 1529 | const block_id: usize = f.next_block_index; | |
| 1530 | f.next_block_index += 1; | |
| 1531 | const writer = f.object.writer(); | |
| 1364 | 1532 | |
| 1365 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1366 | const result = if (inst_ty.tag() != .void and !o.liveness.isUnused(inst)) blk: { | |
| 1533 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1534 | const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) blk: { | |
| 1367 | 1535 | // allocate a location for the result |
| 1368 | const local = try o.allocLocal(inst_ty, .Mut); | |
| 1536 | const local = try f.allocLocal(inst_ty, .Mut); | |
| 1369 | 1537 | try writer.writeAll(";\n"); |
| 1370 | 1538 | break :blk local; |
| 1371 | 1539 | } else CValue{ .none = {} }; |
| 1372 | 1540 | |
| 1373 | try o.blocks.putNoClobber(o.gpa, inst, .{ | |
| 1541 | try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{ | |
| 1374 | 1542 | .block_id = block_id, |
| 1375 | 1543 | .result = result, |
| 1376 | 1544 | }); |
| 1377 | 1545 | |
| 1378 | try genBody(o, body); | |
| 1379 | try o.indent_writer.insertNewline(); | |
| 1546 | try genBody(f, body); | |
| 1547 | try f.object.indent_writer.insertNewline(); | |
| 1380 | 1548 | // label must be followed by an expression, add an empty one. |
| 1381 | 1549 | try writer.print("zig_block_{d}:;\n", .{block_id}); |
| 1382 | 1550 | return result; |
| 1383 | 1551 | } |
| 1384 | 1552 | |
| 1385 | fn airBr(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1386 | const branch = o.air.instructions.items(.data)[inst].br; | |
| 1387 | const block = o.blocks.get(branch.block_inst).?; | |
| 1553 | fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1554 | const branch = f.air.instructions.items(.data)[inst].br; | |
| 1555 | const block = f.blocks.get(branch.block_inst).?; | |
| 1388 | 1556 | const result = block.result; |
| 1389 | const writer = o.writer(); | |
| 1557 | const writer = f.object.writer(); | |
| 1390 | 1558 | |
| 1391 | 1559 | // If result is .none then the value of the block is unused. |
| 1392 | 1560 | if (result != .none) { |
| 1393 | const operand = try o.resolveInst(branch.operand); | |
| 1394 | try o.writeCValue(writer, result); | |
| 1561 | const operand = try f.resolveInst(branch.operand); | |
| 1562 | try f.writeCValue(writer, result); | |
| 1395 | 1563 | try writer.writeAll(" = "); |
| 1396 | try o.writeCValue(writer, operand); | |
| 1564 | try f.writeCValue(writer, operand); | |
| 1397 | 1565 | try writer.writeAll(";\n"); |
| 1398 | 1566 | } |
| 1399 | 1567 | |
| 1400 | try o.writer().print("goto zig_block_{d};\n", .{block.block_id}); | |
| 1568 | try f.object.writer().print("goto zig_block_{d};\n", .{block.block_id}); | |
| 1401 | 1569 | return CValue.none; |
| 1402 | 1570 | } |
| 1403 | 1571 | |
| 1404 | fn airBitcast(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1405 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1406 | const operand = try o.resolveInst(ty_op.operand); | |
| 1572 | fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1573 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1574 | const operand = try f.resolveInst(ty_op.operand); | |
| 1407 | 1575 | |
| 1408 | const writer = o.writer(); | |
| 1409 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1576 | const writer = f.object.writer(); | |
| 1577 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1410 | 1578 | if (inst_ty.zigTypeTag() == .Pointer and |
| 1411 | o.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer) | |
| 1579 | f.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer) | |
| 1412 | 1580 | { |
| 1413 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1581 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1414 | 1582 | try writer.writeAll(" = ("); |
| 1415 | try o.dg.renderType(writer, inst_ty); | |
| 1583 | try f.renderType(writer, inst_ty); | |
| 1416 | 1584 | |
| 1417 | 1585 | try writer.writeAll(")"); |
| 1418 | try o.writeCValue(writer, operand); | |
| 1586 | try f.writeCValue(writer, operand); | |
| 1419 | 1587 | try writer.writeAll(";\n"); |
| 1420 | 1588 | return local; |
| 1421 | 1589 | } |
| 1422 | 1590 | |
| 1423 | const local = try o.allocLocal(inst_ty, .Mut); | |
| 1591 | const local = try f.allocLocal(inst_ty, .Mut); | |
| 1424 | 1592 | try writer.writeAll(";\n"); |
| 1425 | 1593 | |
| 1426 | 1594 | try writer.writeAll("memcpy(&"); |
| 1427 | try o.writeCValue(writer, local); | |
| 1595 | try f.writeCValue(writer, local); | |
| 1428 | 1596 | try writer.writeAll(", &"); |
| 1429 | try o.writeCValue(writer, operand); | |
| 1597 | try f.writeCValue(writer, operand); | |
| 1430 | 1598 | try writer.writeAll(", sizeof "); |
| 1431 | try o.writeCValue(writer, local); | |
| 1599 | try f.writeCValue(writer, local); | |
| 1432 | 1600 | try writer.writeAll(");\n"); |
| 1433 | 1601 | |
| 1434 | 1602 | return local; |
| 1435 | 1603 | } |
| 1436 | 1604 | |
| 1437 | fn airBreakpoint(o: *Object) !CValue { | |
| 1438 | try o.writer().writeAll("zig_breakpoint();\n"); | |
| 1605 | fn airBreakpoint(f: *Function) !CValue { | |
| 1606 | try f.object.writer().writeAll("zig_breakpoint();\n"); | |
| 1607 | return CValue.none; | |
| 1608 | } | |
| 1609 | ||
| 1610 | fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1611 | const atomic_order = f.air.instructions.items(.data)[inst].fence; | |
| 1612 | const writer = f.object.writer(); | |
| 1613 | ||
| 1614 | try writer.writeAll("zig_fence("); | |
| 1615 | try writeMemoryOrder(writer, atomic_order); | |
| 1616 | try writer.writeAll(");\n"); | |
| 1617 | ||
| 1439 | 1618 | return CValue.none; |
| 1440 | 1619 | } |
| 1441 | 1620 | |
| 1442 | fn airUnreach(o: *Object) !CValue { | |
| 1443 | try o.writer().writeAll("zig_unreachable();\n"); | |
| 1621 | fn airUnreach(f: *Function) !CValue { | |
| 1622 | try f.object.writer().writeAll("zig_unreachable();\n"); | |
| 1444 | 1623 | return CValue.none; |
| 1445 | 1624 | } |
| 1446 | 1625 | |
| 1447 | fn airLoop(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1448 | const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; | |
| 1449 | const loop = o.air.extraData(Air.Block, ty_pl.payload); | |
| 1450 | const body = o.air.extra[loop.end..][0..loop.data.body_len]; | |
| 1451 | try o.writer().writeAll("while (true) "); | |
| 1452 | try genBody(o, body); | |
| 1453 | try o.indent_writer.insertNewline(); | |
| 1626 | fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1627 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; | |
| 1628 | const loop = f.air.extraData(Air.Block, ty_pl.payload); | |
| 1629 | const body = f.air.extra[loop.end..][0..loop.data.body_len]; | |
| 1630 | try f.object.writer().writeAll("while (true) "); | |
| 1631 | try genBody(f, body); | |
| 1632 | try f.object.indent_writer.insertNewline(); | |
| 1454 | 1633 | return CValue.none; |
| 1455 | 1634 | } |
| 1456 | 1635 | |
| 1457 | fn airCondBr(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1458 | const pl_op = o.air.instructions.items(.data)[inst].pl_op; | |
| 1459 | const cond = try o.resolveInst(pl_op.operand); | |
| 1460 | const extra = o.air.extraData(Air.CondBr, pl_op.payload); | |
| 1461 | const then_body = o.air.extra[extra.end..][0..extra.data.then_body_len]; | |
| 1462 | const else_body = o.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | |
| 1463 | const writer = o.writer(); | |
| 1636 | fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1637 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | |
| 1638 | const cond = try f.resolveInst(pl_op.operand); | |
| 1639 | const extra = f.air.extraData(Air.CondBr, pl_op.payload); | |
| 1640 | const then_body = f.air.extra[extra.end..][0..extra.data.then_body_len]; | |
| 1641 | const else_body = f.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | |
| 1642 | const writer = f.object.writer(); | |
| 1464 | 1643 | |
| 1465 | 1644 | try writer.writeAll("if ("); |
| 1466 | try o.writeCValue(writer, cond); | |
| 1645 | try f.writeCValue(writer, cond); | |
| 1467 | 1646 | try writer.writeAll(") "); |
| 1468 | try genBody(o, then_body); | |
| 1647 | try genBody(f, then_body); | |
| 1469 | 1648 | try writer.writeAll(" else "); |
| 1470 | try genBody(o, else_body); | |
| 1471 | try o.indent_writer.insertNewline(); | |
| 1649 | try genBody(f, else_body); | |
| 1650 | try f.object.indent_writer.insertNewline(); | |
| 1472 | 1651 | |
| 1473 | 1652 | return CValue.none; |
| 1474 | 1653 | } |
| 1475 | 1654 | |
| 1476 | fn airSwitchBr(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1477 | const pl_op = o.air.instructions.items(.data)[inst].pl_op; | |
| 1478 | const condition = try o.resolveInst(pl_op.operand); | |
| 1479 | const condition_ty = o.air.typeOf(pl_op.operand); | |
| 1480 | const switch_br = o.air.extraData(Air.SwitchBr, pl_op.payload); | |
| 1481 | const writer = o.writer(); | |
| 1655 | fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1656 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | |
| 1657 | const condition = try f.resolveInst(pl_op.operand); | |
| 1658 | const condition_ty = f.air.typeOf(pl_op.operand); | |
| 1659 | const switch_br = f.air.extraData(Air.SwitchBr, pl_op.payload); | |
| 1660 | const writer = f.object.writer(); | |
| 1482 | 1661 | |
| 1483 | 1662 | try writer.writeAll("switch ("); |
| 1484 | try o.writeCValue(writer, condition); | |
| 1663 | try f.writeCValue(writer, condition); | |
| 1485 | 1664 | try writer.writeAll(") {"); |
| 1486 | o.indent_writer.pushIndent(); | |
| 1665 | f.object.indent_writer.pushIndent(); | |
| 1487 | 1666 | |
| 1488 | 1667 | var extra_index: usize = switch_br.end; |
| 1489 | 1668 | var case_i: u32 = 0; |
| 1490 | 1669 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| 1491 | const case = o.air.extraData(Air.SwitchBr.Case, extra_index); | |
| 1492 | const items = @bitCast([]const Air.Inst.Ref, o.air.extra[case.end..][0..case.data.items_len]); | |
| 1493 | const case_body = o.air.extra[case.end + items.len ..][0..case.data.body_len]; | |
| 1670 | const case = f.air.extraData(Air.SwitchBr.Case, extra_index); | |
| 1671 | const items = @bitCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]); | |
| 1672 | const case_body = f.air.extra[case.end + items.len ..][0..case.data.body_len]; | |
| 1494 | 1673 | extra_index = case.end + case.data.items_len + case_body.len; |
| 1495 | 1674 | |
| 1496 | 1675 | for (items) |item| { |
| 1497 | try o.indent_writer.insertNewline(); | |
| 1676 | try f.object.indent_writer.insertNewline(); | |
| 1498 | 1677 | try writer.writeAll("case "); |
| 1499 | try o.dg.renderValue(writer, condition_ty, o.air.value(item).?); | |
| 1678 | try f.object.dg.renderValue(writer, condition_ty, f.air.value(item).?); | |
| 1500 | 1679 | try writer.writeAll(": "); |
| 1501 | 1680 | } |
| 1502 | 1681 | // The case body must be noreturn so we don't need to insert a break. |
| 1503 | try genBody(o, case_body); | |
| 1682 | try genBody(f, case_body); | |
| 1504 | 1683 | } |
| 1505 | 1684 | |
| 1506 | const else_body = o.air.extra[extra_index..][0..switch_br.data.else_body_len]; | |
| 1507 | try o.indent_writer.insertNewline(); | |
| 1685 | const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len]; | |
| 1686 | try f.object.indent_writer.insertNewline(); | |
| 1508 | 1687 | try writer.writeAll("default: "); |
| 1509 | try genBody(o, else_body); | |
| 1510 | try o.indent_writer.insertNewline(); | |
| 1688 | try genBody(f, else_body); | |
| 1689 | try f.object.indent_writer.insertNewline(); | |
| 1511 | 1690 | |
| 1512 | o.indent_writer.popIndent(); | |
| 1691 | f.object.indent_writer.popIndent(); | |
| 1513 | 1692 | try writer.writeAll("}\n"); |
| 1514 | 1693 | return CValue.none; |
| 1515 | 1694 | } |
| 1516 | 1695 | |
| 1517 | fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1518 | const air_datas = o.air.instructions.items(.data); | |
| 1519 | const air_extra = o.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | |
| 1520 | const zir = o.dg.decl.namespace.file_scope.zir; | |
| 1696 | fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1697 | const air_datas = f.air.instructions.items(.data); | |
| 1698 | const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | |
| 1699 | const zir = f.object.dg.decl.namespace.file_scope.zir; | |
| 1521 | 1700 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; |
| 1522 | 1701 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); |
| 1523 | 1702 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); |
| ... | ... | @@ -1526,14 +1705,14 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1526 | 1705 | const clobbers_len = @truncate(u5, extended.small >> 10); |
| 1527 | 1706 | _ = clobbers_len; // TODO honor these |
| 1528 | 1707 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; |
| 1529 | const outputs = @bitCast([]const Air.Inst.Ref, o.air.extra[air_extra.end..][0..outputs_len]); | |
| 1530 | const args = @bitCast([]const Air.Inst.Ref, o.air.extra[air_extra.end + outputs.len ..][0..args_len]); | |
| 1708 | const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]); | |
| 1709 | const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]); | |
| 1531 | 1710 | |
| 1532 | 1711 | if (outputs_len > 1) { |
| 1533 | return o.dg.fail("TODO implement codegen for asm with more than 1 output", .{}); | |
| 1712 | return f.fail("TODO implement codegen for asm with more than 1 output", .{}); | |
| 1534 | 1713 | } |
| 1535 | 1714 | |
| 1536 | if (o.liveness.isUnused(inst) and !is_volatile) | |
| 1715 | if (f.liveness.isUnused(inst) and !is_volatile) | |
| 1537 | 1716 | return CValue.none; |
| 1538 | 1717 | |
| 1539 | 1718 | var extra_i: usize = zir_extra.end; |
| ... | ... | @@ -1548,28 +1727,28 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1548 | 1727 | }; |
| 1549 | 1728 | const args_extra_begin = extra_i; |
| 1550 | 1729 | |
| 1551 | const writer = o.writer(); | |
| 1730 | const writer = f.object.writer(); | |
| 1552 | 1731 | for (args) |arg| { |
| 1553 | 1732 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); |
| 1554 | 1733 | extra_i = input.end; |
| 1555 | 1734 | const constraint = zir.nullTerminatedString(input.data.constraint); |
| 1556 | 1735 | if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { |
| 1557 | 1736 | const reg = constraint[1 .. constraint.len - 1]; |
| 1558 | const arg_c_value = try o.resolveInst(arg); | |
| 1737 | const arg_c_value = try f.resolveInst(arg); | |
| 1559 | 1738 | try writer.writeAll("register "); |
| 1560 | try o.dg.renderType(writer, o.air.typeOf(arg)); | |
| 1739 | try f.renderType(writer, f.air.typeOf(arg)); | |
| 1561 | 1740 | |
| 1562 | 1741 | try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); |
| 1563 | try o.writeCValue(writer, arg_c_value); | |
| 1742 | try f.writeCValue(writer, arg_c_value); | |
| 1564 | 1743 | try writer.writeAll(";\n"); |
| 1565 | 1744 | } else { |
| 1566 | return o.dg.fail("TODO non-explicit inline asm regs", .{}); | |
| 1745 | return f.fail("TODO non-explicit inline asm regs", .{}); | |
| 1567 | 1746 | } |
| 1568 | 1747 | } |
| 1569 | 1748 | const volatile_string: []const u8 = if (is_volatile) "volatile " else ""; |
| 1570 | 1749 | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source }); |
| 1571 | 1750 | if (output_constraint) |_| { |
| 1572 | return o.dg.fail("TODO: CBE inline asm output", .{}); | |
| 1751 | return f.fail("TODO: CBE inline asm output", .{}); | |
| 1573 | 1752 | } |
| 1574 | 1753 | if (args.len > 0) { |
| 1575 | 1754 | if (output_constraint == null) { |
| ... | ... | @@ -1595,30 +1774,30 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1595 | 1774 | } |
| 1596 | 1775 | try writer.writeAll(");\n"); |
| 1597 | 1776 | |
| 1598 | if (o.liveness.isUnused(inst)) | |
| 1777 | if (f.liveness.isUnused(inst)) | |
| 1599 | 1778 | return CValue.none; |
| 1600 | 1779 | |
| 1601 | return o.dg.fail("TODO: C backend: inline asm expression result used", .{}); | |
| 1780 | return f.fail("TODO: C backend: inline asm expression result used", .{}); | |
| 1602 | 1781 | } |
| 1603 | 1782 | |
| 1604 | 1783 | fn airIsNull( |
| 1605 | o: *Object, | |
| 1784 | f: *Function, | |
| 1606 | 1785 | inst: Air.Inst.Index, |
| 1607 | 1786 | operator: [*:0]const u8, |
| 1608 | 1787 | deref_suffix: [*:0]const u8, |
| 1609 | 1788 | ) !CValue { |
| 1610 | if (o.liveness.isUnused(inst)) | |
| 1789 | if (f.liveness.isUnused(inst)) | |
| 1611 | 1790 | return CValue.none; |
| 1612 | 1791 | |
| 1613 | const un_op = o.air.instructions.items(.data)[inst].un_op; | |
| 1614 | const writer = o.writer(); | |
| 1615 | const operand = try o.resolveInst(un_op); | |
| 1792 | const un_op = f.air.instructions.items(.data)[inst].un_op; | |
| 1793 | const writer = f.object.writer(); | |
| 1794 | const operand = try f.resolveInst(un_op); | |
| 1616 | 1795 | |
| 1617 | const local = try o.allocLocal(Type.initTag(.bool), .Const); | |
| 1796 | const local = try f.allocLocal(Type.initTag(.bool), .Const); | |
| 1618 | 1797 | try writer.writeAll(" = ("); |
| 1619 | try o.writeCValue(writer, operand); | |
| 1798 | try f.writeCValue(writer, operand); | |
| 1620 | 1799 | |
| 1621 | if (o.air.typeOf(un_op).isPtrLikeOptional()) { | |
| 1800 | if (f.air.typeOf(un_op).isPtrLikeOptional()) { | |
| 1622 | 1801 | // operand is a regular pointer, test `operand !=/== NULL` |
| 1623 | 1802 | try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); |
| 1624 | 1803 | } else { |
| ... | ... | @@ -1627,14 +1806,14 @@ fn airIsNull( |
| 1627 | 1806 | return local; |
| 1628 | 1807 | } |
| 1629 | 1808 | |
| 1630 | fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1631 | if (o.liveness.isUnused(inst)) | |
| 1809 | fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1810 | if (f.liveness.isUnused(inst)) | |
| 1632 | 1811 | return CValue.none; |
| 1633 | 1812 | |
| 1634 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1635 | const writer = o.writer(); | |
| 1636 | const operand = try o.resolveInst(ty_op.operand); | |
| 1637 | const operand_ty = o.air.typeOf(ty_op.operand); | |
| 1813 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1814 | const writer = f.object.writer(); | |
| 1815 | const operand = try f.resolveInst(ty_op.operand); | |
| 1816 | const operand_ty = f.air.typeOf(ty_op.operand); | |
| 1638 | 1817 | |
| 1639 | 1818 | const opt_ty = if (operand_ty.zigTypeTag() == .Pointer) |
| 1640 | 1819 | operand_ty.elemType() |
| ... | ... | @@ -1647,98 +1826,98 @@ fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1647 | 1826 | return operand; |
| 1648 | 1827 | } |
| 1649 | 1828 | |
| 1650 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1829 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1651 | 1830 | const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else "."; |
| 1652 | 1831 | const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else ""; |
| 1653 | 1832 | |
| 1654 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1833 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1655 | 1834 | try writer.print(" = {s}(", .{maybe_addrof}); |
| 1656 | try o.writeCValue(writer, operand); | |
| 1835 | try f.writeCValue(writer, operand); | |
| 1657 | 1836 | |
| 1658 | 1837 | try writer.print("){s}payload;\n", .{maybe_deref}); |
| 1659 | 1838 | return local; |
| 1660 | 1839 | } |
| 1661 | 1840 | |
| 1662 | fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1663 | if (o.liveness.isUnused(inst)) | |
| 1841 | fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1842 | if (f.liveness.isUnused(inst)) | |
| 1664 | 1843 | // TODO this @as is needed because of a stage1 bug |
| 1665 | 1844 | return @as(CValue, CValue.none); |
| 1666 | 1845 | |
| 1667 | const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; | |
| 1668 | const extra = o.air.extraData(Air.StructField, ty_pl.payload).data; | |
| 1669 | const struct_ptr = try o.resolveInst(extra.struct_operand); | |
| 1670 | const struct_ptr_ty = o.air.typeOf(extra.struct_operand); | |
| 1671 | return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, extra.field_index); | |
| 1846 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; | |
| 1847 | const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; | |
| 1848 | const struct_ptr = try f.resolveInst(extra.struct_operand); | |
| 1849 | const struct_ptr_ty = f.air.typeOf(extra.struct_operand); | |
| 1850 | return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, extra.field_index); | |
| 1672 | 1851 | } |
| 1673 | 1852 | |
| 1674 | fn airStructFieldPtrIndex(o: *Object, inst: Air.Inst.Index, index: u8) !CValue { | |
| 1675 | if (o.liveness.isUnused(inst)) | |
| 1853 | fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue { | |
| 1854 | if (f.liveness.isUnused(inst)) | |
| 1676 | 1855 | // TODO this @as is needed because of a stage1 bug |
| 1677 | 1856 | return @as(CValue, CValue.none); |
| 1678 | 1857 | |
| 1679 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1680 | const struct_ptr = try o.resolveInst(ty_op.operand); | |
| 1681 | const struct_ptr_ty = o.air.typeOf(ty_op.operand); | |
| 1682 | return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, index); | |
| 1858 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1859 | const struct_ptr = try f.resolveInst(ty_op.operand); | |
| 1860 | const struct_ptr_ty = f.air.typeOf(ty_op.operand); | |
| 1861 | return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, index); | |
| 1683 | 1862 | } |
| 1684 | 1863 | |
| 1685 | fn structFieldPtr(o: *Object, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue { | |
| 1686 | const writer = o.writer(); | |
| 1864 | fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue { | |
| 1865 | const writer = f.object.writer(); | |
| 1687 | 1866 | const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data; |
| 1688 | 1867 | const field_name = struct_obj.fields.keys()[index]; |
| 1689 | 1868 | |
| 1690 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1691 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1869 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1870 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1692 | 1871 | switch (struct_ptr) { |
| 1693 | 1872 | .local_ref => |i| { |
| 1694 | 1873 | try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) }); |
| 1695 | 1874 | }, |
| 1696 | 1875 | else => { |
| 1697 | 1876 | try writer.writeAll(" = &"); |
| 1698 | try o.writeCValue(writer, struct_ptr); | |
| 1877 | try f.writeCValue(writer, struct_ptr); | |
| 1699 | 1878 | try writer.print("->{};\n", .{fmtIdent(field_name)}); |
| 1700 | 1879 | }, |
| 1701 | 1880 | } |
| 1702 | 1881 | return local; |
| 1703 | 1882 | } |
| 1704 | 1883 | |
| 1705 | fn airStructFieldVal(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1706 | if (o.liveness.isUnused(inst)) | |
| 1884 | fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1885 | if (f.liveness.isUnused(inst)) | |
| 1707 | 1886 | return CValue.none; |
| 1708 | 1887 | |
| 1709 | const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; | |
| 1710 | const extra = o.air.extraData(Air.StructField, ty_pl.payload).data; | |
| 1711 | const writer = o.writer(); | |
| 1712 | const struct_byval = try o.resolveInst(extra.struct_operand); | |
| 1713 | const struct_ty = o.air.typeOf(extra.struct_operand); | |
| 1888 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; | |
| 1889 | const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; | |
| 1890 | const writer = f.object.writer(); | |
| 1891 | const struct_byval = try f.resolveInst(extra.struct_operand); | |
| 1892 | const struct_ty = f.air.typeOf(extra.struct_operand); | |
| 1714 | 1893 | const struct_obj = struct_ty.castTag(.@"struct").?.data; |
| 1715 | 1894 | const field_name = struct_obj.fields.keys()[extra.field_index]; |
| 1716 | 1895 | |
| 1717 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1718 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1896 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1897 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1719 | 1898 | try writer.writeAll(" = "); |
| 1720 | try o.writeCValue(writer, struct_byval); | |
| 1899 | try f.writeCValue(writer, struct_byval); | |
| 1721 | 1900 | try writer.print(".{};\n", .{fmtIdent(field_name)}); |
| 1722 | 1901 | return local; |
| 1723 | 1902 | } |
| 1724 | 1903 | |
| 1725 | 1904 | // *(E!T) -> E NOT *E |
| 1726 | fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1727 | if (o.liveness.isUnused(inst)) | |
| 1905 | fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1906 | if (f.liveness.isUnused(inst)) | |
| 1728 | 1907 | return CValue.none; |
| 1729 | 1908 | |
| 1730 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1731 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1732 | const writer = o.writer(); | |
| 1733 | const operand = try o.resolveInst(ty_op.operand); | |
| 1734 | const operand_ty = o.air.typeOf(ty_op.operand); | |
| 1909 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1910 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1911 | const writer = f.object.writer(); | |
| 1912 | const operand = try f.resolveInst(ty_op.operand); | |
| 1913 | const operand_ty = f.air.typeOf(ty_op.operand); | |
| 1735 | 1914 | |
| 1736 | 1915 | const payload_ty = operand_ty.errorUnionPayload(); |
| 1737 | 1916 | if (!payload_ty.hasCodeGenBits()) { |
| 1738 | 1917 | if (operand_ty.zigTypeTag() == .Pointer) { |
| 1739 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1918 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1740 | 1919 | try writer.writeAll(" = *"); |
| 1741 | try o.writeCValue(writer, operand); | |
| 1920 | try f.writeCValue(writer, operand); | |
| 1742 | 1921 | try writer.writeAll(";\n"); |
| 1743 | 1922 | return local; |
| 1744 | 1923 | } else { |
| ... | ... | @@ -1748,154 +1927,189 @@ fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue { |
| 1748 | 1927 | |
| 1749 | 1928 | const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else "."; |
| 1750 | 1929 | |
| 1751 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1930 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1752 | 1931 | try writer.writeAll(" = ("); |
| 1753 | try o.writeCValue(writer, operand); | |
| 1932 | try f.writeCValue(writer, operand); | |
| 1754 | 1933 | |
| 1755 | 1934 | try writer.print("){s}error;\n", .{maybe_deref}); |
| 1756 | 1935 | return local; |
| 1757 | 1936 | } |
| 1758 | 1937 | |
| 1759 | fn airUnwrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1760 | if (o.liveness.isUnused(inst)) | |
| 1938 | fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1939 | if (f.liveness.isUnused(inst)) | |
| 1761 | 1940 | return CValue.none; |
| 1762 | 1941 | |
| 1763 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1764 | const writer = o.writer(); | |
| 1765 | const operand = try o.resolveInst(ty_op.operand); | |
| 1766 | const operand_ty = o.air.typeOf(ty_op.operand); | |
| 1942 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1943 | const writer = f.object.writer(); | |
| 1944 | const operand = try f.resolveInst(ty_op.operand); | |
| 1945 | const operand_ty = f.air.typeOf(ty_op.operand); | |
| 1767 | 1946 | |
| 1768 | 1947 | const payload_ty = operand_ty.errorUnionPayload(); |
| 1769 | 1948 | if (!payload_ty.hasCodeGenBits()) { |
| 1770 | 1949 | return CValue.none; |
| 1771 | 1950 | } |
| 1772 | 1951 | |
| 1773 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1952 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1774 | 1953 | const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else "."; |
| 1775 | 1954 | const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else ""; |
| 1776 | 1955 | |
| 1777 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1956 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1778 | 1957 | try writer.print(" = {s}(", .{maybe_addrof}); |
| 1779 | try o.writeCValue(writer, operand); | |
| 1958 | try f.writeCValue(writer, operand); | |
| 1780 | 1959 | |
| 1781 | 1960 | try writer.print("){s}payload;\n", .{maybe_deref}); |
| 1782 | 1961 | return local; |
| 1783 | 1962 | } |
| 1784 | 1963 | |
| 1785 | fn airWrapOptional(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1786 | if (o.liveness.isUnused(inst)) | |
| 1964 | fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1965 | if (f.liveness.isUnused(inst)) | |
| 1787 | 1966 | return CValue.none; |
| 1788 | 1967 | |
| 1789 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1790 | const writer = o.writer(); | |
| 1791 | const operand = try o.resolveInst(ty_op.operand); | |
| 1968 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1969 | const writer = f.object.writer(); | |
| 1970 | const operand = try f.resolveInst(ty_op.operand); | |
| 1792 | 1971 | |
| 1793 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1972 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1794 | 1973 | if (inst_ty.isPtrLikeOptional()) { |
| 1795 | 1974 | // the operand is just a regular pointer, no need to do anything special. |
| 1796 | 1975 | return operand; |
| 1797 | 1976 | } |
| 1798 | 1977 | |
| 1799 | 1978 | // .wrap_optional is used to convert non-optionals into optionals so it can never be null. |
| 1800 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1979 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1801 | 1980 | try writer.writeAll(" = { .is_null = false, .payload ="); |
| 1802 | try o.writeCValue(writer, operand); | |
| 1981 | try f.writeCValue(writer, operand); | |
| 1803 | 1982 | try writer.writeAll("};\n"); |
| 1804 | 1983 | return local; |
| 1805 | 1984 | } |
| 1806 | fn airWrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1807 | if (o.liveness.isUnused(inst)) | |
| 1985 | fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1986 | if (f.liveness.isUnused(inst)) | |
| 1808 | 1987 | return CValue.none; |
| 1809 | 1988 | |
| 1810 | const writer = o.writer(); | |
| 1811 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1812 | const operand = try o.resolveInst(ty_op.operand); | |
| 1989 | const writer = f.object.writer(); | |
| 1990 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 1991 | const operand = try f.resolveInst(ty_op.operand); | |
| 1813 | 1992 | |
| 1814 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1815 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1993 | const inst_ty = f.air.typeOfIndex(inst); | |
| 1994 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1816 | 1995 | try writer.writeAll(" = { .error = "); |
| 1817 | try o.writeCValue(writer, operand); | |
| 1996 | try f.writeCValue(writer, operand); | |
| 1818 | 1997 | try writer.writeAll(" };\n"); |
| 1819 | 1998 | return local; |
| 1820 | 1999 | } |
| 1821 | 2000 | |
| 1822 | fn airWrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1823 | if (o.liveness.isUnused(inst)) | |
| 2001 | fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 2002 | if (f.liveness.isUnused(inst)) | |
| 1824 | 2003 | return CValue.none; |
| 1825 | 2004 | |
| 1826 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1827 | const writer = o.writer(); | |
| 1828 | const operand = try o.resolveInst(ty_op.operand); | |
| 2005 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 2006 | const writer = f.object.writer(); | |
| 2007 | const operand = try f.resolveInst(ty_op.operand); | |
| 1829 | 2008 | |
| 1830 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1831 | const local = try o.allocLocal(inst_ty, .Const); | |
| 2009 | const inst_ty = f.air.typeOfIndex(inst); | |
| 2010 | const local = try f.allocLocal(inst_ty, .Const); | |
| 1832 | 2011 | try writer.writeAll(" = { .error = 0, .payload = "); |
| 1833 | try o.writeCValue(writer, operand); | |
| 2012 | try f.writeCValue(writer, operand); | |
| 1834 | 2013 | try writer.writeAll(" };\n"); |
| 1835 | 2014 | return local; |
| 1836 | 2015 | } |
| 1837 | 2016 | |
| 1838 | 2017 | fn airIsErr( |
| 1839 | o: *Object, | |
| 2018 | f: *Function, | |
| 1840 | 2019 | inst: Air.Inst.Index, |
| 1841 | 2020 | deref_prefix: [*:0]const u8, |
| 1842 | 2021 | deref_suffix: [*:0]const u8, |
| 1843 | 2022 | op_str: [*:0]const u8, |
| 1844 | 2023 | ) !CValue { |
| 1845 | if (o.liveness.isUnused(inst)) | |
| 2024 | if (f.liveness.isUnused(inst)) | |
| 1846 | 2025 | return CValue.none; |
| 1847 | 2026 | |
| 1848 | const un_op = o.air.instructions.items(.data)[inst].un_op; | |
| 1849 | const writer = o.writer(); | |
| 1850 | const operand = try o.resolveInst(un_op); | |
| 1851 | const operand_ty = o.air.typeOf(un_op); | |
| 1852 | const local = try o.allocLocal(Type.initTag(.bool), .Const); | |
| 2027 | const un_op = f.air.instructions.items(.data)[inst].un_op; | |
| 2028 | const writer = f.object.writer(); | |
| 2029 | const operand = try f.resolveInst(un_op); | |
| 2030 | const operand_ty = f.air.typeOf(un_op); | |
| 2031 | const local = try f.allocLocal(Type.initTag(.bool), .Const); | |
| 1853 | 2032 | const payload_ty = operand_ty.errorUnionPayload(); |
| 1854 | 2033 | if (!payload_ty.hasCodeGenBits()) { |
| 1855 | 2034 | try writer.print(" = {s}", .{deref_prefix}); |
| 1856 | try o.writeCValue(writer, operand); | |
| 2035 | try f.writeCValue(writer, operand); | |
| 1857 | 2036 | try writer.print(" {s} 0;\n", .{op_str}); |
| 1858 | 2037 | } else { |
| 1859 | 2038 | try writer.writeAll(" = "); |
| 1860 | try o.writeCValue(writer, operand); | |
| 2039 | try f.writeCValue(writer, operand); | |
| 1861 | 2040 | try writer.print("{s}error {s} 0;\n", .{ deref_suffix, op_str }); |
| 1862 | 2041 | } |
| 1863 | 2042 | return local; |
| 1864 | 2043 | } |
| 1865 | 2044 | |
| 1866 | fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue { | |
| 1867 | if (o.liveness.isUnused(inst)) | |
| 2045 | fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 2046 | if (f.liveness.isUnused(inst)) | |
| 1868 | 2047 | return CValue.none; |
| 1869 | 2048 | |
| 1870 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1871 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1872 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; | |
| 1873 | const writer = o.writer(); | |
| 1874 | const operand = try o.resolveInst(ty_op.operand); | |
| 1875 | const array_len = o.air.typeOf(ty_op.operand).elemType().arrayLen(); | |
| 2049 | const inst_ty = f.air.typeOfIndex(inst); | |
| 2050 | const local = try f.allocLocal(inst_ty, .Const); | |
| 2051 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 2052 | const writer = f.object.writer(); | |
| 2053 | const operand = try f.resolveInst(ty_op.operand); | |
| 2054 | const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen(); | |
| 1876 | 2055 | |
| 1877 | 2056 | try writer.writeAll(" = { .ptr = "); |
| 1878 | try o.writeCValue(writer, operand); | |
| 2057 | try f.writeCValue(writer, operand); | |
| 1879 | 2058 | try writer.print(", .len = {d} }};\n", .{array_len}); |
| 1880 | 2059 | return local; |
| 1881 | 2060 | } |
| 1882 | 2061 | |
| 1883 | fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { | |
| 1884 | const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; | |
| 1885 | const extra = o.air.extraData(Air.Cmpxchg, ty_pl.payload).data; | |
| 1886 | const inst_ty = o.air.typeOfIndex(inst); | |
| 1887 | const ptr = try o.resolveInst(extra.ptr); | |
| 1888 | const expected_value = try o.resolveInst(extra.expected_value); | |
| 1889 | const new_value = try o.resolveInst(extra.new_value); | |
| 1890 | const local = try o.allocLocal(inst_ty, .Const); | |
| 1891 | const writer = o.writer(); | |
| 2062 | /// Emits a local variable with the result type and initializes it | |
| 2063 | /// with the operand. | |
| 2064 | fn airSimpleCast(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 2065 | if (f.liveness.isUnused(inst)) | |
| 2066 | return CValue.none; | |
| 2067 | ||
| 2068 | const inst_ty = f.air.typeOfIndex(inst); | |
| 2069 | const local = try f.allocLocal(inst_ty, .Const); | |
| 2070 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 2071 | const writer = f.object.writer(); | |
| 2072 | const operand = try f.resolveInst(ty_op.operand); | |
| 2073 | ||
| 2074 | try writer.writeAll(" = "); | |
| 2075 | try f.writeCValue(writer, operand); | |
| 2076 | try writer.writeAll(";\n"); | |
| 2077 | return local; | |
| 2078 | } | |
| 2079 | ||
| 2080 | fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !CValue { | |
| 2081 | if (f.liveness.isUnused(inst)) return CValue.none; | |
| 2082 | ||
| 2083 | const inst_ty = f.air.typeOfIndex(inst); | |
| 2084 | const local = try f.allocLocal(inst_ty, .Const); | |
| 2085 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 2086 | const writer = f.object.writer(); | |
| 2087 | const operand = try f.resolveInst(ty_op.operand); | |
| 2088 | ||
| 2089 | // TODO implement the function in zig.h and call it here | |
| 2090 | ||
| 2091 | try writer.print(" = {s}(", .{fn_name}); | |
| 2092 | try f.writeCValue(writer, operand); | |
| 2093 | try writer.writeAll(");\n"); | |
| 2094 | return local; | |
| 2095 | } | |
| 2096 | ||
| 2097 | fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { | |
| 2098 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; | |
| 2099 | const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data; | |
| 2100 | const inst_ty = f.air.typeOfIndex(inst); | |
| 2101 | const ptr = try f.resolveInst(extra.ptr); | |
| 2102 | const expected_value = try f.resolveInst(extra.expected_value); | |
| 2103 | const new_value = try f.resolveInst(extra.new_value); | |
| 2104 | const local = try f.allocLocal(inst_ty, .Const); | |
| 2105 | const writer = f.object.writer(); | |
| 1892 | 2106 | |
| 1893 | 2107 | try writer.print(" = zig_cmpxchg_{s}(", .{flavor}); |
| 1894 | try o.writeCValue(writer, ptr); | |
| 2108 | try f.writeCValue(writer, ptr); | |
| 1895 | 2109 | try writer.writeAll(", "); |
| 1896 | try o.writeCValue(writer, expected_value); | |
| 2110 | try f.writeCValue(writer, expected_value); | |
| 1897 | 2111 | try writer.writeAll(", "); |
| 1898 | try o.writeCValue(writer, new_value); | |
| 2112 | try f.writeCValue(writer, new_value); | |
| 1899 | 2113 | try writer.writeAll(", "); |
| 1900 | 2114 | try writeMemoryOrder(writer, extra.successOrder()); |
| 1901 | 2115 | try writer.writeAll(", "); |
| ... | ... | @@ -1905,8 +2119,134 @@ fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { |
| 1905 | 2119 | return local; |
| 1906 | 2120 | } |
| 1907 | 2121 | |
| 1908 | fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void { | |
| 1909 | const str = switch (order) { | |
| 2122 | fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 2123 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | |
| 2124 | const extra = f.air.extraData(Air.AtomicRmw, pl_op.payload).data; | |
| 2125 | const inst_ty = f.air.typeOfIndex(inst); | |
| 2126 | const ptr = try f.resolveInst(pl_op.operand); | |
| 2127 | const operand = try f.resolveInst(extra.operand); | |
| 2128 | const local = try f.allocLocal(inst_ty, .Const); | |
| 2129 | const writer = f.object.writer(); | |
| 2130 | ||
| 2131 | try writer.print(" = zig_atomicrmw_{s}(", .{toAtomicRmwSuffix(extra.op())}); | |
| 2132 | try f.writeCValue(writer, ptr); | |
| 2133 | try writer.writeAll(", "); | |
| 2134 | try f.writeCValue(writer, operand); | |
| 2135 | try writer.writeAll(", "); | |
| 2136 | try writeMemoryOrder(writer, extra.ordering()); | |
| 2137 | try writer.writeAll(");\n"); | |
| 2138 | ||
| 2139 | return local; | |
| 2140 | } | |
| 2141 | ||
| 2142 | fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 2143 | const atomic_load = f.air.instructions.items(.data)[inst].atomic_load; | |
| 2144 | const ptr = try f.resolveInst(atomic_load.ptr); | |
| 2145 | const ptr_ty = f.air.typeOf(atomic_load.ptr); | |
| 2146 | if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) | |
| 2147 | return CValue.none; | |
| 2148 | ||
| 2149 | const inst_ty = f.air.typeOfIndex(inst); | |
| 2150 | const local = try f.allocLocal(inst_ty, .Const); | |
| 2151 | const writer = f.object.writer(); | |
| 2152 | ||
| 2153 | try writer.writeAll(" = zig_atomic_load("); | |
| 2154 | try f.writeCValue(writer, ptr); | |
| 2155 | try writer.writeAll(", "); | |
| 2156 | try writeMemoryOrder(writer, atomic_load.order); | |
| 2157 | try writer.writeAll(");\n"); | |
| 2158 | ||
| 2159 | return local; | |
| 2160 | } | |
| 2161 | ||
| 2162 | fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CValue { | |
| 2163 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | |
| 2164 | const ptr = try f.resolveInst(bin_op.lhs); | |
| 2165 | const element = try f.resolveInst(bin_op.rhs); | |
| 2166 | const inst_ty = f.air.typeOfIndex(inst); | |
| 2167 | const local = try f.allocLocal(inst_ty, .Const); | |
| 2168 | const writer = f.object.writer(); | |
| 2169 | ||
| 2170 | try writer.writeAll(" = zig_atomic_store("); | |
| 2171 | try f.writeCValue(writer, ptr); | |
| 2172 | try writer.writeAll(", "); | |
| 2173 | try f.writeCValue(writer, element); | |
| 2174 | try writer.print(", {s});\n", .{order}); | |
| 2175 | ||
| 2176 | return local; | |
| 2177 | } | |
| 2178 | ||
| 2179 | fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 2180 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | |
| 2181 | const extra = f.air.extraData(Air.Bin, pl_op.payload).data; | |
| 2182 | const dest_ptr = try f.resolveInst(pl_op.operand); | |
| 2183 | const value = try f.resolveInst(extra.lhs); | |
| 2184 | const len = try f.resolveInst(extra.rhs); | |
| 2185 | const writer = f.object.writer(); | |
| 2186 | ||
| 2187 | try writer.writeAll("memset("); | |
| 2188 | try f.writeCValue(writer, dest_ptr); | |
| 2189 | try writer.writeAll(", "); | |
| 2190 | try f.writeCValue(writer, value); | |
| 2191 | try writer.writeAll(", "); | |
| 2192 | try f.writeCValue(writer, len); | |
| 2193 | try writer.writeAll(");\n"); | |
| 2194 | ||
| 2195 | return CValue.none; | |
| 2196 | } | |
| 2197 | ||
| 2198 | fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 2199 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | |
| 2200 | const extra = f.air.extraData(Air.Bin, pl_op.payload).data; | |
| 2201 | const dest_ptr = try f.resolveInst(pl_op.operand); | |
| 2202 | const src_ptr = try f.resolveInst(extra.lhs); | |
| 2203 | const len = try f.resolveInst(extra.rhs); | |
| 2204 | const writer = f.object.writer(); | |
| 2205 | ||
| 2206 | try writer.writeAll("memcpy("); | |
| 2207 | try f.writeCValue(writer, dest_ptr); | |
| 2208 | try writer.writeAll(", "); | |
| 2209 | try f.writeCValue(writer, src_ptr); | |
| 2210 | try writer.writeAll(", "); | |
| 2211 | try f.writeCValue(writer, len); | |
| 2212 | try writer.writeAll(");\n"); | |
| 2213 | ||
| 2214 | return CValue.none; | |
| 2215 | } | |
| 2216 | ||
| 2217 | fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 2218 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | |
| 2219 | const union_ptr = try f.resolveInst(bin_op.lhs); | |
| 2220 | const new_tag = try f.resolveInst(bin_op.rhs); | |
| 2221 | const writer = f.object.writer(); | |
| 2222 | ||
| 2223 | try writer.writeAll("*"); | |
| 2224 | try f.writeCValue(writer, union_ptr); | |
| 2225 | try writer.writeAll(" = "); | |
| 2226 | try f.writeCValue(writer, new_tag); | |
| 2227 | try writer.writeAll(";\n"); | |
| 2228 | ||
| 2229 | return CValue.none; | |
| 2230 | } | |
| 2231 | ||
| 2232 | fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 2233 | if (f.liveness.isUnused(inst)) | |
| 2234 | return CValue.none; | |
| 2235 | ||
| 2236 | const inst_ty = f.air.typeOfIndex(inst); | |
| 2237 | const local = try f.allocLocal(inst_ty, .Const); | |
| 2238 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | |
| 2239 | const writer = f.object.writer(); | |
| 2240 | const operand = try f.resolveInst(ty_op.operand); | |
| 2241 | ||
| 2242 | try writer.writeAll("get_union_tag("); | |
| 2243 | try f.writeCValue(writer, operand); | |
| 2244 | try writer.writeAll(");\n"); | |
| 2245 | return local; | |
| 2246 | } | |
| 2247 | ||
| 2248 | fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { | |
| 2249 | return switch (order) { | |
| 1910 | 2250 | .Unordered => "memory_order_relaxed", |
| 1911 | 2251 | .Monotonic => "memory_order_consume", |
| 1912 | 2252 | .Acquire => "memory_order_acquire", |
| ... | ... | @@ -1914,7 +2254,24 @@ fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void { |
| 1914 | 2254 | .AcqRel => "memory_order_acq_rel", |
| 1915 | 2255 | .SeqCst => "memory_order_seq_cst", |
| 1916 | 2256 | }; |
| 1917 | return w.writeAll(str); | |
| 2257 | } | |
| 2258 | ||
| 2259 | fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void { | |
| 2260 | return w.writeAll(toMemoryOrder(order)); | |
| 2261 | } | |
| 2262 | ||
| 2263 | fn toAtomicRmwSuffix(order: std.builtin.AtomicRmwOp) []const u8 { | |
| 2264 | return switch (order) { | |
| 2265 | .Xchg => "xchg", | |
| 2266 | .Add => "add", | |
| 2267 | .Sub => "sub", | |
| 2268 | .And => "and", | |
| 2269 | .Nand => "nand", | |
| 2270 | .Or => "or", | |
| 2271 | .Xor => "xor", | |
| 2272 | .Max => "max", | |
| 2273 | .Min => "min", | |
| 2274 | }; | |
| 1918 | 2275 | } |
| 1919 | 2276 | |
| 1920 | 2277 | fn IndentWriter(comptime UnderlyingWriter: type) type { |
src/codegen/llvm.zig+1032-210| ... | ... | @@ -158,15 +158,42 @@ pub const Object = struct { |
| 158 | 158 | llvm_module: *const llvm.Module, |
| 159 | 159 | context: *const llvm.Context, |
| 160 | 160 | target_machine: *const llvm.TargetMachine, |
| 161 | ||
| 162 | pub fn create(gpa: *Allocator, options: link.Options) !*Object { | |
| 161 | /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function, | |
| 162 | /// but that has some downsides: | |
| 163 | /// * we have to compute the fully qualified name every time we want to do the lookup | |
| 164 | /// * for externally linked functions, the name is not fully qualified, but when | |
| 165 | /// a Decl goes from exported to not exported and vice-versa, we would use the wrong | |
| 166 | /// version of the name and incorrectly get function not found in the llvm module. | |
| 167 | /// * it works for functions not all globals. | |
| 168 | /// Therefore, this table keeps track of the mapping. | |
| 169 | decl_map: std.AutoHashMapUnmanaged(*const Module.Decl, *const llvm.Value), | |
| 170 | /// Maps Zig types to LLVM types. The table memory itself is backed by the GPA of | |
| 171 | /// the compiler, but the Type/Value memory here is backed by `type_map_arena`. | |
| 172 | /// TODO we need to remove entries from this map in response to incremental compilation | |
| 173 | /// but I think the frontend won't tell us about types that get deleted because | |
| 174 | /// hasCodeGenBits() is false for types. | |
| 175 | type_map: TypeMap, | |
| 176 | /// The backing memory for `type_map`. Periodically garbage collected after flush(). | |
| 177 | /// The code for doing the periodical GC is not yet implemented. | |
| 178 | type_map_arena: std.heap.ArenaAllocator, | |
| 179 | /// Where to put the output object file, relative to bin_file.options.emit directory. | |
| 180 | sub_path: []const u8, | |
| 181 | ||
| 182 | pub const TypeMap = std.HashMapUnmanaged( | |
| 183 | Type, | |
| 184 | *const llvm.Type, | |
| 185 | Type.HashContext64, | |
| 186 | std.hash_map.default_max_load_percentage, | |
| 187 | ); | |
| 188 | ||
| 189 | pub fn create(gpa: *Allocator, sub_path: []const u8, options: link.Options) !*Object { | |
| 163 | 190 | const obj = try gpa.create(Object); |
| 164 | 191 | errdefer gpa.destroy(obj); |
| 165 | obj.* = try Object.init(gpa, options); | |
| 192 | obj.* = try Object.init(gpa, sub_path, options); | |
| 166 | 193 | return obj; |
| 167 | 194 | } |
| 168 | 195 | |
| 169 | pub fn init(gpa: *Allocator, options: link.Options) !Object { | |
| 196 | pub fn init(gpa: *Allocator, sub_path: []const u8, options: link.Options) !Object { | |
| 170 | 197 | const context = llvm.Context.create(); |
| 171 | 198 | errdefer context.dispose(); |
| 172 | 199 | |
| ... | ... | @@ -240,22 +267,34 @@ pub const Object = struct { |
| 240 | 267 | ); |
| 241 | 268 | errdefer target_machine.dispose(); |
| 242 | 269 | |
| 270 | const target_data = target_machine.createTargetDataLayout(); | |
| 271 | defer target_data.dispose(); | |
| 272 | ||
| 273 | llvm_module.setModuleDataLayout(target_data); | |
| 274 | ||
| 243 | 275 | return Object{ |
| 244 | 276 | .llvm_module = llvm_module, |
| 245 | 277 | .context = context, |
| 246 | 278 | .target_machine = target_machine, |
| 279 | .decl_map = .{}, | |
| 280 | .type_map = .{}, | |
| 281 | .type_map_arena = std.heap.ArenaAllocator.init(gpa), | |
| 282 | .sub_path = sub_path, | |
| 247 | 283 | }; |
| 248 | 284 | } |
| 249 | 285 | |
| 250 | pub fn deinit(self: *Object) void { | |
| 286 | pub fn deinit(self: *Object, gpa: *Allocator) void { | |
| 251 | 287 | self.target_machine.dispose(); |
| 252 | 288 | self.llvm_module.dispose(); |
| 253 | 289 | self.context.dispose(); |
| 290 | self.decl_map.deinit(gpa); | |
| 291 | self.type_map.deinit(gpa); | |
| 292 | self.type_map_arena.deinit(); | |
| 254 | 293 | self.* = undefined; |
| 255 | 294 | } |
| 256 | 295 | |
| 257 | 296 | pub fn destroy(self: *Object, gpa: *Allocator) void { |
| 258 | self.deinit(); | |
| 297 | self.deinit(gpa); | |
| 259 | 298 | gpa.destroy(self); |
| 260 | 299 | } |
| 261 | 300 | |
| ... | ... | @@ -293,23 +332,18 @@ pub const Object = struct { |
| 293 | 332 | const mod = comp.bin_file.options.module.?; |
| 294 | 333 | const cache_dir = mod.zig_cache_artifact_directory; |
| 295 | 334 | |
| 296 | const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit != null) blk: { | |
| 297 | const obj_basename = try std.zig.binNameAlloc(arena, .{ | |
| 298 | .root_name = comp.bin_file.options.root_name, | |
| 299 | .target = comp.bin_file.options.target, | |
| 300 | .output_mode = .Obj, | |
| 301 | }); | |
| 302 | if (cache_dir.joinZ(arena, &[_][]const u8{obj_basename})) |p| { | |
| 303 | break :blk p.ptr; | |
| 304 | } else |err| { | |
| 305 | return err; | |
| 306 | } | |
| 307 | } else null; | |
| 335 | const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| | |
| 336 | try emit.directory.joinZ(arena, &[_][]const u8{self.sub_path}) | |
| 337 | else | |
| 338 | null; | |
| 308 | 339 | |
| 309 | 340 | const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir); |
| 310 | 341 | const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir); |
| 311 | 342 | const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir); |
| 312 | 343 | |
| 344 | const debug_emit_path = emit_bin_path orelse "(none)"; | |
| 345 | log.debug("emit LLVM object to {s}", .{debug_emit_path}); | |
| 346 | ||
| 313 | 347 | var error_message: [*:0]const u8 = undefined; |
| 314 | 348 | if (self.target_machine.emitToFile( |
| 315 | 349 | self.llvm_module, |
| ... | ... | @@ -358,18 +392,31 @@ pub const Object = struct { |
| 358 | 392 | |
| 359 | 393 | const llvm_func = try dg.resolveLlvmFunction(decl); |
| 360 | 394 | |
| 395 | if (module.align_stack_fns.get(func)) |align_info| { | |
| 396 | dg.addFnAttrInt(llvm_func, "alignstack", align_info.alignment); | |
| 397 | dg.addFnAttr(llvm_func, "noinline"); | |
| 398 | } else { | |
| 399 | DeclGen.removeFnAttr(llvm_func, "alignstack"); | |
| 400 | if (!func.is_noinline) DeclGen.removeFnAttr(llvm_func, "noinline"); | |
| 401 | } | |
| 402 | ||
| 403 | if (func.is_cold) { | |
| 404 | dg.addFnAttr(llvm_func, "cold"); | |
| 405 | } else { | |
| 406 | DeclGen.removeFnAttr(llvm_func, "cold"); | |
| 407 | } | |
| 408 | ||
| 361 | 409 | // This gets the LLVM values from the function and stores them in `dg.args`. |
| 362 | const fn_param_len = decl.ty.fnParamLen(); | |
| 363 | var args = try dg.gpa.alloc(*const llvm.Value, fn_param_len); | |
| 410 | const fn_info = decl.ty.fnInfo(); | |
| 411 | var args = try dg.gpa.alloc(*const llvm.Value, fn_info.param_types.len); | |
| 364 | 412 | |
| 365 | 413 | for (args) |*arg, i| { |
| 366 | 414 | arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); |
| 367 | 415 | } |
| 368 | 416 | |
| 369 | // We remove all the basic blocks of a function to support incremental | |
| 370 | // compilation! | |
| 371 | // TODO: remove all basic blocks if functions can have more than one | |
| 372 | if (llvm_func.getFirstBasicBlock()) |bb| { | |
| 417 | // Remove all the basic blocks of a function in order to start over, generating | |
| 418 | // LLVM IR from an empty function body. | |
| 419 | while (llvm_func.getFirstBasicBlock()) |bb| { | |
| 373 | 420 | bb.deleteBasicBlock(); |
| 374 | 421 | } |
| 375 | 422 | |
| ... | ... | @@ -440,30 +487,62 @@ pub const Object = struct { |
| 440 | 487 | ) !void { |
| 441 | 488 | // If the module does not already have the function, we ignore this function call |
| 442 | 489 | // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`. |
| 443 | const llvm_fn = self.llvm_module.getNamedFunction(decl.name) orelse return; | |
| 490 | const llvm_fn = self.decl_map.get(decl) orelse return; | |
| 444 | 491 | const is_extern = decl.val.tag() == .extern_fn; |
| 445 | if (is_extern or exports.len != 0) { | |
| 492 | if (is_extern) { | |
| 493 | llvm_fn.setValueName(decl.name); | |
| 494 | llvm_fn.setUnnamedAddr(.False); | |
| 446 | 495 | llvm_fn.setLinkage(.External); |
| 496 | } else if (exports.len != 0) { | |
| 497 | const exp_name = exports[0].options.name; | |
| 498 | llvm_fn.setValueName2(exp_name.ptr, exp_name.len); | |
| 447 | 499 | llvm_fn.setUnnamedAddr(.False); |
| 500 | switch (exports[0].options.linkage) { | |
| 501 | .Internal => unreachable, | |
| 502 | .Strong => llvm_fn.setLinkage(.External), | |
| 503 | .Weak => llvm_fn.setLinkage(.WeakODR), | |
| 504 | .LinkOnce => llvm_fn.setLinkage(.LinkOnceODR), | |
| 505 | } | |
| 506 | // If a Decl is exported more than one time (which is rare), | |
| 507 | // we add aliases for all but the first export. | |
| 508 | // TODO LLVM C API does not support deleting aliases. We need to | |
| 509 | // patch it to support this or figure out how to wrap the C++ API ourselves. | |
| 510 | // Until then we iterate over existing aliases and make them point | |
| 511 | // to the correct decl, or otherwise add a new alias. Old aliases are leaked. | |
| 512 | for (exports[1..]) |exp| { | |
| 513 | const exp_name_z = try module.gpa.dupeZ(u8, exp.options.name); | |
| 514 | defer module.gpa.free(exp_name_z); | |
| 515 | ||
| 516 | if (self.llvm_module.getNamedGlobalAlias(exp_name_z.ptr, exp_name_z.len)) |alias| { | |
| 517 | alias.setAliasee(llvm_fn); | |
| 518 | } else { | |
| 519 | const alias = self.llvm_module.addAlias(llvm_fn.typeOf(), llvm_fn, exp_name_z); | |
| 520 | switch (exp.options.linkage) { | |
| 521 | .Internal => alias.setLinkage(.Internal), | |
| 522 | .Strong => alias.setLinkage(.External), | |
| 523 | .Weak => { | |
| 524 | if (is_extern) { | |
| 525 | alias.setLinkage(.ExternalWeak); | |
| 526 | } else { | |
| 527 | alias.setLinkage(.WeakODR); | |
| 528 | } | |
| 529 | }, | |
| 530 | .LinkOnce => alias.setLinkage(.LinkOnceODR), | |
| 531 | } | |
| 532 | } | |
| 533 | } | |
| 448 | 534 | } else { |
| 535 | const fqn = try decl.getFullyQualifiedName(module.gpa); | |
| 536 | defer module.gpa.free(fqn); | |
| 537 | llvm_fn.setValueName2(fqn.ptr, fqn.len); | |
| 449 | 538 | llvm_fn.setLinkage(.Internal); |
| 450 | 539 | llvm_fn.setUnnamedAddr(.True); |
| 451 | 540 | } |
| 452 | // TODO LLVM C API does not support deleting aliases. We need to | |
| 453 | // patch it to support this or figure out how to wrap the C++ API ourselves. | |
| 454 | // Until then we iterate over existing aliases and make them point | |
| 455 | // to the correct decl, or otherwise add a new alias. Old aliases are leaked. | |
| 456 | for (exports) |exp| { | |
| 457 | const exp_name_z = try module.gpa.dupeZ(u8, exp.options.name); | |
| 458 | defer module.gpa.free(exp_name_z); | |
| 541 | } | |
| 459 | 542 | |
| 460 | if (self.llvm_module.getNamedGlobalAlias(exp_name_z.ptr, exp_name_z.len)) |alias| { | |
| 461 | alias.setAliasee(llvm_fn); | |
| 462 | } else { | |
| 463 | const alias = self.llvm_module.addAlias(llvm_fn.typeOf(), llvm_fn, exp_name_z); | |
| 464 | _ = alias; | |
| 465 | } | |
| 466 | } | |
| 543 | pub fn freeDecl(self: *Object, decl: *Module.Decl) void { | |
| 544 | const llvm_value = self.decl_map.get(decl) orelse return; | |
| 545 | llvm_value.deleteGlobal(); | |
| 467 | 546 | } |
| 468 | 547 | }; |
| 469 | 548 | |
| ... | ... | @@ -472,9 +551,8 @@ pub const DeclGen = struct { |
| 472 | 551 | object: *Object, |
| 473 | 552 | module: *Module, |
| 474 | 553 | decl: *Module.Decl, |
| 475 | err_msg: ?*Module.ErrorMsg, | |
| 476 | ||
| 477 | 554 | gpa: *Allocator, |
| 555 | err_msg: ?*Module.ErrorMsg, | |
| 478 | 556 | |
| 479 | 557 | fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 480 | 558 | @setCold(true); |
| ... | ... | @@ -515,33 +593,47 @@ pub const DeclGen = struct { |
| 515 | 593 | } |
| 516 | 594 | } |
| 517 | 595 | |
| 518 | /// If the llvm function does not exist, create it | |
| 596 | /// If the llvm function does not exist, create it. | |
| 597 | /// Note that this can be called before the function's semantic analysis has | |
| 598 | /// completed, so if any attributes rely on that, they must be done in updateFunc, not here. | |
| 519 | 599 | fn resolveLlvmFunction(self: *DeclGen, decl: *Module.Decl) !*const llvm.Value { |
| 520 | if (self.llvmModule().getNamedFunction(decl.name)) |llvm_fn| return llvm_fn; | |
| 600 | const gop = try self.object.decl_map.getOrPut(self.gpa, decl); | |
| 601 | if (gop.found_existing) return gop.value_ptr.*; | |
| 521 | 602 | |
| 522 | 603 | assert(decl.has_tv); |
| 523 | 604 | const zig_fn_type = decl.ty; |
| 524 | const return_type = zig_fn_type.fnReturnType(); | |
| 525 | const fn_param_len = zig_fn_type.fnParamLen(); | |
| 605 | const fn_info = zig_fn_type.fnInfo(); | |
| 606 | const return_type = fn_info.return_type; | |
| 526 | 607 | |
| 527 | const fn_param_types = try self.gpa.alloc(Type, fn_param_len); | |
| 528 | defer self.gpa.free(fn_param_types); | |
| 529 | zig_fn_type.fnParamTypes(fn_param_types); | |
| 608 | const llvm_param_buffer = try self.gpa.alloc(*const llvm.Type, fn_info.param_types.len); | |
| 609 | defer self.gpa.free(llvm_param_buffer); | |
| 530 | 610 | |
| 531 | const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len); | |
| 532 | defer self.gpa.free(llvm_param); | |
| 533 | ||
| 534 | for (fn_param_types) |fn_param, i| { | |
| 535 | llvm_param[i] = try self.llvmType(fn_param); | |
| 611 | var llvm_params_len: c_uint = 0; | |
| 612 | for (fn_info.param_types) |param_ty| { | |
| 613 | if (param_ty.hasCodeGenBits()) { | |
| 614 | llvm_param_buffer[llvm_params_len] = try self.llvmType(param_ty); | |
| 615 | llvm_params_len += 1; | |
| 616 | } | |
| 536 | 617 | } |
| 537 | 618 | |
| 619 | const llvm_ret_ty = if (!return_type.hasCodeGenBits()) | |
| 620 | self.context.voidType() | |
| 621 | else | |
| 622 | try self.llvmType(return_type); | |
| 623 | ||
| 538 | 624 | const fn_type = llvm.functionType( |
| 539 | try self.llvmType(return_type), | |
| 540 | llvm_param.ptr, | |
| 541 | @intCast(c_uint, fn_param_len), | |
| 625 | llvm_ret_ty, | |
| 626 | llvm_param_buffer.ptr, | |
| 627 | llvm_params_len, | |
| 542 | 628 | .False, |
| 543 | 629 | ); |
| 544 | const llvm_fn = self.llvmModule().addFunction(decl.name, fn_type); | |
| 630 | const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace"); | |
| 631 | ||
| 632 | const fqn = try decl.getFullyQualifiedName(self.gpa); | |
| 633 | defer self.gpa.free(fqn); | |
| 634 | ||
| 635 | const llvm_fn = self.llvmModule().addFunctionInAddressSpace(fqn, fn_type, llvm_addrspace); | |
| 636 | gop.value_ptr.* = llvm_fn; | |
| 545 | 637 | |
| 546 | 638 | const is_extern = decl.val.tag() == .extern_fn; |
| 547 | 639 | if (!is_extern) { |
| ... | ... | @@ -549,8 +641,85 @@ pub const DeclGen = struct { |
| 549 | 641 | llvm_fn.setUnnamedAddr(.True); |
| 550 | 642 | } |
| 551 | 643 | |
| 552 | // TODO: calling convention, linkage, tsan, etc. see codegen.cpp `make_fn_llvm_value`. | |
| 644 | // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`. | |
| 645 | const target = self.module.getTarget(); | |
| 646 | switch (fn_info.cc) { | |
| 647 | .Unspecified, .Inline, .Async => { | |
| 648 | llvm_fn.setFunctionCallConv(.Fast); | |
| 649 | }, | |
| 650 | .C => { | |
| 651 | llvm_fn.setFunctionCallConv(.C); | |
| 652 | }, | |
| 653 | .Naked => { | |
| 654 | self.addFnAttr(llvm_fn, "naked"); | |
| 655 | }, | |
| 656 | .Stdcall => { | |
| 657 | llvm_fn.setFunctionCallConv(.X86_StdCall); | |
| 658 | }, | |
| 659 | .Fastcall => { | |
| 660 | llvm_fn.setFunctionCallConv(.X86_FastCall); | |
| 661 | }, | |
| 662 | .Vectorcall => { | |
| 663 | switch (target.cpu.arch) { | |
| 664 | .i386, .x86_64 => { | |
| 665 | llvm_fn.setFunctionCallConv(.X86_VectorCall); | |
| 666 | }, | |
| 667 | .aarch64, .aarch64_be, .aarch64_32 => { | |
| 668 | llvm_fn.setFunctionCallConv(.AArch64_VectorCall); | |
| 669 | }, | |
| 670 | else => unreachable, | |
| 671 | } | |
| 672 | }, | |
| 673 | .Thiscall => { | |
| 674 | llvm_fn.setFunctionCallConv(.X86_ThisCall); | |
| 675 | }, | |
| 676 | .APCS => { | |
| 677 | llvm_fn.setFunctionCallConv(.ARM_APCS); | |
| 678 | }, | |
| 679 | .AAPCS => { | |
| 680 | llvm_fn.setFunctionCallConv(.ARM_AAPCS); | |
| 681 | }, | |
| 682 | .AAPCSVFP => { | |
| 683 | llvm_fn.setFunctionCallConv(.ARM_AAPCS_VFP); | |
| 684 | }, | |
| 685 | .Interrupt => { | |
| 686 | switch (target.cpu.arch) { | |
| 687 | .i386, .x86_64 => { | |
| 688 | llvm_fn.setFunctionCallConv(.X86_INTR); | |
| 689 | }, | |
| 690 | .avr => { | |
| 691 | llvm_fn.setFunctionCallConv(.AVR_INTR); | |
| 692 | }, | |
| 693 | .msp430 => { | |
| 694 | llvm_fn.setFunctionCallConv(.MSP430_INTR); | |
| 695 | }, | |
| 696 | else => unreachable, | |
| 697 | } | |
| 698 | }, | |
| 699 | .Signal => { | |
| 700 | llvm_fn.setFunctionCallConv(.AVR_SIGNAL); | |
| 701 | }, | |
| 702 | .SysV => { | |
| 703 | llvm_fn.setFunctionCallConv(.X86_64_SysV); | |
| 704 | }, | |
| 705 | } | |
| 553 | 706 | |
| 707 | // Function attributes that are independent of analysis results of the function body. | |
| 708 | if (!self.module.comp.bin_file.options.red_zone) { | |
| 709 | self.addFnAttr(llvm_fn, "noredzone"); | |
| 710 | } | |
| 711 | self.addFnAttr(llvm_fn, "nounwind"); | |
| 712 | if (self.module.comp.unwind_tables) { | |
| 713 | self.addFnAttr(llvm_fn, "uwtable"); | |
| 714 | } | |
| 715 | if (self.module.comp.bin_file.options.optimize_mode == .ReleaseSmall) { | |
| 716 | self.addFnAttr(llvm_fn, "minsize"); | |
| 717 | self.addFnAttr(llvm_fn, "optsize"); | |
| 718 | } | |
| 719 | if (self.module.comp.bin_file.options.tsan) { | |
| 720 | self.addFnAttr(llvm_fn, "sanitize_thread"); | |
| 721 | } | |
| 722 | // TODO add target-cpu and target-features fn attributes | |
| 554 | 723 | if (return_type.isNoReturn()) { |
| 555 | 724 | self.addFnAttr(llvm_fn, "noreturn"); |
| 556 | 725 | } |
| ... | ... | @@ -563,18 +732,41 @@ pub const DeclGen = struct { |
| 563 | 732 | if (llvm_module.getNamedGlobal(decl.name)) |val| return val; |
| 564 | 733 | // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`. |
| 565 | 734 | const llvm_type = try self.llvmType(decl.ty); |
| 566 | return llvm_module.addGlobal(llvm_type, decl.name); | |
| 735 | const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace"); | |
| 736 | return llvm_module.addGlobalInAddressSpace(llvm_type, decl.name, llvm_addrspace); | |
| 737 | } | |
| 738 | ||
| 739 | fn llvmAddressSpace(self: DeclGen, address_space: std.builtin.AddressSpace) c_uint { | |
| 740 | const target = self.module.getTarget(); | |
| 741 | return switch (target.cpu.arch) { | |
| 742 | .i386, .x86_64 => switch (address_space) { | |
| 743 | .generic => llvm.address_space.default, | |
| 744 | .gs => llvm.address_space.x86.gs, | |
| 745 | .fs => llvm.address_space.x86.fs, | |
| 746 | .ss => llvm.address_space.x86.ss, | |
| 747 | }, | |
| 748 | else => switch (address_space) { | |
| 749 | .generic => llvm.address_space.default, | |
| 750 | else => unreachable, | |
| 751 | }, | |
| 752 | }; | |
| 567 | 753 | } |
| 568 | 754 | |
| 569 | 755 | fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type { |
| 756 | const gpa = self.gpa; | |
| 570 | 757 | log.debug("llvmType for {}", .{t}); |
| 571 | 758 | switch (t.zigTypeTag()) { |
| 572 | .Void => return self.context.voidType(), | |
| 573 | .NoReturn => return self.context.voidType(), | |
| 759 | .Void, .NoReturn => return self.context.voidType(), | |
| 574 | 760 | .Int => { |
| 575 | 761 | const info = t.intInfo(self.module.getTarget()); |
| 576 | 762 | return self.context.intType(info.bits); |
| 577 | 763 | }, |
| 764 | .Enum => { | |
| 765 | var buffer: Type.Payload.Bits = undefined; | |
| 766 | const int_ty = t.intTagType(&buffer); | |
| 767 | const bit_count = int_ty.intInfo(self.module.getTarget()).bits; | |
| 768 | return self.context.intType(bit_count); | |
| 769 | }, | |
| 578 | 770 | .Float => switch (t.floatBits(self.module.getTarget())) { |
| 579 | 771 | 16 => return self.context.halfType(), |
| 580 | 772 | 32 => return self.context.floatType(), |
| ... | ... | @@ -586,7 +778,7 @@ pub const DeclGen = struct { |
| 586 | 778 | .Bool => return self.context.intType(1), |
| 587 | 779 | .Pointer => { |
| 588 | 780 | if (t.isSlice()) { |
| 589 | var buf: Type.Payload.ElemType = undefined; | |
| 781 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 590 | 782 | const ptr_type = t.slicePtrFieldType(&buf); |
| 591 | 783 | |
| 592 | 784 | const fields: [2]*const llvm.Type = .{ |
| ... | ... | @@ -596,7 +788,8 @@ pub const DeclGen = struct { |
| 596 | 788 | return self.context.structType(&fields, fields.len, .False); |
| 597 | 789 | } else { |
| 598 | 790 | const elem_type = try self.llvmType(t.elemType()); |
| 599 | return elem_type.pointerType(0); | |
| 791 | const llvm_addrspace = self.llvmAddressSpace(t.ptrAddressSpace()); | |
| 792 | return elem_type.pointerType(llvm_addrspace); | |
| 600 | 793 | } |
| 601 | 794 | }, |
| 602 | 795 | .Array => { |
| ... | ... | @@ -605,18 +798,18 @@ pub const DeclGen = struct { |
| 605 | 798 | return elem_type.arrayType(@intCast(c_uint, total_len)); |
| 606 | 799 | }, |
| 607 | 800 | .Optional => { |
| 608 | if (!t.isPtrLikeOptional()) { | |
| 609 | var buf: Type.Payload.ElemType = undefined; | |
| 610 | const child_type = t.optionalChild(&buf); | |
| 801 | var buf: Type.Payload.ElemType = undefined; | |
| 802 | const child_type = t.optionalChild(&buf); | |
| 803 | const payload_llvm_ty = try self.llvmType(child_type); | |
| 611 | 804 | |
| 612 | const optional_types: [2]*const llvm.Type = .{ | |
| 613 | try self.llvmType(child_type), | |
| 614 | self.context.intType(1), | |
| 615 | }; | |
| 616 | return self.context.structType(&optional_types, 2, .False); | |
| 617 | } else { | |
| 618 | return self.todo("implement optional pointers as actual pointers", .{}); | |
| 805 | if (t.isPtrLikeOptional()) { | |
| 806 | return payload_llvm_ty; | |
| 619 | 807 | } |
| 808 | ||
| 809 | const fields: [2]*const llvm.Type = .{ | |
| 810 | payload_llvm_ty, self.context.intType(1), | |
| 811 | }; | |
| 812 | return self.context.structType(&fields, fields.len, .False); | |
| 620 | 813 | }, |
| 621 | 814 | .ErrorUnion => { |
| 622 | 815 | const error_type = t.errorUnionSet(); |
| ... | ... | @@ -634,24 +827,67 @@ pub const DeclGen = struct { |
| 634 | 827 | return self.context.intType(16); |
| 635 | 828 | }, |
| 636 | 829 | .Struct => { |
| 830 | const gop = try self.object.type_map.getOrPut(gpa, t); | |
| 831 | if (gop.found_existing) return gop.value_ptr.*; | |
| 832 | ||
| 833 | // The Type memory is ephemeral; since we want to store a longer-lived | |
| 834 | // reference, we need to copy it here. | |
| 835 | gop.key_ptr.* = try t.copy(&self.object.type_map_arena.allocator); | |
| 836 | ||
| 637 | 837 | const struct_obj = t.castTag(.@"struct").?.data; |
| 638 | 838 | assert(struct_obj.haveFieldTypes()); |
| 639 | const llvm_fields = try self.gpa.alloc(*const llvm.Type, struct_obj.fields.count()); | |
| 640 | defer self.gpa.free(llvm_fields); | |
| 641 | for (struct_obj.fields.values()) |field, i| { | |
| 642 | llvm_fields[i] = try self.llvmType(field.ty); | |
| 839 | ||
| 840 | const name = try struct_obj.getFullyQualifiedName(gpa); | |
| 841 | defer gpa.free(name); | |
| 842 | ||
| 843 | const llvm_struct_ty = self.context.structCreateNamed(name); | |
| 844 | gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls | |
| 845 | ||
| 846 | var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{}; | |
| 847 | try llvm_field_types.ensureTotalCapacity(gpa, struct_obj.fields.count()); | |
| 848 | defer llvm_field_types.deinit(gpa); | |
| 849 | ||
| 850 | for (struct_obj.fields.values()) |field| { | |
| 851 | if (!field.ty.hasCodeGenBits()) continue; | |
| 852 | llvm_field_types.appendAssumeCapacity(try self.llvmType(field.ty)); | |
| 643 | 853 | } |
| 644 | return self.context.structType( | |
| 645 | llvm_fields.ptr, | |
| 646 | @intCast(c_uint, llvm_fields.len), | |
| 647 | .False, | |
| 854 | ||
| 855 | llvm_struct_ty.structSetBody( | |
| 856 | llvm_field_types.items.ptr, | |
| 857 | @intCast(c_uint, llvm_field_types.items.len), | |
| 858 | llvm.Bool.fromBool(struct_obj.layout == .Packed), | |
| 648 | 859 | ); |
| 860 | ||
| 861 | return llvm_struct_ty; | |
| 862 | }, | |
| 863 | .Union => { | |
| 864 | const union_obj = t.castTag(.@"union").?.data; | |
| 865 | assert(union_obj.haveFieldTypes()); | |
| 866 | ||
| 867 | const enum_tag_ty = union_obj.tag_ty; | |
| 868 | const enum_tag_llvm_ty = try self.llvmType(enum_tag_ty); | |
| 869 | ||
| 870 | if (union_obj.onlyTagHasCodegenBits()) { | |
| 871 | return enum_tag_llvm_ty; | |
| 872 | } | |
| 873 | ||
| 874 | const target = self.module.getTarget(); | |
| 875 | const most_aligned_field_index = union_obj.mostAlignedField(target); | |
| 876 | const most_aligned_field = union_obj.fields.values()[most_aligned_field_index]; | |
| 877 | // TODO handle when the most aligned field is different than the | |
| 878 | // biggest sized field. | |
| 879 | ||
| 880 | const llvm_fields = [_]*const llvm.Type{ | |
| 881 | try self.llvmType(most_aligned_field.ty), | |
| 882 | enum_tag_llvm_ty, | |
| 883 | }; | |
| 884 | return self.context.structType(&llvm_fields, llvm_fields.len, .False); | |
| 649 | 885 | }, |
| 650 | 886 | .Fn => { |
| 651 | 887 | const ret_ty = try self.llvmType(t.fnReturnType()); |
| 652 | 888 | const params_len = t.fnParamLen(); |
| 653 | const llvm_params = try self.gpa.alloc(*const llvm.Type, params_len); | |
| 654 | defer self.gpa.free(llvm_params); | |
| 889 | const llvm_params = try gpa.alloc(*const llvm.Type, params_len); | |
| 890 | defer gpa.free(llvm_params); | |
| 655 | 891 | for (llvm_params) |*llvm_param, i| { |
| 656 | 892 | llvm_param.* = try self.llvmType(t.fnParamType(i)); |
| 657 | 893 | } |
| ... | ... | @@ -662,7 +898,9 @@ pub const DeclGen = struct { |
| 662 | 898 | @intCast(c_uint, llvm_params.len), |
| 663 | 899 | llvm.Bool.fromBool(is_var_args), |
| 664 | 900 | ); |
| 665 | return llvm_fn_ty.pointerType(0); | |
| 901 | // TODO make .Fn not both a pointer type and a prototype | |
| 902 | const llvm_addrspace = self.llvmAddressSpace(.generic); | |
| 903 | return llvm_fn_ty.pointerType(llvm_addrspace); | |
| 666 | 904 | }, |
| 667 | 905 | .ComptimeInt => unreachable, |
| 668 | 906 | .ComptimeFloat => unreachable, |
| ... | ... | @@ -673,8 +911,6 @@ pub const DeclGen = struct { |
| 673 | 911 | |
| 674 | 912 | .BoundFn => @panic("TODO remove BoundFn from the language"), |
| 675 | 913 | |
| 676 | .Enum, | |
| 677 | .Union, | |
| 678 | 914 | .Opaque, |
| 679 | 915 | .Frame, |
| 680 | 916 | .AnyFrame, |
| ... | ... | @@ -701,13 +937,24 @@ pub const DeclGen = struct { |
| 701 | 937 | const llvm_type = try self.llvmType(tv.ty); |
| 702 | 938 | if (bigint.eqZero()) return llvm_type.constNull(); |
| 703 | 939 | |
| 704 | if (bigint.limbs.len != 1) { | |
| 705 | return self.todo("implement bigger bigint", .{}); | |
| 706 | } | |
| 707 | const llvm_int = llvm_type.constInt(bigint.limbs[0], .False); | |
| 940 | const unsigned_val = if (bigint.limbs.len == 1) | |
| 941 | llvm_type.constInt(bigint.limbs[0], .False) | |
| 942 | else | |
| 943 | llvm_type.constIntOfArbitraryPrecision(@intCast(c_uint, bigint.limbs.len), bigint.limbs.ptr); | |
| 708 | 944 | if (!bigint.positive) { |
| 709 | return llvm.constNeg(llvm_int); | |
| 945 | return llvm.constNeg(unsigned_val); | |
| 710 | 946 | } |
| 947 | return unsigned_val; | |
| 948 | }, | |
| 949 | .Enum => { | |
| 950 | const llvm_type = try self.llvmType(tv.ty); | |
| 951 | const uint: u64 = uint: { | |
| 952 | if (tv.val.castTag(.enum_field_index)) |payload| { | |
| 953 | break :uint payload.data; | |
| 954 | } | |
| 955 | break :uint tv.val.toUnsignedInt(); | |
| 956 | }; | |
| 957 | const llvm_int = llvm_type.constInt(uint, .False); | |
| 711 | 958 | return llvm_int; |
| 712 | 959 | }, |
| 713 | 960 | .Float => { |
| ... | ... | @@ -720,7 +967,7 @@ pub const DeclGen = struct { |
| 720 | 967 | .Pointer => switch (tv.val.tag()) { |
| 721 | 968 | .decl_ref => { |
| 722 | 969 | if (tv.ty.isSlice()) { |
| 723 | var buf: Type.Payload.ElemType = undefined; | |
| 970 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 724 | 971 | const ptr_ty = tv.ty.slicePtrFieldType(&buf); |
| 725 | 972 | var slice_len: Value.Payload.U64 = .{ |
| 726 | 973 | .base = .{ .tag = .int_u64 }, |
| ... | ... | @@ -750,12 +997,13 @@ pub const DeclGen = struct { |
| 750 | 997 | decl.alive = true; |
| 751 | 998 | const val = try self.resolveGlobalDecl(decl); |
| 752 | 999 | const llvm_var_type = try self.llvmType(tv.ty); |
| 753 | const llvm_type = llvm_var_type.pointerType(0); | |
| 1000 | const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace"); | |
| 1001 | const llvm_type = llvm_var_type.pointerType(llvm_addrspace); | |
| 754 | 1002 | return val.constBitCast(llvm_type); |
| 755 | 1003 | }, |
| 756 | 1004 | .slice => { |
| 757 | 1005 | const slice = tv.val.castTag(.slice).?.data; |
| 758 | var buf: Type.Payload.ElemType = undefined; | |
| 1006 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 759 | 1007 | const fields: [2]*const llvm.Value = .{ |
| 760 | 1008 | try self.genTypedValue(.{ |
| 761 | 1009 | .ty = tv.ty.slicePtrFieldType(&buf), |
| ... | ... | @@ -873,42 +1121,80 @@ pub const DeclGen = struct { |
| 873 | 1121 | return self.context.constStruct(&fields, fields.len, .False); |
| 874 | 1122 | }, |
| 875 | 1123 | .Struct => { |
| 876 | const fields_len = tv.ty.structFieldCount(); | |
| 1124 | const llvm_struct_ty = try self.llvmType(tv.ty); | |
| 877 | 1125 | const field_vals = tv.val.castTag(.@"struct").?.data; |
| 878 | 1126 | const gpa = self.gpa; |
| 879 | const llvm_fields = try gpa.alloc(*const llvm.Value, fields_len); | |
| 880 | defer gpa.free(llvm_fields); | |
| 881 | for (llvm_fields) |*llvm_field, i| { | |
| 882 | llvm_field.* = try self.genTypedValue(.{ | |
| 883 | .ty = tv.ty.structFieldType(i), | |
| 884 | .val = field_vals[i], | |
| 885 | }); | |
| 1127 | ||
| 1128 | var llvm_fields: std.ArrayListUnmanaged(*const llvm.Value) = .{}; | |
| 1129 | try llvm_fields.ensureTotalCapacity(gpa, field_vals.len); | |
| 1130 | defer llvm_fields.deinit(gpa); | |
| 1131 | ||
| 1132 | for (field_vals) |field_val, i| { | |
| 1133 | const field_ty = tv.ty.structFieldType(i); | |
| 1134 | if (!field_ty.hasCodeGenBits()) continue; | |
| 1135 | ||
| 1136 | llvm_fields.appendAssumeCapacity(try self.genTypedValue(.{ | |
| 1137 | .ty = field_ty, | |
| 1138 | .val = field_val, | |
| 1139 | })); | |
| 886 | 1140 | } |
| 887 | return self.context.constStruct( | |
| 888 | llvm_fields.ptr, | |
| 889 | @intCast(c_uint, llvm_fields.len), | |
| 890 | .False, | |
| 1141 | return llvm_struct_ty.constNamedStruct( | |
| 1142 | llvm_fields.items.ptr, | |
| 1143 | @intCast(c_uint, llvm_fields.items.len), | |
| 891 | 1144 | ); |
| 892 | 1145 | }, |
| 893 | 1146 | .ComptimeInt => unreachable, |
| 894 | 1147 | .ComptimeFloat => unreachable, |
| 895 | 1148 | .Type => unreachable, |
| 896 | 1149 | .EnumLiteral => unreachable, |
| 897 | else => return self.todo("implement const of type '{}'", .{tv.ty}), | |
| 1150 | .Void => unreachable, | |
| 1151 | .NoReturn => unreachable, | |
| 1152 | .Undefined => unreachable, | |
| 1153 | .Null => unreachable, | |
| 1154 | .BoundFn => unreachable, | |
| 1155 | .Opaque => unreachable, | |
| 1156 | ||
| 1157 | .Union, | |
| 1158 | .Frame, | |
| 1159 | .AnyFrame, | |
| 1160 | .Vector, | |
| 1161 | => return self.todo("implement const of type '{}'", .{tv.ty}), | |
| 898 | 1162 | } |
| 899 | 1163 | } |
| 900 | 1164 | |
| 901 | // Helper functions | |
| 902 | fn addAttr(self: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { | |
| 1165 | fn addAttr(dg: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { | |
| 1166 | return dg.addAttrInt(val, index, name, 0); | |
| 1167 | } | |
| 1168 | ||
| 1169 | fn removeAttr(val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { | |
| 903 | 1170 | const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); |
| 904 | 1171 | assert(kind_id != 0); |
| 905 | const llvm_attr = self.context.createEnumAttribute(kind_id, 0); | |
| 1172 | val.removeEnumAttributeAtIndex(index, kind_id); | |
| 1173 | } | |
| 1174 | ||
| 1175 | fn addAttrInt( | |
| 1176 | dg: *DeclGen, | |
| 1177 | val: *const llvm.Value, | |
| 1178 | index: llvm.AttributeIndex, | |
| 1179 | name: []const u8, | |
| 1180 | int: u64, | |
| 1181 | ) void { | |
| 1182 | const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); | |
| 1183 | assert(kind_id != 0); | |
| 1184 | const llvm_attr = dg.context.createEnumAttribute(kind_id, int); | |
| 906 | 1185 | val.addAttributeAtIndex(index, llvm_attr); |
| 907 | 1186 | } |
| 908 | 1187 | |
| 909 | fn addFnAttr(self: *DeclGen, val: *const llvm.Value, attr_name: []const u8) void { | |
| 910 | // TODO: improve this API, `addAttr(-1, attr_name)` | |
| 911 | self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name); | |
| 1188 | fn addFnAttr(dg: *DeclGen, val: *const llvm.Value, name: []const u8) void { | |
| 1189 | dg.addAttr(val, std.math.maxInt(llvm.AttributeIndex), name); | |
| 1190 | } | |
| 1191 | ||
| 1192 | fn removeFnAttr(fn_val: *const llvm.Value, name: []const u8) void { | |
| 1193 | removeAttr(fn_val, std.math.maxInt(llvm.AttributeIndex), name); | |
| 1194 | } | |
| 1195 | ||
| 1196 | fn addFnAttrInt(dg: *DeclGen, fn_val: *const llvm.Value, name: []const u8, int: u64) void { | |
| 1197 | return dg.addAttrInt(fn_val, std.math.maxInt(llvm.AttributeIndex), name, int); | |
| 912 | 1198 | } |
| 913 | 1199 | |
| 914 | 1200 | /// If the operand type of an atomic operation is not byte sized we need to |
| ... | ... | @@ -920,7 +1206,7 @@ pub const DeclGen = struct { |
| 920 | 1206 | var buffer: Type.Payload.Bits = undefined; |
| 921 | 1207 | const int_ty = switch (ty.zigTypeTag()) { |
| 922 | 1208 | .Int => ty, |
| 923 | .Enum => ty.enumTagType(&buffer), | |
| 1209 | .Enum => ty.intTagType(&buffer), | |
| 924 | 1210 | .Float => { |
| 925 | 1211 | if (!is_rmw_xchg) return null; |
| 926 | 1212 | return dg.context.intType(@intCast(c_uint, ty.abiSize(target) * 8)); |
| ... | ... | @@ -950,7 +1236,7 @@ pub const FuncGen = struct { |
| 950 | 1236 | /// in other instructions. This table is cleared before every function is generated. |
| 951 | 1237 | func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Index, *const llvm.Value), |
| 952 | 1238 | |
| 953 | /// These fields are used to refer to the LLVM value of the function paramaters | |
| 1239 | /// These fields are used to refer to the LLVM value of the function parameters | |
| 954 | 1240 | /// in an Arg instruction. |
| 955 | 1241 | args: []*const llvm.Value, |
| 956 | 1242 | arg_index: usize, |
| ... | ... | @@ -1003,22 +1289,27 @@ pub const FuncGen = struct { |
| 1003 | 1289 | for (body) |inst| { |
| 1004 | 1290 | const opt_value: ?*const llvm.Value = switch (air_tags[inst]) { |
| 1005 | 1291 | // zig fmt: off |
| 1006 | .add => try self.airAdd(inst, false), | |
| 1007 | .addwrap => try self.airAdd(inst, true), | |
| 1008 | .sub => try self.airSub(inst, false), | |
| 1009 | .subwrap => try self.airSub(inst, true), | |
| 1010 | .mul => try self.airMul(inst, false), | |
| 1011 | .mulwrap => try self.airMul(inst, true), | |
| 1012 | .div => try self.airDiv(inst), | |
| 1013 | .rem => try self.airRem(inst), | |
| 1014 | .ptr_add => try self.airPtrAdd(inst), | |
| 1015 | .ptr_sub => try self.airPtrSub(inst), | |
| 1292 | .add => try self.airAdd(inst), | |
| 1293 | .addwrap => try self.airAddWrap(inst), | |
| 1294 | .add_sat => try self.airAddSat(inst), | |
| 1295 | .sub => try self.airSub(inst), | |
| 1296 | .subwrap => try self.airSubWrap(inst), | |
| 1297 | .sub_sat => try self.airSubSat(inst), | |
| 1298 | .mul => try self.airMul(inst), | |
| 1299 | .mulwrap => try self.airMulWrap(inst), | |
| 1300 | .mul_sat => try self.airMulSat(inst), | |
| 1301 | .div => try self.airDiv(inst), | |
| 1302 | .rem => try self.airRem(inst), | |
| 1303 | .mod => try self.airMod(inst), | |
| 1304 | .ptr_add => try self.airPtrAdd(inst), | |
| 1305 | .ptr_sub => try self.airPtrSub(inst), | |
| 1306 | .shl => try self.airShl(inst), | |
| 1307 | .shl_sat => try self.airShlSat(inst), | |
| 1308 | .shl_exact => try self.airShlExact(inst), | |
| 1016 | 1309 | |
| 1017 | 1310 | .bit_and, .bool_and => try self.airAnd(inst), |
| 1018 | 1311 | .bit_or, .bool_or => try self.airOr(inst), |
| 1019 | 1312 | .xor => try self.airXor(inst), |
| 1020 | ||
| 1021 | .shl => try self.airShl(inst), | |
| 1022 | 1313 | .shr => try self.airShr(inst), |
| 1023 | 1314 | |
| 1024 | 1315 | .cmp_eq => try self.airCmp(inst, .eq), |
| ... | ... | @@ -1049,7 +1340,8 @@ pub const FuncGen = struct { |
| 1049 | 1340 | .cond_br => try self.airCondBr(inst), |
| 1050 | 1341 | .intcast => try self.airIntCast(inst), |
| 1051 | 1342 | .trunc => try self.airTrunc(inst), |
| 1052 | .floatcast => try self.airFloatCast(inst), | |
| 1343 | .fptrunc => try self.airFptrunc(inst), | |
| 1344 | .fpext => try self.airFpext(inst), | |
| 1053 | 1345 | .ptrtoint => try self.airPtrToInt(inst), |
| 1054 | 1346 | .load => try self.airLoad(inst), |
| 1055 | 1347 | .loop => try self.airLoop(inst), |
| ... | ... | @@ -1060,8 +1352,24 @@ pub const FuncGen = struct { |
| 1060 | 1352 | .slice_ptr => try self.airSliceField(inst, 0), |
| 1061 | 1353 | .slice_len => try self.airSliceField(inst, 1), |
| 1062 | 1354 | .array_to_slice => try self.airArrayToSlice(inst), |
| 1355 | .float_to_int => try self.airFloatToInt(inst), | |
| 1356 | .int_to_float => try self.airIntToFloat(inst), | |
| 1063 | 1357 | .cmpxchg_weak => try self.airCmpxchg(inst, true), |
| 1064 | 1358 | .cmpxchg_strong => try self.airCmpxchg(inst, false), |
| 1359 | .fence => try self.airFence(inst), | |
| 1360 | .atomic_rmw => try self.airAtomicRmw(inst), | |
| 1361 | .atomic_load => try self.airAtomicLoad(inst), | |
| 1362 | .memset => try self.airMemset(inst), | |
| 1363 | .memcpy => try self.airMemcpy(inst), | |
| 1364 | .set_union_tag => try self.airSetUnionTag(inst), | |
| 1365 | .get_union_tag => try self.airGetUnionTag(inst), | |
| 1366 | .clz => try self.airClzCtz(inst, "ctlz"), | |
| 1367 | .ctz => try self.airClzCtz(inst, "cttz"), | |
| 1368 | ||
| 1369 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), | |
| 1370 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), | |
| 1371 | .atomic_store_release => try self.airAtomicStore(inst, .Release), | |
| 1372 | .atomic_store_seq_cst => try self.airAtomicStore(inst, .SequentiallyConsistent), | |
| 1065 | 1373 | |
| 1066 | 1374 | .struct_field_ptr => try self.airStructFieldPtr(inst), |
| 1067 | 1375 | .struct_field_val => try self.airStructFieldVal(inst), |
| ... | ... | @@ -1152,21 +1460,15 @@ pub const FuncGen = struct { |
| 1152 | 1460 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1153 | 1461 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1154 | 1462 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1155 | const inst_ty = self.air.typeOfIndex(inst); | |
| 1463 | const operand_ty = self.air.typeOf(bin_op.lhs); | |
| 1156 | 1464 | |
| 1157 | switch (self.air.typeOf(bin_op.lhs).zigTypeTag()) { | |
| 1158 | .Int, .Bool, .Pointer, .ErrorSet => { | |
| 1159 | const is_signed = inst_ty.isSignedInt(); | |
| 1160 | const operation = switch (op) { | |
| 1161 | .eq => .EQ, | |
| 1162 | .neq => .NE, | |
| 1163 | .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT), | |
| 1164 | .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE), | |
| 1165 | .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT), | |
| 1166 | .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE), | |
| 1167 | }; | |
| 1168 | return self.builder.buildICmp(operation, lhs, rhs, ""); | |
| 1465 | const int_ty = switch (operand_ty.zigTypeTag()) { | |
| 1466 | .Enum => blk: { | |
| 1467 | var buffer: Type.Payload.Bits = undefined; | |
| 1468 | const int_ty = operand_ty.intTagType(&buffer); | |
| 1469 | break :blk int_ty; | |
| 1169 | 1470 | }, |
| 1471 | .Int, .Bool, .Pointer, .ErrorSet => operand_ty, | |
| 1170 | 1472 | .Float => { |
| 1171 | 1473 | const operation: llvm.RealPredicate = switch (op) { |
| 1172 | 1474 | .eq => .OEQ, |
| ... | ... | @@ -1179,7 +1481,17 @@ pub const FuncGen = struct { |
| 1179 | 1481 | return self.builder.buildFCmp(operation, lhs, rhs, ""); |
| 1180 | 1482 | }, |
| 1181 | 1483 | else => unreachable, |
| 1182 | } | |
| 1484 | }; | |
| 1485 | const is_signed = int_ty.isSignedInt(); | |
| 1486 | const operation = switch (op) { | |
| 1487 | .eq => .EQ, | |
| 1488 | .neq => .NE, | |
| 1489 | .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT), | |
| 1490 | .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE), | |
| 1491 | .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT), | |
| 1492 | .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE), | |
| 1493 | }; | |
| 1494 | return self.builder.buildICmp(operation, lhs, rhs, ""); | |
| 1183 | 1495 | } |
| 1184 | 1496 | |
| 1185 | 1497 | fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -1285,19 +1597,57 @@ pub const FuncGen = struct { |
| 1285 | 1597 | return null; |
| 1286 | 1598 | |
| 1287 | 1599 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1288 | const operand = try self.resolveInst(ty_op.operand); | |
| 1289 | const array_len = self.air.typeOf(ty_op.operand).elemType().arrayLen(); | |
| 1290 | const usize_llvm_ty = try self.dg.llvmType(Type.initTag(.usize)); | |
| 1291 | const len = usize_llvm_ty.constInt(array_len, .False); | |
| 1600 | const operand_ty = self.air.typeOf(ty_op.operand); | |
| 1601 | const array_ty = operand_ty.childType(); | |
| 1602 | const llvm_usize = try self.dg.llvmType(Type.usize); | |
| 1603 | const len = llvm_usize.constInt(array_ty.arrayLen(), .False); | |
| 1292 | 1604 | const slice_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); |
| 1605 | if (!array_ty.hasCodeGenBits()) { | |
| 1606 | return self.builder.buildInsertValue(slice_llvm_ty.getUndef(), len, 1, ""); | |
| 1607 | } | |
| 1608 | const operand = try self.resolveInst(ty_op.operand); | |
| 1293 | 1609 | const indices: [2]*const llvm.Value = .{ |
| 1294 | usize_llvm_ty.constNull(), usize_llvm_ty.constNull(), | |
| 1610 | llvm_usize.constNull(), llvm_usize.constNull(), | |
| 1295 | 1611 | }; |
| 1296 | 1612 | const ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); |
| 1297 | 1613 | const partial = self.builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr, 0, ""); |
| 1298 | 1614 | return self.builder.buildInsertValue(partial, len, 1, ""); |
| 1299 | 1615 | } |
| 1300 | 1616 | |
| 1617 | fn airIntToFloat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1618 | if (self.liveness.isUnused(inst)) | |
| 1619 | return null; | |
| 1620 | ||
| 1621 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1622 | const operand = try self.resolveInst(ty_op.operand); | |
| 1623 | const dest_ty = self.air.typeOfIndex(inst); | |
| 1624 | const dest_llvm_ty = try self.dg.llvmType(dest_ty); | |
| 1625 | ||
| 1626 | if (dest_ty.isSignedInt()) { | |
| 1627 | return self.builder.buildSIToFP(operand, dest_llvm_ty, ""); | |
| 1628 | } else { | |
| 1629 | return self.builder.buildUIToFP(operand, dest_llvm_ty, ""); | |
| 1630 | } | |
| 1631 | } | |
| 1632 | ||
| 1633 | fn airFloatToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1634 | if (self.liveness.isUnused(inst)) | |
| 1635 | return null; | |
| 1636 | ||
| 1637 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1638 | const operand = try self.resolveInst(ty_op.operand); | |
| 1639 | const dest_ty = self.air.typeOfIndex(inst); | |
| 1640 | const dest_llvm_ty = try self.dg.llvmType(dest_ty); | |
| 1641 | ||
| 1642 | // TODO set fast math flag | |
| 1643 | ||
| 1644 | if (dest_ty.isSignedInt()) { | |
| 1645 | return self.builder.buildFPToSI(operand, dest_llvm_ty, ""); | |
| 1646 | } else { | |
| 1647 | return self.builder.buildFPToUI(operand, dest_llvm_ty, ""); | |
| 1648 | } | |
| 1649 | } | |
| 1650 | ||
| 1301 | 1651 | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value { |
| 1302 | 1652 | if (self.liveness.isUnused(inst)) |
| 1303 | 1653 | return null; |
| ... | ... | @@ -1398,27 +1748,31 @@ pub const FuncGen = struct { |
| 1398 | 1748 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1399 | 1749 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 1400 | 1750 | const struct_ptr = try self.resolveInst(struct_field.struct_operand); |
| 1401 | const field_index = @intCast(c_uint, struct_field.field_index); | |
| 1402 | return self.builder.buildStructGEP(struct_ptr, field_index, ""); | |
| 1751 | const struct_ptr_ty = self.air.typeOf(struct_field.struct_operand); | |
| 1752 | return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, struct_field.field_index); | |
| 1403 | 1753 | } |
| 1404 | 1754 | |
| 1405 | fn airStructFieldPtrIndex(self: *FuncGen, inst: Air.Inst.Index, field_index: c_uint) !?*const llvm.Value { | |
| 1406 | if (self.liveness.isUnused(inst)) | |
| 1407 | return null; | |
| 1755 | fn airStructFieldPtrIndex( | |
| 1756 | self: *FuncGen, | |
| 1757 | inst: Air.Inst.Index, | |
| 1758 | field_index: u32, | |
| 1759 | ) !?*const llvm.Value { | |
| 1760 | if (self.liveness.isUnused(inst)) return null; | |
| 1408 | 1761 | |
| 1409 | 1762 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1410 | 1763 | const struct_ptr = try self.resolveInst(ty_op.operand); |
| 1411 | return self.builder.buildStructGEP(struct_ptr, field_index, ""); | |
| 1764 | const struct_ptr_ty = self.air.typeOf(ty_op.operand); | |
| 1765 | return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index); | |
| 1412 | 1766 | } |
| 1413 | 1767 | |
| 1414 | 1768 | fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1415 | if (self.liveness.isUnused(inst)) | |
| 1416 | return null; | |
| 1769 | if (self.liveness.isUnused(inst)) return null; | |
| 1417 | 1770 | |
| 1418 | 1771 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1419 | 1772 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 1773 | const struct_ty = self.air.typeOf(struct_field.struct_operand); | |
| 1420 | 1774 | const struct_byval = try self.resolveInst(struct_field.struct_operand); |
| 1421 | const field_index = @intCast(c_uint, struct_field.field_index); | |
| 1775 | const field_index = llvmFieldIndex(struct_ty, struct_field.field_index); | |
| 1422 | 1776 | return self.builder.buildExtractValue(struct_byval, field_index, ""); |
| 1423 | 1777 | } |
| 1424 | 1778 | |
| ... | ... | @@ -1569,6 +1923,13 @@ pub const FuncGen = struct { |
| 1569 | 1923 | const operand = try self.resolveInst(un_op); |
| 1570 | 1924 | |
| 1571 | 1925 | if (operand_is_ptr) { |
| 1926 | const operand_ty = self.air.typeOf(un_op).elemType(); | |
| 1927 | if (operand_ty.isPtrLikeOptional()) { | |
| 1928 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); | |
| 1929 | const loaded = self.builder.buildLoad(operand, ""); | |
| 1930 | return self.builder.buildICmp(.NE, loaded, operand_llvm_ty.constNull(), ""); | |
| 1931 | } | |
| 1932 | ||
| 1572 | 1933 | const index_type = self.context.intType(32); |
| 1573 | 1934 | |
| 1574 | 1935 | var indices: [2]*const llvm.Value = .{ |
| ... | ... | @@ -1577,9 +1938,15 @@ pub const FuncGen = struct { |
| 1577 | 1938 | }; |
| 1578 | 1939 | |
| 1579 | 1940 | return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""), ""); |
| 1580 | } else { | |
| 1581 | return self.builder.buildExtractValue(operand, 1, ""); | |
| 1582 | 1941 | } |
| 1942 | ||
| 1943 | const operand_ty = self.air.typeOf(un_op); | |
| 1944 | if (operand_ty.isPtrLikeOptional()) { | |
| 1945 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); | |
| 1946 | return self.builder.buildICmp(.NE, operand, operand_llvm_ty.constNull(), ""); | |
| 1947 | } | |
| 1948 | ||
| 1949 | return self.builder.buildExtractValue(operand, 1, ""); | |
| 1583 | 1950 | } |
| 1584 | 1951 | |
| 1585 | 1952 | fn airIsNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value { |
| ... | ... | @@ -1632,17 +1999,24 @@ pub const FuncGen = struct { |
| 1632 | 1999 | const operand = try self.resolveInst(ty_op.operand); |
| 1633 | 2000 | |
| 1634 | 2001 | if (operand_is_ptr) { |
| 1635 | const index_type = self.context.intType(32); | |
| 2002 | const operand_ty = self.air.typeOf(ty_op.operand).elemType(); | |
| 2003 | if (operand_ty.isPtrLikeOptional()) { | |
| 2004 | return self.builder.buildLoad(operand, ""); | |
| 2005 | } | |
| 1636 | 2006 | |
| 2007 | const index_type = self.context.intType(32); | |
| 1637 | 2008 | var indices: [2]*const llvm.Value = .{ |
| 1638 | index_type.constNull(), | |
| 1639 | index_type.constNull(), | |
| 2009 | index_type.constNull(), index_type.constNull(), | |
| 1640 | 2010 | }; |
| 1641 | ||
| 1642 | 2011 | return self.builder.buildInBoundsGEP(operand, &indices, 2, ""); |
| 1643 | } else { | |
| 1644 | return self.builder.buildExtractValue(operand, 0, ""); | |
| 1645 | 2012 | } |
| 2013 | ||
| 2014 | const operand_ty = self.air.typeOf(ty_op.operand); | |
| 2015 | if (operand_ty.isPtrLikeOptional()) { | |
| 2016 | return operand; | |
| 2017 | } | |
| 2018 | ||
| 2019 | return self.builder.buildExtractValue(operand, 0, ""); | |
| 1646 | 2020 | } |
| 1647 | 2021 | |
| 1648 | 2022 | fn airErrUnionPayload( |
| ... | ... | @@ -1716,51 +2090,115 @@ pub const FuncGen = struct { |
| 1716 | 2090 | return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{}); |
| 1717 | 2091 | } |
| 1718 | 2092 | |
| 1719 | fn airAdd(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value { | |
| 1720 | if (self.liveness.isUnused(inst)) | |
| 1721 | return null; | |
| 2093 | fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2094 | if (self.liveness.isUnused(inst)) return null; | |
| 1722 | 2095 | |
| 1723 | 2096 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1724 | 2097 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1725 | 2098 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1726 | 2099 | const inst_ty = self.air.typeOfIndex(inst); |
| 1727 | 2100 | |
| 1728 | if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, ""); | |
| 1729 | if (wrap) return self.builder.buildAdd(lhs, rhs, ""); | |
| 2101 | if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, ""); | |
| 1730 | 2102 | if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); |
| 1731 | 2103 | return self.builder.buildNUWAdd(lhs, rhs, ""); |
| 1732 | 2104 | } |
| 1733 | 2105 | |
| 1734 | fn airSub(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value { | |
| 1735 | if (self.liveness.isUnused(inst)) | |
| 1736 | return null; | |
| 2106 | fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2107 | if (self.liveness.isUnused(inst)) return null; | |
| 2108 | ||
| 2109 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2110 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2111 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2112 | ||
| 2113 | return self.builder.buildAdd(lhs, rhs, ""); | |
| 2114 | } | |
| 2115 | ||
| 2116 | fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2117 | if (self.liveness.isUnused(inst)) return null; | |
| 2118 | ||
| 2119 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2120 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2121 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2122 | const inst_ty = self.air.typeOfIndex(inst); | |
| 2123 | ||
| 2124 | if (inst_ty.isAnyFloat()) return self.todo("saturating float add", .{}); | |
| 2125 | if (inst_ty.isSignedInt()) return self.builder.buildSAddSat(lhs, rhs, ""); | |
| 2126 | ||
| 2127 | return self.builder.buildUAddSat(lhs, rhs, ""); | |
| 2128 | } | |
| 2129 | ||
| 2130 | fn airSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2131 | if (self.liveness.isUnused(inst)) return null; | |
| 1737 | 2132 | |
| 1738 | 2133 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1739 | 2134 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1740 | 2135 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1741 | 2136 | const inst_ty = self.air.typeOfIndex(inst); |
| 1742 | 2137 | |
| 1743 | if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, ""); | |
| 1744 | if (wrap) return self.builder.buildSub(lhs, rhs, ""); | |
| 2138 | if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, ""); | |
| 1745 | 2139 | if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); |
| 1746 | 2140 | return self.builder.buildNUWSub(lhs, rhs, ""); |
| 1747 | 2141 | } |
| 1748 | 2142 | |
| 1749 | fn airMul(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value { | |
| 1750 | if (self.liveness.isUnused(inst)) | |
| 1751 | return null; | |
| 2143 | fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2144 | if (self.liveness.isUnused(inst)) return null; | |
| 2145 | ||
| 2146 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2147 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2148 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2149 | ||
| 2150 | return self.builder.buildSub(lhs, rhs, ""); | |
| 2151 | } | |
| 2152 | ||
| 2153 | fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2154 | if (self.liveness.isUnused(inst)) return null; | |
| 1752 | 2155 | |
| 1753 | 2156 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1754 | 2157 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1755 | 2158 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1756 | 2159 | const inst_ty = self.air.typeOfIndex(inst); |
| 1757 | 2160 | |
| 1758 | if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, ""); | |
| 1759 | if (wrap) return self.builder.buildMul(lhs, rhs, ""); | |
| 2161 | if (inst_ty.isAnyFloat()) return self.todo("saturating float sub", .{}); | |
| 2162 | if (inst_ty.isSignedInt()) return self.builder.buildSSubSat(lhs, rhs, ""); | |
| 2163 | return self.builder.buildUSubSat(lhs, rhs, ""); | |
| 2164 | } | |
| 2165 | ||
| 2166 | fn airMul(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2167 | if (self.liveness.isUnused(inst)) return null; | |
| 2168 | ||
| 2169 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2170 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2171 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2172 | const inst_ty = self.air.typeOfIndex(inst); | |
| 2173 | ||
| 2174 | if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, ""); | |
| 1760 | 2175 | if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); |
| 1761 | 2176 | return self.builder.buildNUWMul(lhs, rhs, ""); |
| 1762 | 2177 | } |
| 1763 | 2178 | |
| 2179 | fn airMulWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2180 | if (self.liveness.isUnused(inst)) return null; | |
| 2181 | ||
| 2182 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2183 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2184 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2185 | ||
| 2186 | return self.builder.buildMul(lhs, rhs, ""); | |
| 2187 | } | |
| 2188 | ||
| 2189 | fn airMulSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2190 | if (self.liveness.isUnused(inst)) return null; | |
| 2191 | ||
| 2192 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2193 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2194 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2195 | const inst_ty = self.air.typeOfIndex(inst); | |
| 2196 | ||
| 2197 | if (inst_ty.isAnyFloat()) return self.todo("saturating float mul", .{}); | |
| 2198 | if (inst_ty.isSignedInt()) return self.builder.buildSMulFixSat(lhs, rhs, ""); | |
| 2199 | return self.builder.buildUMulFixSat(lhs, rhs, ""); | |
| 2200 | } | |
| 2201 | ||
| 1764 | 2202 | fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1765 | 2203 | if (self.liveness.isUnused(inst)) |
| 1766 | 2204 | return null; |
| ... | ... | @@ -1770,7 +2208,7 @@ pub const FuncGen = struct { |
| 1770 | 2208 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1771 | 2209 | const inst_ty = self.air.typeOfIndex(inst); |
| 1772 | 2210 | |
| 1773 | if (inst_ty.isFloat()) return self.builder.buildFDiv(lhs, rhs, ""); | |
| 2211 | if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, ""); | |
| 1774 | 2212 | if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); |
| 1775 | 2213 | return self.builder.buildUDiv(lhs, rhs, ""); |
| 1776 | 2214 | } |
| ... | ... | @@ -1783,11 +2221,39 @@ pub const FuncGen = struct { |
| 1783 | 2221 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1784 | 2222 | const inst_ty = self.air.typeOfIndex(inst); |
| 1785 | 2223 | |
| 1786 | if (inst_ty.isFloat()) return self.builder.buildFRem(lhs, rhs, ""); | |
| 2224 | if (inst_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, ""); | |
| 1787 | 2225 | if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); |
| 1788 | 2226 | return self.builder.buildURem(lhs, rhs, ""); |
| 1789 | 2227 | } |
| 1790 | 2228 | |
| 2229 | fn airMod(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2230 | if (self.liveness.isUnused(inst)) return null; | |
| 2231 | ||
| 2232 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2233 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2234 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2235 | const inst_ty = self.air.typeOfIndex(inst); | |
| 2236 | const inst_llvm_ty = try self.dg.llvmType(inst_ty); | |
| 2237 | ||
| 2238 | if (inst_ty.isRuntimeFloat()) { | |
| 2239 | const a = self.builder.buildFRem(lhs, rhs, ""); | |
| 2240 | const b = self.builder.buildFAdd(a, rhs, ""); | |
| 2241 | const c = self.builder.buildFRem(b, rhs, ""); | |
| 2242 | const zero = inst_llvm_ty.constNull(); | |
| 2243 | const ltz = self.builder.buildFCmp(.OLT, lhs, zero, ""); | |
| 2244 | return self.builder.buildSelect(ltz, c, a, ""); | |
| 2245 | } | |
| 2246 | if (inst_ty.isSignedInt()) { | |
| 2247 | const a = self.builder.buildSRem(lhs, rhs, ""); | |
| 2248 | const b = self.builder.buildNSWAdd(a, rhs, ""); | |
| 2249 | const c = self.builder.buildSRem(b, rhs, ""); | |
| 2250 | const zero = inst_llvm_ty.constNull(); | |
| 2251 | const ltz = self.builder.buildICmp(.SLT, lhs, zero, ""); | |
| 2252 | return self.builder.buildSelect(ltz, c, a, ""); | |
| 2253 | } | |
| 2254 | return self.builder.buildURem(lhs, rhs, ""); | |
| 2255 | } | |
| 2256 | ||
| 1791 | 2257 | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1792 | 2258 | if (self.liveness.isUnused(inst)) |
| 1793 | 2259 | return null; |
| ... | ... | @@ -1838,9 +2304,25 @@ pub const FuncGen = struct { |
| 1838 | 2304 | return self.builder.buildXor(lhs, rhs, ""); |
| 1839 | 2305 | } |
| 1840 | 2306 | |
| 2307 | fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2308 | if (self.liveness.isUnused(inst)) return null; | |
| 2309 | ||
| 2310 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2311 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2312 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2313 | const lhs_type = self.air.typeOf(bin_op.lhs); | |
| 2314 | const tg = self.dg.module.getTarget(); | |
| 2315 | const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) | |
| 2316 | self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") | |
| 2317 | else | |
| 2318 | rhs; | |
| 2319 | if (lhs_type.isSignedInt()) return self.builder.buildNSWShl(lhs, casted_rhs, ""); | |
| 2320 | return self.builder.buildNUWShl(lhs, casted_rhs, ""); | |
| 2321 | } | |
| 2322 | ||
| 1841 | 2323 | fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1842 | if (self.liveness.isUnused(inst)) | |
| 1843 | return null; | |
| 2324 | if (self.liveness.isUnused(inst)) return null; | |
| 2325 | ||
| 1844 | 2326 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1845 | 2327 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1846 | 2328 | const rhs = try self.resolveInst(bin_op.rhs); |
| ... | ... | @@ -1853,6 +2335,22 @@ pub const FuncGen = struct { |
| 1853 | 2335 | return self.builder.buildShl(lhs, casted_rhs, ""); |
| 1854 | 2336 | } |
| 1855 | 2337 | |
| 2338 | fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2339 | if (self.liveness.isUnused(inst)) return null; | |
| 2340 | ||
| 2341 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2342 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 2343 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 2344 | const lhs_type = self.air.typeOf(bin_op.lhs); | |
| 2345 | const tg = self.dg.module.getTarget(); | |
| 2346 | const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) | |
| 2347 | self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") | |
| 2348 | else | |
| 2349 | rhs; | |
| 2350 | if (lhs_type.isSignedInt()) return self.builder.buildSShlSat(lhs, casted_rhs, ""); | |
| 2351 | return self.builder.buildUShlSat(lhs, casted_rhs, ""); | |
| 2352 | } | |
| 2353 | ||
| 1856 | 2354 | fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1857 | 2355 | if (self.liveness.isUnused(inst)) |
| 1858 | 2356 | return null; |
| ... | ... | @@ -1877,14 +2375,22 @@ pub const FuncGen = struct { |
| 1877 | 2375 | if (self.liveness.isUnused(inst)) |
| 1878 | 2376 | return null; |
| 1879 | 2377 | |
| 2378 | const target = self.dg.module.getTarget(); | |
| 1880 | 2379 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2380 | const dest_ty = self.air.typeOfIndex(inst); | |
| 2381 | const dest_info = dest_ty.intInfo(target); | |
| 2382 | const dest_llvm_ty = try self.dg.llvmType(dest_ty); | |
| 1881 | 2383 | const operand = try self.resolveInst(ty_op.operand); |
| 1882 | const inst_ty = self.air.typeOfIndex(inst); | |
| 2384 | const operand_ty = self.air.typeOf(ty_op.operand); | |
| 2385 | const operand_info = operand_ty.intInfo(target); | |
| 1883 | 2386 | |
| 1884 | const signed = inst_ty.isSignedInt(); | |
| 1885 | // TODO: Should we use intcast here or just a simple bitcast? | |
| 1886 | // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes | |
| 1887 | return self.builder.buildIntCast2(operand, try self.dg.llvmType(inst_ty), llvm.Bool.fromBool(signed), ""); | |
| 2387 | if (operand_info.bits < dest_info.bits) { | |
| 2388 | switch (operand_info.signedness) { | |
| 2389 | .signed => return self.builder.buildSExt(operand, dest_llvm_ty, ""), | |
| 2390 | .unsigned => return self.builder.buildZExt(operand, dest_llvm_ty, ""), | |
| 2391 | } | |
| 2392 | } | |
| 2393 | return self.builder.buildTrunc(operand, dest_llvm_ty, ""); | |
| 1888 | 2394 | } |
| 1889 | 2395 | |
| 1890 | 2396 | fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -1897,12 +2403,26 @@ pub const FuncGen = struct { |
| 1897 | 2403 | return self.builder.buildTrunc(operand, dest_llvm_ty, ""); |
| 1898 | 2404 | } |
| 1899 | 2405 | |
| 1900 | fn airFloatCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2406 | fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1901 | 2407 | if (self.liveness.isUnused(inst)) |
| 1902 | 2408 | return null; |
| 1903 | 2409 | |
| 1904 | // TODO split floatcast AIR into float_widen and float_shorten | |
| 1905 | return self.todo("implement 'airFloatCast'", .{}); | |
| 2410 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 2411 | const operand = try self.resolveInst(ty_op.operand); | |
| 2412 | const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); | |
| 2413 | ||
| 2414 | return self.builder.buildFPTrunc(operand, dest_llvm_ty, ""); | |
| 2415 | } | |
| 2416 | ||
| 2417 | fn airFpext(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2418 | if (self.liveness.isUnused(inst)) | |
| 2419 | return null; | |
| 2420 | ||
| 2421 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 2422 | const operand = try self.resolveInst(ty_op.operand); | |
| 2423 | const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); | |
| 2424 | ||
| 2425 | return self.builder.buildFPExt(operand, dest_llvm_ty, ""); | |
| 1906 | 2426 | } |
| 1907 | 2427 | |
| 1908 | 2428 | fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -1947,16 +2467,17 @@ pub const FuncGen = struct { |
| 1947 | 2467 | } |
| 1948 | 2468 | |
| 1949 | 2469 | fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1950 | if (self.liveness.isUnused(inst)) | |
| 1951 | return null; | |
| 2470 | if (self.liveness.isUnused(inst)) return null; | |
| 1952 | 2471 | // buildAlloca expects the pointee type, not the pointer type, so assert that |
| 1953 | 2472 | // a Payload.PointerSimple is passed to the alloc instruction. |
| 1954 | const inst_ty = self.air.typeOfIndex(inst); | |
| 1955 | const pointee_type = inst_ty.castPointer().?.data; | |
| 1956 | ||
| 1957 | // TODO: figure out a way to get the name of the var decl. | |
| 1958 | // TODO: set alignment and volatile | |
| 1959 | return self.buildAlloca(try self.dg.llvmType(pointee_type)); | |
| 2473 | const ptr_ty = self.air.typeOfIndex(inst); | |
| 2474 | const pointee_type = ptr_ty.elemType(); | |
| 2475 | if (!pointee_type.hasCodeGenBits()) return null; | |
| 2476 | const pointee_llvm_ty = try self.dg.llvmType(pointee_type); | |
| 2477 | const target = self.dg.module.getTarget(); | |
| 2478 | const alloca_inst = self.buildAlloca(pointee_llvm_ty); | |
| 2479 | alloca_inst.setAlignment(ptr_ty.ptrAlignment(target)); | |
| 2480 | return alloca_inst; | |
| 1960 | 2481 | } |
| 1961 | 2482 | |
| 1962 | 2483 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| ... | ... | @@ -1986,20 +2507,19 @@ pub const FuncGen = struct { |
| 1986 | 2507 | fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1987 | 2508 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1988 | 2509 | const dest_ptr = try self.resolveInst(bin_op.lhs); |
| 2510 | const ptr_ty = self.air.typeOf(bin_op.lhs); | |
| 1989 | 2511 | const src_operand = try self.resolveInst(bin_op.rhs); |
| 1990 | // TODO set volatile on this store properly | |
| 1991 | _ = self.builder.buildStore(src_operand, dest_ptr); | |
| 2512 | _ = self.store(dest_ptr, ptr_ty, src_operand); | |
| 1992 | 2513 | return null; |
| 1993 | 2514 | } |
| 1994 | 2515 | |
| 1995 | 2516 | fn airLoad(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1996 | 2517 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1997 | const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr(); | |
| 1998 | if (!is_volatile and self.liveness.isUnused(inst)) | |
| 2518 | const ptr_ty = self.air.typeOf(ty_op.operand); | |
| 2519 | if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) | |
| 1999 | 2520 | return null; |
| 2000 | 2521 | const ptr = try self.resolveInst(ty_op.operand); |
| 2001 | // TODO set volatile on this load properly | |
| 2002 | return self.builder.buildLoad(ptr, ""); | |
| 2522 | return self.load(ptr, ptr_ty); | |
| 2003 | 2523 | } |
| 2004 | 2524 | |
| 2005 | 2525 | fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -2009,6 +2529,14 @@ pub const FuncGen = struct { |
| 2009 | 2529 | return null; |
| 2010 | 2530 | } |
| 2011 | 2531 | |
| 2532 | fn airFence(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2533 | const atomic_order = self.air.instructions.items(.data)[inst].fence; | |
| 2534 | const llvm_memory_order = toLlvmAtomicOrdering(atomic_order); | |
| 2535 | const single_threaded = llvm.Bool.fromBool(self.single_threaded); | |
| 2536 | _ = self.builder.buildFence(llvm_memory_order, single_threaded, ""); | |
| 2537 | return null; | |
| 2538 | } | |
| 2539 | ||
| 2012 | 2540 | fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !?*const llvm.Value { |
| 2013 | 2541 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2014 | 2542 | const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data; |
| ... | ... | @@ -2039,8 +2567,6 @@ pub const FuncGen = struct { |
| 2039 | 2567 | result.setWeak(llvm.Bool.fromBool(is_weak)); |
| 2040 | 2568 | |
| 2041 | 2569 | const optional_ty = self.air.typeOfIndex(inst); |
| 2042 | var buffer: Type.Payload.ElemType = undefined; | |
| 2043 | const child_ty = optional_ty.optionalChild(&buffer); | |
| 2044 | 2570 | |
| 2045 | 2571 | var payload = self.builder.buildExtractValue(result, 0, ""); |
| 2046 | 2572 | if (opt_abi_ty != null) { |
| ... | ... | @@ -2049,8 +2575,7 @@ pub const FuncGen = struct { |
| 2049 | 2575 | const success_bit = self.builder.buildExtractValue(result, 1, ""); |
| 2050 | 2576 | |
| 2051 | 2577 | if (optional_ty.isPtrLikeOptional()) { |
| 2052 | const child_llvm_ty = try self.dg.llvmType(child_ty); | |
| 2053 | return self.builder.buildSelect(success_bit, child_llvm_ty.constNull(), payload, ""); | |
| 2578 | return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, ""); | |
| 2054 | 2579 | } |
| 2055 | 2580 | |
| 2056 | 2581 | const optional_llvm_ty = try self.dg.llvmType(optional_ty); |
| ... | ... | @@ -2059,6 +2584,252 @@ pub const FuncGen = struct { |
| 2059 | 2584 | return self.builder.buildInsertValue(partial, non_null_bit, 1, ""); |
| 2060 | 2585 | } |
| 2061 | 2586 | |
| 2587 | fn airAtomicRmw(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2588 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | |
| 2589 | const extra = self.air.extraData(Air.AtomicRmw, pl_op.payload).data; | |
| 2590 | const ptr = try self.resolveInst(pl_op.operand); | |
| 2591 | const ptr_ty = self.air.typeOf(pl_op.operand); | |
| 2592 | const operand_ty = ptr_ty.elemType(); | |
| 2593 | const operand = try self.resolveInst(extra.operand); | |
| 2594 | const is_signed_int = operand_ty.isSignedInt(); | |
| 2595 | const is_float = operand_ty.isRuntimeFloat(); | |
| 2596 | const op = toLlvmAtomicRmwBinOp(extra.op(), is_signed_int, is_float); | |
| 2597 | const ordering = toLlvmAtomicOrdering(extra.ordering()); | |
| 2598 | const single_threaded = llvm.Bool.fromBool(self.single_threaded); | |
| 2599 | const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, op == .Xchg); | |
| 2600 | if (opt_abi_ty) |abi_ty| { | |
| 2601 | // operand needs widening and truncating or bitcasting. | |
| 2602 | const casted_ptr = self.builder.buildBitCast(ptr, abi_ty.pointerType(0), ""); | |
| 2603 | const casted_operand = if (is_float) | |
| 2604 | self.builder.buildBitCast(operand, abi_ty, "") | |
| 2605 | else if (is_signed_int) | |
| 2606 | self.builder.buildSExt(operand, abi_ty, "") | |
| 2607 | else | |
| 2608 | self.builder.buildZExt(operand, abi_ty, ""); | |
| 2609 | ||
| 2610 | const uncasted_result = self.builder.buildAtomicRmw( | |
| 2611 | op, | |
| 2612 | casted_ptr, | |
| 2613 | casted_operand, | |
| 2614 | ordering, | |
| 2615 | single_threaded, | |
| 2616 | ); | |
| 2617 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); | |
| 2618 | if (is_float) { | |
| 2619 | return self.builder.buildBitCast(uncasted_result, operand_llvm_ty, ""); | |
| 2620 | } else { | |
| 2621 | return self.builder.buildTrunc(uncasted_result, operand_llvm_ty, ""); | |
| 2622 | } | |
| 2623 | } | |
| 2624 | ||
| 2625 | if (operand.typeOf().getTypeKind() != .Pointer) { | |
| 2626 | return self.builder.buildAtomicRmw(op, ptr, operand, ordering, single_threaded); | |
| 2627 | } | |
| 2628 | ||
| 2629 | // It's a pointer but we need to treat it as an int. | |
| 2630 | const usize_llvm_ty = try self.dg.llvmType(Type.initTag(.usize)); | |
| 2631 | const casted_ptr = self.builder.buildBitCast(ptr, usize_llvm_ty.pointerType(0), ""); | |
| 2632 | const casted_operand = self.builder.buildPtrToInt(operand, usize_llvm_ty, ""); | |
| 2633 | const uncasted_result = self.builder.buildAtomicRmw( | |
| 2634 | op, | |
| 2635 | casted_ptr, | |
| 2636 | casted_operand, | |
| 2637 | ordering, | |
| 2638 | single_threaded, | |
| 2639 | ); | |
| 2640 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); | |
| 2641 | return self.builder.buildIntToPtr(uncasted_result, operand_llvm_ty, ""); | |
| 2642 | } | |
| 2643 | ||
| 2644 | fn airAtomicLoad(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2645 | const atomic_load = self.air.instructions.items(.data)[inst].atomic_load; | |
| 2646 | const ptr = try self.resolveInst(atomic_load.ptr); | |
| 2647 | const ptr_ty = self.air.typeOf(atomic_load.ptr); | |
| 2648 | if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) | |
| 2649 | return null; | |
| 2650 | const ordering = toLlvmAtomicOrdering(atomic_load.order); | |
| 2651 | const operand_ty = ptr_ty.elemType(); | |
| 2652 | const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false); | |
| 2653 | ||
| 2654 | if (opt_abi_ty) |abi_ty| { | |
| 2655 | // operand needs widening and truncating | |
| 2656 | const casted_ptr = self.builder.buildBitCast(ptr, abi_ty.pointerType(0), ""); | |
| 2657 | const load_inst = self.load(casted_ptr, ptr_ty); | |
| 2658 | load_inst.setOrdering(ordering); | |
| 2659 | return self.builder.buildTrunc(load_inst, try self.dg.llvmType(operand_ty), ""); | |
| 2660 | } | |
| 2661 | const load_inst = self.load(ptr, ptr_ty); | |
| 2662 | load_inst.setOrdering(ordering); | |
| 2663 | return load_inst; | |
| 2664 | } | |
| 2665 | ||
| 2666 | fn airAtomicStore( | |
| 2667 | self: *FuncGen, | |
| 2668 | inst: Air.Inst.Index, | |
| 2669 | ordering: llvm.AtomicOrdering, | |
| 2670 | ) !?*const llvm.Value { | |
| 2671 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2672 | var ptr = try self.resolveInst(bin_op.lhs); | |
| 2673 | const ptr_ty = self.air.typeOf(bin_op.lhs); | |
| 2674 | var element = try self.resolveInst(bin_op.rhs); | |
| 2675 | const operand_ty = ptr_ty.elemType(); | |
| 2676 | const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false); | |
| 2677 | ||
| 2678 | if (opt_abi_ty) |abi_ty| { | |
| 2679 | // operand needs widening | |
| 2680 | ptr = self.builder.buildBitCast(ptr, abi_ty.pointerType(0), ""); | |
| 2681 | if (operand_ty.isSignedInt()) { | |
| 2682 | element = self.builder.buildSExt(element, abi_ty, ""); | |
| 2683 | } else { | |
| 2684 | element = self.builder.buildZExt(element, abi_ty, ""); | |
| 2685 | } | |
| 2686 | } | |
| 2687 | const store_inst = self.store(ptr, ptr_ty, element); | |
| 2688 | store_inst.setOrdering(ordering); | |
| 2689 | return null; | |
| 2690 | } | |
| 2691 | ||
| 2692 | fn airMemset(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2693 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | |
| 2694 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | |
| 2695 | const dest_ptr = try self.resolveInst(pl_op.operand); | |
| 2696 | const ptr_ty = self.air.typeOf(pl_op.operand); | |
| 2697 | const value = try self.resolveInst(extra.lhs); | |
| 2698 | const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndef() else false; | |
| 2699 | const len = try self.resolveInst(extra.rhs); | |
| 2700 | const u8_llvm_ty = self.context.intType(8); | |
| 2701 | const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0); | |
| 2702 | const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, ""); | |
| 2703 | const fill_char = if (val_is_undef) u8_llvm_ty.constInt(0xaa, .False) else value; | |
| 2704 | const target = self.dg.module.getTarget(); | |
| 2705 | const dest_ptr_align = ptr_ty.ptrAlignment(target); | |
| 2706 | _ = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr()); | |
| 2707 | ||
| 2708 | if (val_is_undef and self.dg.module.comp.bin_file.options.valgrind) { | |
| 2709 | // TODO generate valgrind client request to mark byte range as undefined | |
| 2710 | // see gen_valgrind_undef() in codegen.cpp | |
| 2711 | } | |
| 2712 | return null; | |
| 2713 | } | |
| 2714 | ||
| 2715 | fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2716 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | |
| 2717 | const extra = self.air.extraData(Air.Bin, pl_op.payload).data; | |
| 2718 | const dest_ptr = try self.resolveInst(pl_op.operand); | |
| 2719 | const dest_ptr_ty = self.air.typeOf(pl_op.operand); | |
| 2720 | const src_ptr = try self.resolveInst(extra.lhs); | |
| 2721 | const src_ptr_ty = self.air.typeOf(extra.lhs); | |
| 2722 | const len = try self.resolveInst(extra.rhs); | |
| 2723 | const u8_llvm_ty = self.context.intType(8); | |
| 2724 | const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0); | |
| 2725 | const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, ""); | |
| 2726 | const src_ptr_u8 = self.builder.buildBitCast(src_ptr, ptr_u8_llvm_ty, ""); | |
| 2727 | const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr(); | |
| 2728 | const target = self.dg.module.getTarget(); | |
| 2729 | _ = self.builder.buildMemCpy( | |
| 2730 | dest_ptr_u8, | |
| 2731 | dest_ptr_ty.ptrAlignment(target), | |
| 2732 | src_ptr_u8, | |
| 2733 | src_ptr_ty.ptrAlignment(target), | |
| 2734 | len, | |
| 2735 | is_volatile, | |
| 2736 | ); | |
| 2737 | return null; | |
| 2738 | } | |
| 2739 | ||
| 2740 | fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2741 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 2742 | const union_ptr = try self.resolveInst(bin_op.lhs); | |
| 2743 | // TODO handle when onlyTagHasCodegenBits() == true | |
| 2744 | const new_tag = try self.resolveInst(bin_op.rhs); | |
| 2745 | const tag_field_ptr = self.builder.buildStructGEP(union_ptr, 1, ""); | |
| 2746 | ||
| 2747 | _ = self.builder.buildStore(new_tag, tag_field_ptr); | |
| 2748 | return null; | |
| 2749 | } | |
| 2750 | ||
| 2751 | fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 2752 | if (self.liveness.isUnused(inst)) | |
| 2753 | return null; | |
| 2754 | ||
| 2755 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 2756 | const un_ty = self.air.typeOf(ty_op.operand); | |
| 2757 | const un = try self.resolveInst(ty_op.operand); | |
| 2758 | ||
| 2759 | _ = un_ty; // TODO handle when onlyTagHasCodegenBits() == true and other union forms | |
| 2760 | return self.builder.buildExtractValue(un, 1, ""); | |
| 2761 | } | |
| 2762 | ||
| 2763 | fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value { | |
| 2764 | if (self.liveness.isUnused(inst)) return null; | |
| 2765 | ||
| 2766 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 2767 | const operand_ty = self.air.typeOf(ty_op.operand); | |
| 2768 | const operand = try self.resolveInst(ty_op.operand); | |
| 2769 | const target = self.dg.module.getTarget(); | |
| 2770 | const bits = operand_ty.intInfo(target).bits; | |
| 2771 | ||
| 2772 | var fn_name_buf: [100]u8 = undefined; | |
| 2773 | const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{ | |
| 2774 | prefix, bits, | |
| 2775 | }) catch unreachable; | |
| 2776 | const llvm_i1 = self.context.intType(1); | |
| 2777 | const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { | |
| 2778 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); | |
| 2779 | const param_types = [_]*const llvm.Type{ operand_llvm_ty, llvm_i1 }; | |
| 2780 | const fn_type = llvm.functionType(operand_llvm_ty, &param_types, param_types.len, .False); | |
| 2781 | break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); | |
| 2782 | }; | |
| 2783 | ||
| 2784 | const params = [_]*const llvm.Value{ operand, llvm_i1.constNull() }; | |
| 2785 | const wrong_size_result = self.builder.buildCall(fn_val, &params, params.len, ""); | |
| 2786 | const result_ty = self.air.typeOfIndex(inst); | |
| 2787 | const result_llvm_ty = try self.dg.llvmType(result_ty); | |
| 2788 | const result_bits = result_ty.intInfo(target).bits; | |
| 2789 | if (bits > result_bits) { | |
| 2790 | return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, ""); | |
| 2791 | } else if (bits < result_bits) { | |
| 2792 | return self.builder.buildZExt(wrong_size_result, result_llvm_ty, ""); | |
| 2793 | } else { | |
| 2794 | return wrong_size_result; | |
| 2795 | } | |
| 2796 | } | |
| 2797 | ||
| 2798 | fn fieldPtr( | |
| 2799 | self: *FuncGen, | |
| 2800 | inst: Air.Inst.Index, | |
| 2801 | struct_ptr: *const llvm.Value, | |
| 2802 | struct_ptr_ty: Type, | |
| 2803 | field_index: u32, | |
| 2804 | ) !?*const llvm.Value { | |
| 2805 | const struct_ty = struct_ptr_ty.childType(); | |
| 2806 | switch (struct_ty.zigTypeTag()) { | |
| 2807 | .Struct => { | |
| 2808 | const llvm_field_index = llvmFieldIndex(struct_ty, field_index); | |
| 2809 | return self.builder.buildStructGEP(struct_ptr, llvm_field_index, ""); | |
| 2810 | }, | |
| 2811 | .Union => return self.unionFieldPtr(inst, struct_ptr, struct_ty, field_index), | |
| 2812 | else => unreachable, | |
| 2813 | } | |
| 2814 | } | |
| 2815 | ||
| 2816 | fn unionFieldPtr( | |
| 2817 | self: *FuncGen, | |
| 2818 | inst: Air.Inst.Index, | |
| 2819 | union_ptr: *const llvm.Value, | |
| 2820 | union_ty: Type, | |
| 2821 | field_index: c_uint, | |
| 2822 | ) !?*const llvm.Value { | |
| 2823 | const union_obj = union_ty.cast(Type.Payload.Union).?.data; | |
| 2824 | const field = &union_obj.fields.values()[field_index]; | |
| 2825 | const result_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); | |
| 2826 | if (!field.ty.hasCodeGenBits()) { | |
| 2827 | return null; | |
| 2828 | } | |
| 2829 | const union_field_ptr = self.builder.buildStructGEP(union_ptr, 0, ""); | |
| 2830 | return self.builder.buildBitCast(union_field_ptr, result_llvm_ty, ""); | |
| 2831 | } | |
| 2832 | ||
| 2062 | 2833 | fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value { |
| 2063 | 2834 | const id = llvm.lookupIntrinsicID(name.ptr, name.len); |
| 2064 | 2835 | assert(id != 0); |
| ... | ... | @@ -2067,6 +2838,27 @@ pub const FuncGen = struct { |
| 2067 | 2838 | // `getIntrinsicDeclaration` |
| 2068 | 2839 | return self.llvmModule().getIntrinsicDeclaration(id, null, 0); |
| 2069 | 2840 | } |
| 2841 | ||
| 2842 | fn load(self: *FuncGen, ptr: *const llvm.Value, ptr_ty: Type) *const llvm.Value { | |
| 2843 | const llvm_inst = self.builder.buildLoad(ptr, ""); | |
| 2844 | const target = self.dg.module.getTarget(); | |
| 2845 | llvm_inst.setAlignment(ptr_ty.ptrAlignment(target)); | |
| 2846 | llvm_inst.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr())); | |
| 2847 | return llvm_inst; | |
| 2848 | } | |
| 2849 | ||
| 2850 | fn store( | |
| 2851 | self: *FuncGen, | |
| 2852 | ptr: *const llvm.Value, | |
| 2853 | ptr_ty: Type, | |
| 2854 | elem: *const llvm.Value, | |
| 2855 | ) *const llvm.Value { | |
| 2856 | const llvm_inst = self.builder.buildStore(elem, ptr); | |
| 2857 | const target = self.dg.module.getTarget(); | |
| 2858 | llvm_inst.setAlignment(ptr_ty.ptrAlignment(target)); | |
| 2859 | llvm_inst.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr())); | |
| 2860 | return llvm_inst; | |
| 2861 | } | |
| 2070 | 2862 | }; |
| 2071 | 2863 | |
| 2072 | 2864 | fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { |
| ... | ... | @@ -2261,3 +3053,33 @@ fn toLlvmAtomicOrdering(atomic_order: std.builtin.AtomicOrder) llvm.AtomicOrderi |
| 2261 | 3053 | .SeqCst => .SequentiallyConsistent, |
| 2262 | 3054 | }; |
| 2263 | 3055 | } |
| 3056 | ||
| 3057 | fn toLlvmAtomicRmwBinOp( | |
| 3058 | op: std.builtin.AtomicRmwOp, | |
| 3059 | is_signed: bool, | |
| 3060 | is_float: bool, | |
| 3061 | ) llvm.AtomicRMWBinOp { | |
| 3062 | return switch (op) { | |
| 3063 | .Xchg => .Xchg, | |
| 3064 | .Add => if (is_float) llvm.AtomicRMWBinOp.FAdd else return .Add, | |
| 3065 | .Sub => if (is_float) llvm.AtomicRMWBinOp.FSub else return .Sub, | |
| 3066 | .And => .And, | |
| 3067 | .Nand => .Nand, | |
| 3068 | .Or => .Or, | |
| 3069 | .Xor => .Xor, | |
| 3070 | .Max => if (is_signed) llvm.AtomicRMWBinOp.Max else return .UMax, | |
| 3071 | .Min => if (is_signed) llvm.AtomicRMWBinOp.Min else return .UMin, | |
| 3072 | }; | |
| 3073 | } | |
| 3074 | ||
| 3075 | /// Take into account 0 bit fields. | |
| 3076 | fn llvmFieldIndex(ty: Type, index: u32) c_uint { | |
| 3077 | const struct_obj = ty.castTag(.@"struct").?.data; | |
| 3078 | var result: c_uint = 0; | |
| 3079 | for (struct_obj.fields.values()[0..index]) |field| { | |
| 3080 | if (field.ty.hasCodeGenBits()) { | |
| 3081 | result += 1; | |
| 3082 | } | |
| 3083 | } | |
| 3084 | return result; | |
| 3085 | } |
src/codegen/llvm/bindings.zig+332-2| ... | ... | @@ -85,6 +85,9 @@ pub const Value = opaque { |
| 85 | 85 | pub const addAttributeAtIndex = LLVMAddAttributeAtIndex; |
| 86 | 86 | extern fn LLVMAddAttributeAtIndex(*const Value, Idx: AttributeIndex, A: *const Attribute) void; |
| 87 | 87 | |
| 88 | pub const removeEnumAttributeAtIndex = LLVMRemoveEnumAttributeAtIndex; | |
| 89 | extern fn LLVMRemoveEnumAttributeAtIndex(F: *const Value, Idx: AttributeIndex, KindID: c_uint) void; | |
| 90 | ||
| 88 | 91 | pub const getFirstBasicBlock = LLVMGetFirstBasicBlock; |
| 89 | 92 | extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock; |
| 90 | 93 | |
| ... | ... | @@ -136,6 +139,30 @@ pub const Value = opaque { |
| 136 | 139 | |
| 137 | 140 | pub const setWeak = LLVMSetWeak; |
| 138 | 141 | extern fn LLVMSetWeak(CmpXchgInst: *const Value, IsWeak: Bool) void; |
| 142 | ||
| 143 | pub const setOrdering = LLVMSetOrdering; | |
| 144 | extern fn LLVMSetOrdering(MemoryAccessInst: *const Value, Ordering: AtomicOrdering) void; | |
| 145 | ||
| 146 | pub const setVolatile = LLVMSetVolatile; | |
| 147 | extern fn LLVMSetVolatile(MemoryAccessInst: *const Value, IsVolatile: Bool) void; | |
| 148 | ||
| 149 | pub const setAlignment = LLVMSetAlignment; | |
| 150 | extern fn LLVMSetAlignment(V: *const Value, Bytes: c_uint) void; | |
| 151 | ||
| 152 | pub const getFunctionCallConv = LLVMGetFunctionCallConv; | |
| 153 | extern fn LLVMGetFunctionCallConv(Fn: *const Value) CallConv; | |
| 154 | ||
| 155 | pub const setFunctionCallConv = LLVMSetFunctionCallConv; | |
| 156 | extern fn LLVMSetFunctionCallConv(Fn: *const Value, CC: CallConv) void; | |
| 157 | ||
| 158 | pub const setValueName = LLVMSetValueName; | |
| 159 | extern fn LLVMSetValueName(Val: *const Value, Name: [*:0]const u8) void; | |
| 160 | ||
| 161 | pub const setValueName2 = LLVMSetValueName2; | |
| 162 | extern fn LLVMSetValueName2(Val: *const Value, Name: [*]const u8, NameLen: usize) void; | |
| 163 | ||
| 164 | pub const deleteFunction = LLVMDeleteFunction; | |
| 165 | extern fn LLVMDeleteFunction(Fn: *const Value) void; | |
| 139 | 166 | }; |
| 140 | 167 | |
| 141 | 168 | pub const Type = opaque { |
| ... | ... | @@ -148,12 +175,22 @@ pub const Type = opaque { |
| 148 | 175 | pub const constInt = LLVMConstInt; |
| 149 | 176 | extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value; |
| 150 | 177 | |
| 178 | pub const constIntOfArbitraryPrecision = LLVMConstIntOfArbitraryPrecision; | |
| 179 | extern fn LLVMConstIntOfArbitraryPrecision(IntTy: *const Type, NumWords: c_uint, Words: [*]const u64) *const Value; | |
| 180 | ||
| 151 | 181 | pub const constReal = LLVMConstReal; |
| 152 | 182 | extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value; |
| 153 | 183 | |
| 154 | 184 | pub const constArray = LLVMConstArray; |
| 155 | 185 | extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]*const Value, Length: c_uint) *const Value; |
| 156 | 186 | |
| 187 | pub const constNamedStruct = LLVMConstNamedStruct; | |
| 188 | extern fn LLVMConstNamedStruct( | |
| 189 | StructTy: *const Type, | |
| 190 | ConstantVals: [*]const *const Value, | |
| 191 | Count: c_uint, | |
| 192 | ) *const Value; | |
| 193 | ||
| 157 | 194 | pub const getUndef = LLVMGetUndef; |
| 158 | 195 | extern fn LLVMGetUndef(Ty: *const Type) *const Value; |
| 159 | 196 | |
| ... | ... | @@ -170,6 +207,9 @@ pub const Type = opaque { |
| 170 | 207 | ElementCount: c_uint, |
| 171 | 208 | Packed: Bool, |
| 172 | 209 | ) void; |
| 210 | ||
| 211 | pub const getTypeKind = LLVMGetTypeKind; | |
| 212 | extern fn LLVMGetTypeKind(Ty: *const Type) TypeKind; | |
| 173 | 213 | }; |
| 174 | 214 | |
| 175 | 215 | pub const Module = opaque { |
| ... | ... | @@ -182,9 +222,15 @@ pub const Module = opaque { |
| 182 | 222 | pub const verify = LLVMVerifyModule; |
| 183 | 223 | extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) Bool; |
| 184 | 224 | |
| 225 | pub const setModuleDataLayout = LLVMSetModuleDataLayout; | |
| 226 | extern fn LLVMSetModuleDataLayout(*const Module, *const TargetData) void; | |
| 227 | ||
| 185 | 228 | pub const addFunction = LLVMAddFunction; |
| 186 | 229 | extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value; |
| 187 | 230 | |
| 231 | pub const addFunctionInAddressSpace = ZigLLVMAddFunctionInAddressSpace; | |
| 232 | extern fn ZigLLVMAddFunctionInAddressSpace(*const Module, Name: [*:0]const u8, FunctionTy: *const Type, AddressSpace: c_uint) *const Value; | |
| 233 | ||
| 188 | 234 | pub const getNamedFunction = LLVMGetNamedFunction; |
| 189 | 235 | extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value; |
| 190 | 236 | |
| ... | ... | @@ -197,6 +243,9 @@ pub const Module = opaque { |
| 197 | 243 | pub const addGlobal = LLVMAddGlobal; |
| 198 | 244 | extern fn LLVMAddGlobal(M: *const Module, Ty: *const Type, Name: [*:0]const u8) *const Value; |
| 199 | 245 | |
| 246 | pub const addGlobalInAddressSpace = LLVMAddGlobalInAddressSpace; | |
| 247 | extern fn LLVMAddGlobalInAddressSpace(M: *const Module, Ty: *const Type, Name: [*:0]const u8, AddressSpace: c_uint) *const Value; | |
| 248 | ||
| 200 | 249 | pub const getNamedGlobal = LLVMGetNamedGlobal; |
| 201 | 250 | extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value; |
| 202 | 251 | |
| ... | ... | @@ -268,7 +317,7 @@ extern fn LLVMGetInlineAsm( |
| 268 | 317 | pub const functionType = LLVMFunctionType; |
| 269 | 318 | extern fn LLVMFunctionType( |
| 270 | 319 | ReturnType: *const Type, |
| 271 | ParamTypes: [*]*const Type, | |
| 320 | ParamTypes: [*]const *const Type, | |
| 272 | 321 | ParamCount: c_uint, |
| 273 | 322 | IsVarArg: Bool, |
| 274 | 323 | ) *const Type; |
| ... | ... | @@ -314,7 +363,7 @@ pub const Builder = opaque { |
| 314 | 363 | extern fn LLVMBuildCall( |
| 315 | 364 | *const Builder, |
| 316 | 365 | Fn: *const Value, |
| 317 | Args: [*]*const Value, | |
| 366 | Args: [*]const *const Value, | |
| 318 | 367 | NumArgs: c_uint, |
| 319 | 368 | Name: [*:0]const u8, |
| 320 | 369 | ) *const Value; |
| ... | ... | @@ -365,6 +414,12 @@ pub const Builder = opaque { |
| 365 | 414 | pub const buildNUWAdd = LLVMBuildNUWAdd; |
| 366 | 415 | extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 367 | 416 | |
| 417 | pub const buildSAddSat = ZigLLVMBuildSAddSat; | |
| 418 | extern fn ZigLLVMBuildSAddSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 419 | ||
| 420 | pub const buildUAddSat = ZigLLVMBuildUAddSat; | |
| 421 | extern fn ZigLLVMBuildUAddSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 422 | ||
| 368 | 423 | pub const buildFSub = LLVMBuildFSub; |
| 369 | 424 | extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 370 | 425 | |
| ... | ... | @@ -377,6 +432,12 @@ pub const Builder = opaque { |
| 377 | 432 | pub const buildNUWSub = LLVMBuildNUWSub; |
| 378 | 433 | extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 379 | 434 | |
| 435 | pub const buildSSubSat = ZigLLVMBuildSSubSat; | |
| 436 | extern fn ZigLLVMBuildSSubSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 437 | ||
| 438 | pub const buildUSubSat = ZigLLVMBuildUSubSat; | |
| 439 | extern fn ZigLLVMBuildUSubSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 440 | ||
| 380 | 441 | pub const buildFMul = LLVMBuildFMul; |
| 381 | 442 | extern fn LLVMBuildFMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 382 | 443 | |
| ... | ... | @@ -389,6 +450,12 @@ pub const Builder = opaque { |
| 389 | 450 | pub const buildNUWMul = LLVMBuildNUWMul; |
| 390 | 451 | extern fn LLVMBuildNUWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 391 | 452 | |
| 453 | pub const buildSMulFixSat = ZigLLVMBuildSMulFixSat; | |
| 454 | extern fn ZigLLVMBuildSMulFixSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 455 | ||
| 456 | pub const buildUMulFixSat = ZigLLVMBuildUMulFixSat; | |
| 457 | extern fn ZigLLVMBuildUMulFixSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 458 | ||
| 392 | 459 | pub const buildUDiv = LLVMBuildUDiv; |
| 393 | 460 | extern fn LLVMBuildUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 394 | 461 | |
| ... | ... | @@ -419,6 +486,18 @@ pub const Builder = opaque { |
| 419 | 486 | pub const buildShl = LLVMBuildShl; |
| 420 | 487 | extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 421 | 488 | |
| 489 | pub const buildNUWShl = ZigLLVMBuildNUWShl; | |
| 490 | extern fn ZigLLVMBuildNUWShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 491 | ||
| 492 | pub const buildNSWShl = ZigLLVMBuildNSWShl; | |
| 493 | extern fn ZigLLVMBuildNSWShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 494 | ||
| 495 | pub const buildSShlSat = ZigLLVMBuildSShlSat; | |
| 496 | extern fn ZigLLVMBuildSShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 497 | ||
| 498 | pub const buildUShlSat = ZigLLVMBuildUShlSat; | |
| 499 | extern fn ZigLLVMBuildUShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | |
| 500 | ||
| 422 | 501 | pub const buildOr = LLVMBuildOr; |
| 423 | 502 | extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 424 | 503 | |
| ... | ... | @@ -481,6 +560,14 @@ pub const Builder = opaque { |
| 481 | 560 | Name: [*:0]const u8, |
| 482 | 561 | ) *const Value; |
| 483 | 562 | |
| 563 | pub const buildIntToPtr = LLVMBuildIntToPtr; | |
| 564 | extern fn LLVMBuildIntToPtr( | |
| 565 | *const Builder, | |
| 566 | Val: *const Value, | |
| 567 | DestTy: *const Type, | |
| 568 | Name: [*:0]const u8, | |
| 569 | ) *const Value; | |
| 570 | ||
| 484 | 571 | pub const buildStructGEP = LLVMBuildStructGEP; |
| 485 | 572 | extern fn LLVMBuildStructGEP( |
| 486 | 573 | B: *const Builder, |
| ... | ... | @@ -525,6 +612,93 @@ pub const Builder = opaque { |
| 525 | 612 | Else: *const Value, |
| 526 | 613 | Name: [*:0]const u8, |
| 527 | 614 | ) *const Value; |
| 615 | ||
| 616 | pub const buildFence = LLVMBuildFence; | |
| 617 | extern fn LLVMBuildFence( | |
| 618 | B: *const Builder, | |
| 619 | ordering: AtomicOrdering, | |
| 620 | singleThread: Bool, | |
| 621 | Name: [*:0]const u8, | |
| 622 | ) *const Value; | |
| 623 | ||
| 624 | pub const buildAtomicRmw = LLVMBuildAtomicRMW; | |
| 625 | extern fn LLVMBuildAtomicRMW( | |
| 626 | B: *const Builder, | |
| 627 | op: AtomicRMWBinOp, | |
| 628 | PTR: *const Value, | |
| 629 | Val: *const Value, | |
| 630 | ordering: AtomicOrdering, | |
| 631 | singleThread: Bool, | |
| 632 | ) *const Value; | |
| 633 | ||
| 634 | pub const buildFPToUI = LLVMBuildFPToUI; | |
| 635 | extern fn LLVMBuildFPToUI( | |
| 636 | *const Builder, | |
| 637 | Val: *const Value, | |
| 638 | DestTy: *const Type, | |
| 639 | Name: [*:0]const u8, | |
| 640 | ) *const Value; | |
| 641 | ||
| 642 | pub const buildFPToSI = LLVMBuildFPToSI; | |
| 643 | extern fn LLVMBuildFPToSI( | |
| 644 | *const Builder, | |
| 645 | Val: *const Value, | |
| 646 | DestTy: *const Type, | |
| 647 | Name: [*:0]const u8, | |
| 648 | ) *const Value; | |
| 649 | ||
| 650 | pub const buildUIToFP = LLVMBuildUIToFP; | |
| 651 | extern fn LLVMBuildUIToFP( | |
| 652 | *const Builder, | |
| 653 | Val: *const Value, | |
| 654 | DestTy: *const Type, | |
| 655 | Name: [*:0]const u8, | |
| 656 | ) *const Value; | |
| 657 | ||
| 658 | pub const buildSIToFP = LLVMBuildSIToFP; | |
| 659 | extern fn LLVMBuildSIToFP( | |
| 660 | *const Builder, | |
| 661 | Val: *const Value, | |
| 662 | DestTy: *const Type, | |
| 663 | Name: [*:0]const u8, | |
| 664 | ) *const Value; | |
| 665 | ||
| 666 | pub const buildFPTrunc = LLVMBuildFPTrunc; | |
| 667 | extern fn LLVMBuildFPTrunc( | |
| 668 | *const Builder, | |
| 669 | Val: *const Value, | |
| 670 | DestTy: *const Type, | |
| 671 | Name: [*:0]const u8, | |
| 672 | ) *const Value; | |
| 673 | ||
| 674 | pub const buildFPExt = LLVMBuildFPExt; | |
| 675 | extern fn LLVMBuildFPExt( | |
| 676 | *const Builder, | |
| 677 | Val: *const Value, | |
| 678 | DestTy: *const Type, | |
| 679 | Name: [*:0]const u8, | |
| 680 | ) *const Value; | |
| 681 | ||
| 682 | pub const buildMemSet = ZigLLVMBuildMemSet; | |
| 683 | extern fn ZigLLVMBuildMemSet( | |
| 684 | B: *const Builder, | |
| 685 | Ptr: *const Value, | |
| 686 | Val: *const Value, | |
| 687 | Len: *const Value, | |
| 688 | Align: c_uint, | |
| 689 | is_volatile: bool, | |
| 690 | ) *const Value; | |
| 691 | ||
| 692 | pub const buildMemCpy = ZigLLVMBuildMemCpy; | |
| 693 | extern fn ZigLLVMBuildMemCpy( | |
| 694 | B: *const Builder, | |
| 695 | Dst: *const Value, | |
| 696 | DstAlign: c_uint, | |
| 697 | Src: *const Value, | |
| 698 | SrcAlign: c_uint, | |
| 699 | Size: *const Value, | |
| 700 | is_volatile: bool, | |
| 701 | ) *const Value; | |
| 528 | 702 | }; |
| 529 | 703 | |
| 530 | 704 | pub const IntPredicate = enum(c_uint) { |
| ... | ... | @@ -598,6 +772,14 @@ pub const TargetMachine = opaque { |
| 598 | 772 | llvm_ir_filename: ?[*:0]const u8, |
| 599 | 773 | bitcode_filename: ?[*:0]const u8, |
| 600 | 774 | ) bool; |
| 775 | ||
| 776 | pub const createTargetDataLayout = LLVMCreateTargetDataLayout; | |
| 777 | extern fn LLVMCreateTargetDataLayout(*const TargetMachine) *const TargetData; | |
| 778 | }; | |
| 779 | ||
| 780 | pub const TargetData = opaque { | |
| 781 | pub const dispose = LLVMDisposeTargetData; | |
| 782 | extern fn LLVMDisposeTargetData(*const TargetData) void; | |
| 601 | 783 | }; |
| 602 | 784 | |
| 603 | 785 | pub const CodeModel = enum(c_int) { |
| ... | ... | @@ -915,3 +1097,151 @@ pub const AtomicOrdering = enum(c_uint) { |
| 915 | 1097 | AcquireRelease = 6, |
| 916 | 1098 | SequentiallyConsistent = 7, |
| 917 | 1099 | }; |
| 1100 | ||
| 1101 | pub const AtomicRMWBinOp = enum(c_int) { | |
| 1102 | Xchg, | |
| 1103 | Add, | |
| 1104 | Sub, | |
| 1105 | And, | |
| 1106 | Nand, | |
| 1107 | Or, | |
| 1108 | Xor, | |
| 1109 | Max, | |
| 1110 | Min, | |
| 1111 | UMax, | |
| 1112 | UMin, | |
| 1113 | FAdd, | |
| 1114 | FSub, | |
| 1115 | }; | |
| 1116 | ||
| 1117 | pub const TypeKind = enum(c_int) { | |
| 1118 | Void, | |
| 1119 | Half, | |
| 1120 | Float, | |
| 1121 | Double, | |
| 1122 | X86_FP80, | |
| 1123 | FP128, | |
| 1124 | PPC_FP128, | |
| 1125 | Label, | |
| 1126 | Integer, | |
| 1127 | Function, | |
| 1128 | Struct, | |
| 1129 | Array, | |
| 1130 | Pointer, | |
| 1131 | Vector, | |
| 1132 | Metadata, | |
| 1133 | X86_MMX, | |
| 1134 | Token, | |
| 1135 | ScalableVector, | |
| 1136 | BFloat, | |
| 1137 | X86_AMX, | |
| 1138 | }; | |
| 1139 | ||
| 1140 | pub const CallConv = enum(c_uint) { | |
| 1141 | C = 0, | |
| 1142 | Fast = 8, | |
| 1143 | Cold = 9, | |
| 1144 | GHC = 10, | |
| 1145 | HiPE = 11, | |
| 1146 | WebKit_JS = 12, | |
| 1147 | AnyReg = 13, | |
| 1148 | PreserveMost = 14, | |
| 1149 | PreserveAll = 15, | |
| 1150 | Swift = 16, | |
| 1151 | CXX_FAST_TLS = 17, | |
| 1152 | ||
| 1153 | X86_StdCall = 64, | |
| 1154 | X86_FastCall = 65, | |
| 1155 | ARM_APCS = 66, | |
| 1156 | ARM_AAPCS = 67, | |
| 1157 | ARM_AAPCS_VFP = 68, | |
| 1158 | MSP430_INTR = 69, | |
| 1159 | X86_ThisCall = 70, | |
| 1160 | PTX_Kernel = 71, | |
| 1161 | PTX_Device = 72, | |
| 1162 | SPIR_FUNC = 75, | |
| 1163 | SPIR_KERNEL = 76, | |
| 1164 | Intel_OCL_BI = 77, | |
| 1165 | X86_64_SysV = 78, | |
| 1166 | Win64 = 79, | |
| 1167 | X86_VectorCall = 80, | |
| 1168 | HHVM = 81, | |
| 1169 | HHVM_C = 82, | |
| 1170 | X86_INTR = 83, | |
| 1171 | AVR_INTR = 84, | |
| 1172 | AVR_SIGNAL = 85, | |
| 1173 | AVR_BUILTIN = 86, | |
| 1174 | AMDGPU_VS = 87, | |
| 1175 | AMDGPU_GS = 88, | |
| 1176 | AMDGPU_PS = 89, | |
| 1177 | AMDGPU_CS = 90, | |
| 1178 | AMDGPU_KERNEL = 91, | |
| 1179 | X86_RegCall = 92, | |
| 1180 | AMDGPU_HS = 93, | |
| 1181 | MSP430_BUILTIN = 94, | |
| 1182 | AMDGPU_LS = 95, | |
| 1183 | AMDGPU_ES = 96, | |
| 1184 | AArch64_VectorCall = 97, | |
| 1185 | }; | |
| 1186 | ||
| 1187 | pub const address_space = struct { | |
| 1188 | pub const default: c_uint = 0; | |
| 1189 | ||
| 1190 | // See llvm/lib/Target/X86/X86.h | |
| 1191 | pub const x86_64 = x86; | |
| 1192 | pub const x86 = struct { | |
| 1193 | pub const gs: c_uint = 256; | |
| 1194 | pub const fs: c_uint = 257; | |
| 1195 | pub const ss: c_uint = 258; | |
| 1196 | ||
| 1197 | pub const ptr32_sptr: c_uint = 270; | |
| 1198 | pub const ptr32_uptr: c_uint = 271; | |
| 1199 | pub const ptr64: c_uint = 272; | |
| 1200 | }; | |
| 1201 | ||
| 1202 | // See llvm/lib/Target/AVR/AVR.h | |
| 1203 | pub const avr = struct { | |
| 1204 | pub const data_memory: c_uint = 0; | |
| 1205 | pub const program_memory: c_uint = 1; | |
| 1206 | }; | |
| 1207 | ||
| 1208 | // See llvm/lib/Target/NVPTX/NVPTX.h | |
| 1209 | pub const nvptx = struct { | |
| 1210 | pub const generic: c_uint = 0; | |
| 1211 | pub const global: c_uint = 1; | |
| 1212 | pub const constant: c_uint = 2; | |
| 1213 | pub const shared: c_uint = 3; | |
| 1214 | pub const param: c_uint = 4; | |
| 1215 | pub const local: c_uint = 5; | |
| 1216 | }; | |
| 1217 | ||
| 1218 | // See llvm/lib/Target/AMDGPU/AMDGPU.h | |
| 1219 | pub const amdgpu = struct { | |
| 1220 | pub const flat: c_uint = 0; | |
| 1221 | pub const global: c_uint = 1; | |
| 1222 | pub const region: c_uint = 2; | |
| 1223 | pub const local: c_uint = 3; | |
| 1224 | pub const constant: c_uint = 4; | |
| 1225 | pub const private: c_uint = 5; | |
| 1226 | pub const constant_32bit: c_uint = 6; | |
| 1227 | pub const buffer_fat_pointer: c_uint = 7; | |
| 1228 | pub const param_d: c_uint = 6; | |
| 1229 | pub const param_i: c_uint = 7; | |
| 1230 | pub const constant_buffer_0: c_uint = 8; | |
| 1231 | pub const constant_buffer_1: c_uint = 9; | |
| 1232 | pub const constant_buffer_2: c_uint = 10; | |
| 1233 | pub const constant_buffer_3: c_uint = 11; | |
| 1234 | pub const constant_buffer_4: c_uint = 12; | |
| 1235 | pub const constant_buffer_5: c_uint = 13; | |
| 1236 | pub const constant_buffer_6: c_uint = 14; | |
| 1237 | pub const constant_buffer_7: c_uint = 15; | |
| 1238 | pub const constant_buffer_8: c_uint = 16; | |
| 1239 | pub const constant_buffer_9: c_uint = 17; | |
| 1240 | pub const constant_buffer_10: c_uint = 18; | |
| 1241 | pub const constant_buffer_11: c_uint = 19; | |
| 1242 | pub const constant_buffer_12: c_uint = 20; | |
| 1243 | pub const constant_buffer_13: c_uint = 21; | |
| 1244 | pub const constant_buffer_14: c_uint = 22; | |
| 1245 | pub const constant_buffer_15: c_uint = 23; | |
| 1246 | }; | |
| 1247 | }; |
src/codegen/riscv64.zig deleted-470| ... | ... | @@ -1,470 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const DW = std.dwarf; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const testing = std.testing; | |
| 5 | ||
| 6 | // TODO: this is only tagged to facilitate the monstrosity. | |
| 7 | // Once packed structs work make it packed. | |
| 8 | pub const Instruction = union(enum) { | |
| 9 | R: packed struct { | |
| 10 | opcode: u7, | |
| 11 | rd: u5, | |
| 12 | funct3: u3, | |
| 13 | rs1: u5, | |
| 14 | rs2: u5, | |
| 15 | funct7: u7, | |
| 16 | }, | |
| 17 | I: packed struct { | |
| 18 | opcode: u7, | |
| 19 | rd: u5, | |
| 20 | funct3: u3, | |
| 21 | rs1: u5, | |
| 22 | imm0_11: u12, | |
| 23 | }, | |
| 24 | S: packed struct { | |
| 25 | opcode: u7, | |
| 26 | imm0_4: u5, | |
| 27 | funct3: u3, | |
| 28 | rs1: u5, | |
| 29 | rs2: u5, | |
| 30 | imm5_11: u7, | |
| 31 | }, | |
| 32 | B: packed struct { | |
| 33 | opcode: u7, | |
| 34 | imm11: u1, | |
| 35 | imm1_4: u4, | |
| 36 | funct3: u3, | |
| 37 | rs1: u5, | |
| 38 | rs2: u5, | |
| 39 | imm5_10: u6, | |
| 40 | imm12: u1, | |
| 41 | }, | |
| 42 | U: packed struct { | |
| 43 | opcode: u7, | |
| 44 | rd: u5, | |
| 45 | imm12_31: u20, | |
| 46 | }, | |
| 47 | J: packed struct { | |
| 48 | opcode: u7, | |
| 49 | rd: u5, | |
| 50 | imm12_19: u8, | |
| 51 | imm11: u1, | |
| 52 | imm1_10: u10, | |
| 53 | imm20: u1, | |
| 54 | }, | |
| 55 | ||
| 56 | // TODO: once packed structs work we can remove this monstrosity. | |
| 57 | pub fn toU32(self: Instruction) u32 { | |
| 58 | return switch (self) { | |
| 59 | .R => |v| @bitCast(u32, v), | |
| 60 | .I => |v| @bitCast(u32, v), | |
| 61 | .S => |v| @bitCast(u32, v), | |
| 62 | .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31), | |
| 63 | .U => |v| @bitCast(u32, v), | |
| 64 | .J => |v| @bitCast(u32, v), | |
| 65 | }; | |
| 66 | } | |
| 67 | ||
| 68 | fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction { | |
| 69 | return Instruction{ | |
| 70 | .R = .{ | |
| 71 | .opcode = op, | |
| 72 | .funct3 = fn3, | |
| 73 | .funct7 = fn7, | |
| 74 | .rd = @enumToInt(rd), | |
| 75 | .rs1 = @enumToInt(r1), | |
| 76 | .rs2 = @enumToInt(r2), | |
| 77 | }, | |
| 78 | }; | |
| 79 | } | |
| 80 | ||
| 81 | // RISC-V is all signed all the time -- convert immediates to unsigned for processing | |
| 82 | fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction { | |
| 83 | const umm = @bitCast(u12, imm); | |
| 84 | ||
| 85 | return Instruction{ | |
| 86 | .I = .{ | |
| 87 | .opcode = op, | |
| 88 | .funct3 = fn3, | |
| 89 | .rd = @enumToInt(rd), | |
| 90 | .rs1 = @enumToInt(r1), | |
| 91 | .imm0_11 = umm, | |
| 92 | }, | |
| 93 | }; | |
| 94 | } | |
| 95 | ||
| 96 | fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction { | |
| 97 | const umm = @bitCast(u12, imm); | |
| 98 | ||
| 99 | return Instruction{ | |
| 100 | .S = .{ | |
| 101 | .opcode = op, | |
| 102 | .funct3 = fn3, | |
| 103 | .rs1 = @enumToInt(r1), | |
| 104 | .rs2 = @enumToInt(r2), | |
| 105 | .imm0_4 = @truncate(u5, umm), | |
| 106 | .imm5_11 = @truncate(u7, umm >> 5), | |
| 107 | }, | |
| 108 | }; | |
| 109 | } | |
| 110 | ||
| 111 | // Use significance value rather than bit value, same for J-type | |
| 112 | // -- less burden on callsite, bonus semantic checking | |
| 113 | fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction { | |
| 114 | const umm = @bitCast(u13, imm); | |
| 115 | assert(umm % 2 == 0); // misaligned branch target | |
| 116 | ||
| 117 | return Instruction{ | |
| 118 | .B = .{ | |
| 119 | .opcode = op, | |
| 120 | .funct3 = fn3, | |
| 121 | .rs1 = @enumToInt(r1), | |
| 122 | .rs2 = @enumToInt(r2), | |
| 123 | .imm1_4 = @truncate(u4, umm >> 1), | |
| 124 | .imm5_10 = @truncate(u6, umm >> 5), | |
| 125 | .imm11 = @truncate(u1, umm >> 11), | |
| 126 | .imm12 = @truncate(u1, umm >> 12), | |
| 127 | }, | |
| 128 | }; | |
| 129 | } | |
| 130 | ||
| 131 | // We have to extract the 20 bits anyway -- let's not make it more painful | |
| 132 | fn uType(op: u7, rd: Register, imm: i20) Instruction { | |
| 133 | const umm = @bitCast(u20, imm); | |
| 134 | ||
| 135 | return Instruction{ | |
| 136 | .U = .{ | |
| 137 | .opcode = op, | |
| 138 | .rd = @enumToInt(rd), | |
| 139 | .imm12_31 = umm, | |
| 140 | }, | |
| 141 | }; | |
| 142 | } | |
| 143 | ||
| 144 | fn jType(op: u7, rd: Register, imm: i21) Instruction { | |
| 145 | const umm = @bitCast(u21, imm); | |
| 146 | assert(umm % 2 == 0); // misaligned jump target | |
| 147 | ||
| 148 | return Instruction{ | |
| 149 | .J = .{ | |
| 150 | .opcode = op, | |
| 151 | .rd = @enumToInt(rd), | |
| 152 | .imm1_10 = @truncate(u10, umm >> 1), | |
| 153 | .imm11 = @truncate(u1, umm >> 11), | |
| 154 | .imm12_19 = @truncate(u8, umm >> 12), | |
| 155 | .imm20 = @truncate(u1, umm >> 20), | |
| 156 | }, | |
| 157 | }; | |
| 158 | } | |
| 159 | ||
| 160 | // The meat and potatoes. Arguments are in the order in which they would appear in assembly code. | |
| 161 | ||
| 162 | // Arithmetic/Logical, Register-Register | |
| 163 | ||
| 164 | pub fn add(rd: Register, r1: Register, r2: Register) Instruction { | |
| 165 | return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2); | |
| 166 | } | |
| 167 | ||
| 168 | pub fn sub(rd: Register, r1: Register, r2: Register) Instruction { | |
| 169 | return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2); | |
| 170 | } | |
| 171 | ||
| 172 | pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction { | |
| 173 | return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2); | |
| 174 | } | |
| 175 | ||
| 176 | pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction { | |
| 177 | return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2); | |
| 178 | } | |
| 179 | ||
| 180 | pub fn xor(rd: Register, r1: Register, r2: Register) Instruction { | |
| 181 | return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2); | |
| 182 | } | |
| 183 | ||
| 184 | pub fn sll(rd: Register, r1: Register, r2: Register) Instruction { | |
| 185 | return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2); | |
| 186 | } | |
| 187 | ||
| 188 | pub fn srl(rd: Register, r1: Register, r2: Register) Instruction { | |
| 189 | return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2); | |
| 190 | } | |
| 191 | ||
| 192 | pub fn sra(rd: Register, r1: Register, r2: Register) Instruction { | |
| 193 | return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2); | |
| 194 | } | |
| 195 | ||
| 196 | pub fn slt(rd: Register, r1: Register, r2: Register) Instruction { | |
| 197 | return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2); | |
| 198 | } | |
| 199 | ||
| 200 | pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction { | |
| 201 | return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2); | |
| 202 | } | |
| 203 | ||
| 204 | // Arithmetic/Logical, Register-Register (32-bit) | |
| 205 | ||
| 206 | pub fn addw(rd: Register, r1: Register, r2: Register) Instruction { | |
| 207 | return rType(0b0111011, 0b000, rd, r1, r2); | |
| 208 | } | |
| 209 | ||
| 210 | pub fn subw(rd: Register, r1: Register, r2: Register) Instruction { | |
| 211 | return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2); | |
| 212 | } | |
| 213 | ||
| 214 | pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction { | |
| 215 | return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2); | |
| 216 | } | |
| 217 | ||
| 218 | pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction { | |
| 219 | return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2); | |
| 220 | } | |
| 221 | ||
| 222 | pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction { | |
| 223 | return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2); | |
| 224 | } | |
| 225 | ||
| 226 | // Arithmetic/Logical, Register-Immediate | |
| 227 | ||
| 228 | pub fn addi(rd: Register, r1: Register, imm: i12) Instruction { | |
| 229 | return iType(0b0010011, 0b000, rd, r1, imm); | |
| 230 | } | |
| 231 | ||
| 232 | pub fn andi(rd: Register, r1: Register, imm: i12) Instruction { | |
| 233 | return iType(0b0010011, 0b111, rd, r1, imm); | |
| 234 | } | |
| 235 | ||
| 236 | pub fn ori(rd: Register, r1: Register, imm: i12) Instruction { | |
| 237 | return iType(0b0010011, 0b110, rd, r1, imm); | |
| 238 | } | |
| 239 | ||
| 240 | pub fn xori(rd: Register, r1: Register, imm: i12) Instruction { | |
| 241 | return iType(0b0010011, 0b100, rd, r1, imm); | |
| 242 | } | |
| 243 | ||
| 244 | pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction { | |
| 245 | return iType(0b0010011, 0b001, rd, r1, shamt); | |
| 246 | } | |
| 247 | ||
| 248 | pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction { | |
| 249 | return iType(0b0010011, 0b101, rd, r1, shamt); | |
| 250 | } | |
| 251 | ||
| 252 | pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction { | |
| 253 | return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt); | |
| 254 | } | |
| 255 | ||
| 256 | pub fn slti(rd: Register, r1: Register, imm: i12) Instruction { | |
| 257 | return iType(0b0010011, 0b010, rd, r1, imm); | |
| 258 | } | |
| 259 | ||
| 260 | pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction { | |
| 261 | return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm)); | |
| 262 | } | |
| 263 | ||
| 264 | // Arithmetic/Logical, Register-Immediate (32-bit) | |
| 265 | ||
| 266 | pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction { | |
| 267 | return iType(0b0011011, 0b000, rd, r1, imm); | |
| 268 | } | |
| 269 | ||
| 270 | pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction { | |
| 271 | return iType(0b0011011, 0b001, rd, r1, shamt); | |
| 272 | } | |
| 273 | ||
| 274 | pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction { | |
| 275 | return iType(0b0011011, 0b101, rd, r1, shamt); | |
| 276 | } | |
| 277 | ||
| 278 | pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction { | |
| 279 | return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt); | |
| 280 | } | |
| 281 | ||
| 282 | // Upper Immediate | |
| 283 | ||
| 284 | pub fn lui(rd: Register, imm: i20) Instruction { | |
| 285 | return uType(0b0110111, rd, imm); | |
| 286 | } | |
| 287 | ||
| 288 | pub fn auipc(rd: Register, imm: i20) Instruction { | |
| 289 | return uType(0b0010111, rd, imm); | |
| 290 | } | |
| 291 | ||
| 292 | // Load | |
| 293 | ||
| 294 | pub fn ld(rd: Register, offset: i12, base: Register) Instruction { | |
| 295 | return iType(0b0000011, 0b011, rd, base, offset); | |
| 296 | } | |
| 297 | ||
| 298 | pub fn lw(rd: Register, offset: i12, base: Register) Instruction { | |
| 299 | return iType(0b0000011, 0b010, rd, base, offset); | |
| 300 | } | |
| 301 | ||
| 302 | pub fn lwu(rd: Register, offset: i12, base: Register) Instruction { | |
| 303 | return iType(0b0000011, 0b110, rd, base, offset); | |
| 304 | } | |
| 305 | ||
| 306 | pub fn lh(rd: Register, offset: i12, base: Register) Instruction { | |
| 307 | return iType(0b0000011, 0b001, rd, base, offset); | |
| 308 | } | |
| 309 | ||
| 310 | pub fn lhu(rd: Register, offset: i12, base: Register) Instruction { | |
| 311 | return iType(0b0000011, 0b101, rd, base, offset); | |
| 312 | } | |
| 313 | ||
| 314 | pub fn lb(rd: Register, offset: i12, base: Register) Instruction { | |
| 315 | return iType(0b0000011, 0b000, rd, base, offset); | |
| 316 | } | |
| 317 | ||
| 318 | pub fn lbu(rd: Register, offset: i12, base: Register) Instruction { | |
| 319 | return iType(0b0000011, 0b100, rd, base, offset); | |
| 320 | } | |
| 321 | ||
| 322 | // Store | |
| 323 | ||
| 324 | pub fn sd(rs: Register, offset: i12, base: Register) Instruction { | |
| 325 | return sType(0b0100011, 0b011, base, rs, offset); | |
| 326 | } | |
| 327 | ||
| 328 | pub fn sw(rs: Register, offset: i12, base: Register) Instruction { | |
| 329 | return sType(0b0100011, 0b010, base, rs, offset); | |
| 330 | } | |
| 331 | ||
| 332 | pub fn sh(rs: Register, offset: i12, base: Register) Instruction { | |
| 333 | return sType(0b0100011, 0b001, base, rs, offset); | |
| 334 | } | |
| 335 | ||
| 336 | pub fn sb(rs: Register, offset: i12, base: Register) Instruction { | |
| 337 | return sType(0b0100011, 0b000, base, rs, offset); | |
| 338 | } | |
| 339 | ||
| 340 | // Fence | |
| 341 | // TODO: implement fence | |
| 342 | ||
| 343 | // Branch | |
| 344 | ||
| 345 | pub fn beq(r1: Register, r2: Register, offset: i13) Instruction { | |
| 346 | return bType(0b1100011, 0b000, r1, r2, offset); | |
| 347 | } | |
| 348 | ||
| 349 | pub fn bne(r1: Register, r2: Register, offset: i13) Instruction { | |
| 350 | return bType(0b1100011, 0b001, r1, r2, offset); | |
| 351 | } | |
| 352 | ||
| 353 | pub fn blt(r1: Register, r2: Register, offset: i13) Instruction { | |
| 354 | return bType(0b1100011, 0b100, r1, r2, offset); | |
| 355 | } | |
| 356 | ||
| 357 | pub fn bge(r1: Register, r2: Register, offset: i13) Instruction { | |
| 358 | return bType(0b1100011, 0b101, r1, r2, offset); | |
| 359 | } | |
| 360 | ||
| 361 | pub fn bltu(r1: Register, r2: Register, offset: i13) Instruction { | |
| 362 | return bType(0b1100011, 0b110, r1, r2, offset); | |
| 363 | } | |
| 364 | ||
| 365 | pub fn bgeu(r1: Register, r2: Register, offset: i13) Instruction { | |
| 366 | return bType(0b1100011, 0b111, r1, r2, offset); | |
| 367 | } | |
| 368 | ||
| 369 | // Jump | |
| 370 | ||
| 371 | pub fn jal(link: Register, offset: i21) Instruction { | |
| 372 | return jType(0b1101111, link, offset); | |
| 373 | } | |
| 374 | ||
| 375 | pub fn jalr(link: Register, offset: i12, base: Register) Instruction { | |
| 376 | return iType(0b1100111, 0b000, link, base, offset); | |
| 377 | } | |
| 378 | ||
| 379 | // System | |
| 380 | ||
| 381 | pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000); | |
| 382 | pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001); | |
| 383 | }; | |
| 384 | ||
| 385 | // zig fmt: off | |
| 386 | pub const RawRegister = enum(u5) { | |
| 387 | x0, x1, x2, x3, x4, x5, x6, x7, | |
| 388 | x8, x9, x10, x11, x12, x13, x14, x15, | |
| 389 | x16, x17, x18, x19, x20, x21, x22, x23, | |
| 390 | x24, x25, x26, x27, x28, x29, x30, x31, | |
| 391 | ||
| 392 | pub fn dwarfLocOp(reg: RawRegister) u8 { | |
| 393 | return @enumToInt(reg) + DW.OP.reg0; | |
| 394 | } | |
| 395 | }; | |
| 396 | ||
| 397 | pub const Register = enum(u5) { | |
| 398 | // 64 bit registers | |
| 399 | zero, // zero | |
| 400 | ra, // return address. caller saved | |
| 401 | sp, // stack pointer. callee saved. | |
| 402 | gp, // global pointer | |
| 403 | tp, // thread pointer | |
| 404 | t0, t1, t2, // temporaries. caller saved. | |
| 405 | s0, // s0/fp, callee saved. | |
| 406 | s1, // callee saved. | |
| 407 | a0, a1, // fn args/return values. caller saved. | |
| 408 | a2, a3, a4, a5, a6, a7, // fn args. caller saved. | |
| 409 | s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved. | |
| 410 | t3, t4, t5, t6, // caller saved | |
| 411 | ||
| 412 | pub fn parseRegName(name: []const u8) ?Register { | |
| 413 | if(std.meta.stringToEnum(Register, name)) |reg| return reg; | |
| 414 | if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg)); | |
| 415 | return null; | |
| 416 | } | |
| 417 | ||
| 418 | /// Returns the index into `callee_preserved_regs`. | |
| 419 | pub fn allocIndex(self: Register) ?u4 { | |
| 420 | inline for(callee_preserved_regs) |cpreg, i| { | |
| 421 | if(self == cpreg) return i; | |
| 422 | } | |
| 423 | return null; | |
| 424 | } | |
| 425 | ||
| 426 | pub fn dwarfLocOp(reg: Register) u8 { | |
| 427 | return @as(u8, @enumToInt(reg)) + DW.OP.reg0; | |
| 428 | } | |
| 429 | }; | |
| 430 | ||
| 431 | // zig fmt: on | |
| 432 | ||
| 433 | pub const callee_preserved_regs = [_]Register{ | |
| 434 | .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, | |
| 435 | }; | |
| 436 | ||
| 437 | test "serialize instructions" { | |
| 438 | const Testcase = struct { | |
| 439 | inst: Instruction, | |
| 440 | expected: u32, | |
| 441 | }; | |
| 442 | ||
| 443 | const testcases = [_]Testcase{ | |
| 444 | .{ // add t6, zero, zero | |
| 445 | .inst = Instruction.add(.t6, .zero, .zero), | |
| 446 | .expected = 0b0000000_00000_00000_000_11111_0110011, | |
| 447 | }, | |
| 448 | .{ // sd s0, 0x7f(s0) | |
| 449 | .inst = Instruction.sd(.s0, 0x7f, .s0), | |
| 450 | .expected = 0b0000011_01000_01000_011_11111_0100011, | |
| 451 | }, | |
| 452 | .{ // bne s0, s1, 0x42 | |
| 453 | .inst = Instruction.bne(.s0, .s1, 0x42), | |
| 454 | .expected = 0b0_000010_01001_01000_001_0001_0_1100011, | |
| 455 | }, | |
| 456 | .{ // j 0x1a | |
| 457 | .inst = Instruction.jal(.zero, 0x1a), | |
| 458 | .expected = 0b0_0000001101_0_00000000_00000_1101111, | |
| 459 | }, | |
| 460 | .{ // ebreak | |
| 461 | .inst = Instruction.ebreak, | |
| 462 | .expected = 0b000000000001_00000_000_00000_1110011, | |
| 463 | }, | |
| 464 | }; | |
| 465 | ||
| 466 | for (testcases) |case| { | |
| 467 | const actual = case.inst.toU32(); | |
| 468 | try testing.expectEqual(case.expected, actual); | |
| 469 | } | |
| 470 | } |
src/codegen/spirv.zig+3-3| ... | ... | @@ -260,7 +260,7 @@ pub const DeclGen = struct { |
| 260 | 260 | }; |
| 261 | 261 | } |
| 262 | 262 | |
| 263 | /// Generate the code for `decl`. If a reportable error occured during code generation, | |
| 263 | /// Generate the code for `decl`. If a reportable error occurred during code generation, | |
| 264 | 264 | /// a message is returned by this function. Callee owns the memory. If this function |
| 265 | 265 | /// returns such a reportable error, it is valid to be called again for a different decl. |
| 266 | 266 | pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg { |
| ... | ... | @@ -565,7 +565,7 @@ pub const DeclGen = struct { |
| 565 | 565 | } |
| 566 | 566 | }, |
| 567 | 567 | // When recursively generating a type, we cannot infer the pointer's storage class. See genPointerType. |
| 568 | .Pointer => return self.fail("Cannot create pointer with unkown storage class", .{}), | |
| 568 | .Pointer => return self.fail("Cannot create pointer with unknown storage class", .{}), | |
| 569 | 569 | .Vector => { |
| 570 | 570 | // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations |
| 571 | 571 | // which work on them), so simply use those. |
| ... | ... | @@ -629,7 +629,7 @@ pub const DeclGen = struct { |
| 629 | 629 | const params = decl.ty.fnParamLen(); |
| 630 | 630 | var i: usize = 0; |
| 631 | 631 | |
| 632 | try self.args.ensureCapacity(params); | |
| 632 | try self.args.ensureTotalCapacity(params); | |
| 633 | 633 | while (i < params) : (i += 1) { |
| 634 | 634 | const param_type_id = self.spv.types.get(decl.ty.fnParamType(i)).?; |
| 635 | 635 | const arg_result_id = self.spv.allocResultId(); |
src/codegen/wasm.zig+3-3| ... | ... | @@ -1005,7 +1005,7 @@ pub const Context = struct { |
| 1005 | 1005 | const rhs = self.resolveInst(bin_op.rhs); |
| 1006 | 1006 | |
| 1007 | 1007 | // it's possible for both lhs and/or rhs to return an offset as well, |
| 1008 | // in which case we return the first offset occurance we find. | |
| 1008 | // in which case we return the first offset occurrence we find. | |
| 1009 | 1009 | const offset = blk: { |
| 1010 | 1010 | if (lhs == .code_offset) break :blk lhs.code_offset; |
| 1011 | 1011 | if (rhs == .code_offset) break :blk rhs.code_offset; |
| ... | ... | @@ -1031,7 +1031,7 @@ pub const Context = struct { |
| 1031 | 1031 | const rhs = self.resolveInst(bin_op.rhs); |
| 1032 | 1032 | |
| 1033 | 1033 | // it's possible for both lhs and/or rhs to return an offset as well, |
| 1034 | // in which case we return the first offset occurance we find. | |
| 1034 | // in which case we return the first offset occurrence we find. | |
| 1035 | 1035 | const offset = blk: { |
| 1036 | 1036 | if (lhs == .code_offset) break :blk lhs.code_offset; |
| 1037 | 1037 | if (rhs == .code_offset) break :blk rhs.code_offset; |
| ... | ... | @@ -1395,7 +1395,7 @@ pub const Context = struct { |
| 1395 | 1395 | } |
| 1396 | 1396 | |
| 1397 | 1397 | // We map every block to its block index. |
| 1398 | // We then determine how far we have to jump to it by substracting it from current block depth | |
| 1398 | // We then determine how far we have to jump to it by subtracting it from current block depth | |
| 1399 | 1399 | const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?; |
| 1400 | 1400 | const writer = self.code.writer(); |
| 1401 | 1401 | try writer.writeByte(wasm.opcode(.br)); |
src/codegen/x86.zig deleted-123| ... | ... | @@ -1,123 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const DW = std.dwarf; | |
| 3 | ||
| 4 | // zig fmt: off | |
| 5 | pub const Register = enum(u8) { | |
| 6 | // 0 through 7, 32-bit registers. id is int value | |
| 7 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | |
| 8 | ||
| 9 | // 8-15, 16-bit registers. id is int value - 8. | |
| 10 | ax, cx, dx, bx, sp, bp, si, di, | |
| 11 | ||
| 12 | // 16-23, 8-bit registers. id is int value - 16. | |
| 13 | al, cl, dl, bl, ah, ch, dh, bh, | |
| 14 | ||
| 15 | /// Returns the bit-width of the register. | |
| 16 | pub fn size(self: @This()) u7 { | |
| 17 | return switch (@enumToInt(self)) { | |
| 18 | 0...7 => 32, | |
| 19 | 8...15 => 16, | |
| 20 | 16...23 => 8, | |
| 21 | else => unreachable, | |
| 22 | }; | |
| 23 | } | |
| 24 | ||
| 25 | /// Returns the register's id. This is used in practically every opcode the | |
| 26 | /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move | |
| 27 | /// instruction, and is used in the R/M byte. | |
| 28 | pub fn id(self: @This()) u3 { | |
| 29 | return @truncate(u3, @enumToInt(self)); | |
| 30 | } | |
| 31 | ||
| 32 | /// Returns the index into `callee_preserved_regs`. | |
| 33 | pub fn allocIndex(self: Register) ?u4 { | |
| 34 | return switch (self) { | |
| 35 | .eax, .ax, .al => 0, | |
| 36 | .ecx, .cx, .cl => 1, | |
| 37 | .edx, .dx, .dl => 2, | |
| 38 | .esi, .si => 3, | |
| 39 | .edi, .di => 4, | |
| 40 | else => null, | |
| 41 | }; | |
| 42 | } | |
| 43 | ||
| 44 | /// Convert from any register to its 32 bit alias. | |
| 45 | pub fn to32(self: Register) Register { | |
| 46 | return @intToEnum(Register, @as(u8, self.id())); | |
| 47 | } | |
| 48 | ||
| 49 | /// Convert from any register to its 16 bit alias. | |
| 50 | pub fn to16(self: Register) Register { | |
| 51 | return @intToEnum(Register, @as(u8, self.id()) + 8); | |
| 52 | } | |
| 53 | ||
| 54 | /// Convert from any register to its 8 bit alias. | |
| 55 | pub fn to8(self: Register) Register { | |
| 56 | return @intToEnum(Register, @as(u8, self.id()) + 16); | |
| 57 | } | |
| 58 | ||
| 59 | ||
| 60 | pub fn dwarfLocOp(reg: Register) u8 { | |
| 61 | return switch (reg.to32()) { | |
| 62 | .eax => DW.OP.reg0, | |
| 63 | .ecx => DW.OP.reg1, | |
| 64 | .edx => DW.OP.reg2, | |
| 65 | .ebx => DW.OP.reg3, | |
| 66 | .esp => DW.OP.reg4, | |
| 67 | .ebp => DW.OP.reg5, | |
| 68 | .esi => DW.OP.reg6, | |
| 69 | .edi => DW.OP.reg7, | |
| 70 | else => unreachable, | |
| 71 | }; | |
| 72 | } | |
| 73 | }; | |
| 74 | ||
| 75 | // zig fmt: on | |
| 76 | ||
| 77 | pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi }; | |
| 78 | ||
| 79 | // TODO add these to Register enum and corresponding dwarfLocOp | |
| 80 | // // Return Address register. This is stored in `0(%esp, "")` and is not a physical register. | |
| 81 | // RA = (8, "RA"), | |
| 82 | // | |
| 83 | // ST0 = (11, "st0"), | |
| 84 | // ST1 = (12, "st1"), | |
| 85 | // ST2 = (13, "st2"), | |
| 86 | // ST3 = (14, "st3"), | |
| 87 | // ST4 = (15, "st4"), | |
| 88 | // ST5 = (16, "st5"), | |
| 89 | // ST6 = (17, "st6"), | |
| 90 | // ST7 = (18, "st7"), | |
| 91 | // | |
| 92 | // XMM0 = (21, "xmm0"), | |
| 93 | // XMM1 = (22, "xmm1"), | |
| 94 | // XMM2 = (23, "xmm2"), | |
| 95 | // XMM3 = (24, "xmm3"), | |
| 96 | // XMM4 = (25, "xmm4"), | |
| 97 | // XMM5 = (26, "xmm5"), | |
| 98 | // XMM6 = (27, "xmm6"), | |
| 99 | // XMM7 = (28, "xmm7"), | |
| 100 | // | |
| 101 | // MM0 = (29, "mm0"), | |
| 102 | // MM1 = (30, "mm1"), | |
| 103 | // MM2 = (31, "mm2"), | |
| 104 | // MM3 = (32, "mm3"), | |
| 105 | // MM4 = (33, "mm4"), | |
| 106 | // MM5 = (34, "mm5"), | |
| 107 | // MM6 = (35, "mm6"), | |
| 108 | // MM7 = (36, "mm7"), | |
| 109 | // | |
| 110 | // MXCSR = (39, "mxcsr"), | |
| 111 | // | |
| 112 | // ES = (40, "es"), | |
| 113 | // CS = (41, "cs"), | |
| 114 | // SS = (42, "ss"), | |
| 115 | // DS = (43, "ds"), | |
| 116 | // FS = (44, "fs"), | |
| 117 | // GS = (45, "gs"), | |
| 118 | // | |
| 119 | // TR = (48, "tr"), | |
| 120 | // LDTR = (49, "ldtr"), | |
| 121 | // | |
| 122 | // FS_BASE = (93, "fs.base"), | |
| 123 | // GS_BASE = (94, "gs.base"), |
src/codegen/x86_64.zig deleted-716| ... | ... | @@ -1,716 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const testing = std.testing; | |
| 3 | const mem = std.mem; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const ArrayList = std.ArrayList; | |
| 6 | const Allocator = std.mem.Allocator; | |
| 7 | const DW = std.dwarf; | |
| 8 | ||
| 9 | // zig fmt: off | |
| 10 | ||
| 11 | /// Definitions of all of the x64 registers. The order is semantically meaningful. | |
| 12 | /// The registers are defined such that IDs go in descending order of 64-bit, | |
| 13 | /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen | |
| 14 | /// registers. This results in some useful properties: | |
| 15 | /// | |
| 16 | /// Any 64-bit register can be turned into its 32-bit form by adding 16, and | |
| 17 | /// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it | |
| 18 | /// works for all except for sp, bp, si, and di, which do *not* have an 8-bit | |
| 19 | /// form. | |
| 20 | /// | |
| 21 | /// If (register & 8) is set, the register is extended. | |
| 22 | /// | |
| 23 | /// The ID can be easily determined by figuring out what range the register is | |
| 24 | /// in, and then subtracting the base. | |
| 25 | pub const Register = enum(u8) { | |
| 26 | // 0 through 15, 64-bit registers. 8-15 are extended. | |
| 27 | // id is just the int value. | |
| 28 | rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, | |
| 29 | r8, r9, r10, r11, r12, r13, r14, r15, | |
| 30 | ||
| 31 | // 16 through 31, 32-bit registers. 24-31 are extended. | |
| 32 | // id is int value - 16. | |
| 33 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | |
| 34 | r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, | |
| 35 | ||
| 36 | // 32-47, 16-bit registers. 40-47 are extended. | |
| 37 | // id is int value - 32. | |
| 38 | ax, cx, dx, bx, sp, bp, si, di, | |
| 39 | r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, | |
| 40 | ||
| 41 | // 48-63, 8-bit registers. 56-63 are extended. | |
| 42 | // id is int value - 48. | |
| 43 | al, cl, dl, bl, ah, ch, dh, bh, | |
| 44 | r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b, | |
| 45 | ||
| 46 | /// Returns the bit-width of the register. | |
| 47 | pub fn size(self: Register) u7 { | |
| 48 | return switch (@enumToInt(self)) { | |
| 49 | 0...15 => 64, | |
| 50 | 16...31 => 32, | |
| 51 | 32...47 => 16, | |
| 52 | 48...64 => 8, | |
| 53 | else => unreachable, | |
| 54 | }; | |
| 55 | } | |
| 56 | ||
| 57 | /// Returns whether the register is *extended*. Extended registers are the | |
| 58 | /// new registers added with amd64, r8 through r15. This also includes any | |
| 59 | /// other variant of access to those registers, such as r8b, r15d, and so | |
| 60 | /// on. This is needed because access to these registers requires special | |
| 61 | /// handling via the REX prefix, via the B or R bits, depending on context. | |
| 62 | pub fn isExtended(self: Register) bool { | |
| 63 | return @enumToInt(self) & 0x08 != 0; | |
| 64 | } | |
| 65 | ||
| 66 | /// This returns the 4-bit register ID, which is used in practically every | |
| 67 | /// opcode. Note that bit 3 (the highest bit) is *never* used directly in | |
| 68 | /// an instruction (@see isExtended), and requires special handling. The | |
| 69 | /// lower three bits are often embedded directly in instructions (such as | |
| 70 | /// the B8 variant of moves), or used in R/M bytes. | |
| 71 | pub fn id(self: Register) u4 { | |
| 72 | return @truncate(u4, @enumToInt(self)); | |
| 73 | } | |
| 74 | ||
| 75 | /// Like id, but only returns the lower 3 bits. | |
| 76 | pub fn low_id(self: Register) u3 { | |
| 77 | return @truncate(u3, @enumToInt(self)); | |
| 78 | } | |
| 79 | ||
| 80 | /// Returns the index into `callee_preserved_regs`. | |
| 81 | pub fn allocIndex(self: Register) ?u4 { | |
| 82 | return switch (self) { | |
| 83 | .rax, .eax, .ax, .al => 0, | |
| 84 | .rcx, .ecx, .cx, .cl => 1, | |
| 85 | .rdx, .edx, .dx, .dl => 2, | |
| 86 | .rsi, .esi, .si => 3, | |
| 87 | .rdi, .edi, .di => 4, | |
| 88 | .r8, .r8d, .r8w, .r8b => 5, | |
| 89 | .r9, .r9d, .r9w, .r9b => 6, | |
| 90 | .r10, .r10d, .r10w, .r10b => 7, | |
| 91 | .r11, .r11d, .r11w, .r11b => 8, | |
| 92 | else => null, | |
| 93 | }; | |
| 94 | } | |
| 95 | ||
| 96 | /// Convert from any register to its 64 bit alias. | |
| 97 | pub fn to64(self: Register) Register { | |
| 98 | return @intToEnum(Register, self.id()); | |
| 99 | } | |
| 100 | ||
| 101 | /// Convert from any register to its 32 bit alias. | |
| 102 | pub fn to32(self: Register) Register { | |
| 103 | return @intToEnum(Register, @as(u8, self.id()) + 16); | |
| 104 | } | |
| 105 | ||
| 106 | /// Convert from any register to its 16 bit alias. | |
| 107 | pub fn to16(self: Register) Register { | |
| 108 | return @intToEnum(Register, @as(u8, self.id()) + 32); | |
| 109 | } | |
| 110 | ||
| 111 | /// Convert from any register to its 8 bit alias. | |
| 112 | pub fn to8(self: Register) Register { | |
| 113 | return @intToEnum(Register, @as(u8, self.id()) + 48); | |
| 114 | } | |
| 115 | ||
| 116 | pub fn dwarfLocOp(self: Register) u8 { | |
| 117 | return switch (self.to64()) { | |
| 118 | .rax => DW.OP.reg0, | |
| 119 | .rdx => DW.OP.reg1, | |
| 120 | .rcx => DW.OP.reg2, | |
| 121 | .rbx => DW.OP.reg3, | |
| 122 | .rsi => DW.OP.reg4, | |
| 123 | .rdi => DW.OP.reg5, | |
| 124 | .rbp => DW.OP.reg6, | |
| 125 | .rsp => DW.OP.reg7, | |
| 126 | ||
| 127 | .r8 => DW.OP.reg8, | |
| 128 | .r9 => DW.OP.reg9, | |
| 129 | .r10 => DW.OP.reg10, | |
| 130 | .r11 => DW.OP.reg11, | |
| 131 | .r12 => DW.OP.reg12, | |
| 132 | .r13 => DW.OP.reg13, | |
| 133 | .r14 => DW.OP.reg14, | |
| 134 | .r15 => DW.OP.reg15, | |
| 135 | ||
| 136 | else => unreachable, | |
| 137 | }; | |
| 138 | } | |
| 139 | }; | |
| 140 | ||
| 141 | // zig fmt: on | |
| 142 | ||
| 143 | /// These registers belong to the called function. | |
| 144 | pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | |
| 145 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; | |
| 146 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; | |
| 147 | ||
| 148 | /// Encoding helper functions for x86_64 instructions | |
| 149 | /// | |
| 150 | /// Many of these helpers do very little, but they can help make things | |
| 151 | /// slightly more readable with more descriptive field names / function names. | |
| 152 | /// | |
| 153 | /// Some of them also have asserts to ensure that we aren't doing dumb things. | |
| 154 | /// For example, trying to use register 4 (esp) in an indirect modr/m byte is illegal, | |
| 155 | /// you need to encode it with an SIB byte. | |
| 156 | /// | |
| 157 | /// Note that ALL of these helper functions will assume capacity, | |
| 158 | /// so ensure that the `code` has sufficient capacity before using them. | |
| 159 | /// The `init` method is the recommended way to ensure capacity. | |
| 160 | pub const Encoder = struct { | |
| 161 | /// Non-owning reference to the code array | |
| 162 | code: *ArrayList(u8), | |
| 163 | ||
| 164 | const Self = @This(); | |
| 165 | ||
| 166 | /// Wrap `code` in Encoder to make it easier to call these helper functions | |
| 167 | /// | |
| 168 | /// maximum_inst_size should contain the maximum number of bytes | |
| 169 | /// that the encoded instruction will take. | |
| 170 | /// This is because the helper functions will assume capacity | |
| 171 | /// in order to avoid bounds checking. | |
| 172 | pub fn init(code: *ArrayList(u8), maximum_inst_size: u8) !Self { | |
| 173 | try code.ensureUnusedCapacity(maximum_inst_size); | |
| 174 | return Self{ .code = code }; | |
| 175 | } | |
| 176 | ||
| 177 | /// Directly write a number to the code array with big endianness | |
| 178 | pub fn writeIntBig(self: Self, comptime T: type, value: T) void { | |
| 179 | mem.writeIntBig( | |
| 180 | T, | |
| 181 | self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)), | |
| 182 | value, | |
| 183 | ); | |
| 184 | } | |
| 185 | ||
| 186 | /// Directly write a number to the code array with little endianness | |
| 187 | pub fn writeIntLittle(self: Self, comptime T: type, value: T) void { | |
| 188 | mem.writeIntLittle( | |
| 189 | T, | |
| 190 | self.code.addManyAsArrayAssumeCapacity(@divExact(@typeInfo(T).Int.bits, 8)), | |
| 191 | value, | |
| 192 | ); | |
| 193 | } | |
| 194 | ||
| 195 | // -------- | |
| 196 | // Prefixes | |
| 197 | // -------- | |
| 198 | ||
| 199 | pub const LegacyPrefixes = packed struct { | |
| 200 | /// LOCK | |
| 201 | prefix_f0: bool = false, | |
| 202 | /// REPNZ, REPNE, REP, Scalar Double-precision | |
| 203 | prefix_f2: bool = false, | |
| 204 | /// REPZ, REPE, REP, Scalar Single-precision | |
| 205 | prefix_f3: bool = false, | |
| 206 | ||
| 207 | /// CS segment override or Branch not taken | |
| 208 | prefix_2e: bool = false, | |
| 209 | /// DS segment override | |
| 210 | prefix_36: bool = false, | |
| 211 | /// ES segment override | |
| 212 | prefix_26: bool = false, | |
| 213 | /// FS segment override | |
| 214 | prefix_64: bool = false, | |
| 215 | /// GS segment override | |
| 216 | prefix_65: bool = false, | |
| 217 | ||
| 218 | /// Branch taken | |
| 219 | prefix_3e: bool = false, | |
| 220 | ||
| 221 | /// Operand size override (enables 16 bit operation) | |
| 222 | prefix_66: bool = false, | |
| 223 | ||
| 224 | /// Address size override (enables 16 bit address size) | |
| 225 | prefix_67: bool = false, | |
| 226 | ||
| 227 | padding: u5 = 0, | |
| 228 | }; | |
| 229 | ||
| 230 | /// Encodes legacy prefixes | |
| 231 | pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) void { | |
| 232 | if (@bitCast(u16, prefixes) != 0) { | |
| 233 | // Hopefully this path isn't taken very often, so we'll do it the slow way for now | |
| 234 | ||
| 235 | // LOCK | |
| 236 | if (prefixes.prefix_f0) self.code.appendAssumeCapacity(0xf0); | |
| 237 | // REPNZ, REPNE, REP, Scalar Double-precision | |
| 238 | if (prefixes.prefix_f2) self.code.appendAssumeCapacity(0xf2); | |
| 239 | // REPZ, REPE, REP, Scalar Single-precision | |
| 240 | if (prefixes.prefix_f3) self.code.appendAssumeCapacity(0xf3); | |
| 241 | ||
| 242 | // CS segment override or Branch not taken | |
| 243 | if (prefixes.prefix_2e) self.code.appendAssumeCapacity(0x2e); | |
| 244 | // DS segment override | |
| 245 | if (prefixes.prefix_36) self.code.appendAssumeCapacity(0x36); | |
| 246 | // ES segment override | |
| 247 | if (prefixes.prefix_26) self.code.appendAssumeCapacity(0x26); | |
| 248 | // FS segment override | |
| 249 | if (prefixes.prefix_64) self.code.appendAssumeCapacity(0x64); | |
| 250 | // GS segment override | |
| 251 | if (prefixes.prefix_65) self.code.appendAssumeCapacity(0x65); | |
| 252 | ||
| 253 | // Branch taken | |
| 254 | if (prefixes.prefix_3e) self.code.appendAssumeCapacity(0x3e); | |
| 255 | ||
| 256 | // Operand size override | |
| 257 | if (prefixes.prefix_66) self.code.appendAssumeCapacity(0x66); | |
| 258 | ||
| 259 | // Address size override | |
| 260 | if (prefixes.prefix_67) self.code.appendAssumeCapacity(0x67); | |
| 261 | } | |
| 262 | } | |
| 263 | ||
| 264 | /// Use 16 bit operand size | |
| 265 | /// | |
| 266 | /// Note that this flag is overridden by REX.W, if both are present. | |
| 267 | pub fn prefix16BitMode(self: Self) void { | |
| 268 | self.code.appendAssumeCapacity(0x66); | |
| 269 | } | |
| 270 | ||
| 271 | /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB | |
| 272 | pub const Rex = struct { | |
| 273 | /// Wide, enables 64-bit operation | |
| 274 | w: bool = false, | |
| 275 | /// Extends the reg field in the ModR/M byte | |
| 276 | r: bool = false, | |
| 277 | /// Extends the index field in the SIB byte | |
| 278 | x: bool = false, | |
| 279 | /// Extends the r/m field in the ModR/M byte, | |
| 280 | /// or the base field in the SIB byte, | |
| 281 | /// or the reg field in the Opcode byte | |
| 282 | b: bool = false, | |
| 283 | }; | |
| 284 | ||
| 285 | /// Encodes a REX prefix byte given all the fields | |
| 286 | /// | |
| 287 | /// Use this byte whenever you need 64 bit operation, | |
| 288 | /// or one of reg, index, r/m, base, or opcode-reg might be extended. | |
| 289 | /// | |
| 290 | /// See struct `Rex` for a description of each field. | |
| 291 | /// | |
| 292 | /// Does not add a prefix byte if none of the fields are set! | |
| 293 | pub fn rex(self: Self, byte: Rex) void { | |
| 294 | var value: u8 = 0b0100_0000; | |
| 295 | ||
| 296 | if (byte.w) value |= 0b1000; | |
| 297 | if (byte.r) value |= 0b0100; | |
| 298 | if (byte.x) value |= 0b0010; | |
| 299 | if (byte.b) value |= 0b0001; | |
| 300 | ||
| 301 | if (value != 0b0100_0000) { | |
| 302 | self.code.appendAssumeCapacity(value); | |
| 303 | } | |
| 304 | } | |
| 305 | ||
| 306 | // ------ | |
| 307 | // Opcode | |
| 308 | // ------ | |
| 309 | ||
| 310 | /// Encodes a 1 byte opcode | |
| 311 | pub fn opcode_1byte(self: Self, opcode: u8) void { | |
| 312 | self.code.appendAssumeCapacity(opcode); | |
| 313 | } | |
| 314 | ||
| 315 | /// Encodes a 2 byte opcode | |
| 316 | /// | |
| 317 | /// e.g. IMUL has the opcode 0x0f 0xaf, so you use | |
| 318 | /// | |
| 319 | /// encoder.opcode_2byte(0x0f, 0xaf); | |
| 320 | pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) void { | |
| 321 | self.code.appendAssumeCapacity(prefix); | |
| 322 | self.code.appendAssumeCapacity(opcode); | |
| 323 | } | |
| 324 | ||
| 325 | /// Encodes a 1 byte opcode with a reg field | |
| 326 | /// | |
| 327 | /// Remember to add a REX prefix byte if reg is extended! | |
| 328 | pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) void { | |
| 329 | assert(opcode & 0b111 == 0); | |
| 330 | self.code.appendAssumeCapacity(opcode | reg); | |
| 331 | } | |
| 332 | ||
| 333 | // ------ | |
| 334 | // ModR/M | |
| 335 | // ------ | |
| 336 | ||
| 337 | /// Construct a ModR/M byte given all the fields | |
| 338 | /// | |
| 339 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 340 | pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) void { | |
| 341 | self.code.appendAssumeCapacity( | |
| 342 | @as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm, | |
| 343 | ); | |
| 344 | } | |
| 345 | ||
| 346 | /// Construct a ModR/M byte using direct r/m addressing | |
| 347 | /// r/m effective address: r/m | |
| 348 | /// | |
| 349 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 350 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 351 | pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) void { | |
| 352 | self.modRm(0b11, reg_or_opx, rm); | |
| 353 | } | |
| 354 | ||
| 355 | /// Construct a ModR/M byte using indirect r/m addressing | |
| 356 | /// r/m effective address: [r/m] | |
| 357 | /// | |
| 358 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 359 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 360 | pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) void { | |
| 361 | assert(rm != 4 and rm != 5); | |
| 362 | self.modRm(0b00, reg_or_opx, rm); | |
| 363 | } | |
| 364 | ||
| 365 | /// Construct a ModR/M byte using indirect SIB addressing | |
| 366 | /// r/m effective address: [SIB] | |
| 367 | /// | |
| 368 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 369 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 370 | pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) void { | |
| 371 | self.modRm(0b00, reg_or_opx, 0b100); | |
| 372 | } | |
| 373 | ||
| 374 | /// Construct a ModR/M byte using RIP-relative addressing | |
| 375 | /// r/m effective address: [RIP + disp32] | |
| 376 | /// | |
| 377 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 378 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 379 | pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) void { | |
| 380 | self.modRm(0b00, reg_or_opx, 0b101); | |
| 381 | } | |
| 382 | ||
| 383 | /// Construct a ModR/M byte using indirect r/m with a 8bit displacement | |
| 384 | /// r/m effective address: [r/m + disp8] | |
| 385 | /// | |
| 386 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 387 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 388 | pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) void { | |
| 389 | assert(rm != 4); | |
| 390 | self.modRm(0b01, reg_or_opx, rm); | |
| 391 | } | |
| 392 | ||
| 393 | /// Construct a ModR/M byte using indirect SIB with a 8bit displacement | |
| 394 | /// r/m effective address: [SIB + disp8] | |
| 395 | /// | |
| 396 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 397 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 398 | pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) void { | |
| 399 | self.modRm(0b01, reg_or_opx, 0b100); | |
| 400 | } | |
| 401 | ||
| 402 | /// Construct a ModR/M byte using indirect r/m with a 32bit displacement | |
| 403 | /// r/m effective address: [r/m + disp32] | |
| 404 | /// | |
| 405 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 406 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 407 | pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) void { | |
| 408 | assert(rm != 4); | |
| 409 | self.modRm(0b10, reg_or_opx, rm); | |
| 410 | } | |
| 411 | ||
| 412 | /// Construct a ModR/M byte using indirect SIB with a 32bit displacement | |
| 413 | /// r/m effective address: [SIB + disp32] | |
| 414 | /// | |
| 415 | /// Note reg's effective address is always just reg for the ModR/M byte. | |
| 416 | /// Remember to add a REX prefix byte if reg or rm are extended! | |
| 417 | pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) void { | |
| 418 | self.modRm(0b10, reg_or_opx, 0b100); | |
| 419 | } | |
| 420 | ||
| 421 | // --- | |
| 422 | // SIB | |
| 423 | // --- | |
| 424 | ||
| 425 | /// Construct a SIB byte given all the fields | |
| 426 | /// | |
| 427 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 428 | pub fn sib(self: Self, scale: u2, index: u3, base: u3) void { | |
| 429 | self.code.appendAssumeCapacity( | |
| 430 | @as(u8, scale) << 6 | @as(u8, index) << 3 | base, | |
| 431 | ); | |
| 432 | } | |
| 433 | ||
| 434 | /// Construct a SIB byte with scale * index + base, no frills. | |
| 435 | /// r/m effective address: [base + scale * index] | |
| 436 | /// | |
| 437 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 438 | pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) void { | |
| 439 | assert(base != 5); | |
| 440 | ||
| 441 | self.sib(scale, index, base); | |
| 442 | } | |
| 443 | ||
| 444 | /// Construct a SIB byte with scale * index + disp32 | |
| 445 | /// r/m effective address: [scale * index + disp32] | |
| 446 | /// | |
| 447 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 448 | pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) void { | |
| 449 | assert(index != 4); | |
| 450 | ||
| 451 | // scale is actually ignored | |
| 452 | // index = 4 means no index | |
| 453 | // base = 5 means no base, if mod == 0. | |
| 454 | self.sib(scale, index, 5); | |
| 455 | } | |
| 456 | ||
| 457 | /// Construct a SIB byte with just base | |
| 458 | /// r/m effective address: [base] | |
| 459 | /// | |
| 460 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 461 | pub fn sib_base(self: Self, base: u3) void { | |
| 462 | assert(base != 5); | |
| 463 | ||
| 464 | // scale is actually ignored | |
| 465 | // index = 4 means no index | |
| 466 | self.sib(0, 4, base); | |
| 467 | } | |
| 468 | ||
| 469 | /// Construct a SIB byte with just disp32 | |
| 470 | /// r/m effective address: [disp32] | |
| 471 | /// | |
| 472 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 473 | pub fn sib_disp32(self: Self) void { | |
| 474 | // scale is actually ignored | |
| 475 | // index = 4 means no index | |
| 476 | // base = 5 means no base, if mod == 0. | |
| 477 | self.sib(0, 4, 5); | |
| 478 | } | |
| 479 | ||
| 480 | /// Construct a SIB byte with scale * index + base + disp8 | |
| 481 | /// r/m effective address: [base + scale * index + disp8] | |
| 482 | /// | |
| 483 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 484 | pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) void { | |
| 485 | self.sib(scale, index, base); | |
| 486 | } | |
| 487 | ||
| 488 | /// Construct a SIB byte with base + disp8, no index | |
| 489 | /// r/m effective address: [base + disp8] | |
| 490 | /// | |
| 491 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 492 | pub fn sib_baseDisp8(self: Self, base: u3) void { | |
| 493 | // scale is ignored | |
| 494 | // index = 4 means no index | |
| 495 | self.sib(0, 4, base); | |
| 496 | } | |
| 497 | ||
| 498 | /// Construct a SIB byte with scale * index + base + disp32 | |
| 499 | /// r/m effective address: [base + scale * index + disp32] | |
| 500 | /// | |
| 501 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 502 | pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) void { | |
| 503 | self.sib(scale, index, base); | |
| 504 | } | |
| 505 | ||
| 506 | /// Construct a SIB byte with base + disp32, no index | |
| 507 | /// r/m effective address: [base + disp32] | |
| 508 | /// | |
| 509 | /// Remember to add a REX prefix byte if index or base are extended! | |
| 510 | pub fn sib_baseDisp32(self: Self, base: u3) void { | |
| 511 | // scale is ignored | |
| 512 | // index = 4 means no index | |
| 513 | self.sib(0, 4, base); | |
| 514 | } | |
| 515 | ||
| 516 | // ------------------------- | |
| 517 | // Trivial (no bit fiddling) | |
| 518 | // ------------------------- | |
| 519 | ||
| 520 | /// Encode an 8 bit immediate | |
| 521 | /// | |
| 522 | /// It is sign-extended to 64 bits by the cpu. | |
| 523 | pub fn imm8(self: Self, imm: i8) void { | |
| 524 | self.code.appendAssumeCapacity(@bitCast(u8, imm)); | |
| 525 | } | |
| 526 | ||
| 527 | /// Encode an 8 bit displacement | |
| 528 | /// | |
| 529 | /// It is sign-extended to 64 bits by the cpu. | |
| 530 | pub fn disp8(self: Self, disp: i8) void { | |
| 531 | self.code.appendAssumeCapacity(@bitCast(u8, disp)); | |
| 532 | } | |
| 533 | ||
| 534 | /// Encode an 16 bit immediate | |
| 535 | /// | |
| 536 | /// It is sign-extended to 64 bits by the cpu. | |
| 537 | pub fn imm16(self: Self, imm: i16) void { | |
| 538 | self.writeIntLittle(i16, imm); | |
| 539 | } | |
| 540 | ||
| 541 | /// Encode an 32 bit immediate | |
| 542 | /// | |
| 543 | /// It is sign-extended to 64 bits by the cpu. | |
| 544 | pub fn imm32(self: Self, imm: i32) void { | |
| 545 | self.writeIntLittle(i32, imm); | |
| 546 | } | |
| 547 | ||
| 548 | /// Encode an 32 bit displacement | |
| 549 | /// | |
| 550 | /// It is sign-extended to 64 bits by the cpu. | |
| 551 | pub fn disp32(self: Self, disp: i32) void { | |
| 552 | self.writeIntLittle(i32, disp); | |
| 553 | } | |
| 554 | ||
| 555 | /// Encode an 64 bit immediate | |
| 556 | /// | |
| 557 | /// It is sign-extended to 64 bits by the cpu. | |
| 558 | pub fn imm64(self: Self, imm: u64) void { | |
| 559 | self.writeIntLittle(u64, imm); | |
| 560 | } | |
| 561 | }; | |
| 562 | ||
| 563 | test "x86_64 Encoder helpers" { | |
| 564 | var code = ArrayList(u8).init(testing.allocator); | |
| 565 | defer code.deinit(); | |
| 566 | ||
| 567 | // simple integer multiplication | |
| 568 | ||
| 569 | // imul eax,edi | |
| 570 | // 0faf c7 | |
| 571 | { | |
| 572 | try code.resize(0); | |
| 573 | const encoder = try Encoder.init(&code, 4); | |
| 574 | encoder.rex(.{ | |
| 575 | .r = Register.eax.isExtended(), | |
| 576 | .b = Register.edi.isExtended(), | |
| 577 | }); | |
| 578 | encoder.opcode_2byte(0x0f, 0xaf); | |
| 579 | encoder.modRm_direct( | |
| 580 | Register.eax.low_id(), | |
| 581 | Register.edi.low_id(), | |
| 582 | ); | |
| 583 | ||
| 584 | try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items); | |
| 585 | } | |
| 586 | ||
| 587 | // simple mov | |
| 588 | ||
| 589 | // mov eax,edi | |
| 590 | // 89 f8 | |
| 591 | { | |
| 592 | try code.resize(0); | |
| 593 | const encoder = try Encoder.init(&code, 3); | |
| 594 | encoder.rex(.{ | |
| 595 | .r = Register.edi.isExtended(), | |
| 596 | .b = Register.eax.isExtended(), | |
| 597 | }); | |
| 598 | encoder.opcode_1byte(0x89); | |
| 599 | encoder.modRm_direct( | |
| 600 | Register.edi.low_id(), | |
| 601 | Register.eax.low_id(), | |
| 602 | ); | |
| 603 | ||
| 604 | try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items); | |
| 605 | } | |
| 606 | ||
| 607 | // signed integer addition of 32-bit sign extended immediate to 64 bit register | |
| 608 | ||
| 609 | // add rcx, 2147483647 | |
| 610 | // | |
| 611 | // Using the following opcode: REX.W + 81 /0 id, we expect the following encoding | |
| 612 | // | |
| 613 | // 48 : REX.W set for 64 bit operand (*r*cx) | |
| 614 | // 81 : opcode for "<arithmetic> with immediate" | |
| 615 | // c1 : id = rcx, | |
| 616 | // : c1 = 11 <-- mod = 11 indicates r/m is register (rcx) | |
| 617 | // : 000 <-- opcode_extension = 0 because opcode extension is /0. /0 specifies ADD | |
| 618 | // : 001 <-- 001 is rcx | |
| 619 | // ffffff7f : 2147483647 | |
| 620 | { | |
| 621 | try code.resize(0); | |
| 622 | const encoder = try Encoder.init(&code, 7); | |
| 623 | encoder.rex(.{ .w = true }); // use 64 bit operation | |
| 624 | encoder.opcode_1byte(0x81); | |
| 625 | encoder.modRm_direct( | |
| 626 | 0, | |
| 627 | Register.rcx.low_id(), | |
| 628 | ); | |
| 629 | encoder.imm32(2147483647); | |
| 630 | ||
| 631 | try testing.expectEqualSlices(u8, &[_]u8{ 0x48, 0x81, 0xc1, 0xff, 0xff, 0xff, 0x7f }, code.items); | |
| 632 | } | |
| 633 | } | |
| 634 | ||
| 635 | // TODO add these registers to the enum and populate dwarfLocOp | |
| 636 | // // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. | |
| 637 | // RA = (16, "RA"), | |
| 638 | // | |
| 639 | // XMM0 = (17, "xmm0"), | |
| 640 | // XMM1 = (18, "xmm1"), | |
| 641 | // XMM2 = (19, "xmm2"), | |
| 642 | // XMM3 = (20, "xmm3"), | |
| 643 | // XMM4 = (21, "xmm4"), | |
| 644 | // XMM5 = (22, "xmm5"), | |
| 645 | // XMM6 = (23, "xmm6"), | |
| 646 | // XMM7 = (24, "xmm7"), | |
| 647 | // | |
| 648 | // XMM8 = (25, "xmm8"), | |
| 649 | // XMM9 = (26, "xmm9"), | |
| 650 | // XMM10 = (27, "xmm10"), | |
| 651 | // XMM11 = (28, "xmm11"), | |
| 652 | // XMM12 = (29, "xmm12"), | |
| 653 | // XMM13 = (30, "xmm13"), | |
| 654 | // XMM14 = (31, "xmm14"), | |
| 655 | // XMM15 = (32, "xmm15"), | |
| 656 | // | |
| 657 | // ST0 = (33, "st0"), | |
| 658 | // ST1 = (34, "st1"), | |
| 659 | // ST2 = (35, "st2"), | |
| 660 | // ST3 = (36, "st3"), | |
| 661 | // ST4 = (37, "st4"), | |
| 662 | // ST5 = (38, "st5"), | |
| 663 | // ST6 = (39, "st6"), | |
| 664 | // ST7 = (40, "st7"), | |
| 665 | // | |
| 666 | // MM0 = (41, "mm0"), | |
| 667 | // MM1 = (42, "mm1"), | |
| 668 | // MM2 = (43, "mm2"), | |
| 669 | // MM3 = (44, "mm3"), | |
| 670 | // MM4 = (45, "mm4"), | |
| 671 | // MM5 = (46, "mm5"), | |
| 672 | // MM6 = (47, "mm6"), | |
| 673 | // MM7 = (48, "mm7"), | |
| 674 | // | |
| 675 | // RFLAGS = (49, "rFLAGS"), | |
| 676 | // ES = (50, "es"), | |
| 677 | // CS = (51, "cs"), | |
| 678 | // SS = (52, "ss"), | |
| 679 | // DS = (53, "ds"), | |
| 680 | // FS = (54, "fs"), | |
| 681 | // GS = (55, "gs"), | |
| 682 | // | |
| 683 | // FS_BASE = (58, "fs.base"), | |
| 684 | // GS_BASE = (59, "gs.base"), | |
| 685 | // | |
| 686 | // TR = (62, "tr"), | |
| 687 | // LDTR = (63, "ldtr"), | |
| 688 | // MXCSR = (64, "mxcsr"), | |
| 689 | // FCW = (65, "fcw"), | |
| 690 | // FSW = (66, "fsw"), | |
| 691 | // | |
| 692 | // XMM16 = (67, "xmm16"), | |
| 693 | // XMM17 = (68, "xmm17"), | |
| 694 | // XMM18 = (69, "xmm18"), | |
| 695 | // XMM19 = (70, "xmm19"), | |
| 696 | // XMM20 = (71, "xmm20"), | |
| 697 | // XMM21 = (72, "xmm21"), | |
| 698 | // XMM22 = (73, "xmm22"), | |
| 699 | // XMM23 = (74, "xmm23"), | |
| 700 | // XMM24 = (75, "xmm24"), | |
| 701 | // XMM25 = (76, "xmm25"), | |
| 702 | // XMM26 = (77, "xmm26"), | |
| 703 | // XMM27 = (78, "xmm27"), | |
| 704 | // XMM28 = (79, "xmm28"), | |
| 705 | // XMM29 = (80, "xmm29"), | |
| 706 | // XMM30 = (81, "xmm30"), | |
| 707 | // XMM31 = (82, "xmm31"), | |
| 708 | // | |
| 709 | // K0 = (118, "k0"), | |
| 710 | // K1 = (119, "k1"), | |
| 711 | // K2 = (120, "k2"), | |
| 712 | // K3 = (121, "k3"), | |
| 713 | // K4 = (122, "k4"), | |
| 714 | // K5 = (123, "k5"), | |
| 715 | // K6 = (124, "k6"), | |
| 716 | // K7 = (125, "k7"), |
src/crash_report.zig created+581| ... | ... | @@ -0,0 +1,581 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const debug = std.debug; | |
| 4 | const os = std.os; | |
| 5 | const io = std.io; | |
| 6 | const print_zir = @import("print_zir.zig"); | |
| 7 | ||
| 8 | const Module = @import("Module.zig"); | |
| 9 | const Sema = @import("Sema.zig"); | |
| 10 | const Zir = @import("Zir.zig"); | |
| 11 | ||
| 12 | pub const is_enabled = builtin.mode == .Debug; | |
| 13 | ||
| 14 | /// To use these crash report diagnostics, publish these symbols in your main file. | |
| 15 | /// You will also need to call initialize() on startup, preferably as the very first operation in your program. | |
| 16 | pub const root_decls = struct { | |
| 17 | pub const panic = if (is_enabled) compilerPanic else std.builtin.default_panic; | |
| 18 | pub const enable_segfault_handler = if (is_enabled) false else debug.default_enable_segfault_handler; | |
| 19 | }; | |
| 20 | ||
| 21 | /// Install signal handlers to identify crashes and report diagnostics. | |
| 22 | pub fn initialize() void { | |
| 23 | if (is_enabled and debug.have_segfault_handling_support) { | |
| 24 | attachSegfaultHandler(); | |
| 25 | } | |
| 26 | } | |
| 27 | ||
| 28 | fn En(comptime T: type) type { | |
| 29 | return if (is_enabled) T else void; | |
| 30 | } | |
| 31 | ||
| 32 | fn en(val: anytype) En(@TypeOf(val)) { | |
| 33 | return if (is_enabled) val else {}; | |
| 34 | } | |
| 35 | ||
| 36 | pub const AnalyzeBody = struct { | |
| 37 | parent: if (is_enabled) ?*AnalyzeBody else void, | |
| 38 | sema: En(*Sema), | |
| 39 | block: En(*Module.Scope.Block), | |
| 40 | body: En([]const Zir.Inst.Index), | |
| 41 | body_index: En(usize), | |
| 42 | ||
| 43 | pub fn push(self: *@This()) void { | |
| 44 | if (!is_enabled) return; | |
| 45 | const head = &zir_state; | |
| 46 | debug.assert(self.parent == null); | |
| 47 | self.parent = head.*; | |
| 48 | head.* = self; | |
| 49 | } | |
| 50 | ||
| 51 | pub fn pop(self: *@This()) void { | |
| 52 | if (!is_enabled) return; | |
| 53 | const head = &zir_state; | |
| 54 | const old = head.*.?; | |
| 55 | debug.assert(old == self); | |
| 56 | head.* = old.parent; | |
| 57 | } | |
| 58 | ||
| 59 | pub fn setBodyIndex(self: *@This(), index: usize) void { | |
| 60 | if (!is_enabled) return; | |
| 61 | self.body_index = index; | |
| 62 | } | |
| 63 | }; | |
| 64 | ||
| 65 | threadlocal var zir_state: ?*AnalyzeBody = if (is_enabled) null else @compileError("Cannot use zir_state if crash_report is disabled."); | |
| 66 | ||
| 67 | pub fn prepAnalyzeBody(sema: *Sema, block: *Module.Scope.Block, body: []const Zir.Inst.Index) AnalyzeBody { | |
| 68 | if (is_enabled) { | |
| 69 | return .{ | |
| 70 | .parent = null, | |
| 71 | .sema = sema, | |
| 72 | .block = block, | |
| 73 | .body = body, | |
| 74 | .body_index = 0, | |
| 75 | }; | |
| 76 | } else { | |
| 77 | if (@sizeOf(AnalyzeBody) != 0) | |
| 78 | @compileError("AnalyzeBody must have zero size when crash reports are disabled"); | |
| 79 | return undefined; | |
| 80 | } | |
| 81 | } | |
| 82 | ||
| 83 | fn dumpStatusReport() !void { | |
| 84 | const anal = zir_state orelse return; | |
| 85 | // Note: We have the panic mutex here, so we can safely use the global crash heap. | |
| 86 | var fba = std.heap.FixedBufferAllocator.init(&crash_heap); | |
| 87 | const allocator = &fba.allocator; | |
| 88 | ||
| 89 | const stderr = io.getStdErr().writer(); | |
| 90 | const block: *Scope.Block = anal.block; | |
| 91 | ||
| 92 | try stderr.writeAll("Analyzing "); | |
| 93 | try writeFullyQualifiedDeclWithFile(block.src_decl, stderr); | |
| 94 | try stderr.writeAll("\n"); | |
| 95 | ||
| 96 | print_zir.renderInstructionContext( | |
| 97 | allocator, | |
| 98 | anal.body, | |
| 99 | anal.body_index, | |
| 100 | block.src_decl.getFileScope(), | |
| 101 | block.src_decl.src_node, | |
| 102 | 6, // indent | |
| 103 | stderr, | |
| 104 | ) catch |err| switch (err) { | |
| 105 | error.OutOfMemory => try stderr.writeAll(" <out of memory dumping zir>\n"), | |
| 106 | else => |e| return e, | |
| 107 | }; | |
| 108 | try stderr.writeAll(" For full context, use the command\n zig ast-check -t "); | |
| 109 | try writeFilePath(block.src_decl.getFileScope(), stderr); | |
| 110 | try stderr.writeAll("\n\n"); | |
| 111 | ||
| 112 | var parent = anal.parent; | |
| 113 | while (parent) |curr| { | |
| 114 | fba.reset(); | |
| 115 | try stderr.writeAll(" in "); | |
| 116 | try writeFullyQualifiedDeclWithFile(curr.block.src_decl, stderr); | |
| 117 | try stderr.writeAll("\n > "); | |
| 118 | print_zir.renderSingleInstruction( | |
| 119 | allocator, | |
| 120 | curr.body[curr.body_index], | |
| 121 | curr.block.src_decl.getFileScope(), | |
| 122 | curr.block.src_decl.src_node, | |
| 123 | 6, // indent | |
| 124 | stderr, | |
| 125 | ) catch |err| switch (err) { | |
| 126 | error.OutOfMemory => try stderr.writeAll(" <out of memory dumping zir>\n"), | |
| 127 | else => |e| return e, | |
| 128 | }; | |
| 129 | try stderr.writeAll("\n"); | |
| 130 | ||
| 131 | parent = curr.parent; | |
| 132 | } | |
| 133 | ||
| 134 | try stderr.writeAll("\n"); | |
| 135 | } | |
| 136 | ||
| 137 | const Scope = Module.Scope; | |
| 138 | const Decl = Module.Decl; | |
| 139 | ||
| 140 | var crash_heap: [16 * 4096]u8 = undefined; | |
| 141 | ||
| 142 | fn writeFilePath(file: *Scope.File, stream: anytype) !void { | |
| 143 | if (file.pkg.root_src_directory.path) |path| { | |
| 144 | try stream.writeAll(path); | |
| 145 | try stream.writeAll(std.fs.path.sep_str); | |
| 146 | } | |
| 147 | try stream.writeAll(file.sub_file_path); | |
| 148 | } | |
| 149 | ||
| 150 | fn writeFullyQualifiedDeclWithFile(decl: *Decl, stream: anytype) !void { | |
| 151 | try writeFilePath(decl.getFileScope(), stream); | |
| 152 | try stream.writeAll(": "); | |
| 153 | try decl.namespace.renderFullyQualifiedName(std.mem.sliceTo(decl.name, 0), stream); | |
| 154 | } | |
| 155 | ||
| 156 | fn compilerPanic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { | |
| 157 | PanicSwitch.preDispatch(); | |
| 158 | @setCold(true); | |
| 159 | const ret_addr = @returnAddress(); | |
| 160 | const stack_ctx: StackContext = .{ .current = .{ .ret_addr = ret_addr } }; | |
| 161 | PanicSwitch.dispatch(error_return_trace, stack_ctx, msg); | |
| 162 | } | |
| 163 | ||
| 164 | /// Attaches a global SIGSEGV handler | |
| 165 | pub fn attachSegfaultHandler() void { | |
| 166 | if (!debug.have_segfault_handling_support) { | |
| 167 | @compileError("segfault handler not supported for this target"); | |
| 168 | } | |
| 169 | if (builtin.os.tag == .windows) { | |
| 170 | _ = os.windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); | |
| 171 | return; | |
| 172 | } | |
| 173 | var act = os.Sigaction{ | |
| 174 | .handler = .{ .sigaction = handleSegfaultLinux }, | |
| 175 | .mask = os.empty_sigset, | |
| 176 | .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND), | |
| 177 | }; | |
| 178 | ||
| 179 | os.sigaction(os.SIG.SEGV, &act, null); | |
| 180 | os.sigaction(os.SIG.ILL, &act, null); | |
| 181 | os.sigaction(os.SIG.BUS, &act, null); | |
| 182 | } | |
| 183 | ||
| 184 | fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) noreturn { | |
| 185 | // TODO: use alarm() here to prevent infinite loops | |
| 186 | PanicSwitch.preDispatch(); | |
| 187 | ||
| 188 | const addr = switch (builtin.os.tag) { | |
| 189 | .linux => @ptrToInt(info.fields.sigfault.addr), | |
| 190 | .freebsd => @ptrToInt(info.addr), | |
| 191 | .netbsd => @ptrToInt(info.info.reason.fault.addr), | |
| 192 | .openbsd => @ptrToInt(info.data.fault.addr), | |
| 193 | .solaris => @ptrToInt(info.reason.fault.addr), | |
| 194 | else => @compileError("TODO implement handleSegfaultLinux for new linux OS"), | |
| 195 | }; | |
| 196 | ||
| 197 | var err_buffer: [128]u8 = undefined; | |
| 198 | const error_msg = switch (sig) { | |
| 199 | os.SIG.SEGV => std.fmt.bufPrint(&err_buffer, "Segmentation fault at address 0x{x}", .{addr}) catch "Segmentation fault", | |
| 200 | os.SIG.ILL => std.fmt.bufPrint(&err_buffer, "Illegal instruction at address 0x{x}", .{addr}) catch "Illegal instruction", | |
| 201 | os.SIG.BUS => std.fmt.bufPrint(&err_buffer, "Bus error at address 0x{x}", .{addr}) catch "Bus error", | |
| 202 | else => std.fmt.bufPrint(&err_buffer, "Unknown error (signal {}) at address 0x{x}", .{ sig, addr }) catch "Unknown error", | |
| 203 | }; | |
| 204 | ||
| 205 | const stack_ctx: StackContext = switch (builtin.cpu.arch) { | |
| 206 | .i386 => ctx: { | |
| 207 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | |
| 208 | const ip = @intCast(usize, ctx.mcontext.gregs[os.REG.EIP]); | |
| 209 | const bp = @intCast(usize, ctx.mcontext.gregs[os.REG.EBP]); | |
| 210 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | |
| 211 | }, | |
| 212 | .x86_64 => ctx: { | |
| 213 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | |
| 214 | const ip = switch (builtin.os.tag) { | |
| 215 | .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RIP]), | |
| 216 | .freebsd => @intCast(usize, ctx.mcontext.rip), | |
| 217 | .openbsd => @intCast(usize, ctx.sc_rip), | |
| 218 | else => unreachable, | |
| 219 | }; | |
| 220 | const bp = switch (builtin.os.tag) { | |
| 221 | .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RBP]), | |
| 222 | .openbsd => @intCast(usize, ctx.sc_rbp), | |
| 223 | .freebsd => @intCast(usize, ctx.mcontext.rbp), | |
| 224 | else => unreachable, | |
| 225 | }; | |
| 226 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | |
| 227 | }, | |
| 228 | .arm => ctx: { | |
| 229 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | |
| 230 | const ip = @intCast(usize, ctx.mcontext.arm_pc); | |
| 231 | const bp = @intCast(usize, ctx.mcontext.arm_fp); | |
| 232 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | |
| 233 | }, | |
| 234 | .aarch64 => ctx: { | |
| 235 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | |
| 236 | const ip = @intCast(usize, ctx.mcontext.pc); | |
| 237 | // x29 is the ABI-designated frame pointer | |
| 238 | const bp = @intCast(usize, ctx.mcontext.regs[29]); | |
| 239 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | |
| 240 | }, | |
| 241 | else => .not_supported, | |
| 242 | }; | |
| 243 | ||
| 244 | PanicSwitch.dispatch(null, stack_ctx, error_msg); | |
| 245 | } | |
| 246 | ||
| 247 | const WindowsSegfaultMessage = union(enum) { | |
| 248 | literal: []const u8, | |
| 249 | segfault: void, | |
| 250 | illegal_instruction: void, | |
| 251 | }; | |
| 252 | ||
| 253 | fn handleSegfaultWindows(info: *os.windows.EXCEPTION_POINTERS) callconv(os.windows.WINAPI) c_long { | |
| 254 | switch (info.ExceptionRecord.ExceptionCode) { | |
| 255 | os.windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, .{ .literal = "Unaligned Memory Access" }), | |
| 256 | os.windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, .segfault), | |
| 257 | os.windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, .illegal_instruction), | |
| 258 | os.windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, .{ .literal = "Stack Overflow" }), | |
| 259 | else => return os.windows.EXCEPTION_CONTINUE_SEARCH, | |
| 260 | } | |
| 261 | } | |
| 262 | ||
| 263 | fn handleSegfaultWindowsExtra(info: *os.windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn { | |
| 264 | PanicSwitch.preDispatch(); | |
| 265 | ||
| 266 | const stack_ctx = if (@hasDecl(os.windows, "CONTEXT")) ctx: { | |
| 267 | const regs = info.ContextRecord.getRegs(); | |
| 268 | break :ctx StackContext{ .exception = .{ .bp = regs.bp, .ip = regs.ip } }; | |
| 269 | } else ctx: { | |
| 270 | const addr = @ptrToInt(info.ExceptionRecord.ExceptionAddress); | |
| 271 | break :ctx StackContext{ .current = .{ .ret_addr = addr } }; | |
| 272 | }; | |
| 273 | ||
| 274 | switch (msg) { | |
| 275 | .literal => |err| PanicSwitch.dispatch(null, stack_ctx, err), | |
| 276 | .segfault => { | |
| 277 | const format_item = "Segmentation fault at address 0x{x}"; | |
| 278 | var buf: [format_item.len + 32]u8 = undefined; // 32 is arbitrary, but sufficiently large | |
| 279 | const to_print = std.fmt.bufPrint(&buf, format_item, .{info.ExceptionRecord.ExceptionInformation[1]}) catch unreachable; | |
| 280 | PanicSwitch.dispatch(null, stack_ctx, to_print); | |
| 281 | }, | |
| 282 | .illegal_instruction => { | |
| 283 | const ip: ?usize = switch (stack_ctx) { | |
| 284 | .exception => |ex| ex.ip, | |
| 285 | .current => |cur| cur.ret_addr, | |
| 286 | .not_supported => null, | |
| 287 | }; | |
| 288 | ||
| 289 | if (ip) |addr| { | |
| 290 | const format_item = "Illegal instruction at address 0x{x}"; | |
| 291 | var buf: [format_item.len + 32]u8 = undefined; // 32 is arbitrary, but sufficiently large | |
| 292 | const to_print = std.fmt.bufPrint(&buf, format_item, .{addr}) catch unreachable; | |
| 293 | PanicSwitch.dispatch(null, stack_ctx, to_print); | |
| 294 | } else { | |
| 295 | PanicSwitch.dispatch(null, stack_ctx, "Illegal Instruction"); | |
| 296 | } | |
| 297 | }, | |
| 298 | } | |
| 299 | } | |
| 300 | ||
| 301 | const StackContext = union(enum) { | |
| 302 | current: struct { | |
| 303 | ret_addr: ?usize, | |
| 304 | }, | |
| 305 | exception: struct { | |
| 306 | bp: usize, | |
| 307 | ip: usize, | |
| 308 | }, | |
| 309 | not_supported: void, | |
| 310 | ||
| 311 | pub fn dumpStackTrace(ctx: @This()) void { | |
| 312 | switch (ctx) { | |
| 313 | .current => |ct| { | |
| 314 | debug.dumpCurrentStackTrace(ct.ret_addr); | |
| 315 | }, | |
| 316 | .exception => |ex| { | |
| 317 | debug.dumpStackTraceFromBase(ex.bp, ex.ip); | |
| 318 | }, | |
| 319 | .not_supported => { | |
| 320 | const stderr = io.getStdErr().writer(); | |
| 321 | stderr.writeAll("Stack trace not supported on this platform.\n") catch {}; | |
| 322 | }, | |
| 323 | } | |
| 324 | } | |
| 325 | }; | |
| 326 | ||
| 327 | const PanicSwitch = struct { | |
| 328 | const RecoverStage = enum { | |
| 329 | initialize, | |
| 330 | report_stack, | |
| 331 | release_mutex, | |
| 332 | release_ref_count, | |
| 333 | abort, | |
| 334 | silent_abort, | |
| 335 | }; | |
| 336 | ||
| 337 | const RecoverVerbosity = enum { | |
| 338 | message_and_stack, | |
| 339 | message_only, | |
| 340 | silent, | |
| 341 | }; | |
| 342 | ||
| 343 | const PanicState = struct { | |
| 344 | recover_stage: RecoverStage = .initialize, | |
| 345 | recover_verbosity: RecoverVerbosity = .message_and_stack, | |
| 346 | panic_ctx: StackContext = undefined, | |
| 347 | panic_trace: ?*const std.builtin.StackTrace = null, | |
| 348 | awaiting_dispatch: bool = false, | |
| 349 | }; | |
| 350 | ||
| 351 | /// Counter for the number of threads currently panicking. | |
| 352 | /// Updated atomically before taking the panic_mutex. | |
| 353 | /// In recoverable cases, the program will not abort | |
| 354 | /// until all panicking threads have dumped their traces. | |
| 355 | var panicking: u8 = 0; | |
| 356 | ||
| 357 | // Locked to avoid interleaving panic messages from multiple threads. | |
| 358 | var panic_mutex = std.Thread.Mutex{}; | |
| 359 | ||
| 360 | /// Tracks the state of the current panic. If the code within the | |
| 361 | /// panic triggers a secondary panic, this allows us to recover. | |
| 362 | threadlocal var panic_state_raw: PanicState = .{}; | |
| 363 | ||
| 364 | /// The segfault handlers above need to do some work before they can dispatch | |
| 365 | /// this switch. Calling preDispatch() first makes that work fault tolerant. | |
| 366 | pub fn preDispatch() void { | |
| 367 | // TODO: We want segfaults to trigger the panic recursively here, | |
| 368 | // but if there is a segfault accessing this TLS slot it will cause an | |
| 369 | // infinite loop. We should use `alarm()` to prevent the infinite | |
| 370 | // loop and maybe also use a non-thread-local global to detect if | |
| 371 | // it's happening and print a message. | |
| 372 | var panic_state: *volatile PanicState = &panic_state_raw; | |
| 373 | if (panic_state.awaiting_dispatch) { | |
| 374 | dispatch(null, .{ .current = .{ .ret_addr = null } }, "Panic while preparing callstack"); | |
| 375 | } | |
| 376 | panic_state.awaiting_dispatch = true; | |
| 377 | } | |
| 378 | ||
| 379 | /// This is the entry point to a panic-tolerant panic handler. | |
| 380 | /// preDispatch() *MUST* be called exactly once before calling this. | |
| 381 | /// A threadlocal "recover_stage" is updated throughout the process. | |
| 382 | /// If a panic happens during the panic, the recover_stage will be | |
| 383 | /// used to select a recover* function to call to resume the panic. | |
| 384 | /// The recover_verbosity field is used to handle panics while reporting | |
| 385 | /// panics within panics. If the panic handler triggers a panic, it will | |
| 386 | /// attempt to log an additional stack trace for the secondary panic. If | |
| 387 | /// that panics, it will fall back to just logging the panic message. If | |
| 388 | /// it can't even do that witout panicing, it will recover without logging | |
| 389 | /// anything about the internal panic. Depending on the state, "recover" | |
| 390 | /// here may just mean "call abort". | |
| 391 | pub fn dispatch( | |
| 392 | trace: ?*const std.builtin.StackTrace, | |
| 393 | stack_ctx: StackContext, | |
| 394 | msg: []const u8, | |
| 395 | ) noreturn { | |
| 396 | var panic_state: *volatile PanicState = &panic_state_raw; | |
| 397 | debug.assert(panic_state.awaiting_dispatch); | |
| 398 | panic_state.awaiting_dispatch = false; | |
| 399 | nosuspend switch (panic_state.recover_stage) { | |
| 400 | .initialize => goTo(initPanic, .{ panic_state, trace, stack_ctx, msg }), | |
| 401 | .report_stack => goTo(recoverReportStack, .{ panic_state, trace, stack_ctx, msg }), | |
| 402 | .release_mutex => goTo(recoverReleaseMutex, .{ panic_state, trace, stack_ctx, msg }), | |
| 403 | .release_ref_count => goTo(recoverReleaseRefCount, .{ panic_state, trace, stack_ctx, msg }), | |
| 404 | .abort => goTo(recoverAbort, .{ panic_state, trace, stack_ctx, msg }), | |
| 405 | .silent_abort => goTo(abort, .{}), | |
| 406 | }; | |
| 407 | } | |
| 408 | ||
| 409 | noinline fn initPanic( | |
| 410 | state: *volatile PanicState, | |
| 411 | trace: ?*const std.builtin.StackTrace, | |
| 412 | stack: StackContext, | |
| 413 | msg: []const u8, | |
| 414 | ) noreturn { | |
| 415 | // use a temporary so there's only one volatile store | |
| 416 | const new_state = PanicState{ | |
| 417 | .recover_stage = .abort, | |
| 418 | .panic_ctx = stack, | |
| 419 | .panic_trace = trace, | |
| 420 | }; | |
| 421 | state.* = new_state; | |
| 422 | ||
| 423 | _ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst); | |
| 424 | ||
| 425 | state.recover_stage = .release_ref_count; | |
| 426 | ||
| 427 | _ = panic_mutex.acquire(); | |
| 428 | ||
| 429 | state.recover_stage = .release_mutex; | |
| 430 | ||
| 431 | const stderr = io.getStdErr().writer(); | |
| 432 | if (builtin.single_threaded) { | |
| 433 | stderr.print("panic: ", .{}) catch goTo(releaseMutex, .{state}); | |
| 434 | } else { | |
| 435 | const current_thread_id = std.Thread.getCurrentId(); | |
| 436 | stderr.print("thread {} panic: ", .{current_thread_id}) catch goTo(releaseMutex, .{state}); | |
| 437 | } | |
| 438 | stderr.print("{s}\n", .{msg}) catch goTo(releaseMutex, .{state}); | |
| 439 | ||
| 440 | state.recover_stage = .report_stack; | |
| 441 | ||
| 442 | dumpStatusReport() catch |err| { | |
| 443 | stderr.print("\nIntercepted error.{} while dumping current state. Continuing...\n", .{err}) catch {}; | |
| 444 | }; | |
| 445 | ||
| 446 | goTo(reportStack, .{state}); | |
| 447 | } | |
| 448 | ||
| 449 | noinline fn recoverReportStack( | |
| 450 | state: *volatile PanicState, | |
| 451 | trace: ?*const std.builtin.StackTrace, | |
| 452 | stack: StackContext, | |
| 453 | msg: []const u8, | |
| 454 | ) noreturn { | |
| 455 | recover(state, trace, stack, msg); | |
| 456 | ||
| 457 | state.recover_stage = .release_mutex; | |
| 458 | const stderr = io.getStdErr().writer(); | |
| 459 | stderr.writeAll("\nOriginal Error:\n") catch {}; | |
| 460 | goTo(reportStack, .{state}); | |
| 461 | } | |
| 462 | ||
| 463 | noinline fn reportStack(state: *volatile PanicState) noreturn { | |
| 464 | state.recover_stage = .release_mutex; | |
| 465 | ||
| 466 | if (state.panic_trace) |t| { | |
| 467 | debug.dumpStackTrace(t.*); | |
| 468 | } | |
| 469 | state.panic_ctx.dumpStackTrace(); | |
| 470 | ||
| 471 | goTo(releaseMutex, .{state}); | |
| 472 | } | |
| 473 | ||
| 474 | noinline fn recoverReleaseMutex( | |
| 475 | state: *volatile PanicState, | |
| 476 | trace: ?*const std.builtin.StackTrace, | |
| 477 | stack: StackContext, | |
| 478 | msg: []const u8, | |
| 479 | ) noreturn { | |
| 480 | recover(state, trace, stack, msg); | |
| 481 | goTo(releaseMutex, .{state}); | |
| 482 | } | |
| 483 | ||
| 484 | noinline fn releaseMutex(state: *volatile PanicState) noreturn { | |
| 485 | state.recover_stage = .abort; | |
| 486 | ||
| 487 | panic_mutex.releaseDirect(); | |
| 488 | ||
| 489 | goTo(releaseRefCount, .{state}); | |
| 490 | } | |
| 491 | ||
| 492 | noinline fn recoverReleaseRefCount( | |
| 493 | state: *volatile PanicState, | |
| 494 | trace: ?*const std.builtin.StackTrace, | |
| 495 | stack: StackContext, | |
| 496 | msg: []const u8, | |
| 497 | ) noreturn { | |
| 498 | recover(state, trace, stack, msg); | |
| 499 | goTo(releaseRefCount, .{state}); | |
| 500 | } | |
| 501 | ||
| 502 | noinline fn releaseRefCount(state: *volatile PanicState) noreturn { | |
| 503 | state.recover_stage = .abort; | |
| 504 | ||
| 505 | if (@atomicRmw(u8, &panicking, .Sub, 1, .SeqCst) != 1) { | |
| 506 | // Another thread is panicking, wait for the last one to finish | |
| 507 | // and call abort() | |
| 508 | ||
| 509 | // Sleep forever without hammering the CPU | |
| 510 | var event: std.Thread.StaticResetEvent = .{}; | |
| 511 | event.wait(); | |
| 512 | // This should be unreachable, recurse into recoverAbort. | |
| 513 | @panic("event.wait() returned"); | |
| 514 | } | |
| 515 | ||
| 516 | goTo(abort, .{}); | |
| 517 | } | |
| 518 | ||
| 519 | noinline fn recoverAbort( | |
| 520 | state: *volatile PanicState, | |
| 521 | trace: ?*const std.builtin.StackTrace, | |
| 522 | stack: StackContext, | |
| 523 | msg: []const u8, | |
| 524 | ) noreturn { | |
| 525 | recover(state, trace, stack, msg); | |
| 526 | ||
| 527 | state.recover_stage = .silent_abort; | |
| 528 | const stderr = io.getStdErr().writer(); | |
| 529 | stderr.writeAll("Aborting...\n") catch {}; | |
| 530 | goTo(abort, .{}); | |
| 531 | } | |
| 532 | ||
| 533 | noinline fn abort() noreturn { | |
| 534 | os.abort(); | |
| 535 | } | |
| 536 | ||
| 537 | inline fn goTo(comptime func: anytype, args: anytype) noreturn { | |
| 538 | // TODO: Tailcall is broken right now, but eventually this should be used | |
| 539 | // to avoid blowing up the stack. It's ok for now though, there are no | |
| 540 | // cycles in the state machine so the max stack usage is bounded. | |
| 541 | //@call(.{.modifier = .always_tail}, func, args); | |
| 542 | @call(.{}, func, args); | |
| 543 | } | |
| 544 | ||
| 545 | fn recover( | |
| 546 | state: *volatile PanicState, | |
| 547 | trace: ?*const std.builtin.StackTrace, | |
| 548 | stack: StackContext, | |
| 549 | msg: []const u8, | |
| 550 | ) void { | |
| 551 | switch (state.recover_verbosity) { | |
| 552 | .message_and_stack => { | |
| 553 | // lower the verbosity, and restore it at the end if we don't panic. | |
| 554 | state.recover_verbosity = .message_only; | |
| 555 | ||
| 556 | const stderr = io.getStdErr().writer(); | |
| 557 | stderr.writeAll("\nPanicked during a panic: ") catch {}; | |
| 558 | stderr.writeAll(msg) catch {}; | |
| 559 | stderr.writeAll("\nInner panic stack:\n") catch {}; | |
| 560 | if (trace) |t| { | |
| 561 | debug.dumpStackTrace(t.*); | |
| 562 | } | |
| 563 | stack.dumpStackTrace(); | |
| 564 | ||
| 565 | state.recover_verbosity = .message_and_stack; | |
| 566 | }, | |
| 567 | .message_only => { | |
| 568 | state.recover_verbosity = .silent; | |
| 569 | ||
| 570 | const stderr = io.getStdErr().writer(); | |
| 571 | stderr.writeAll("\nPanicked while dumping inner panic stack: ") catch {}; | |
| 572 | stderr.writeAll(msg) catch {}; | |
| 573 | stderr.writeAll("\n") catch {}; | |
| 574 | ||
| 575 | // If we succeed, restore all the way to dumping the stack. | |
| 576 | state.recover_verbosity = .message_and_stack; | |
| 577 | }, | |
| 578 | .silent => {}, | |
| 579 | } | |
| 580 | } | |
| 581 | }; |
src/libc_installation.zig+1| ... | ... | @@ -221,6 +221,7 @@ pub const LibCInstallation = struct { |
| 221 | 221 | batch.add(&async self.findNativeIncludeDirPosix(args)); |
| 222 | 222 | switch (Target.current.os.tag) { |
| 223 | 223 | .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib"), |
| 224 | .solaris => self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib/64"), | |
| 224 | 225 | .linux => batch.add(&async self.findNativeCrtDirPosix(args)), |
| 225 | 226 | else => {}, |
| 226 | 227 | } |
src/libcxx.zig+2-2| ... | ... | @@ -110,7 +110,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { |
| 110 | 110 | const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" }); |
| 111 | 111 | const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }); |
| 112 | 112 | var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); |
| 113 | try c_source_files.ensureCapacity(libcxx_files.len); | |
| 113 | try c_source_files.ensureTotalCapacity(libcxx_files.len); | |
| 114 | 114 | |
| 115 | 115 | for (libcxx_files) |cxx_src| { |
| 116 | 116 | var cflags = std.ArrayList([]const u8).init(arena); |
| ... | ... | @@ -250,7 +250,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { |
| 250 | 250 | const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" }); |
| 251 | 251 | const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }); |
| 252 | 252 | var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); |
| 253 | try c_source_files.ensureCapacity(libcxxabi_files.len); | |
| 253 | try c_source_files.ensureTotalCapacity(libcxxabi_files.len); | |
| 254 | 254 | |
| 255 | 255 | for (libcxxabi_files) |cxxabi_src| { |
| 256 | 256 | var cflags = std.ArrayList([]const u8).init(arena); |
src/libtsan.zig+6-6| ... | ... | @@ -34,7 +34,7 @@ pub fn buildTsan(comp: *Compilation) !void { |
| 34 | 34 | }; |
| 35 | 35 | |
| 36 | 36 | var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); |
| 37 | try c_source_files.ensureCapacity(c_source_files.items.len + tsan_sources.len); | |
| 37 | try c_source_files.ensureUnusedCapacity(tsan_sources.len); | |
| 38 | 38 | |
| 39 | 39 | const tsan_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"tsan"}); |
| 40 | 40 | for (tsan_sources) |tsan_src| { |
| ... | ... | @@ -58,7 +58,7 @@ pub fn buildTsan(comp: *Compilation) !void { |
| 58 | 58 | &darwin_tsan_sources |
| 59 | 59 | else |
| 60 | 60 | &unix_tsan_sources; |
| 61 | try c_source_files.ensureCapacity(c_source_files.items.len + platform_tsan_sources.len); | |
| 61 | try c_source_files.ensureUnusedCapacity(platform_tsan_sources.len); | |
| 62 | 62 | for (platform_tsan_sources) |tsan_src| { |
| 63 | 63 | var cflags = std.ArrayList([]const u8).init(arena); |
| 64 | 64 | |
| ... | ... | @@ -96,7 +96,7 @@ pub fn buildTsan(comp: *Compilation) !void { |
| 96 | 96 | }); |
| 97 | 97 | } |
| 98 | 98 | |
| 99 | try c_source_files.ensureCapacity(c_source_files.items.len + sanitizer_common_sources.len); | |
| 99 | try c_source_files.ensureUnusedCapacity(sanitizer_common_sources.len); | |
| 100 | 100 | const sanitizer_common_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ |
| 101 | 101 | "tsan", "sanitizer_common", |
| 102 | 102 | }); |
| ... | ... | @@ -123,7 +123,7 @@ pub fn buildTsan(comp: *Compilation) !void { |
| 123 | 123 | &sanitizer_libcdep_sources |
| 124 | 124 | else |
| 125 | 125 | &sanitizer_nolibc_sources; |
| 126 | try c_source_files.ensureCapacity(c_source_files.items.len + to_c_or_not_to_c_sources.len); | |
| 126 | try c_source_files.ensureUnusedCapacity(to_c_or_not_to_c_sources.len); | |
| 127 | 127 | for (to_c_or_not_to_c_sources) |c_src| { |
| 128 | 128 | var cflags = std.ArrayList([]const u8).init(arena); |
| 129 | 129 | |
| ... | ... | @@ -143,7 +143,7 @@ pub fn buildTsan(comp: *Compilation) !void { |
| 143 | 143 | }); |
| 144 | 144 | } |
| 145 | 145 | |
| 146 | try c_source_files.ensureCapacity(c_source_files.items.len + sanitizer_symbolizer_sources.len); | |
| 146 | try c_source_files.ensureUnusedCapacity(sanitizer_symbolizer_sources.len); | |
| 147 | 147 | for (sanitizer_symbolizer_sources) |c_src| { |
| 148 | 148 | var cflags = std.ArrayList([]const u8).init(arena); |
| 149 | 149 | |
| ... | ... | @@ -168,7 +168,7 @@ pub fn buildTsan(comp: *Compilation) !void { |
| 168 | 168 | &[_][]const u8{"interception"}, |
| 169 | 169 | ); |
| 170 | 170 | |
| 171 | try c_source_files.ensureCapacity(c_source_files.items.len + interception_sources.len); | |
| 171 | try c_source_files.ensureUnusedCapacity(interception_sources.len); | |
| 172 | 172 | for (interception_sources) |c_src| { |
| 173 | 173 | var cflags = std.ArrayList([]const u8).init(arena); |
| 174 | 174 |
src/link.zig+10-5| ... | ... | @@ -149,7 +149,7 @@ pub const File = struct { |
| 149 | 149 | coff: Coff.TextBlock, |
| 150 | 150 | macho: MachO.TextBlock, |
| 151 | 151 | plan9: Plan9.DeclBlock, |
| 152 | c: C.DeclBlock, | |
| 152 | c: void, | |
| 153 | 153 | wasm: Wasm.DeclBlock, |
| 154 | 154 | spirv: void, |
| 155 | 155 | }; |
| ... | ... | @@ -159,7 +159,7 @@ pub const File = struct { |
| 159 | 159 | coff: Coff.SrcFn, |
| 160 | 160 | macho: MachO.SrcFn, |
| 161 | 161 | plan9: void, |
| 162 | c: C.FnBlock, | |
| 162 | c: void, | |
| 163 | 163 | wasm: Wasm.FnData, |
| 164 | 164 | spirv: SpirV.FnData, |
| 165 | 165 | }; |
| ... | ... | @@ -245,6 +245,9 @@ pub const File = struct { |
| 245 | 245 | }; |
| 246 | 246 | |
| 247 | 247 | if (use_lld) { |
| 248 | // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`, | |
| 249 | // we also want to put the intermediary object file in the cache while the | |
| 250 | // main emit directory is the cwd. | |
| 248 | 251 | file.intermediary_basename = sub_path; |
| 249 | 252 | } |
| 250 | 253 | |
| ... | ... | @@ -372,16 +375,18 @@ pub const File = struct { |
| 372 | 375 | |
| 373 | 376 | /// Must be called before any call to updateDecl or updateDeclExports for |
| 374 | 377 | /// any given Decl. |
| 378 | /// TODO we're transitioning to deleting this function and instead having | |
| 379 | /// each linker backend notice the first time updateDecl or updateFunc is called, or | |
| 380 | /// a callee referenced from AIR. | |
| 375 | 381 | pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void { |
| 376 | 382 | log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name }); |
| 377 | 383 | switch (base.tag) { |
| 378 | 384 | .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl), |
| 379 | 385 | .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), |
| 380 | 386 | .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), |
| 381 | .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl), | |
| 382 | 387 | .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl), |
| 383 | 388 | .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl), |
| 384 | .spirv => {}, | |
| 389 | .c, .spirv => {}, | |
| 385 | 390 | } |
| 386 | 391 | } |
| 387 | 392 | |
| ... | ... | @@ -635,7 +640,7 @@ pub const File = struct { |
| 635 | 640 | var object_files = std.ArrayList([*:0]const u8).init(base.allocator); |
| 636 | 641 | defer object_files.deinit(); |
| 637 | 642 | |
| 638 | try object_files.ensureCapacity(base.options.objects.len + comp.c_object_table.count() + 2); | |
| 643 | try object_files.ensureTotalCapacity(base.options.objects.len + comp.c_object_table.count() + 2); | |
| 639 | 644 | for (base.options.objects) |obj_path| { |
| 640 | 645 | object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj_path)); |
| 641 | 646 | } |
src/link/C.zig+166-98| ... | ... | @@ -21,30 +21,34 @@ base: link.File, |
| 21 | 21 | /// This linker backend does not try to incrementally link output C source code. |
| 22 | 22 | /// Instead, it tracks all declarations in this table, and iterates over it |
| 23 | 23 | /// in the flush function, stitching pre-rendered pieces of C code together. |
| 24 | decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{}, | |
| 24 | decl_table: std.AutoArrayHashMapUnmanaged(*const Module.Decl, DeclBlock) = .{}, | |
| 25 | /// Stores Type/Value data for `typedefs` to reference. | |
| 26 | /// Accumulates allocations and then there is a periodic garbage collection after flush(). | |
| 27 | arena: std.heap.ArenaAllocator, | |
| 25 | 28 | |
| 26 | 29 | /// Per-declaration data. For functions this is the body, and |
| 27 | 30 | /// the forward declaration is stored in the FnBlock. |
| 28 | pub const DeclBlock = struct { | |
| 29 | code: std.ArrayListUnmanaged(u8), | |
| 30 | ||
| 31 | pub const empty: DeclBlock = .{ | |
| 32 | .code = .{}, | |
| 33 | }; | |
| 34 | }; | |
| 35 | ||
| 36 | /// Per-function data. | |
| 37 | pub const FnBlock = struct { | |
| 38 | fwd_decl: std.ArrayListUnmanaged(u8), | |
| 39 | typedefs: codegen.TypedefMap.Unmanaged, | |
| 40 | ||
| 41 | pub const empty: FnBlock = .{ | |
| 42 | .fwd_decl = .{}, | |
| 43 | .typedefs = .{}, | |
| 44 | }; | |
| 31 | const DeclBlock = struct { | |
| 32 | code: std.ArrayListUnmanaged(u8) = .{}, | |
| 33 | fwd_decl: std.ArrayListUnmanaged(u8) = .{}, | |
| 34 | /// Each Decl stores a mapping of Zig Types to corresponding C types, for every | |
| 35 | /// Zig Type used by the Decl. In flush(), we iterate over each Decl | |
| 36 | /// and emit the typedef code for all types, making sure to not emit the same thing twice. | |
| 37 | /// Any arena memory the Type points to lives in the `arena` field of `C`. | |
| 38 | typedefs: codegen.TypedefMap.Unmanaged = .{}, | |
| 39 | ||
| 40 | fn deinit(db: *DeclBlock, gpa: *Allocator) void { | |
| 41 | db.code.deinit(gpa); | |
| 42 | db.fwd_decl.deinit(gpa); | |
| 43 | for (db.typedefs.values()) |typedef| { | |
| 44 | gpa.free(typedef.rendered); | |
| 45 | } | |
| 46 | db.typedefs.deinit(gpa); | |
| 47 | db.* = undefined; | |
| 48 | } | |
| 45 | 49 | }; |
| 46 | 50 | |
| 47 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C { | |
| 51 | pub fn openPath(gpa: *Allocator, sub_path: []const u8, options: link.Options) !*C { | |
| 48 | 52 | assert(options.object_format == .c); |
| 49 | 53 | |
| 50 | 54 | if (options.use_llvm) return error.LLVMHasNoCBackend; |
| ... | ... | @@ -57,15 +61,16 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 57 | 61 | }); |
| 58 | 62 | errdefer file.close(); |
| 59 | 63 | |
| 60 | var c_file = try allocator.create(C); | |
| 61 | errdefer allocator.destroy(c_file); | |
| 64 | var c_file = try gpa.create(C); | |
| 65 | errdefer gpa.destroy(c_file); | |
| 62 | 66 | |
| 63 | 67 | c_file.* = C{ |
| 68 | .arena = std.heap.ArenaAllocator.init(gpa), | |
| 64 | 69 | .base = .{ |
| 65 | 70 | .tag = .c, |
| 66 | 71 | .options = options, |
| 67 | 72 | .file = file, |
| 68 | .allocator = allocator, | |
| 73 | .allocator = gpa, | |
| 69 | 74 | }, |
| 70 | 75 | }; |
| 71 | 76 | |
| ... | ... | @@ -73,38 +78,105 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 73 | 78 | } |
| 74 | 79 | |
| 75 | 80 | pub fn deinit(self: *C) void { |
| 76 | for (self.decl_table.keys()) |key| { | |
| 77 | deinitDecl(self.base.allocator, key); | |
| 81 | const gpa = self.base.allocator; | |
| 82 | ||
| 83 | for (self.decl_table.values()) |*db| { | |
| 84 | db.deinit(gpa); | |
| 78 | 85 | } |
| 79 | self.decl_table.deinit(self.base.allocator); | |
| 80 | } | |
| 86 | self.decl_table.deinit(gpa); | |
| 81 | 87 | |
| 82 | pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void { | |
| 83 | _ = self; | |
| 84 | _ = decl; | |
| 88 | self.arena.deinit(); | |
| 85 | 89 | } |
| 86 | 90 | |
| 87 | 91 | pub fn freeDecl(self: *C, decl: *Module.Decl) void { |
| 88 | _ = self.decl_table.swapRemove(decl); | |
| 89 | deinitDecl(self.base.allocator, decl); | |
| 92 | const gpa = self.base.allocator; | |
| 93 | if (self.decl_table.fetchSwapRemove(decl)) |*kv| { | |
| 94 | kv.value.deinit(gpa); | |
| 95 | } | |
| 90 | 96 | } |
| 91 | 97 | |
| 92 | fn deinitDecl(gpa: *Allocator, decl: *Module.Decl) void { | |
| 93 | decl.link.c.code.deinit(gpa); | |
| 94 | decl.fn_link.c.fwd_decl.deinit(gpa); | |
| 95 | for (decl.fn_link.c.typedefs.values()) |value| { | |
| 96 | gpa.free(value.rendered); | |
| 98 | pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { | |
| 99 | const tracy = trace(@src()); | |
| 100 | defer tracy.end(); | |
| 101 | ||
| 102 | const decl = func.owner_decl; | |
| 103 | const gop = try self.decl_table.getOrPut(self.base.allocator, decl); | |
| 104 | if (!gop.found_existing) { | |
| 105 | gop.value_ptr.* = .{}; | |
| 106 | } | |
| 107 | const fwd_decl = &gop.value_ptr.fwd_decl; | |
| 108 | const typedefs = &gop.value_ptr.typedefs; | |
| 109 | const code = &gop.value_ptr.code; | |
| 110 | fwd_decl.shrinkRetainingCapacity(0); | |
| 111 | { | |
| 112 | for (typedefs.values()) |value| { | |
| 113 | module.gpa.free(value.rendered); | |
| 114 | } | |
| 115 | } | |
| 116 | typedefs.clearRetainingCapacity(); | |
| 117 | code.shrinkRetainingCapacity(0); | |
| 118 | ||
| 119 | var function: codegen.Function = .{ | |
| 120 | .value_map = codegen.CValueMap.init(module.gpa), | |
| 121 | .air = air, | |
| 122 | .liveness = liveness, | |
| 123 | .func = func, | |
| 124 | .object = .{ | |
| 125 | .dg = .{ | |
| 126 | .gpa = module.gpa, | |
| 127 | .module = module, | |
| 128 | .error_msg = null, | |
| 129 | .decl = decl, | |
| 130 | .fwd_decl = fwd_decl.toManaged(module.gpa), | |
| 131 | .typedefs = typedefs.promote(module.gpa), | |
| 132 | .typedefs_arena = &self.arena.allocator, | |
| 133 | }, | |
| 134 | .code = code.toManaged(module.gpa), | |
| 135 | .indent_writer = undefined, // set later so we can get a pointer to object.code | |
| 136 | }, | |
| 137 | }; | |
| 138 | ||
| 139 | function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; | |
| 140 | defer { | |
| 141 | function.value_map.deinit(); | |
| 142 | function.blocks.deinit(module.gpa); | |
| 143 | function.object.code.deinit(); | |
| 144 | function.object.dg.fwd_decl.deinit(); | |
| 145 | for (function.object.dg.typedefs.values()) |value| { | |
| 146 | module.gpa.free(value.rendered); | |
| 147 | } | |
| 148 | function.object.dg.typedefs.deinit(); | |
| 97 | 149 | } |
| 98 | decl.fn_link.c.typedefs.deinit(gpa); | |
| 150 | ||
| 151 | codegen.genFunc(&function) catch |err| switch (err) { | |
| 152 | error.AnalysisFail => { | |
| 153 | try module.failed_decls.put(module.gpa, decl, function.object.dg.error_msg.?); | |
| 154 | return; | |
| 155 | }, | |
| 156 | else => |e| return e, | |
| 157 | }; | |
| 158 | ||
| 159 | fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged(); | |
| 160 | typedefs.* = function.object.dg.typedefs.unmanaged; | |
| 161 | function.object.dg.typedefs.unmanaged = .{}; | |
| 162 | code.* = function.object.code.moveToUnmanaged(); | |
| 163 | ||
| 164 | // Free excess allocated memory for this Decl. | |
| 165 | fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len); | |
| 166 | code.shrinkAndFree(module.gpa, code.items.len); | |
| 99 | 167 | } |
| 100 | 168 | |
| 101 | pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air, liveness: Liveness) !void { | |
| 102 | // Keep track of all decls so we can iterate over them on flush(). | |
| 103 | _ = try self.decl_table.getOrPut(self.base.allocator, decl); | |
| 169 | pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { | |
| 170 | const tracy = trace(@src()); | |
| 171 | defer tracy.end(); | |
| 104 | 172 | |
| 105 | const fwd_decl = &decl.fn_link.c.fwd_decl; | |
| 106 | const typedefs = &decl.fn_link.c.typedefs; | |
| 107 | const code = &decl.link.c.code; | |
| 173 | const gop = try self.decl_table.getOrPut(self.base.allocator, decl); | |
| 174 | if (!gop.found_existing) { | |
| 175 | gop.value_ptr.* = .{}; | |
| 176 | } | |
| 177 | const fwd_decl = &gop.value_ptr.fwd_decl; | |
| 178 | const typedefs = &gop.value_ptr.typedefs; | |
| 179 | const code = &gop.value_ptr.code; | |
| 108 | 180 | fwd_decl.shrinkRetainingCapacity(0); |
| 109 | 181 | { |
| 110 | 182 | for (typedefs.values()) |value| { |
| ... | ... | @@ -116,23 +188,19 @@ pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air, |
| 116 | 188 | |
| 117 | 189 | var object: codegen.Object = .{ |
| 118 | 190 | .dg = .{ |
| 191 | .gpa = module.gpa, | |
| 119 | 192 | .module = module, |
| 120 | 193 | .error_msg = null, |
| 121 | 194 | .decl = decl, |
| 122 | 195 | .fwd_decl = fwd_decl.toManaged(module.gpa), |
| 123 | 196 | .typedefs = typedefs.promote(module.gpa), |
| 197 | .typedefs_arena = &self.arena.allocator, | |
| 124 | 198 | }, |
| 125 | .gpa = module.gpa, | |
| 126 | 199 | .code = code.toManaged(module.gpa), |
| 127 | .value_map = codegen.CValueMap.init(module.gpa), | |
| 128 | 200 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 129 | .air = air, | |
| 130 | .liveness = liveness, | |
| 131 | 201 | }; |
| 132 | 202 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; |
| 133 | 203 | defer { |
| 134 | object.value_map.deinit(); | |
| 135 | object.blocks.deinit(module.gpa); | |
| 136 | 204 | object.code.deinit(); |
| 137 | 205 | object.dg.fwd_decl.deinit(); |
| 138 | 206 | for (object.dg.typedefs.values()) |value| { |
| ... | ... | @@ -159,24 +227,12 @@ pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air, |
| 159 | 227 | code.shrinkAndFree(module.gpa, code.items.len); |
| 160 | 228 | } |
| 161 | 229 | |
| 162 | pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { | |
| 163 | const tracy = trace(@src()); | |
| 164 | defer tracy.end(); | |
| 165 | ||
| 166 | return self.finishUpdateDecl(module, func.owner_decl, air, liveness); | |
| 167 | } | |
| 168 | ||
| 169 | pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { | |
| 170 | const tracy = trace(@src()); | |
| 171 | defer tracy.end(); | |
| 172 | ||
| 173 | return self.finishUpdateDecl(module, decl, undefined, undefined); | |
| 174 | } | |
| 175 | ||
| 176 | 230 | pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void { |
| 177 | 231 | // The C backend does not have the ability to fix line numbers without re-generating |
| 178 | 232 | // the entire Decl. |
| 179 | return self.updateDecl(module, decl); | |
| 233 | _ = self; | |
| 234 | _ = module; | |
| 235 | _ = decl; | |
| 180 | 236 | } |
| 181 | 237 | |
| 182 | 238 | pub fn flush(self: *C, comp: *Compilation) !void { |
| ... | ... | @@ -197,7 +253,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 197 | 253 | defer all_buffers.deinit(); |
| 198 | 254 | |
| 199 | 255 | // This is at least enough until we get to the function bodies without error handling. |
| 200 | try all_buffers.ensureCapacity(self.decl_table.count() + 2); | |
| 256 | try all_buffers.ensureTotalCapacity(self.decl_table.count() + 2); | |
| 201 | 257 | |
| 202 | 258 | var file_size: u64 = zig_h.len; |
| 203 | 259 | all_buffers.appendAssumeCapacity(.{ |
| ... | ... | @@ -223,32 +279,42 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 223 | 279 | var typedefs = std.HashMap(Type, void, Type.HashContext64, std.hash_map.default_max_load_percentage).init(comp.gpa); |
| 224 | 280 | defer typedefs.deinit(); |
| 225 | 281 | |
| 226 | // Typedefs, forward decls and non-functions first. | |
| 282 | // Typedefs, forward decls, and non-functions first. | |
| 227 | 283 | // TODO: performance investigation: would keeping a list of Decls that we should |
| 228 | 284 | // generate, rather than querying here, be faster? |
| 229 | for (self.decl_table.keys()) |decl| { | |
| 230 | if (!decl.has_tv) continue; | |
| 231 | const buf = buf: { | |
| 232 | if (decl.val.castTag(.function)) |_| { | |
| 233 | try typedefs.ensureUnusedCapacity(@intCast(u32, decl.fn_link.c.typedefs.count())); | |
| 234 | var it = decl.fn_link.c.typedefs.iterator(); | |
| 235 | while (it.next()) |new| { | |
| 236 | const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*); | |
| 237 | if (!gop.found_existing) { | |
| 238 | try err_typedef_writer.writeAll(new.value_ptr.rendered); | |
| 239 | } | |
| 285 | const decl_keys = self.decl_table.keys(); | |
| 286 | const decl_values = self.decl_table.values(); | |
| 287 | for (decl_keys) |decl, i| { | |
| 288 | if (!decl.has_tv) continue; // TODO do we really need this branch? | |
| 289 | ||
| 290 | const decl_block = &decl_values[i]; | |
| 291 | ||
| 292 | if (decl_block.fwd_decl.items.len != 0) { | |
| 293 | try typedefs.ensureUnusedCapacity(@intCast(u32, decl_block.typedefs.count())); | |
| 294 | var it = decl_block.typedefs.iterator(); | |
| 295 | while (it.next()) |new| { | |
| 296 | const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*); | |
| 297 | if (!gop.found_existing) { | |
| 298 | try err_typedef_writer.writeAll(new.value_ptr.rendered); | |
| 240 | 299 | } |
| 241 | fn_count += 1; | |
| 242 | break :buf decl.fn_link.c.fwd_decl.items; | |
| 243 | } else { | |
| 244 | break :buf decl.link.c.code.items; | |
| 245 | 300 | } |
| 246 | }; | |
| 247 | all_buffers.appendAssumeCapacity(.{ | |
| 248 | .iov_base = buf.ptr, | |
| 249 | .iov_len = buf.len, | |
| 250 | }); | |
| 251 | file_size += buf.len; | |
| 301 | const buf = decl_block.fwd_decl.items; | |
| 302 | all_buffers.appendAssumeCapacity(.{ | |
| 303 | .iov_base = buf.ptr, | |
| 304 | .iov_len = buf.len, | |
| 305 | }); | |
| 306 | file_size += buf.len; | |
| 307 | } | |
| 308 | if (decl.getFunction() != null) { | |
| 309 | fn_count += 1; | |
| 310 | } else if (decl_block.code.items.len != 0) { | |
| 311 | const buf = decl_block.code.items; | |
| 312 | all_buffers.appendAssumeCapacity(.{ | |
| 313 | .iov_base = buf.ptr, | |
| 314 | .iov_len = buf.len, | |
| 315 | }); | |
| 316 | file_size += buf.len; | |
| 317 | } | |
| 252 | 318 | } |
| 253 | 319 | |
| 254 | 320 | err_typedef_item.* = .{ |
| ... | ... | @@ -258,16 +324,18 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 258 | 324 | file_size += err_typedef_buf.items.len; |
| 259 | 325 | |
| 260 | 326 | // Now the function bodies. |
| 261 | try all_buffers.ensureCapacity(all_buffers.items.len + fn_count); | |
| 262 | for (self.decl_table.keys()) |decl| { | |
| 263 | if (!decl.has_tv) continue; | |
| 264 | if (decl.val.castTag(.function)) |_| { | |
| 265 | const buf = decl.link.c.code.items; | |
| 266 | all_buffers.appendAssumeCapacity(.{ | |
| 267 | .iov_base = buf.ptr, | |
| 268 | .iov_len = buf.len, | |
| 269 | }); | |
| 270 | file_size += buf.len; | |
| 327 | try all_buffers.ensureUnusedCapacity(fn_count); | |
| 328 | for (decl_keys) |decl, i| { | |
| 329 | if (decl.getFunction() != null) { | |
| 330 | const decl_block = &decl_values[i]; | |
| 331 | const buf = decl_block.code.items; | |
| 332 | if (buf.len != 0) { | |
| 333 | all_buffers.appendAssumeCapacity(.{ | |
| 334 | .iov_base = buf.ptr, | |
| 335 | .iov_len = buf.len, | |
| 336 | }); | |
| 337 | file_size += buf.len; | |
| 338 | } | |
| 271 | 339 | } |
| 272 | 340 | } |
| 273 | 341 | |
| ... | ... | @@ -286,7 +354,7 @@ pub fn flushEmitH(module: *Module) !void { |
| 286 | 354 | var all_buffers = std.ArrayList(std.os.iovec_const).init(module.gpa); |
| 287 | 355 | defer all_buffers.deinit(); |
| 288 | 356 | |
| 289 | try all_buffers.ensureCapacity(emit_h.decl_table.count() + 1); | |
| 357 | try all_buffers.ensureTotalCapacity(emit_h.decl_table.count() + 1); | |
| 290 | 358 | |
| 291 | 359 | var file_size: u64 = zig_h.len; |
| 292 | 360 | all_buffers.appendAssumeCapacity(.{ |
src/link/C/zig.h+148-5| ... | ... | @@ -62,14 +62,62 @@ |
| 62 | 62 | |
| 63 | 63 | #if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) |
| 64 | 64 | #include <stdatomic.h> |
| 65 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) | |
| 66 | #define zig_cmpxchg_weak(obj, expected, desired, succ, fail) atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) | |
| 65 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail) | |
| 66 | #define zig_cmpxchg_weak (obj, expected, desired, succ, fail) atomic_compare_exchange_weak_explicit (obj, &(expected), desired, succ, fail) | |
| 67 | #define zig_atomicrmw_xchg(obj, arg, order) atomic_exchange_explicit (obj, arg, order) | |
| 68 | #define zig_atomicrmw_add (obj, arg, order) atomic_fetch_add_explicit (obj, arg, order) | |
| 69 | #define zig_atomicrmw_sub (obj, arg, order) atomic_fetch_sub_explicit (obj, arg, order) | |
| 70 | #define zig_atomicrmw_or (obj, arg, order) atomic_fetch_or_explicit (obj, arg, order) | |
| 71 | #define zig_atomicrmw_xor (obj, arg, order) atomic_fetch_xor_explicit (obj, arg, order) | |
| 72 | #define zig_atomicrmw_and (obj, arg, order) atomic_fetch_and_explicit (obj, arg, order) | |
| 73 | #define zig_atomicrmw_nand(obj, arg, order) atomic_fetch_nand_explicit(obj, arg, order) | |
| 74 | #define zig_atomicrmw_min (obj, arg, order) atomic_fetch_min_explicit (obj, arg, order) | |
| 75 | #define zig_atomicrmw_max (obj, arg, order) atomic_fetch_max_explicit (obj, arg, order) | |
| 76 | #define zig_atomic_store (obj, arg, order) atomic_store_explicit (obj, arg, order) | |
| 77 | #define zig_atomic_load (obj, order) atomic_load_explicit (obj, order) | |
| 78 | #define zig_fence(order) atomic_thread_fence(order) | |
| 67 | 79 | #elif __GNUC__ |
| 68 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired) | |
| 69 | #define zig_cmpxchg_weak(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired) | |
| 80 | #define memory_order_relaxed __ATOMIC_RELAXED | |
| 81 | #define memory_order_consume __ATOMIC_CONSUME | |
| 82 | #define memory_order_acquire __ATOMIC_ACQUIRE | |
| 83 | #define memory_order_release __ATOMIC_RELEASE | |
| 84 | #define memory_order_acq_rel __ATOMIC_ACQ_REL | |
| 85 | #define memory_order_seq_cst __ATOMIC_SEQ_CST | |
| 86 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, false, succ, fail) | |
| 87 | #define zig_cmpxchg_weak (obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, true , succ, fail) | |
| 88 | #define zig_atomicrmw_xchg(obj, arg, order) __atomic_exchange_n(obj, arg, order) | |
| 89 | #define zig_atomicrmw_add (obj, arg, order) __atomic_fetch_add (obj, arg, order) | |
| 90 | #define zig_atomicrmw_sub (obj, arg, order) __atomic_fetch_sub (obj, arg, order) | |
| 91 | #define zig_atomicrmw_or (obj, arg, order) __atomic_fetch_or (obj, arg, order) | |
| 92 | #define zig_atomicrmw_xor (obj, arg, order) __atomic_fetch_xor (obj, arg, order) | |
| 93 | #define zig_atomicrmw_and (obj, arg, order) __atomic_fetch_and (obj, arg, order) | |
| 94 | #define zig_atomicrmw_nand(obj, arg, order) __atomic_fetch_nand(obj, arg, order) | |
| 95 | #define zig_atomicrmw_min (obj, arg, order) __atomic_fetch_min (obj, arg, order) | |
| 96 | #define zig_atomicrmw_max (obj, arg, order) __atomic_fetch_max (obj, arg, order) | |
| 97 | #define zig_atomic_store (obj, arg, order) __atomic_store (obj, arg, order) | |
| 98 | #define zig_atomic_load (obj, order) __atomic_load (obj, order) | |
| 99 | #define zig_fence(order) __atomic_thread_fence(order) | |
| 70 | 100 | #else |
| 101 | #define memory_order_relaxed 0 | |
| 102 | #define memory_order_consume 1 | |
| 103 | #define memory_order_acquire 2 | |
| 104 | #define memory_order_release 3 | |
| 105 | #define memory_order_acq_rel 4 | |
| 106 | #define memory_order_seq_cst 5 | |
| 71 | 107 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail) zig_unimplemented() |
| 72 | #define zig_cmpxchg_weak(obj, expected, desired, succ, fail) zig_unimplemented() | |
| 108 | #define zig_cmpxchg_weak (obj, expected, desired, succ, fail) zig_unimplemented() | |
| 109 | #define zig_atomicrmw_xchg(obj, arg, order) zig_unimplemented() | |
| 110 | #define zig_atomicrmw_add (obj, arg, order) zig_unimplemented() | |
| 111 | #define zig_atomicrmw_sub (obj, arg, order) zig_unimplemented() | |
| 112 | #define zig_atomicrmw_or (obj, arg, order) zig_unimplemented() | |
| 113 | #define zig_atomicrmw_xor (obj, arg, order) zig_unimplemented() | |
| 114 | #define zig_atomicrmw_and (obj, arg, order) zig_unimplemented() | |
| 115 | #define zig_atomicrmw_nand(obj, arg, order) zig_unimplemented() | |
| 116 | #define zig_atomicrmw_min (obj, arg, order) zig_unimplemented() | |
| 117 | #define zig_atomicrmw_max (obj, arg, order) zig_unimplemented() | |
| 118 | #define zig_atomic_store (obj, arg, order) zig_unimplemented() | |
| 119 | #define zig_atomic_load (obj, order) zig_unimplemented() | |
| 120 | #define zig_fence(order) zig_unimplemented() | |
| 73 | 121 | #endif |
| 74 | 122 | |
| 75 | 123 | #include <stdint.h> |
| ... | ... | @@ -78,6 +126,7 @@ |
| 78 | 126 | #define int128_t __int128 |
| 79 | 127 | #define uint128_t unsigned __int128 |
| 80 | 128 | ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); |
| 129 | ZIG_EXTERN_C void *memset (void *, int, size_t); | |
| 81 | 130 | |
| 82 | 131 | static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { |
| 83 | 132 | uint8_t thresh = max - rhs; |
| ... | ... | @@ -307,3 +356,97 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon |
| 307 | 356 | return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs)); |
| 308 | 357 | } |
| 309 | 358 | |
| 359 | #define zig_add_sat_u(ZT, T) static inline T zig_adds_##ZT(T x, T y, T max) { \ | |
| 360 | return (x > max - y) ? max : x + y; \ | |
| 361 | } | |
| 362 | ||
| 363 | #define zig_add_sat_s(ZT, T, T2) static inline T zig_adds_##ZT(T2 x, T2 y, T2 min, T2 max) { \ | |
| 364 | T2 res = x + y; \ | |
| 365 | return (res < min) ? min : (res > max) ? max : res; \ | |
| 366 | } | |
| 367 | ||
| 368 | zig_add_sat_u( u8, uint8_t) | |
| 369 | zig_add_sat_s( i8, int8_t, int16_t) | |
| 370 | zig_add_sat_u(u16, uint16_t) | |
| 371 | zig_add_sat_s(i16, int16_t, int32_t) | |
| 372 | zig_add_sat_u(u32, uint32_t) | |
| 373 | zig_add_sat_s(i32, int32_t, int64_t) | |
| 374 | zig_add_sat_u(u64, uint64_t) | |
| 375 | zig_add_sat_s(i64, int64_t, int128_t) | |
| 376 | zig_add_sat_s(isize, intptr_t, int128_t) | |
| 377 | zig_add_sat_s(short, short, int) | |
| 378 | zig_add_sat_s(int, int, long) | |
| 379 | zig_add_sat_s(long, long, long long) | |
| 380 | ||
| 381 | #define zig_sub_sat_u(ZT, T) static inline T zig_subs_##ZT(T x, T y, T max) { \ | |
| 382 | return (x > max + y) ? max : x - y; \ | |
| 383 | } | |
| 384 | ||
| 385 | #define zig_sub_sat_s(ZT, T, T2) static inline T zig_subs_##ZT(T2 x, T2 y, T2 min, T2 max) { \ | |
| 386 | T2 res = x - y; \ | |
| 387 | return (res < min) ? min : (res > max) ? max : res; \ | |
| 388 | } | |
| 389 | ||
| 390 | zig_sub_sat_u( u8, uint8_t) | |
| 391 | zig_sub_sat_s( i8, int8_t, int16_t) | |
| 392 | zig_sub_sat_u(u16, uint16_t) | |
| 393 | zig_sub_sat_s(i16, int16_t, int32_t) | |
| 394 | zig_sub_sat_u(u32, uint32_t) | |
| 395 | zig_sub_sat_s(i32, int32_t, int64_t) | |
| 396 | zig_sub_sat_u(u64, uint64_t) | |
| 397 | zig_sub_sat_s(i64, int64_t, int128_t) | |
| 398 | zig_sub_sat_s(isize, intptr_t, int128_t) | |
| 399 | zig_sub_sat_s(short, short, int) | |
| 400 | zig_sub_sat_s(int, int, long) | |
| 401 | zig_sub_sat_s(long, long, long long) | |
| 402 | ||
| 403 | ||
| 404 | #define zig_mul_sat_u(ZT, T, T2) static inline T zig_muls_##ZT(T2 x, T2 y, T2 max) { \ | |
| 405 | T2 res = x * y; \ | |
| 406 | return (res > max) ? max : res; \ | |
| 407 | } | |
| 408 | ||
| 409 | #define zig_mul_sat_s(ZT, T, T2) static inline T zig_muls_##ZT(T2 x, T2 y, T2 min, T2 max) { \ | |
| 410 | T2 res = x * y; \ | |
| 411 | return (res < min) ? min : (res > max) ? max : res; \ | |
| 412 | } | |
| 413 | ||
| 414 | zig_mul_sat_u(u8, uint8_t, uint16_t) | |
| 415 | zig_mul_sat_s(i8, int8_t, int16_t) | |
| 416 | zig_mul_sat_u(u16, uint16_t, uint32_t) | |
| 417 | zig_mul_sat_s(i16, int16_t, int32_t) | |
| 418 | zig_mul_sat_u(u32, uint32_t, uint64_t) | |
| 419 | zig_mul_sat_s(i32, int32_t, int64_t) | |
| 420 | zig_mul_sat_u(u64, uint64_t, uint128_t) | |
| 421 | zig_mul_sat_s(i64, int64_t, int128_t) | |
| 422 | zig_mul_sat_s(isize, intptr_t, int128_t) | |
| 423 | zig_mul_sat_s(short, short, int) | |
| 424 | zig_mul_sat_s(int, int, long) | |
| 425 | zig_mul_sat_s(long, long, long long) | |
| 426 | ||
| 427 | #define zig_shl_sat_u(ZT, T, bits) static inline T zig_shls_##ZT(T x, T y, T max) { \ | |
| 428 | if(x == 0) return 0; \ | |
| 429 | T bits_set = 64 - __builtin_clzll(x); \ | |
| 430 | return (bits_set + y > bits) ? max : x << y; \ | |
| 431 | } | |
| 432 | ||
| 433 | #define zig_shl_sat_s(ZT, T, bits) static inline T zig_shls_##ZT(T x, T y, T min, T max) { \ | |
| 434 | if(x == 0) return 0; \ | |
| 435 | T x_twos_comp = x < 0 ? -x : x; \ | |
| 436 | T bits_set = 64 - __builtin_clzll(x_twos_comp); \ | |
| 437 | T min_or_max = (x < 0) ? min : max; \ | |
| 438 | return (y + bits_set > bits ) ? min_or_max : x << y; \ | |
| 439 | } | |
| 440 | ||
| 441 | zig_shl_sat_u(u8, uint8_t, 8) | |
| 442 | zig_shl_sat_s(i8, int8_t, 7) | |
| 443 | zig_shl_sat_u(u16, uint16_t, 16) | |
| 444 | zig_shl_sat_s(i16, int16_t, 15) | |
| 445 | zig_shl_sat_u(u32, uint32_t, 32) | |
| 446 | zig_shl_sat_s(i32, int32_t, 31) | |
| 447 | zig_shl_sat_u(u64, uint64_t, 64) | |
| 448 | zig_shl_sat_s(i64, int64_t, 63) | |
| 449 | zig_shl_sat_s(isize, intptr_t, ((sizeof(intptr_t)) * CHAR_BIT - 1)) | |
| 450 | zig_shl_sat_s(short, short, ((sizeof(short )) * CHAR_BIT - 1)) | |
| 451 | zig_shl_sat_s(int, int, ((sizeof(int )) * CHAR_BIT - 1)) | |
| 452 | zig_shl_sat_s(long, long, ((sizeof(long )) * CHAR_BIT - 1)) |
src/link/Coff.zig+22-22| ... | ... | @@ -50,7 +50,7 @@ last_text_block: ?*TextBlock = null, |
| 50 | 50 | section_table_offset: u32 = 0, |
| 51 | 51 | /// Section data file pointer. |
| 52 | 52 | section_data_offset: u32 = 0, |
| 53 | /// Optiona header file pointer. | |
| 53 | /// Optional header file pointer. | |
| 54 | 54 | optional_header_offset: u32 = 0, |
| 55 | 55 | |
| 56 | 56 | /// Absolute virtual address of the offset table when the executable is loaded in memory. |
| ... | ... | @@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 132 | 132 | const self = try createEmpty(allocator, options); |
| 133 | 133 | errdefer self.base.destroy(); |
| 134 | 134 | |
| 135 | self.llvm_object = try LlvmObject.create(allocator, options); | |
| 135 | self.llvm_object = try LlvmObject.create(allocator, sub_path, options); | |
| 136 | 136 | return self; |
| 137 | 137 | } |
| 138 | 138 | |
| ... | ... | @@ -418,7 +418,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff { |
| 418 | 418 | pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { |
| 419 | 419 | if (self.llvm_object) |_| return; |
| 420 | 420 | |
| 421 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); | |
| 421 | try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); | |
| 422 | 422 | |
| 423 | 423 | if (self.offset_table_free_list.popOrNull()) |i| { |
| 424 | 424 | decl.link.coff.offset_table_index = i; |
| ... | ... | @@ -602,7 +602,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { |
| 602 | 602 | const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment); |
| 603 | 603 | const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment); |
| 604 | 604 | // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section |
| 605 | // and the virutal size of the `.got` section | |
| 605 | // and the virtual size of the `.got` section | |
| 606 | 606 | |
| 607 | 607 | if (new_virtual_size != current_virtual_size) { |
| 608 | 608 | log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size }); |
| ... | ... | @@ -770,7 +770,9 @@ fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []co |
| 770 | 770 | } |
| 771 | 771 | |
| 772 | 772 | pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { |
| 773 | if (self.llvm_object) |_| return; | |
| 773 | if (build_options.have_llvm) { | |
| 774 | if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); | |
| 775 | } | |
| 774 | 776 | |
| 775 | 777 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 776 | 778 | self.freeTextBlock(&decl.link.coff); |
| ... | ... | @@ -793,7 +795,7 @@ pub fn updateDeclExports( |
| 793 | 795 | for (exports) |exp| { |
| 794 | 796 | if (exp.options.section) |section_name| { |
| 795 | 797 | if (!mem.eql(u8, section_name, ".text")) { |
| 796 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1); | |
| 798 | try module.failed_exports.ensureUnusedCapacity(module.gpa, 1); | |
| 797 | 799 | module.failed_exports.putAssumeCapacityNoClobber( |
| 798 | 800 | exp, |
| 799 | 801 | try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}), |
| ... | ... | @@ -804,7 +806,7 @@ pub fn updateDeclExports( |
| 804 | 806 | if (mem.eql(u8, exp.options.name, "_start")) { |
| 805 | 807 | self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base; |
| 806 | 808 | } else { |
| 807 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1); | |
| 809 | try module.failed_exports.ensureUnusedCapacity(module.gpa, 1); | |
| 808 | 810 | module.failed_exports.putAssumeCapacityNoClobber( |
| 809 | 811 | exp, |
| 810 | 812 | try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: Exports other than '_start'", .{}), |
| ... | ... | @@ -882,11 +884,8 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 882 | 884 | // If there is no Zig code to compile, then we should skip flushing the output file because it |
| 883 | 885 | // will not be part of the linker line anyway. |
| 884 | 886 | const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { |
| 885 | // Both stage1 and stage2 LLVM backend put the object file in the cache directory. | |
| 886 | if (self.base.options.use_llvm) { | |
| 887 | // Stage2 has to call flushModule since that outputs the LLVM object file. | |
| 888 | if (!build_options.is_stage1 or !self.base.options.use_stage1) try self.flushModule(comp); | |
| 889 | ||
| 887 | const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; | |
| 888 | if (use_stage1) { | |
| 890 | 889 | const obj_basename = try std.zig.binNameAlloc(arena, .{ |
| 891 | 890 | .root_name = self.base.options.root_name, |
| 892 | 891 | .target = self.base.options.target, |
| ... | ... | @@ -981,7 +980,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 981 | 980 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); |
| 982 | 981 | |
| 983 | 982 | if (self.base.options.output_mode == .Obj) { |
| 984 | // LLD's COFF driver does not support the equvialent of `-r` so we do a simple file copy | |
| 983 | // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy | |
| 985 | 984 | // here. TODO: think carefully about how we can avoid this redundant operation when doing |
| 986 | 985 | // build-obj. See also the corresponding TODO in linkAsArchive. |
| 987 | 986 | const the_object_path = blk: { |
| ... | ... | @@ -1267,22 +1266,23 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 1267 | 1266 | try argv.append(comp.libunwind_static_lib.?.full_object_path); |
| 1268 | 1267 | } |
| 1269 | 1268 | |
| 1270 | // TODO: remove when stage2 can build compiler_rt.zig, c.zig and ssp.zig | |
| 1271 | // compiler-rt, libc and libssp | |
| 1272 | if (is_exe_or_dyn_lib and | |
| 1273 | !self.base.options.skip_linker_dependencies and | |
| 1274 | build_options.is_stage1 and self.base.options.use_stage1) | |
| 1275 | { | |
| 1269 | if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) { | |
| 1276 | 1270 | if (!self.base.options.link_libc) { |
| 1277 | try argv.append(comp.libc_static_lib.?.full_object_path); | |
| 1271 | if (comp.libc_static_lib) |lib| { | |
| 1272 | try argv.append(lib.full_object_path); | |
| 1273 | } | |
| 1278 | 1274 | } |
| 1279 | 1275 | // MinGW doesn't provide libssp symbols |
| 1280 | 1276 | if (target.abi.isGnu()) { |
| 1281 | try argv.append(comp.libssp_static_lib.?.full_object_path); | |
| 1277 | if (comp.libssp_static_lib) |lib| { | |
| 1278 | try argv.append(lib.full_object_path); | |
| 1279 | } | |
| 1282 | 1280 | } |
| 1283 | 1281 | // MSVC compiler_rt is missing some stuff, so we build it unconditionally but |
| 1284 | 1282 | // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. |
| 1285 | try argv.append(comp.compiler_rt_static_lib.?.full_object_path); | |
| 1283 | if (comp.compiler_rt_static_lib) |lib| { | |
| 1284 | try argv.append(lib.full_object_path); | |
| 1285 | } | |
| 1286 | 1286 | } |
| 1287 | 1287 | |
| 1288 | 1288 | try argv.ensureUnusedCapacity(self.base.options.system_libs.count()); |
src/link/Elf.zig+39-41| ... | ... | @@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 235 | 235 | const self = try createEmpty(allocator, options); |
| 236 | 236 | errdefer self.base.destroy(); |
| 237 | 237 | |
| 238 | self.llvm_object = try LlvmObject.create(allocator, options); | |
| 238 | self.llvm_object = try LlvmObject.create(allocator, sub_path, options); | |
| 239 | 239 | return self; |
| 240 | 240 | } |
| 241 | 241 | |
| ... | ... | @@ -411,7 +411,7 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 { |
| 411 | 411 | |
| 412 | 412 | /// TODO Improve this to use a table. |
| 413 | 413 | fn makeString(self: *Elf, bytes: []const u8) !u32 { |
| 414 | try self.shstrtab.ensureCapacity(self.base.allocator, self.shstrtab.items.len + bytes.len + 1); | |
| 414 | try self.shstrtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1); | |
| 415 | 415 | const result = self.shstrtab.items.len; |
| 416 | 416 | self.shstrtab.appendSliceAssumeCapacity(bytes); |
| 417 | 417 | self.shstrtab.appendAssumeCapacity(0); |
| ... | ... | @@ -420,7 +420,7 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 { |
| 420 | 420 | |
| 421 | 421 | /// TODO Improve this to use a table. |
| 422 | 422 | fn makeDebugString(self: *Elf, bytes: []const u8) !u32 { |
| 423 | try self.debug_strtab.ensureCapacity(self.base.allocator, self.debug_strtab.items.len + bytes.len + 1); | |
| 423 | try self.debug_strtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1); | |
| 424 | 424 | const result = self.debug_strtab.items.len; |
| 425 | 425 | self.debug_strtab.appendSliceAssumeCapacity(bytes); |
| 426 | 426 | self.debug_strtab.appendAssumeCapacity(0); |
| ... | ... | @@ -856,7 +856,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 856 | 856 | |
| 857 | 857 | // We have a function to compute the upper bound size, because it's needed |
| 858 | 858 | // for determining where to put the offset of the first `LinkBlock`. |
| 859 | try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes()); | |
| 859 | try di_buf.ensureTotalCapacity(self.dbgInfoNeededHeaderBytes()); | |
| 860 | 860 | |
| 861 | 861 | // initial length - length of the .debug_info contribution for this compilation unit, |
| 862 | 862 | // not including the initial length itself. |
| ... | ... | @@ -925,7 +925,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 925 | 925 | |
| 926 | 926 | // Enough for all the data without resizing. When support for more compilation units |
| 927 | 927 | // is added, the size of this section will become more variable. |
| 928 | try di_buf.ensureCapacity(100); | |
| 928 | try di_buf.ensureTotalCapacity(100); | |
| 929 | 929 | |
| 930 | 930 | // initial length - length of the .debug_aranges contribution for this compilation unit, |
| 931 | 931 | // not including the initial length itself. |
| ... | ... | @@ -1004,7 +1004,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1004 | 1004 | // The size of this header is variable, depending on the number of directories, |
| 1005 | 1005 | // files, and padding. We have a function to compute the upper bound size, however, |
| 1006 | 1006 | // because it's needed for determining where to put the offset of the first `SrcFn`. |
| 1007 | try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes()); | |
| 1007 | try di_buf.ensureTotalCapacity(self.dbgLineNeededHeaderBytes()); | |
| 1008 | 1008 | |
| 1009 | 1009 | // initial length - length of the .debug_line contribution for this compilation unit, |
| 1010 | 1010 | // not including the initial length itself. |
| ... | ... | @@ -1254,11 +1254,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1254 | 1254 | // If there is no Zig code to compile, then we should skip flushing the output file because it |
| 1255 | 1255 | // will not be part of the linker line anyway. |
| 1256 | 1256 | const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { |
| 1257 | // Both stage1 and stage2 LLVM backend put the object file in the cache directory. | |
| 1258 | if (self.base.options.use_llvm) { | |
| 1259 | // Stage2 has to call flushModule since that outputs the LLVM object file. | |
| 1260 | if (!build_options.is_stage1 or !self.base.options.use_stage1) try self.flushModule(comp); | |
| 1261 | ||
| 1257 | // stage1 puts the object file in the cache directory. | |
| 1258 | if (self.base.options.use_stage1) { | |
| 1262 | 1259 | const obj_basename = try std.zig.binNameAlloc(arena, .{ |
| 1263 | 1260 | .root_name = self.base.options.root_name, |
| 1264 | 1261 | .target = self.base.options.target, |
| ... | ... | @@ -1285,20 +1282,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1285 | 1282 | const gc_sections = self.base.options.gc_sections orelse !is_obj; |
| 1286 | 1283 | const stack_size = self.base.options.stack_size_override orelse 16777216; |
| 1287 | 1284 | const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; |
| 1288 | const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt) blk: { | |
| 1289 | // TODO: remove when stage2 can build compiler_rt.zig | |
| 1290 | if (!build_options.is_stage1 or !self.base.options.use_stage1) break :blk null; | |
| 1291 | ||
| 1292 | // In the case of build-obj we include the compiler-rt symbols directly alongside | |
| 1293 | // the symbols of the root source file, in the same compilation unit. | |
| 1294 | if (is_obj) break :blk null; | |
| 1295 | ||
| 1296 | if (is_exe_or_dyn_lib) { | |
| 1297 | break :blk comp.compiler_rt_static_lib.?.full_object_path; | |
| 1298 | } else { | |
| 1299 | break :blk comp.compiler_rt_obj.?.full_object_path; | |
| 1300 | } | |
| 1301 | } else null; | |
| 1285 | const compiler_rt_path: ?[]const u8 = blk: { | |
| 1286 | if (comp.compiler_rt_static_lib) |x| break :blk x.full_object_path; | |
| 1287 | if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; | |
| 1288 | break :blk null; | |
| 1289 | }; | |
| 1302 | 1290 | |
| 1303 | 1291 | // Here we want to determine whether we can save time by not invoking LLD when the |
| 1304 | 1292 | // output is unchanged. None of the linker options or the object files that are being |
| ... | ... | @@ -1621,14 +1609,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1621 | 1609 | } |
| 1622 | 1610 | |
| 1623 | 1611 | // libc |
| 1624 | // TODO: enable when stage2 can build c.zig | |
| 1625 | 1612 | if (is_exe_or_dyn_lib and |
| 1626 | 1613 | !self.base.options.skip_linker_dependencies and |
| 1627 | !self.base.options.link_libc and | |
| 1628 | build_options.is_stage1 and | |
| 1629 | self.base.options.use_stage1) | |
| 1614 | !self.base.options.link_libc) | |
| 1630 | 1615 | { |
| 1631 | try argv.append(comp.libc_static_lib.?.full_object_path); | |
| 1616 | if (comp.libc_static_lib) |lib| { | |
| 1617 | try argv.append(lib.full_object_path); | |
| 1618 | } | |
| 1632 | 1619 | } |
| 1633 | 1620 | |
| 1634 | 1621 | // compiler-rt |
| ... | ... | @@ -1639,7 +1626,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1639 | 1626 | // Shared libraries. |
| 1640 | 1627 | if (is_exe_or_dyn_lib) { |
| 1641 | 1628 | const system_libs = self.base.options.system_libs.keys(); |
| 1642 | try argv.ensureCapacity(argv.items.len + system_libs.len); | |
| 1629 | try argv.ensureUnusedCapacity(system_libs.len); | |
| 1643 | 1630 | for (system_libs) |link_lib| { |
| 1644 | 1631 | // By this time, we depend on these libs being dynamically linked libraries and not static libraries |
| 1645 | 1632 | // (the check for that needs to be earlier), but they could be full paths to .so files, in which |
| ... | ... | @@ -2113,8 +2100,8 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { |
| 2113 | 2100 | |
| 2114 | 2101 | if (decl.link.elf.local_sym_index != 0) return; |
| 2115 | 2102 | |
| 2116 | try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1); | |
| 2117 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); | |
| 2103 | try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1); | |
| 2104 | try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); | |
| 2118 | 2105 | |
| 2119 | 2106 | if (self.local_symbol_free_list.popOrNull()) |i| { |
| 2120 | 2107 | log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); |
| ... | ... | @@ -2147,7 +2134,9 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { |
| 2147 | 2134 | } |
| 2148 | 2135 | |
| 2149 | 2136 | pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { |
| 2150 | if (self.llvm_object) |_| return; | |
| 2137 | if (build_options.have_llvm) { | |
| 2138 | if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); | |
| 2139 | } | |
| 2151 | 2140 | |
| 2152 | 2141 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 2153 | 2142 | self.freeTextBlock(&decl.link.elf); |
| ... | ... | @@ -2316,7 +2305,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven |
| 2316 | 2305 | defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs); |
| 2317 | 2306 | |
| 2318 | 2307 | // For functions we need to add a prologue to the debug line program. |
| 2319 | try dbg_line_buffer.ensureCapacity(26); | |
| 2308 | try dbg_line_buffer.ensureTotalCapacity(26); | |
| 2320 | 2309 | |
| 2321 | 2310 | const decl = func.owner_decl; |
| 2322 | 2311 | const line_off = @intCast(u28, decl.src_line + func.lbrace_line); |
| ... | ... | @@ -2351,7 +2340,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven |
| 2351 | 2340 | |
| 2352 | 2341 | // .debug_info subprogram |
| 2353 | 2342 | const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1]; |
| 2354 | try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 25 + decl_name_with_null.len); | |
| 2343 | try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len); | |
| 2355 | 2344 | |
| 2356 | 2345 | const fn_ret_type = decl.ty.fnReturnType(); |
| 2357 | 2346 | const fn_ret_has_bits = fn_ret_type.hasCodeGenBits(); |
| ... | ... | @@ -2593,7 +2582,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo |
| 2593 | 2582 | }, |
| 2594 | 2583 | .Int => { |
| 2595 | 2584 | const info = ty.intInfo(self.base.options.target); |
| 2596 | try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12); | |
| 2585 | try dbg_info_buffer.ensureUnusedCapacity(12); | |
| 2597 | 2586 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); |
| 2598 | 2587 | // DW.AT.encoding, DW.FORM.data1 |
| 2599 | 2588 | dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { |
| ... | ... | @@ -2607,7 +2596,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo |
| 2607 | 2596 | }, |
| 2608 | 2597 | .Optional => { |
| 2609 | 2598 | if (ty.isPtrLikeOptional()) { |
| 2610 | try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12); | |
| 2599 | try dbg_info_buffer.ensureUnusedCapacity(12); | |
| 2611 | 2600 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); |
| 2612 | 2601 | // DW.AT.encoding, DW.FORM.data1 |
| 2613 | 2602 | dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); |
| ... | ... | @@ -2747,14 +2736,14 @@ pub fn updateDeclExports( |
| 2747 | 2736 | const tracy = trace(@src()); |
| 2748 | 2737 | defer tracy.end(); |
| 2749 | 2738 | |
| 2750 | try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len); | |
| 2739 | try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len); | |
| 2751 | 2740 | if (decl.link.elf.local_sym_index == 0) return; |
| 2752 | 2741 | const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index]; |
| 2753 | 2742 | |
| 2754 | 2743 | for (exports) |exp| { |
| 2755 | 2744 | if (exp.options.section) |section_name| { |
| 2756 | 2745 | if (!mem.eql(u8, section_name, ".text")) { |
| 2757 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1); | |
| 2746 | try module.failed_exports.ensureUnusedCapacity(module.gpa, 1); | |
| 2758 | 2747 | module.failed_exports.putAssumeCapacityNoClobber( |
| 2759 | 2748 | exp, |
| 2760 | 2749 | try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}), |
| ... | ... | @@ -2772,7 +2761,7 @@ pub fn updateDeclExports( |
| 2772 | 2761 | }, |
| 2773 | 2762 | .Weak => elf.STB_WEAK, |
| 2774 | 2763 | .LinkOnce => { |
| 2775 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1); | |
| 2764 | try module.failed_exports.ensureUnusedCapacity(module.gpa, 1); | |
| 2776 | 2765 | module.failed_exports.putAssumeCapacityNoClobber( |
| 2777 | 2766 | exp, |
| 2778 | 2767 | try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}), |
| ... | ... | @@ -3448,6 +3437,15 @@ const CsuObjects = struct { |
| 3448 | 3437 | .static_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), |
| 3449 | 3438 | // zig fmt: on |
| 3450 | 3439 | }, |
| 3440 | .solaris => switch (mode) { | |
| 3441 | // zig fmt: off | |
| 3442 | .dynamic_lib => result.set( null, "crti.o", null, null, "crtn.o" ), | |
| 3443 | .dynamic_exe, | |
| 3444 | .dynamic_pie => result.set( "crt1.o", "crti.o", null, null, "crtn.o" ), | |
| 3445 | .static_exe, | |
| 3446 | .static_pie => result.set( null, null, null, null, null ), | |
| 3447 | // zig fmt: on | |
| 3448 | }, | |
| 3451 | 3449 | else => {}, |
| 3452 | 3450 | } |
| 3453 | 3451 | } |
src/link/MachO.zig+270-118| ... | ... | @@ -12,7 +12,7 @@ const math = std.math; |
| 12 | 12 | const mem = std.mem; |
| 13 | 13 | const meta = std.meta; |
| 14 | 14 | |
| 15 | const aarch64 = @import("../codegen/aarch64.zig"); | |
| 15 | const aarch64 = @import("../arch/aarch64/bits.zig"); | |
| 16 | 16 | const bind = @import("MachO/bind.zig"); |
| 17 | 17 | const codegen = @import("../codegen.zig"); |
| 18 | 18 | const commands = @import("MachO/commands.zig"); |
| ... | ... | @@ -200,7 +200,7 @@ atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{}, |
| 200 | 200 | |
| 201 | 201 | /// List of atoms that are owned directly by the linker. |
| 202 | 202 | /// Currently these are only atoms that are the result of linking |
| 203 | /// object files. Atoms which take part in incremental linking are | |
| 203 | /// object files. Atoms which take part in incremental linking are | |
| 204 | 204 | /// at present owned by Module.Decl. |
| 205 | 205 | /// TODO consolidate this. |
| 206 | 206 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, |
| ... | ... | @@ -213,7 +213,7 @@ decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{}, |
| 213 | 213 | |
| 214 | 214 | /// Currently active Module.Decl. |
| 215 | 215 | /// TODO this might not be necessary if we figure out how to pass Module.Decl instance |
| 216 | /// to codegen.genSetReg() or alterntively move PIE displacement for MCValue{ .memory = x } | |
| 216 | /// to codegen.genSetReg() or alternatively move PIE displacement for MCValue{ .memory = x } | |
| 217 | 217 | /// somewhere else in the codegen. |
| 218 | 218 | active_decl: ?*Module.Decl = null, |
| 219 | 219 | |
| ... | ... | @@ -231,7 +231,7 @@ const SymbolWithLoc = struct { |
| 231 | 231 | }, |
| 232 | 232 | where_index: u32, |
| 233 | 233 | local_sym_index: u32 = 0, |
| 234 | file: u16 = 0, | |
| 234 | file: ?u16 = null, // null means Zig module | |
| 235 | 235 | }; |
| 236 | 236 | |
| 237 | 237 | pub const GotIndirectionKey = struct { |
| ... | ... | @@ -290,7 +290,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 290 | 290 | const self = try createEmpty(allocator, options); |
| 291 | 291 | errdefer self.base.destroy(); |
| 292 | 292 | |
| 293 | self.llvm_object = try LlvmObject.create(allocator, options); | |
| 293 | self.llvm_object = try LlvmObject.create(allocator, sub_path, options); | |
| 294 | 294 | return self; |
| 295 | 295 | } |
| 296 | 296 | |
| ... | ... | @@ -512,7 +512,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 512 | 512 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); |
| 513 | 513 | |
| 514 | 514 | if (self.base.options.output_mode == .Obj) { |
| 515 | // LLD's MachO driver does not support the equvialent of `-r` so we do a simple file copy | |
| 515 | // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy | |
| 516 | 516 | // here. TODO: think carefully about how we can avoid this redundant operation when doing |
| 517 | 517 | // build-obj. See also the corresponding TODO in linkAsArchive. |
| 518 | 518 | const the_object_path = blk: { |
| ... | ... | @@ -543,9 +543,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 543 | 543 | .mode = link.determineMode(self.base.options), |
| 544 | 544 | }); |
| 545 | 545 | try self.populateMissingMetadata(); |
| 546 | ||
| 547 | // TODO mimicking insertion of null symbol from incremental linker. | |
| 548 | // This will need to moved. | |
| 549 | 546 | try self.locals.append(self.base.allocator, .{ |
| 550 | 547 | .n_strx = 0, |
| 551 | 548 | .n_type = macho.N_UNDF, |
| ... | ... | @@ -557,13 +554,56 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 557 | 554 | } |
| 558 | 555 | |
| 559 | 556 | if (needs_full_relink) { |
| 557 | for (self.objects.items) |*object| { | |
| 558 | object.free(self.base.allocator, self); | |
| 559 | object.deinit(self.base.allocator); | |
| 560 | } | |
| 560 | 561 | self.objects.clearRetainingCapacity(); |
| 562 | ||
| 563 | for (self.archives.items) |*archive| { | |
| 564 | archive.deinit(self.base.allocator); | |
| 565 | } | |
| 561 | 566 | self.archives.clearRetainingCapacity(); |
| 567 | ||
| 568 | for (self.dylibs.items) |*dylib| { | |
| 569 | dylib.deinit(self.base.allocator); | |
| 570 | } | |
| 562 | 571 | self.dylibs.clearRetainingCapacity(); |
| 563 | 572 | self.dylibs_map.clearRetainingCapacity(); |
| 564 | 573 | self.referenced_dylibs.clearRetainingCapacity(); |
| 565 | 574 | |
| 566 | // TODO figure out how to clear atoms from objects, etc. | |
| 575 | { | |
| 576 | var to_remove = std.ArrayList(u32).init(self.base.allocator); | |
| 577 | defer to_remove.deinit(); | |
| 578 | var it = self.symbol_resolver.iterator(); | |
| 579 | while (it.next()) |entry| { | |
| 580 | const key = entry.key_ptr.*; | |
| 581 | const value = entry.value_ptr.*; | |
| 582 | if (value.file != null) { | |
| 583 | try to_remove.append(key); | |
| 584 | } | |
| 585 | } | |
| 586 | ||
| 587 | for (to_remove.items) |key| { | |
| 588 | if (self.symbol_resolver.fetchRemove(key)) |entry| { | |
| 589 | const resolv = entry.value; | |
| 590 | switch (resolv.where) { | |
| 591 | .global => { | |
| 592 | self.globals_free_list.append(self.base.allocator, resolv.where_index) catch {}; | |
| 593 | const sym = &self.globals.items[resolv.where_index]; | |
| 594 | sym.n_strx = 0; | |
| 595 | sym.n_type = 0; | |
| 596 | sym.n_value = 0; | |
| 597 | }, | |
| 598 | .undef => { | |
| 599 | const sym = &self.undefs.items[resolv.where_index]; | |
| 600 | sym.n_strx = 0; | |
| 601 | sym.n_desc = 0; | |
| 602 | }, | |
| 603 | } | |
| 604 | } | |
| 605 | } | |
| 606 | } | |
| 567 | 607 | |
| 568 | 608 | // Positional arguments to the linker such as object files and static archives. |
| 569 | 609 | var positionals = std.ArrayList([]const u8).init(arena); |
| ... | ... | @@ -802,13 +842,35 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { |
| 802 | 842 | try self.createDsoHandleAtom(); |
| 803 | 843 | try self.addCodeSignatureLC(); |
| 804 | 844 | |
| 845 | // log.warn("locals:", .{}); | |
| 846 | // for (self.locals.items) |sym, id| { | |
| 847 | // log.warn(" {d}: {s}: {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 848 | // } | |
| 849 | // log.warn("globals:", .{}); | |
| 850 | // for (self.globals.items) |sym, id| { | |
| 851 | // log.warn(" {d}: {s}: {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 852 | // } | |
| 853 | // log.warn("undefs:", .{}); | |
| 854 | // for (self.undefs.items) |sym, id| { | |
| 855 | // log.warn(" {d}: {s}: {}", .{ id, self.getString(sym.n_strx), sym }); | |
| 856 | // } | |
| 857 | // { | |
| 858 | // log.warn("resolver:", .{}); | |
| 859 | // var it = self.symbol_resolver.iterator(); | |
| 860 | // while (it.next()) |entry| { | |
| 861 | // log.warn(" {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* }); | |
| 862 | // } | |
| 863 | // } | |
| 864 | ||
| 805 | 865 | for (self.unresolved.keys()) |index| { |
| 806 | 866 | const sym = self.undefs.items[index]; |
| 807 | 867 | const sym_name = self.getString(sym.n_strx); |
| 808 | 868 | const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable; |
| 809 | 869 | |
| 810 | 870 | log.err("undefined reference to symbol '{s}'", .{sym_name}); |
| 811 | log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name}); | |
| 871 | if (resolv.file) |file| { | |
| 872 | log.err(" first referenced in '{s}'", .{self.objects.items[file].name}); | |
| 873 | } | |
| 812 | 874 | } |
| 813 | 875 | if (self.unresolved.count() > 0) { |
| 814 | 876 | return error.UndefinedSymbolReference; |
| ... | ... | @@ -1807,7 +1869,7 @@ fn writeAtoms(self: *MachO) !void { |
| 1807 | 1869 | pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom { |
| 1808 | 1870 | const local_sym_index = @intCast(u32, self.locals.items.len); |
| 1809 | 1871 | try self.locals.append(self.base.allocator, .{ |
| 1810 | .n_strx = try self.makeString("l_zld_got_entry"), | |
| 1872 | .n_strx = 0, | |
| 1811 | 1873 | .n_type = macho.N_SECT, |
| 1812 | 1874 | .n_sect = 0, |
| 1813 | 1875 | .n_desc = 0, |
| ... | ... | @@ -1845,7 +1907,7 @@ fn createDyldPrivateAtom(self: *MachO) !void { |
| 1845 | 1907 | const local_sym_index = @intCast(u32, self.locals.items.len); |
| 1846 | 1908 | const sym = try self.locals.addOne(self.base.allocator); |
| 1847 | 1909 | sym.* = .{ |
| 1848 | .n_strx = try self.makeString("l_zld_dyld_private"), | |
| 1910 | .n_strx = 0, | |
| 1849 | 1911 | .n_type = macho.N_SECT, |
| 1850 | 1912 | .n_sect = 0, |
| 1851 | 1913 | .n_desc = 0, |
| ... | ... | @@ -1879,7 +1941,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1879 | 1941 | const local_sym_index = @intCast(u32, self.locals.items.len); |
| 1880 | 1942 | const sym = try self.locals.addOne(self.base.allocator); |
| 1881 | 1943 | sym.* = .{ |
| 1882 | .n_strx = try self.makeString("l_zld_stub_preamble"), | |
| 1944 | .n_strx = 0, | |
| 1883 | 1945 | .n_type = macho.N_SECT, |
| 1884 | 1946 | .n_sect = 0, |
| 1885 | 1947 | .n_desc = 0, |
| ... | ... | @@ -2021,7 +2083,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 2021 | 2083 | }; |
| 2022 | 2084 | const local_sym_index = @intCast(u32, self.locals.items.len); |
| 2023 | 2085 | try self.locals.append(self.base.allocator, .{ |
| 2024 | .n_strx = try self.makeString("l_zld_stub_in_stub_helper"), | |
| 2086 | .n_strx = 0, | |
| 2025 | 2087 | .n_type = macho.N_SECT, |
| 2026 | 2088 | .n_sect = 0, |
| 2027 | 2089 | .n_desc = 0, |
| ... | ... | @@ -2076,7 +2138,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 2076 | 2138 | pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym_index: u32) !*Atom { |
| 2077 | 2139 | const local_sym_index = @intCast(u32, self.locals.items.len); |
| 2078 | 2140 | try self.locals.append(self.base.allocator, .{ |
| 2079 | .n_strx = try self.makeString("l_zld_lazy_ptr"), | |
| 2141 | .n_strx = 0, | |
| 2080 | 2142 | .n_type = macho.N_SECT, |
| 2081 | 2143 | .n_sect = 0, |
| 2082 | 2144 | .n_desc = 0, |
| ... | ... | @@ -2117,7 +2179,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 2117 | 2179 | }; |
| 2118 | 2180 | const local_sym_index = @intCast(u32, self.locals.items.len); |
| 2119 | 2181 | try self.locals.append(self.base.allocator, .{ |
| 2120 | .n_strx = try self.makeString("l_zld_stub"), | |
| 2182 | .n_strx = 0, | |
| 2121 | 2183 | .n_type = macho.N_SECT, |
| 2122 | 2184 | .n_sect = 0, |
| 2123 | 2185 | .n_desc = 0, |
| ... | ... | @@ -2183,7 +2245,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 2183 | 2245 | fn createTentativeDefAtoms(self: *MachO) !void { |
| 2184 | 2246 | if (self.tentatives.count() == 0) return; |
| 2185 | 2247 | // Convert any tentative definition into a regular symbol and allocate |
| 2186 | // text blocks for each tentative defintion. | |
| 2248 | // text blocks for each tentative definition. | |
| 2187 | 2249 | while (self.tentatives.popOrNull()) |entry| { |
| 2188 | 2250 | const match = MatchingSection{ |
| 2189 | 2251 | .seg = self.data_segment_cmd_index.?, |
| ... | ... | @@ -2349,7 +2411,9 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void { |
| 2349 | 2411 | !(symbolIsWeakDef(global.*) or symbolIsPext(global.*))) |
| 2350 | 2412 | { |
| 2351 | 2413 | log.err("symbol '{s}' defined multiple times", .{sym_name}); |
| 2352 | log.err(" first definition in '{s}'", .{self.objects.items[resolv.file].name}); | |
| 2414 | if (resolv.file) |file| { | |
| 2415 | log.err(" first definition in '{s}'", .{self.objects.items[file].name}); | |
| 2416 | } | |
| 2353 | 2417 | log.err(" next definition in '{s}'", .{object.name}); |
| 2354 | 2418 | return error.MultipleSymbolDefinitions; |
| 2355 | 2419 | } else if (symbolIsWeakDef(sym) or symbolIsPext(sym)) continue; // Current symbol is weak, so skip it. |
| ... | ... | @@ -2632,10 +2696,10 @@ fn parseObjectsIntoAtoms(self: *MachO) !void { |
| 2632 | 2696 | const tracy = trace(@src()); |
| 2633 | 2697 | defer tracy.end(); |
| 2634 | 2698 | |
| 2635 | var parsed_atoms = Object.ParsedAtoms.init(self.base.allocator); | |
| 2699 | var parsed_atoms = std.AutoArrayHashMap(MatchingSection, *Atom).init(self.base.allocator); | |
| 2636 | 2700 | defer parsed_atoms.deinit(); |
| 2637 | 2701 | |
| 2638 | var first_atoms = Object.ParsedAtoms.init(self.base.allocator); | |
| 2702 | var first_atoms = std.AutoArrayHashMap(MatchingSection, *Atom).init(self.base.allocator); | |
| 2639 | 2703 | defer first_atoms.deinit(); |
| 2640 | 2704 | |
| 2641 | 2705 | var section_metadata = std.AutoHashMap(MatchingSection, struct { |
| ... | ... | @@ -2644,13 +2708,12 @@ fn parseObjectsIntoAtoms(self: *MachO) !void { |
| 2644 | 2708 | }).init(self.base.allocator); |
| 2645 | 2709 | defer section_metadata.deinit(); |
| 2646 | 2710 | |
| 2647 | for (self.objects.items) |*object, object_id| { | |
| 2711 | for (self.objects.items) |*object| { | |
| 2648 | 2712 | if (object.analyzed) continue; |
| 2649 | 2713 | |
| 2650 | var atoms_in_objects = try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_id), self); | |
| 2651 | defer atoms_in_objects.deinit(); | |
| 2714 | try object.parseIntoAtoms(self.base.allocator, self); | |
| 2652 | 2715 | |
| 2653 | var it = atoms_in_objects.iterator(); | |
| 2716 | var it = object.end_atoms.iterator(); | |
| 2654 | 2717 | while (it.next()) |entry| { |
| 2655 | 2718 | const match = entry.key_ptr.*; |
| 2656 | 2719 | const last_atom = entry.value_ptr.*; |
| ... | ... | @@ -3292,8 +3355,6 @@ pub fn updateDeclExports( |
| 3292 | 3355 | decl: *Module.Decl, |
| 3293 | 3356 | exports: []const *Module.Export, |
| 3294 | 3357 | ) !void { |
| 3295 | // TODO If we are exporting with global linkage, check for already defined globals and flag | |
| 3296 | // symbol duplicate/collision! | |
| 3297 | 3358 | if (build_options.skip_non_native and builtin.object_format != .macho) { |
| 3298 | 3359 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 3299 | 3360 | } |
| ... | ... | @@ -3303,7 +3364,7 @@ pub fn updateDeclExports( |
| 3303 | 3364 | const tracy = trace(@src()); |
| 3304 | 3365 | defer tracy.end(); |
| 3305 | 3366 | |
| 3306 | try self.globals.ensureCapacity(self.base.allocator, self.globals.items.len + exports.len); | |
| 3367 | try self.globals.ensureUnusedCapacity(self.base.allocator, exports.len); | |
| 3307 | 3368 | if (decl.link.macho.local_sym_index == 0) return; |
| 3308 | 3369 | const decl_sym = &self.locals.items[decl.link.macho.local_sym_index]; |
| 3309 | 3370 | |
| ... | ... | @@ -3313,15 +3374,76 @@ pub fn updateDeclExports( |
| 3313 | 3374 | |
| 3314 | 3375 | if (exp.options.section) |section_name| { |
| 3315 | 3376 | if (!mem.eql(u8, section_name, "__text")) { |
| 3316 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1); | |
| 3317 | module.failed_exports.putAssumeCapacityNoClobber( | |
| 3377 | try module.failed_exports.putNoClobber( | |
| 3378 | module.gpa, | |
| 3318 | 3379 | exp, |
| 3319 | try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}), | |
| 3380 | try Module.ErrorMsg.create( | |
| 3381 | self.base.allocator, | |
| 3382 | decl.srcLoc(), | |
| 3383 | "Unimplemented: ExportOptions.section", | |
| 3384 | .{}, | |
| 3385 | ), | |
| 3320 | 3386 | ); |
| 3321 | 3387 | continue; |
| 3322 | 3388 | } |
| 3323 | 3389 | } |
| 3324 | 3390 | |
| 3391 | if (exp.options.linkage == .LinkOnce) { | |
| 3392 | try module.failed_exports.putNoClobber( | |
| 3393 | module.gpa, | |
| 3394 | exp, | |
| 3395 | try Module.ErrorMsg.create( | |
| 3396 | self.base.allocator, | |
| 3397 | decl.srcLoc(), | |
| 3398 | "Unimplemented: GlobalLinkage.LinkOnce", | |
| 3399 | .{}, | |
| 3400 | ), | |
| 3401 | ); | |
| 3402 | continue; | |
| 3403 | } | |
| 3404 | ||
| 3405 | const is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak; | |
| 3406 | const n_strx = try self.makeString(exp_name); | |
| 3407 | if (self.symbol_resolver.getPtr(n_strx)) |resolv| { | |
| 3408 | switch (resolv.where) { | |
| 3409 | .global => { | |
| 3410 | if (resolv.local_sym_index == decl.link.macho.local_sym_index) continue; | |
| 3411 | ||
| 3412 | const sym = &self.globals.items[resolv.where_index]; | |
| 3413 | ||
| 3414 | if (symbolIsTentative(sym.*)) { | |
| 3415 | _ = self.tentatives.fetchSwapRemove(resolv.where_index); | |
| 3416 | } else if (!is_weak and !(symbolIsWeakDef(sym.*) or symbolIsPext(sym.*))) { | |
| 3417 | _ = try module.failed_exports.put( | |
| 3418 | module.gpa, | |
| 3419 | exp, | |
| 3420 | try Module.ErrorMsg.create( | |
| 3421 | self.base.allocator, | |
| 3422 | decl.srcLoc(), | |
| 3423 | \\LinkError: symbol '{s}' defined multiple times | |
| 3424 | \\ first definition in '{s}' | |
| 3425 | , | |
| 3426 | .{ exp_name, self.objects.items[resolv.file.?].name }, | |
| 3427 | ), | |
| 3428 | ); | |
| 3429 | continue; | |
| 3430 | } else if (is_weak) continue; // Current symbol is weak, so skip it. | |
| 3431 | ||
| 3432 | // Otherwise, update the resolver and the global symbol. | |
| 3433 | sym.n_type = macho.N_SECT | macho.N_EXT; | |
| 3434 | resolv.local_sym_index = decl.link.macho.local_sym_index; | |
| 3435 | resolv.file = null; | |
| 3436 | exp.link.macho.sym_index = resolv.where_index; | |
| 3437 | ||
| 3438 | continue; | |
| 3439 | }, | |
| 3440 | .undef => { | |
| 3441 | _ = self.unresolved.fetchSwapRemove(resolv.where_index); | |
| 3442 | _ = self.symbol_resolver.remove(n_strx); | |
| 3443 | }, | |
| 3444 | } | |
| 3445 | } | |
| 3446 | ||
| 3325 | 3447 | var n_type: u8 = macho.N_SECT | macho.N_EXT; |
| 3326 | 3448 | var n_desc: u16 = 0; |
| 3327 | 3449 | |
| ... | ... | @@ -3339,14 +3461,7 @@ pub fn updateDeclExports( |
| 3339 | 3461 | // Symbol's n_type is like for a symbol with strong linkage. |
| 3340 | 3462 | n_desc |= macho.N_WEAK_DEF; |
| 3341 | 3463 | }, |
| 3342 | .LinkOnce => { | |
| 3343 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1); | |
| 3344 | module.failed_exports.putAssumeCapacityNoClobber( | |
| 3345 | exp, | |
| 3346 | try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}), | |
| 3347 | ); | |
| 3348 | continue; | |
| 3349 | }, | |
| 3464 | else => unreachable, | |
| 3350 | 3465 | } |
| 3351 | 3466 | |
| 3352 | 3467 | const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: { |
| ... | ... | @@ -3356,8 +3471,6 @@ pub fn updateDeclExports( |
| 3356 | 3471 | }; |
| 3357 | 3472 | break :blk i; |
| 3358 | 3473 | }; |
| 3359 | ||
| 3360 | const n_strx = try self.makeString(exp_name); | |
| 3361 | 3474 | const sym = &self.globals.items[global_sym_index]; |
| 3362 | 3475 | sym.* = .{ |
| 3363 | 3476 | .n_strx = try self.makeString(exp_name), |
| ... | ... | @@ -3368,12 +3481,11 @@ pub fn updateDeclExports( |
| 3368 | 3481 | }; |
| 3369 | 3482 | exp.link.macho.sym_index = global_sym_index; |
| 3370 | 3483 | |
| 3371 | const resolv = try self.symbol_resolver.getOrPut(self.base.allocator, n_strx); | |
| 3372 | resolv.value_ptr.* = .{ | |
| 3484 | try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{ | |
| 3373 | 3485 | .where = .global, |
| 3374 | 3486 | .where_index = global_sym_index, |
| 3375 | 3487 | .local_sym_index = decl.link.macho.local_sym_index, |
| 3376 | }; | |
| 3488 | }); | |
| 3377 | 3489 | } |
| 3378 | 3490 | } |
| 3379 | 3491 | |
| ... | ... | @@ -3381,11 +3493,17 @@ pub fn deleteExport(self: *MachO, exp: Export) void { |
| 3381 | 3493 | const sym_index = exp.sym_index orelse return; |
| 3382 | 3494 | self.globals_free_list.append(self.base.allocator, sym_index) catch {}; |
| 3383 | 3495 | const global = &self.globals.items[sym_index]; |
| 3384 | global.n_type = 0; | |
| 3496 | log.debug("deleting export '{s}': {}", .{ self.getString(global.n_strx), global }); | |
| 3385 | 3497 | assert(self.symbol_resolver.remove(global.n_strx)); |
| 3498 | global.n_type = 0; | |
| 3499 | global.n_strx = 0; | |
| 3500 | global.n_value = 0; | |
| 3386 | 3501 | } |
| 3387 | 3502 | |
| 3388 | 3503 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| 3504 | if (build_options.have_llvm) { | |
| 3505 | if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); | |
| 3506 | } | |
| 3389 | 3507 | log.debug("freeDecl {*}", .{decl}); |
| 3390 | 3508 | _ = self.decls.swapRemove(decl); |
| 3391 | 3509 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| ... | ... | @@ -3970,6 +4088,70 @@ fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 |
| 3970 | 4088 | return mem.alignForwardGeneric(u64, final_off, alignment); |
| 3971 | 4089 | } |
| 3972 | 4090 | |
| 4091 | fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void { | |
| 4092 | const seg = &self.load_commands.items[seg_id].Segment; | |
| 4093 | const new_seg_size = mem.alignForwardGeneric(u64, new_size, self.page_size); | |
| 4094 | assert(new_seg_size > seg.inner.filesize); | |
| 4095 | const offset_amt = new_seg_size - seg.inner.filesize; | |
| 4096 | log.debug("growing segment {s} from 0x{x} to 0x{x}", .{ seg.inner.segname, seg.inner.filesize, new_seg_size }); | |
| 4097 | seg.inner.filesize = new_seg_size; | |
| 4098 | seg.inner.vmsize = new_seg_size; | |
| 4099 | ||
| 4100 | log.debug(" (new segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ | |
| 4101 | seg.inner.fileoff, | |
| 4102 | seg.inner.fileoff + seg.inner.filesize, | |
| 4103 | seg.inner.vmaddr, | |
| 4104 | seg.inner.vmaddr + seg.inner.vmsize, | |
| 4105 | }); | |
| 4106 | ||
| 4107 | // TODO We should probably nop the expanded by distance, or put 0s. | |
| 4108 | ||
| 4109 | // TODO copyRangeAll doesn't automatically extend the file on macOS. | |
| 4110 | const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 4111 | const new_filesize = offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize; | |
| 4112 | try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1); | |
| 4113 | ||
| 4114 | var next: usize = seg_id + 1; | |
| 4115 | while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) { | |
| 4116 | const next_seg = &self.load_commands.items[next].Segment; | |
| 4117 | _ = try self.base.file.?.copyRangeAll( | |
| 4118 | next_seg.inner.fileoff, | |
| 4119 | self.base.file.?, | |
| 4120 | next_seg.inner.fileoff + offset_amt, | |
| 4121 | next_seg.inner.filesize, | |
| 4122 | ); | |
| 4123 | next_seg.inner.fileoff += offset_amt; | |
| 4124 | next_seg.inner.vmaddr += offset_amt; | |
| 4125 | ||
| 4126 | log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ | |
| 4127 | next_seg.inner.segname, | |
| 4128 | next_seg.inner.fileoff, | |
| 4129 | next_seg.inner.fileoff + next_seg.inner.filesize, | |
| 4130 | next_seg.inner.vmaddr, | |
| 4131 | next_seg.inner.vmaddr + next_seg.inner.vmsize, | |
| 4132 | }); | |
| 4133 | ||
| 4134 | for (next_seg.sections.items) |*moved_sect, moved_sect_id| { | |
| 4135 | moved_sect.offset += @intCast(u32, offset_amt); | |
| 4136 | moved_sect.addr += offset_amt; | |
| 4137 | ||
| 4138 | log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ | |
| 4139 | commands.segmentName(moved_sect.*), | |
| 4140 | commands.sectionName(moved_sect.*), | |
| 4141 | moved_sect.offset, | |
| 4142 | moved_sect.offset + moved_sect.size, | |
| 4143 | moved_sect.addr, | |
| 4144 | moved_sect.addr + moved_sect.size, | |
| 4145 | }); | |
| 4146 | ||
| 4147 | try self.allocateLocalSymbols(.{ | |
| 4148 | .seg = @intCast(u16, next), | |
| 4149 | .sect = @intCast(u16, moved_sect_id), | |
| 4150 | }, @intCast(i64, offset_amt)); | |
| 4151 | } | |
| 4152 | } | |
| 4153 | } | |
| 4154 | ||
| 3973 | 4155 | fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void { |
| 3974 | 4156 | const tracy = trace(@src()); |
| 3975 | 4157 | defer tracy.end(); |
| ... | ... | @@ -3983,7 +4165,14 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void { |
| 3983 | 4165 | const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment); |
| 3984 | 4166 | |
| 3985 | 4167 | if (needed_size > max_size) blk: { |
| 3986 | log.debug(" (need to grow!)", .{}); | |
| 4168 | log.debug(" (need to grow! needed 0x{x}, max 0x{x})", .{ needed_size, max_size }); | |
| 4169 | ||
| 4170 | if (match.sect == seg.sections.items.len - 1) { | |
| 4171 | // Last section, just grow segments | |
| 4172 | try self.growSegment(match.seg, seg.inner.filesize + needed_size - max_size); | |
| 4173 | break :blk; | |
| 4174 | } | |
| 4175 | ||
| 3987 | 4176 | // Need to move all sections below in file and address spaces. |
| 3988 | 4177 | const offset_amt = offset: { |
| 3989 | 4178 | const max_alignment = try self.getSectionMaxAlignment(match.seg, match.sect + 1); |
| ... | ... | @@ -3999,70 +4188,10 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void { |
| 3999 | 4188 | |
| 4000 | 4189 | if (last_sect_off + offset_amt > seg_off) { |
| 4001 | 4190 | // Need to grow segment first. |
| 4002 | log.debug(" (need to grow segment first)", .{}); | |
| 4003 | 4191 | const spill_size = (last_sect_off + offset_amt) - seg_off; |
| 4004 | const seg_offset_amt = mem.alignForwardGeneric(u64, spill_size, self.page_size); | |
| 4005 | seg.inner.filesize += seg_offset_amt; | |
| 4006 | seg.inner.vmsize += seg_offset_amt; | |
| 4007 | ||
| 4008 | log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ | |
| 4009 | seg.inner.segname, | |
| 4010 | seg.inner.fileoff, | |
| 4011 | seg.inner.fileoff + seg.inner.filesize, | |
| 4012 | seg.inner.vmaddr, | |
| 4013 | seg.inner.vmaddr + seg.inner.vmsize, | |
| 4014 | }); | |
| 4015 | ||
| 4016 | // TODO We should probably nop the expanded by distance, or put 0s. | |
| 4017 | ||
| 4018 | // TODO copyRangeAll doesn't automatically extend the file on macOS. | |
| 4019 | const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | |
| 4020 | const new_filesize = seg_offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize; | |
| 4021 | try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1); | |
| 4022 | ||
| 4023 | var next: usize = match.seg + 1; | |
| 4024 | while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) { | |
| 4025 | const next_seg = &self.load_commands.items[next].Segment; | |
| 4026 | _ = try self.base.file.?.copyRangeAll( | |
| 4027 | next_seg.inner.fileoff, | |
| 4028 | self.base.file.?, | |
| 4029 | next_seg.inner.fileoff + seg_offset_amt, | |
| 4030 | next_seg.inner.filesize, | |
| 4031 | ); | |
| 4032 | next_seg.inner.fileoff += seg_offset_amt; | |
| 4033 | next_seg.inner.vmaddr += seg_offset_amt; | |
| 4034 | ||
| 4035 | log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ | |
| 4036 | next_seg.inner.segname, | |
| 4037 | next_seg.inner.fileoff, | |
| 4038 | next_seg.inner.fileoff + next_seg.inner.filesize, | |
| 4039 | next_seg.inner.vmaddr, | |
| 4040 | next_seg.inner.vmaddr + next_seg.inner.vmsize, | |
| 4041 | }); | |
| 4042 | ||
| 4043 | for (next_seg.sections.items) |*moved_sect, moved_sect_id| { | |
| 4044 | moved_sect.offset += @intCast(u32, seg_offset_amt); | |
| 4045 | moved_sect.addr += seg_offset_amt; | |
| 4046 | ||
| 4047 | log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{ | |
| 4048 | commands.segmentName(moved_sect.*), | |
| 4049 | commands.sectionName(moved_sect.*), | |
| 4050 | moved_sect.offset, | |
| 4051 | moved_sect.offset + moved_sect.size, | |
| 4052 | moved_sect.addr, | |
| 4053 | moved_sect.addr + moved_sect.size, | |
| 4054 | }); | |
| 4055 | ||
| 4056 | try self.allocateLocalSymbols(.{ | |
| 4057 | .seg = @intCast(u16, next), | |
| 4058 | .sect = @intCast(u16, moved_sect_id), | |
| 4059 | }, @intCast(i64, seg_offset_amt)); | |
| 4060 | } | |
| 4061 | } | |
| 4192 | try self.growSegment(match.seg, seg.inner.filesize + spill_size); | |
| 4062 | 4193 | } |
| 4063 | 4194 | |
| 4064 | if (match.sect + 1 >= seg.sections.items.len) break :blk; | |
| 4065 | ||
| 4066 | 4195 | // We have enough space to expand within the segment, so move all sections by |
| 4067 | 4196 | // the required amount and update their header offsets. |
| 4068 | 4197 | const next_sect = seg.sections.items[match.sect + 1]; |
| ... | ... | @@ -4228,20 +4357,34 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m |
| 4228 | 4357 | return vaddr; |
| 4229 | 4358 | } |
| 4230 | 4359 | |
| 4231 | pub fn addExternFn(self: *MachO, name: []const u8) !u32 { | |
| 4360 | const AddExternFnRes = struct { | |
| 4361 | where: enum { | |
| 4362 | local, | |
| 4363 | undef, | |
| 4364 | }, | |
| 4365 | where_index: u32, | |
| 4366 | }; | |
| 4367 | ||
| 4368 | pub fn addExternFn(self: *MachO, name: []const u8) !AddExternFnRes { | |
| 4232 | 4369 | const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name}); |
| 4233 | 4370 | defer self.base.allocator.free(sym_name); |
| 4371 | const n_strx = try self.makeString(sym_name); | |
| 4234 | 4372 | |
| 4235 | if (self.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{ | |
| 4236 | .bytes = &self.strtab, | |
| 4237 | })) |n_strx| { | |
| 4238 | const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; | |
| 4239 | return resolv.where_index; | |
| 4373 | if (self.symbol_resolver.get(n_strx)) |resolv| { | |
| 4374 | return switch (resolv.where) { | |
| 4375 | .global => AddExternFnRes{ | |
| 4376 | .where = .local, | |
| 4377 | .where_index = resolv.local_sym_index, | |
| 4378 | }, | |
| 4379 | .undef => AddExternFnRes{ | |
| 4380 | .where = .undef, | |
| 4381 | .where_index = resolv.where_index, | |
| 4382 | }, | |
| 4383 | }; | |
| 4240 | 4384 | } |
| 4241 | 4385 | |
| 4242 | 4386 | log.debug("adding new extern function '{s}'", .{sym_name}); |
| 4243 | 4387 | const sym_index = @intCast(u32, self.undefs.items.len); |
| 4244 | const n_strx = try self.makeString(sym_name); | |
| 4245 | 4388 | try self.undefs.append(self.base.allocator, .{ |
| 4246 | 4389 | .n_strx = n_strx, |
| 4247 | 4390 | .n_type = macho.N_UNDF, |
| ... | ... | @@ -4255,7 +4398,10 @@ pub fn addExternFn(self: *MachO, name: []const u8) !u32 { |
| 4255 | 4398 | }); |
| 4256 | 4399 | try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub); |
| 4257 | 4400 | |
| 4258 | return sym_index; | |
| 4401 | return AddExternFnRes{ | |
| 4402 | .where = .undef, | |
| 4403 | .where_index = sym_index, | |
| 4404 | }; | |
| 4259 | 4405 | } |
| 4260 | 4406 | |
| 4261 | 4407 | const NextSegmentAddressAndOffset = struct { |
| ... | ... | @@ -4386,6 +4532,7 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 4386 | 4532 | const base_address = text_segment.inner.vmaddr; |
| 4387 | 4533 | |
| 4388 | 4534 | for (self.globals.items) |sym| { |
| 4535 | if (sym.n_type == 0) continue; | |
| 4389 | 4536 | const sym_name = self.getString(sym.n_strx); |
| 4390 | 4537 | log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value }); |
| 4391 | 4538 | |
| ... | ... | @@ -4462,7 +4609,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { |
| 4462 | 4609 | |
| 4463 | 4610 | // Because we insert lazy binding opcodes in reverse order (from last to the first atom), |
| 4464 | 4611 | // we need reverse the order of atom traversal here as well. |
| 4465 | // TODO figure out a less error prone mechanims for this! | |
| 4612 | // TODO figure out a less error prone mechanisms for this! | |
| 4466 | 4613 | var atom = last_atom; |
| 4467 | 4614 | while (atom.prev) |prev| { |
| 4468 | 4615 | atom = prev; |
| ... | ... | @@ -4608,7 +4755,12 @@ fn writeSymbolTable(self: *MachO) !void { |
| 4608 | 4755 | |
| 4609 | 4756 | var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator); |
| 4610 | 4757 | defer locals.deinit(); |
| 4611 | try locals.appendSlice(self.locals.items); | |
| 4758 | ||
| 4759 | for (self.locals.items) |sym| { | |
| 4760 | if (sym.n_strx == 0) continue; | |
| 4761 | if (symbolIsTemp(sym, self.getString(sym.n_strx))) continue; | |
| 4762 | try locals.append(sym); | |
| 4763 | } | |
| 4612 | 4764 | |
| 4613 | 4765 | if (self.has_stabs) { |
| 4614 | 4766 | for (self.objects.items) |object| { |
| ... | ... | @@ -4638,7 +4790,7 @@ fn writeSymbolTable(self: *MachO) !void { |
| 4638 | 4790 | .n_value = object.mtime orelse 0, |
| 4639 | 4791 | }); |
| 4640 | 4792 | |
| 4641 | for (object.atoms.items) |atom| { | |
| 4793 | for (object.contained_atoms.items) |atom| { | |
| 4642 | 4794 | if (atom.stab) |stab| { |
| 4643 | 4795 | const nlists = try stab.asNlists(atom.local_sym_index, self); |
| 4644 | 4796 | defer self.base.allocator.free(nlists); |
src/link/MachO/Atom.zig+61-54| ... | ... | @@ -2,7 +2,7 @@ const Atom = @This(); |
| 2 | 2 | |
| 3 | 3 | const std = @import("std"); |
| 4 | 4 | const build_options = @import("build_options"); |
| 5 | const aarch64 = @import("../../codegen/aarch64.zig"); | |
| 5 | const aarch64 = @import("../../arch/aarch64/bits.zig"); | |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | 7 | const commands = @import("commands.zig"); |
| 8 | 8 | const log = std.log.scoped(.text_block); |
| ... | ... | @@ -41,7 +41,7 @@ code: std.ArrayListUnmanaged(u8) = .{}, |
| 41 | 41 | size: u64, |
| 42 | 42 | |
| 43 | 43 | /// Alignment of this atom as a power of 2. |
| 44 | /// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned. | |
| 44 | /// For instance, alignment of 0 should be read as 2^0 = 1 byte aligned. | |
| 45 | 45 | alignment: u32, |
| 46 | 46 | |
| 47 | 47 | /// List of relocations belonging to this atom. |
| ... | ... | @@ -645,7 +645,6 @@ const RelocContext = struct { |
| 645 | 645 | allocator: *Allocator, |
| 646 | 646 | object: *Object, |
| 647 | 647 | macho_file: *MachO, |
| 648 | parsed_atoms: *Object.ParsedAtoms, | |
| 649 | 648 | }; |
| 650 | 649 | |
| 651 | 650 | fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Relocation { |
| ... | ... | @@ -664,15 +663,8 @@ fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Reloc |
| 664 | 663 | const sect = seg.sections.items[sect_id]; |
| 665 | 664 | const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable; |
| 666 | 665 | const local_sym_index = @intCast(u32, context.macho_file.locals.items.len); |
| 667 | const sym_name = try std.fmt.allocPrint(context.allocator, "l_{s}_{s}_{s}", .{ | |
| 668 | context.object.name, | |
| 669 | commands.segmentName(sect), | |
| 670 | commands.sectionName(sect), | |
| 671 | }); | |
| 672 | defer context.allocator.free(sym_name); | |
| 673 | ||
| 674 | 666 | try context.macho_file.locals.append(context.allocator, .{ |
| 675 | .n_strx = try context.macho_file.makeString(sym_name), | |
| 667 | .n_strx = 0, | |
| 676 | 668 | .n_type = macho.N_SECT, |
| 677 | 669 | .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1), |
| 678 | 670 | .n_desc = 0, |
| ... | ... | @@ -877,12 +869,16 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC |
| 877 | 869 | .sect = context.macho_file.got_section_index.?, |
| 878 | 870 | }; |
| 879 | 871 | |
| 880 | if (context.parsed_atoms.getPtr(match)) |last| { | |
| 872 | if (!context.object.start_atoms.contains(match)) { | |
| 873 | try context.object.start_atoms.putNoClobber(context.allocator, match, atom); | |
| 874 | } | |
| 875 | ||
| 876 | if (context.object.end_atoms.getPtr(match)) |last| { | |
| 881 | 877 | last.*.next = atom; |
| 882 | 878 | atom.prev = last.*; |
| 883 | 879 | last.* = atom; |
| 884 | 880 | } else { |
| 885 | try context.parsed_atoms.putNoClobber(match, atom); | |
| 881 | try context.object.end_atoms.putNoClobber(context.allocator, match, atom); | |
| 886 | 882 | } |
| 887 | 883 | } else if (parsed_rel.payload == .unsigned) { |
| 888 | 884 | switch (parsed_rel.where) { |
| ... | ... | @@ -939,52 +935,63 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC |
| 939 | 935 | if (parsed_rel.where != .undef) break :blk; |
| 940 | 936 | if (context.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk; |
| 941 | 937 | |
| 942 | const stub_helper_atom = try context.macho_file.createStubHelperAtom(); | |
| 943 | const laptr_atom = try context.macho_file.createLazyPointerAtom( | |
| 944 | stub_helper_atom.local_sym_index, | |
| 945 | parsed_rel.where_index, | |
| 946 | ); | |
| 947 | const stub_atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index); | |
| 948 | try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, stub_atom); | |
| 949 | 938 | // TODO clean this up! |
| 950 | if (context.parsed_atoms.getPtr(.{ | |
| 951 | .seg = context.macho_file.text_segment_cmd_index.?, | |
| 952 | .sect = context.macho_file.stub_helper_section_index.?, | |
| 953 | })) |last| { | |
| 954 | last.*.next = stub_helper_atom; | |
| 955 | stub_helper_atom.prev = last.*; | |
| 956 | last.* = stub_helper_atom; | |
| 957 | } else { | |
| 958 | try context.parsed_atoms.putNoClobber(.{ | |
| 939 | const stub_helper_atom = atom: { | |
| 940 | const atom = try context.macho_file.createStubHelperAtom(); | |
| 941 | const match = MachO.MatchingSection{ | |
| 959 | 942 | .seg = context.macho_file.text_segment_cmd_index.?, |
| 960 | 943 | .sect = context.macho_file.stub_helper_section_index.?, |
| 961 | }, stub_helper_atom); | |
| 962 | } | |
| 963 | if (context.parsed_atoms.getPtr(.{ | |
| 964 | .seg = context.macho_file.text_segment_cmd_index.?, | |
| 965 | .sect = context.macho_file.stubs_section_index.?, | |
| 966 | })) |last| { | |
| 967 | last.*.next = stub_atom; | |
| 968 | stub_atom.prev = last.*; | |
| 969 | last.* = stub_atom; | |
| 970 | } else { | |
| 971 | try context.parsed_atoms.putNoClobber(.{ | |
| 972 | .seg = context.macho_file.text_segment_cmd_index.?, | |
| 973 | .sect = context.macho_file.stubs_section_index.?, | |
| 974 | }, stub_atom); | |
| 975 | } | |
| 976 | if (context.parsed_atoms.getPtr(.{ | |
| 977 | .seg = context.macho_file.data_segment_cmd_index.?, | |
| 978 | .sect = context.macho_file.la_symbol_ptr_section_index.?, | |
| 979 | })) |last| { | |
| 980 | last.*.next = laptr_atom; | |
| 981 | laptr_atom.prev = last.*; | |
| 982 | last.* = laptr_atom; | |
| 983 | } else { | |
| 984 | try context.parsed_atoms.putNoClobber(.{ | |
| 944 | }; | |
| 945 | if (!context.object.start_atoms.contains(match)) { | |
| 946 | try context.object.start_atoms.putNoClobber(context.allocator, match, atom); | |
| 947 | } | |
| 948 | if (context.object.end_atoms.getPtr(match)) |last| { | |
| 949 | last.*.next = atom; | |
| 950 | atom.prev = last.*; | |
| 951 | last.* = atom; | |
| 952 | } else { | |
| 953 | try context.object.end_atoms.putNoClobber(context.allocator, match, atom); | |
| 954 | } | |
| 955 | break :atom atom; | |
| 956 | }; | |
| 957 | const laptr_atom = atom: { | |
| 958 | const atom = try context.macho_file.createLazyPointerAtom( | |
| 959 | stub_helper_atom.local_sym_index, | |
| 960 | parsed_rel.where_index, | |
| 961 | ); | |
| 962 | const match = MachO.MatchingSection{ | |
| 985 | 963 | .seg = context.macho_file.data_segment_cmd_index.?, |
| 986 | 964 | .sect = context.macho_file.la_symbol_ptr_section_index.?, |
| 987 | }, laptr_atom); | |
| 965 | }; | |
| 966 | if (!context.object.start_atoms.contains(match)) { | |
| 967 | try context.object.start_atoms.putNoClobber(context.allocator, match, atom); | |
| 968 | } | |
| 969 | if (context.object.end_atoms.getPtr(match)) |last| { | |
| 970 | last.*.next = atom; | |
| 971 | atom.prev = last.*; | |
| 972 | last.* = atom; | |
| 973 | } else { | |
| 974 | try context.object.end_atoms.putNoClobber(context.allocator, match, atom); | |
| 975 | } | |
| 976 | break :atom atom; | |
| 977 | }; | |
| 978 | { | |
| 979 | const atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index); | |
| 980 | const match = MachO.MatchingSection{ | |
| 981 | .seg = context.macho_file.text_segment_cmd_index.?, | |
| 982 | .sect = context.macho_file.stubs_section_index.?, | |
| 983 | }; | |
| 984 | if (!context.object.start_atoms.contains(match)) { | |
| 985 | try context.object.start_atoms.putNoClobber(context.allocator, match, atom); | |
| 986 | } | |
| 987 | if (context.object.end_atoms.getPtr(match)) |last| { | |
| 988 | last.*.next = atom; | |
| 989 | atom.prev = last.*; | |
| 990 | last.* = atom; | |
| 991 | } else { | |
| 992 | try context.object.end_atoms.putNoClobber(context.allocator, match, atom); | |
| 993 | } | |
| 994 | try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, atom); | |
| 988 | 995 | } |
| 989 | 996 | } |
| 990 | 997 | } |
src/link/MachO/CodeSignature.zig+1-1| ... | ... | @@ -102,7 +102,7 @@ pub fn calcAdhocSignature( |
| 102 | 102 | var buffer = try allocator.alloc(u8, page_size); |
| 103 | 103 | defer allocator.free(buffer); |
| 104 | 104 | |
| 105 | try cdir.data.ensureCapacity(allocator, total_pages * hash_size + id.len + 1); | |
| 105 | try cdir.data.ensureTotalCapacity(allocator, total_pages * hash_size + id.len + 1); | |
| 106 | 106 | |
| 107 | 107 | // 1. Save the identifier and update offsets |
| 108 | 108 | cdir.inner.identOffset = cdir.inner.length; |
src/link/MachO/DebugSymbols.zig+8-8| ... | ... | @@ -353,7 +353,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt |
| 353 | 353 | |
| 354 | 354 | // We have a function to compute the upper bound size, because it's needed |
| 355 | 355 | // for determining where to put the offset of the first `LinkBlock`. |
| 356 | try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes()); | |
| 356 | try di_buf.ensureTotalCapacity(self.dbgInfoNeededHeaderBytes()); | |
| 357 | 357 | |
| 358 | 358 | // initial length - length of the .debug_info contribution for this compilation unit, |
| 359 | 359 | // not including the initial length itself. |
| ... | ... | @@ -408,7 +408,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt |
| 408 | 408 | |
| 409 | 409 | // Enough for all the data without resizing. When support for more compilation units |
| 410 | 410 | // is added, the size of this section will become more variable. |
| 411 | try di_buf.ensureCapacity(100); | |
| 411 | try di_buf.ensureTotalCapacity(100); | |
| 412 | 412 | |
| 413 | 413 | // initial length - length of the .debug_aranges contribution for this compilation unit, |
| 414 | 414 | // not including the initial length itself. |
| ... | ... | @@ -479,7 +479,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt |
| 479 | 479 | // The size of this header is variable, depending on the number of directories, |
| 480 | 480 | // files, and padding. We have a function to compute the upper bound size, however, |
| 481 | 481 | // because it's needed for determining where to put the offset of the first `SrcFn`. |
| 482 | try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes(module)); | |
| 482 | try di_buf.ensureTotalCapacity(self.dbgLineNeededHeaderBytes(module)); | |
| 483 | 483 | |
| 484 | 484 | // initial length - length of the .debug_line contribution for this compilation unit, |
| 485 | 485 | // not including the initial length itself. |
| ... | ... | @@ -607,7 +607,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: Segm |
| 607 | 607 | }; |
| 608 | 608 | mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname); |
| 609 | 609 | |
| 610 | try cmd.sections.ensureCapacity(allocator, cmd.inner.nsects); | |
| 610 | try cmd.sections.ensureTotalCapacity(allocator, cmd.inner.nsects); | |
| 611 | 611 | for (base_cmd.sections.items) |base_sect, i| { |
| 612 | 612 | var sect = macho.section_64{ |
| 613 | 613 | .sectname = undefined, |
| ... | ... | @@ -855,7 +855,7 @@ pub fn initDeclDebugBuffers( |
| 855 | 855 | switch (decl.ty.zigTypeTag()) { |
| 856 | 856 | .Fn => { |
| 857 | 857 | // For functions we need to add a prologue to the debug line program. |
| 858 | try dbg_line_buffer.ensureCapacity(26); | |
| 858 | try dbg_line_buffer.ensureTotalCapacity(26); | |
| 859 | 859 | |
| 860 | 860 | const func = decl.val.castTag(.function).?.data; |
| 861 | 861 | const line_off = @intCast(u28, decl.src_line + func.lbrace_line); |
| ... | ... | @@ -889,7 +889,7 @@ pub fn initDeclDebugBuffers( |
| 889 | 889 | |
| 890 | 890 | // .debug_info subprogram |
| 891 | 891 | const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1]; |
| 892 | try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 27 + decl_name_with_null.len); | |
| 892 | try dbg_info_buffer.ensureUnusedCapacity(27 + decl_name_with_null.len); | |
| 893 | 893 | |
| 894 | 894 | const fn_ret_type = decl.ty.fnReturnType(); |
| 895 | 895 | const fn_ret_has_bits = fn_ret_type.hasCodeGenBits(); |
| ... | ... | @@ -1124,7 +1124,7 @@ fn addDbgInfoType( |
| 1124 | 1124 | }, |
| 1125 | 1125 | .Int => { |
| 1126 | 1126 | const info = ty.intInfo(target); |
| 1127 | try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12); | |
| 1127 | try dbg_info_buffer.ensureUnusedCapacity(12); | |
| 1128 | 1128 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); |
| 1129 | 1129 | // DW.AT.encoding, DW.FORM.data1 |
| 1130 | 1130 | dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { |
| ... | ... | @@ -1261,7 +1261,7 @@ fn getDebugLineProgramEnd(self: DebugSymbols) u32 { |
| 1261 | 1261 | |
| 1262 | 1262 | /// TODO Improve this to use a table. |
| 1263 | 1263 | fn makeDebugString(self: *DebugSymbols, allocator: *Allocator, bytes: []const u8) !u32 { |
| 1264 | try self.debug_string_table.ensureCapacity(allocator, self.debug_string_table.items.len + bytes.len + 1); | |
| 1264 | try self.debug_string_table.ensureUnusedCapacity(allocator, bytes.len + 1); | |
| 1265 | 1265 | const result = self.debug_string_table.items.len; |
| 1266 | 1266 | self.debug_string_table.appendSliceAssumeCapacity(bytes); |
| 1267 | 1267 | self.debug_string_table.appendAssumeCapacity(0); |
src/link/MachO/Dylib.zig+1-1| ... | ... | @@ -180,7 +180,7 @@ pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void { |
| 180 | 180 | fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void { |
| 181 | 181 | const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0; |
| 182 | 182 | |
| 183 | try self.load_commands.ensureCapacity(allocator, self.header.?.ncmds); | |
| 183 | try self.load_commands.ensureTotalCapacity(allocator, self.header.?.ncmds); | |
| 184 | 184 | |
| 185 | 185 | var i: u16 = 0; |
| 186 | 186 | while (i < self.header.?.ncmds) : (i += 1) { |
src/link/MachO/Object.zig+154-396| ... | ... | @@ -31,14 +31,12 @@ header: ?macho.mach_header_64 = null, |
| 31 | 31 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 32 | 32 | |
| 33 | 33 | segment_cmd_index: ?u16 = null, |
| 34 | text_section_index: ?u16 = null, | |
| 34 | 35 | symtab_cmd_index: ?u16 = null, |
| 35 | 36 | dysymtab_cmd_index: ?u16 = null, |
| 36 | 37 | build_version_cmd_index: ?u16 = null, |
| 37 | 38 | data_in_code_cmd_index: ?u16 = null, |
| 38 | 39 | |
| 39 | text_section_index: ?u16 = null, | |
| 40 | mod_init_func_section_index: ?u16 = null, | |
| 41 | ||
| 42 | 40 | // __DWARF segment sections |
| 43 | 41 | dwarf_debug_info_index: ?u16 = null, |
| 44 | 42 | dwarf_debug_abbrev_index: ?u16 = null, |
| ... | ... | @@ -56,7 +54,9 @@ tu_name: ?[]const u8 = null, |
| 56 | 54 | tu_comp_dir: ?[]const u8 = null, |
| 57 | 55 | mtime: ?u64 = null, |
| 58 | 56 | |
| 59 | atoms: std.ArrayListUnmanaged(*Atom) = .{}, | |
| 57 | contained_atoms: std.ArrayListUnmanaged(*Atom) = .{}, | |
| 58 | start_atoms: std.AutoHashMapUnmanaged(MachO.MatchingSection, *Atom) = .{}, | |
| 59 | end_atoms: std.AutoHashMapUnmanaged(MachO.MatchingSection, *Atom) = .{}, | |
| 60 | 60 | sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{}, |
| 61 | 61 | |
| 62 | 62 | // TODO symbol mapping and its inverse can probably be simple arrays |
| ... | ... | @@ -138,12 +138,15 @@ pub fn deinit(self: *Object, allocator: *Allocator) void { |
| 138 | 138 | self.data_in_code_entries.deinit(allocator); |
| 139 | 139 | self.symtab.deinit(allocator); |
| 140 | 140 | self.strtab.deinit(allocator); |
| 141 | self.atoms.deinit(allocator); | |
| 142 | 141 | self.sections_as_symbols.deinit(allocator); |
| 143 | 142 | self.symbol_mapping.deinit(allocator); |
| 144 | 143 | self.reverse_symbol_mapping.deinit(allocator); |
| 145 | 144 | allocator.free(self.name); |
| 146 | 145 | |
| 146 | self.contained_atoms.deinit(allocator); | |
| 147 | self.start_atoms.deinit(allocator); | |
| 148 | self.end_atoms.deinit(allocator); | |
| 149 | ||
| 147 | 150 | if (self.debug_info) |*db| { |
| 148 | 151 | db.deinit(allocator); |
| 149 | 152 | } |
| ... | ... | @@ -157,6 +160,67 @@ pub fn deinit(self: *Object, allocator: *Allocator) void { |
| 157 | 160 | } |
| 158 | 161 | } |
| 159 | 162 | |
| 163 | pub fn free(self: *Object, allocator: *Allocator, macho_file: *MachO) void { | |
| 164 | log.debug("freeObject {*}", .{self}); | |
| 165 | ||
| 166 | var it = self.end_atoms.iterator(); | |
| 167 | while (it.next()) |entry| { | |
| 168 | const match = entry.key_ptr.*; | |
| 169 | const first_atom = self.start_atoms.get(match).?; | |
| 170 | const last_atom = entry.value_ptr.*; | |
| 171 | var atom = first_atom; | |
| 172 | ||
| 173 | while (true) { | |
| 174 | if (atom.local_sym_index != 0) { | |
| 175 | macho_file.locals_free_list.append(allocator, atom.local_sym_index) catch {}; | |
| 176 | const local = &macho_file.locals.items[atom.local_sym_index]; | |
| 177 | local.n_type = 0; | |
| 178 | atom.local_sym_index = 0; | |
| 179 | } | |
| 180 | if (atom == last_atom) { | |
| 181 | break; | |
| 182 | } | |
| 183 | if (atom.next) |next| { | |
| 184 | atom = next; | |
| 185 | } else break; | |
| 186 | } | |
| 187 | } | |
| 188 | ||
| 189 | self.freeAtoms(macho_file); | |
| 190 | } | |
| 191 | ||
| 192 | fn freeAtoms(self: *Object, macho_file: *MachO) void { | |
| 193 | var it = self.end_atoms.iterator(); | |
| 194 | while (it.next()) |entry| { | |
| 195 | const match = entry.key_ptr.*; | |
| 196 | var first_atom: *Atom = self.start_atoms.get(match).?; | |
| 197 | var last_atom: *Atom = entry.value_ptr.*; | |
| 198 | ||
| 199 | if (macho_file.atoms.getPtr(match)) |atom_ptr| { | |
| 200 | if (atom_ptr.* == last_atom) { | |
| 201 | if (first_atom.prev) |prev| { | |
| 202 | // TODO shrink the section size here | |
| 203 | atom_ptr.* = prev; | |
| 204 | } else { | |
| 205 | _ = macho_file.atoms.fetchRemove(match); | |
| 206 | } | |
| 207 | } | |
| 208 | } | |
| 209 | ||
| 210 | if (first_atom.prev) |prev| { | |
| 211 | prev.next = last_atom.next; | |
| 212 | } else { | |
| 213 | first_atom.prev = null; | |
| 214 | } | |
| 215 | ||
| 216 | if (last_atom.next) |next| { | |
| 217 | next.prev = last_atom.prev; | |
| 218 | } else { | |
| 219 | last_atom.next = null; | |
| 220 | } | |
| 221 | } | |
| 222 | } | |
| 223 | ||
| 160 | 224 | pub fn parse(self: *Object, allocator: *Allocator, target: std.Target) !void { |
| 161 | 225 | const reader = self.file.reader(); |
| 162 | 226 | if (self.file_offset) |offset| { |
| ... | ... | @@ -197,7 +261,7 @@ pub fn readLoadCommands(self: *Object, allocator: *Allocator, reader: anytype) ! |
| 197 | 261 | const header = self.header orelse unreachable; // Unreachable here signifies a fatal unexplored condition. |
| 198 | 262 | const offset = self.file_offset orelse 0; |
| 199 | 263 | |
| 200 | try self.load_commands.ensureCapacity(allocator, header.ncmds); | |
| 264 | try self.load_commands.ensureTotalCapacity(allocator, header.ncmds); | |
| 201 | 265 | |
| 202 | 266 | var i: u16 = 0; |
| 203 | 267 | while (i < header.ncmds) : (i += 1) { |
| ... | ... | @@ -226,10 +290,6 @@ pub fn readLoadCommands(self: *Object, allocator: *Allocator, reader: anytype) ! |
| 226 | 290 | if (mem.eql(u8, sectname, "__text")) { |
| 227 | 291 | self.text_section_index = index; |
| 228 | 292 | } |
| 229 | } else if (mem.eql(u8, segname, "__DATA")) { | |
| 230 | if (mem.eql(u8, sectname, "__mod_init_func")) { | |
| 231 | self.mod_init_func_section_index = index; | |
| 232 | } | |
| 233 | 293 | } |
| 234 | 294 | |
| 235 | 295 | sect.offset += offset; |
| ... | ... | @@ -315,166 +375,10 @@ fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) |
| 315 | 375 | return dices[start..end]; |
| 316 | 376 | } |
| 317 | 377 | |
| 318 | const Context = struct { | |
| 319 | allocator: *Allocator, | |
| 320 | object: *Object, | |
| 321 | macho_file: *MachO, | |
| 322 | match: MachO.MatchingSection, | |
| 323 | parsed_atoms: *ParsedAtoms, | |
| 324 | }; | |
| 325 | ||
| 326 | const AtomParser = struct { | |
| 327 | section: macho.section_64, | |
| 328 | code: []u8, | |
| 329 | relocs: []macho.relocation_info, | |
| 330 | nlists: []NlistWithIndex, | |
| 331 | index: u32 = 0, | |
| 332 | ||
| 333 | fn peek(self: AtomParser) ?NlistWithIndex { | |
| 334 | return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null; | |
| 335 | } | |
| 336 | ||
| 337 | fn lessThanBySeniority(context: Context, lhs: NlistWithIndex, rhs: NlistWithIndex) bool { | |
| 338 | if (!MachO.symbolIsExt(rhs.nlist)) { | |
| 339 | return MachO.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx)); | |
| 340 | } else if (MachO.symbolIsPext(rhs.nlist) or MachO.symbolIsWeakDef(rhs.nlist)) { | |
| 341 | return !MachO.symbolIsExt(lhs.nlist); | |
| 342 | } else { | |
| 343 | return false; | |
| 344 | } | |
| 345 | } | |
| 346 | ||
| 347 | pub fn next(self: *AtomParser, context: Context) !?*Atom { | |
| 348 | if (self.index == self.nlists.len) return null; | |
| 349 | ||
| 350 | const tracy = trace(@src()); | |
| 351 | defer tracy.end(); | |
| 352 | ||
| 353 | var aliases = std.ArrayList(NlistWithIndex).init(context.allocator); | |
| 354 | defer aliases.deinit(); | |
| 355 | ||
| 356 | const next_nlist: ?NlistWithIndex = blk: while (true) { | |
| 357 | const curr_nlist = self.nlists[self.index]; | |
| 358 | try aliases.append(curr_nlist); | |
| 359 | ||
| 360 | if (self.peek()) |next_nlist| { | |
| 361 | if (curr_nlist.nlist.n_value == next_nlist.nlist.n_value) { | |
| 362 | self.index += 1; | |
| 363 | continue; | |
| 364 | } | |
| 365 | break :blk next_nlist; | |
| 366 | } | |
| 367 | break :blk null; | |
| 368 | } else null; | |
| 369 | ||
| 370 | for (aliases.items) |*nlist_with_index| { | |
| 371 | nlist_with_index.index = context.object.symbol_mapping.get(nlist_with_index.index) orelse unreachable; | |
| 372 | } | |
| 373 | ||
| 374 | if (aliases.items.len > 1) { | |
| 375 | // Bubble-up senior symbol as the main link to the atom. | |
| 376 | sort.sort( | |
| 377 | NlistWithIndex, | |
| 378 | aliases.items, | |
| 379 | context, | |
| 380 | AtomParser.lessThanBySeniority, | |
| 381 | ); | |
| 382 | } | |
| 383 | ||
| 384 | const senior_nlist = aliases.pop(); | |
| 385 | const senior_sym = &context.macho_file.locals.items[senior_nlist.index]; | |
| 386 | senior_sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1); | |
| 387 | ||
| 388 | const start_addr = senior_nlist.nlist.n_value - self.section.addr; | |
| 389 | const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size; | |
| 390 | ||
| 391 | const code = self.code[start_addr..end_addr]; | |
| 392 | const size = code.len; | |
| 393 | ||
| 394 | const max_align = self.section.@"align"; | |
| 395 | const actual_align = if (senior_nlist.nlist.n_value > 0) | |
| 396 | math.min(@ctz(u64, senior_nlist.nlist.n_value), max_align) | |
| 397 | else | |
| 398 | max_align; | |
| 399 | ||
| 400 | const stab: ?Atom.Stab = if (context.object.debug_info) |di| blk: { | |
| 401 | // TODO there has to be a better to handle this. | |
| 402 | for (di.inner.func_list.items) |func| { | |
| 403 | if (func.pc_range) |range| { | |
| 404 | if (senior_nlist.nlist.n_value >= range.start and senior_nlist.nlist.n_value < range.end) { | |
| 405 | break :blk Atom.Stab{ | |
| 406 | .function = range.end - range.start, | |
| 407 | }; | |
| 408 | } | |
| 409 | } | |
| 410 | } | |
| 411 | // TODO | |
| 412 | // if (self.macho_file.globals.contains(self.macho_file.getString(senior_sym.strx))) break :blk .global; | |
| 413 | break :blk .static; | |
| 414 | } else null; | |
| 415 | ||
| 416 | const atom = try context.macho_file.createEmptyAtom(senior_nlist.index, size, actual_align); | |
| 417 | atom.stab = stab; | |
| 418 | ||
| 419 | const is_zerofill = blk: { | |
| 420 | const section_type = commands.sectionType(self.section); | |
| 421 | break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL; | |
| 422 | }; | |
| 423 | if (!is_zerofill) { | |
| 424 | mem.copy(u8, atom.code.items, code); | |
| 425 | } | |
| 426 | ||
| 427 | try atom.aliases.ensureTotalCapacity(context.allocator, aliases.items.len); | |
| 428 | for (aliases.items) |alias| { | |
| 429 | atom.aliases.appendAssumeCapacity(alias.index); | |
| 430 | const sym = &context.macho_file.locals.items[alias.index]; | |
| 431 | sym.n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(context.match).? + 1); | |
| 432 | } | |
| 433 | ||
| 434 | try atom.parseRelocs(self.relocs, .{ | |
| 435 | .base_addr = self.section.addr, | |
| 436 | .base_offset = start_addr, | |
| 437 | .allocator = context.allocator, | |
| 438 | .object = context.object, | |
| 439 | .macho_file = context.macho_file, | |
| 440 | .parsed_atoms = context.parsed_atoms, | |
| 441 | }); | |
| 442 | ||
| 443 | if (context.macho_file.has_dices) { | |
| 444 | const dices = filterDice( | |
| 445 | context.object.data_in_code_entries.items, | |
| 446 | senior_nlist.nlist.n_value, | |
| 447 | senior_nlist.nlist.n_value + size, | |
| 448 | ); | |
| 449 | try atom.dices.ensureTotalCapacity(context.allocator, dices.len); | |
| 450 | ||
| 451 | for (dices) |dice| { | |
| 452 | atom.dices.appendAssumeCapacity(.{ | |
| 453 | .offset = dice.offset - try math.cast(u32, senior_nlist.nlist.n_value), | |
| 454 | .length = dice.length, | |
| 455 | .kind = dice.kind, | |
| 456 | }); | |
| 457 | } | |
| 458 | } | |
| 459 | ||
| 460 | self.index += 1; | |
| 461 | ||
| 462 | return atom; | |
| 463 | } | |
| 464 | }; | |
| 465 | ||
| 466 | pub const ParsedAtoms = std.AutoHashMap(MachO.MatchingSection, *Atom); | |
| 467 | ||
| 468 | pub fn parseIntoAtoms( | |
| 469 | self: *Object, | |
| 470 | allocator: *Allocator, | |
| 471 | object_id: u16, | |
| 472 | macho_file: *MachO, | |
| 473 | ) !ParsedAtoms { | |
| 378 | pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO) !void { | |
| 474 | 379 | const tracy = trace(@src()); |
| 475 | 380 | defer tracy.end(); |
| 476 | 381 | |
| 477 | var parsed_atoms = ParsedAtoms.init(allocator); | |
| 478 | 382 | const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; |
| 479 | 383 | |
| 480 | 384 | log.debug("analysing {s}", .{self.name}); |
| ... | ... | @@ -540,16 +444,6 @@ pub fn parseIntoAtoms( |
| 540 | 444 | // Symbols within this section only. |
| 541 | 445 | const filtered_nlists = NlistWithIndex.filterInSection(sorted_nlists, sect); |
| 542 | 446 | |
| 543 | // TODO rewrite and re-enable dead-code stripping optimisation. I think it might make sense | |
| 544 | // to do this in a standalone pass after we parse the sections as atoms. | |
| 545 | // In release mode, if the object file was generated with dead code stripping optimisations, | |
| 546 | // note it now and parse sections as atoms. | |
| 547 | // const is_splittable = blk: { | |
| 548 | // if (macho_file.base.options.optimize_mode == .Debug) break :blk false; | |
| 549 | // break :blk self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0; | |
| 550 | // }; | |
| 551 | const is_splittable = false; | |
| 552 | ||
| 553 | 447 | macho_file.has_dices = macho_file.has_dices or blk: { |
| 554 | 448 | if (self.text_section_index) |index| { |
| 555 | 449 | if (index != id) break :blk false; |
| ... | ... | @@ -560,237 +454,101 @@ pub fn parseIntoAtoms( |
| 560 | 454 | }; |
| 561 | 455 | macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null; |
| 562 | 456 | |
| 563 | next: { | |
| 564 | if (is_splittable) atoms: { | |
| 565 | if (filtered_nlists.len == 0) break :atoms; | |
| 566 | ||
| 567 | // If the first nlist does not match the start of the section, | |
| 568 | // then we need to encapsulate the memory range [section start, first symbol) | |
| 569 | // as a temporary symbol and insert the matching Atom. | |
| 570 | const first_nlist = filtered_nlists[0].nlist; | |
| 571 | if (first_nlist.n_value > sect.addr) { | |
| 572 | const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{ | |
| 573 | self.name, | |
| 574 | segmentName(sect), | |
| 575 | sectionName(sect), | |
| 576 | }); | |
| 577 | defer allocator.free(sym_name); | |
| 578 | ||
| 579 | const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: { | |
| 580 | const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len); | |
| 581 | try macho_file.locals.append(allocator, .{ | |
| 582 | .n_strx = try macho_file.makeString(sym_name), | |
| 583 | .n_type = macho.N_SECT, | |
| 584 | .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1), | |
| 585 | .n_desc = 0, | |
| 586 | .n_value = 0, | |
| 587 | }); | |
| 588 | try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index); | |
| 589 | break :blk atom_local_sym_index; | |
| 590 | }; | |
| 591 | const atom_code = code[0 .. first_nlist.n_value - sect.addr]; | |
| 592 | const atom_size = atom_code.len; | |
| 593 | const atom = try macho_file.createEmptyAtom(atom_local_sym_index, atom_size, sect.@"align"); | |
| 594 | ||
| 595 | const is_zerofill = blk: { | |
| 596 | const section_type = commands.sectionType(sect); | |
| 597 | break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL; | |
| 598 | }; | |
| 599 | if (!is_zerofill) { | |
| 600 | mem.copy(u8, atom.code.items, atom_code); | |
| 601 | } | |
| 602 | ||
| 603 | try atom.parseRelocs(relocs, .{ | |
| 604 | .base_addr = sect.addr, | |
| 605 | .base_offset = 0, | |
| 606 | .allocator = allocator, | |
| 607 | .object = self, | |
| 608 | .macho_file = macho_file, | |
| 609 | .parsed_atoms = &parsed_atoms, | |
| 610 | }); | |
| 611 | ||
| 612 | if (macho_file.has_dices) { | |
| 613 | const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + atom_size); | |
| 614 | try atom.dices.ensureTotalCapacity(allocator, dices.len); | |
| 615 | ||
| 616 | for (dices) |dice| { | |
| 617 | atom.dices.appendAssumeCapacity(.{ | |
| 618 | .offset = dice.offset - try math.cast(u32, sect.addr), | |
| 619 | .length = dice.length, | |
| 620 | .kind = dice.kind, | |
| 621 | }); | |
| 622 | } | |
| 623 | } | |
| 624 | ||
| 625 | if (parsed_atoms.getPtr(match)) |last| { | |
| 626 | last.*.next = atom; | |
| 627 | atom.prev = last.*; | |
| 628 | last.* = atom; | |
| 629 | } else { | |
| 630 | try parsed_atoms.putNoClobber(match, atom); | |
| 631 | } | |
| 632 | try self.atoms.append(allocator, atom); | |
| 633 | } | |
| 634 | ||
| 635 | var parser = AtomParser{ | |
| 636 | .section = sect, | |
| 637 | .code = code, | |
| 638 | .relocs = relocs, | |
| 639 | .nlists = filtered_nlists, | |
| 640 | }; | |
| 641 | ||
| 642 | while (try parser.next(.{ | |
| 643 | .allocator = allocator, | |
| 644 | .object = self, | |
| 645 | .macho_file = macho_file, | |
| 646 | .match = match, | |
| 647 | .parsed_atoms = &parsed_atoms, | |
| 648 | })) |atom| { | |
| 649 | const sym = macho_file.locals.items[atom.local_sym_index]; | |
| 650 | const is_ext = blk: { | |
| 651 | const orig_sym_id = self.reverse_symbol_mapping.get(atom.local_sym_index) orelse unreachable; | |
| 652 | break :blk MachO.symbolIsExt(self.symtab.items[orig_sym_id]); | |
| 653 | }; | |
| 654 | if (is_ext) { | |
| 655 | if (macho_file.symbol_resolver.get(sym.n_strx)) |resolv| { | |
| 656 | assert(resolv.where == .global); | |
| 657 | if (resolv.file != object_id) { | |
| 658 | log.debug("deduping definition of {s} in {s}", .{ | |
| 659 | macho_file.getString(sym.n_strx), | |
| 660 | self.name, | |
| 661 | }); | |
| 662 | log.debug(" already defined in {s}", .{ | |
| 663 | macho_file.objects.items[resolv.file].name, | |
| 664 | }); | |
| 665 | continue; | |
| 666 | } | |
| 667 | } | |
| 668 | } | |
| 457 | // Since there is no symbol to refer to this atom, we create | |
| 458 | // a temp one, unless we already did that when working out the relocations | |
| 459 | // of other atoms. | |
| 460 | const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: { | |
| 461 | const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len); | |
| 462 | try macho_file.locals.append(allocator, .{ | |
| 463 | .n_strx = 0, | |
| 464 | .n_type = macho.N_SECT, | |
| 465 | .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1), | |
| 466 | .n_desc = 0, | |
| 467 | .n_value = 0, | |
| 468 | }); | |
| 469 | try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index); | |
| 470 | break :blk atom_local_sym_index; | |
| 471 | }; | |
| 472 | const atom = try macho_file.createEmptyAtom(atom_local_sym_index, sect.size, sect.@"align"); | |
| 669 | 473 | |
| 670 | if (sym.n_value == sect.addr) { | |
| 671 | if (self.sections_as_symbols.get(sect_id)) |alias| { | |
| 672 | // In x86_64 relocs, it can so happen that the compiler refers to the same | |
| 673 | // atom by both the actual assigned symbol and the start of the section. In this | |
| 674 | // case, we need to link the two together so add an alias. | |
| 675 | try atom.aliases.append(allocator, alias); | |
| 676 | } | |
| 677 | } | |
| 474 | const is_zerofill = blk: { | |
| 475 | const section_type = commands.sectionType(sect); | |
| 476 | break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL; | |
| 477 | }; | |
| 478 | if (!is_zerofill) { | |
| 479 | mem.copy(u8, atom.code.items, code); | |
| 480 | } | |
| 678 | 481 | |
| 679 | if (parsed_atoms.getPtr(match)) |last| { | |
| 680 | last.*.next = atom; | |
| 681 | atom.prev = last.*; | |
| 682 | last.* = atom; | |
| 683 | } else { | |
| 684 | try parsed_atoms.putNoClobber(match, atom); | |
| 685 | } | |
| 686 | try self.atoms.append(allocator, atom); | |
| 687 | } | |
| 482 | try atom.parseRelocs(relocs, .{ | |
| 483 | .base_addr = sect.addr, | |
| 484 | .base_offset = 0, | |
| 485 | .allocator = allocator, | |
| 486 | .object = self, | |
| 487 | .macho_file = macho_file, | |
| 488 | }); | |
| 688 | 489 | |
| 689 | break :next; | |
| 690 | } | |
| 490 | if (macho_file.has_dices) { | |
| 491 | const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size); | |
| 492 | try atom.dices.ensureTotalCapacity(allocator, dices.len); | |
| 691 | 493 | |
| 692 | // Since there is no symbol to refer to this atom, we create | |
| 693 | // a temp one, unless we already did that when working out the relocations | |
| 694 | // of other atoms. | |
| 695 | const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{ | |
| 696 | self.name, | |
| 697 | segmentName(sect), | |
| 698 | sectionName(sect), | |
| 699 | }); | |
| 700 | defer allocator.free(sym_name); | |
| 701 | ||
| 702 | const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: { | |
| 703 | const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len); | |
| 704 | try macho_file.locals.append(allocator, .{ | |
| 705 | .n_strx = try macho_file.makeString(sym_name), | |
| 706 | .n_type = macho.N_SECT, | |
| 707 | .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1), | |
| 708 | .n_desc = 0, | |
| 709 | .n_value = 0, | |
| 494 | for (dices) |dice| { | |
| 495 | atom.dices.appendAssumeCapacity(.{ | |
| 496 | .offset = dice.offset - try math.cast(u32, sect.addr), | |
| 497 | .length = dice.length, | |
| 498 | .kind = dice.kind, | |
| 710 | 499 | }); |
| 711 | try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index); | |
| 712 | break :blk atom_local_sym_index; | |
| 713 | }; | |
| 714 | const atom = try macho_file.createEmptyAtom(atom_local_sym_index, sect.size, sect.@"align"); | |
| 715 | ||
| 716 | const is_zerofill = blk: { | |
| 717 | const section_type = commands.sectionType(sect); | |
| 718 | break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL; | |
| 719 | }; | |
| 720 | if (!is_zerofill) { | |
| 721 | mem.copy(u8, atom.code.items, code); | |
| 722 | } | |
| 723 | ||
| 724 | try atom.parseRelocs(relocs, .{ | |
| 725 | .base_addr = sect.addr, | |
| 726 | .base_offset = 0, | |
| 727 | .allocator = allocator, | |
| 728 | .object = self, | |
| 729 | .macho_file = macho_file, | |
| 730 | .parsed_atoms = &parsed_atoms, | |
| 731 | }); | |
| 732 | ||
| 733 | if (macho_file.has_dices) { | |
| 734 | const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size); | |
| 735 | try atom.dices.ensureTotalCapacity(allocator, dices.len); | |
| 736 | ||
| 737 | for (dices) |dice| { | |
| 738 | atom.dices.appendAssumeCapacity(.{ | |
| 739 | .offset = dice.offset - try math.cast(u32, sect.addr), | |
| 740 | .length = dice.length, | |
| 741 | .kind = dice.kind, | |
| 742 | }); | |
| 743 | } | |
| 744 | 500 | } |
| 501 | } | |
| 745 | 502 | |
| 746 | // Since this is atom gets a helper local temporary symbol that didn't exist | |
| 747 | // in the object file which encompasses the entire section, we need traverse | |
| 748 | // the filtered symbols and note which symbol is contained within so that | |
| 749 | // we can properly allocate addresses down the line. | |
| 750 | // While we're at it, we need to update segment,section mapping of each symbol too. | |
| 751 | try atom.contained.ensureTotalCapacity(allocator, filtered_nlists.len); | |
| 752 | ||
| 753 | for (filtered_nlists) |nlist_with_index| { | |
| 754 | const nlist = nlist_with_index.nlist; | |
| 755 | const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable; | |
| 756 | const local = &macho_file.locals.items[local_sym_index]; | |
| 757 | local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1); | |
| 758 | ||
| 759 | const stab: ?Atom.Stab = if (self.debug_info) |di| blk: { | |
| 760 | // TODO there has to be a better to handle this. | |
| 761 | for (di.inner.func_list.items) |func| { | |
| 762 | if (func.pc_range) |range| { | |
| 763 | if (nlist.n_value >= range.start and nlist.n_value < range.end) { | |
| 764 | break :blk Atom.Stab{ | |
| 765 | .function = range.end - range.start, | |
| 766 | }; | |
| 767 | } | |
| 503 | // Since this is atom gets a helper local temporary symbol that didn't exist | |
| 504 | // in the object file which encompasses the entire section, we need traverse | |
| 505 | // the filtered symbols and note which symbol is contained within so that | |
| 506 | // we can properly allocate addresses down the line. | |
| 507 | // While we're at it, we need to update segment,section mapping of each symbol too. | |
| 508 | try atom.contained.ensureTotalCapacity(allocator, filtered_nlists.len); | |
| 509 | ||
| 510 | for (filtered_nlists) |nlist_with_index| { | |
| 511 | const nlist = nlist_with_index.nlist; | |
| 512 | const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable; | |
| 513 | const local = &macho_file.locals.items[local_sym_index]; | |
| 514 | local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1); | |
| 515 | ||
| 516 | const stab: ?Atom.Stab = if (self.debug_info) |di| blk: { | |
| 517 | // TODO there has to be a better to handle this. | |
| 518 | for (di.inner.func_list.items) |func| { | |
| 519 | if (func.pc_range) |range| { | |
| 520 | if (nlist.n_value >= range.start and nlist.n_value < range.end) { | |
| 521 | break :blk Atom.Stab{ | |
| 522 | .function = range.end - range.start, | |
| 523 | }; | |
| 768 | 524 | } |
| 769 | 525 | } |
| 770 | // TODO | |
| 771 | // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global; | |
| 772 | break :blk .static; | |
| 773 | } else null; | |
| 774 | ||
| 775 | atom.contained.appendAssumeCapacity(.{ | |
| 776 | .local_sym_index = local_sym_index, | |
| 777 | .offset = nlist.n_value - sect.addr, | |
| 778 | .stab = stab, | |
| 779 | }); | |
| 780 | } | |
| 526 | } | |
| 527 | // TODO | |
| 528 | // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global; | |
| 529 | break :blk .static; | |
| 530 | } else null; | |
| 531 | ||
| 532 | atom.contained.appendAssumeCapacity(.{ | |
| 533 | .local_sym_index = local_sym_index, | |
| 534 | .offset = nlist.n_value - sect.addr, | |
| 535 | .stab = stab, | |
| 536 | }); | |
| 537 | } | |
| 781 | 538 | |
| 782 | if (parsed_atoms.getPtr(match)) |last| { | |
| 783 | last.*.next = atom; | |
| 784 | atom.prev = last.*; | |
| 785 | last.* = atom; | |
| 786 | } else { | |
| 787 | try parsed_atoms.putNoClobber(match, atom); | |
| 788 | } | |
| 789 | try self.atoms.append(allocator, atom); | |
| 539 | if (!self.start_atoms.contains(match)) { | |
| 540 | try self.start_atoms.putNoClobber(allocator, match, atom); | |
| 790 | 541 | } |
| 791 | } | |
| 792 | 542 | |
| 793 | return parsed_atoms; | |
| 543 | if (self.end_atoms.getPtr(match)) |last| { | |
| 544 | last.*.next = atom; | |
| 545 | atom.prev = last.*; | |
| 546 | last.* = atom; | |
| 547 | } else { | |
| 548 | try self.end_atoms.putNoClobber(allocator, match, atom); | |
| 549 | } | |
| 550 | try self.contained_atoms.append(allocator, atom); | |
| 551 | } | |
| 794 | 552 | } |
| 795 | 553 | |
| 796 | 554 | fn parseSymtab(self: *Object, allocator: *Allocator) !void { |
src/link/MachO/Trie.zig+2-2| ... | ... | @@ -326,7 +326,7 @@ pub fn finalize(self: *Trie, allocator: *Allocator) !void { |
| 326 | 326 | if (!self.trie_dirty) return; |
| 327 | 327 | |
| 328 | 328 | self.ordered_nodes.shrinkRetainingCapacity(0); |
| 329 | try self.ordered_nodes.ensureCapacity(allocator, self.node_count); | |
| 329 | try self.ordered_nodes.ensureTotalCapacity(allocator, self.node_count); | |
| 330 | 330 | |
| 331 | 331 | var fifo = std.fifo.LinearFifo(*Node, .Dynamic).init(allocator); |
| 332 | 332 | defer fifo.deinit(); |
| ... | ... | @@ -506,7 +506,7 @@ test "write Trie to a byte stream" { |
| 506 | 506 | }); |
| 507 | 507 | |
| 508 | 508 | try trie.finalize(gpa); |
| 509 | try trie.finalize(gpa); // Finalizing mulitple times is a nop subsequently unless we add new nodes. | |
| 509 | try trie.finalize(gpa); // Finalizing multiple times is a nop subsequently unless we add new nodes. | |
| 510 | 510 | |
| 511 | 511 | const exp_buffer = [_]u8{ |
| 512 | 512 | 0x0, 0x1, // node root |
src/link/MachO/commands.zig+1-1| ... | ... | @@ -223,7 +223,7 @@ pub const SegmentCommand = struct { |
| 223 | 223 | var segment = SegmentCommand{ |
| 224 | 224 | .inner = inner, |
| 225 | 225 | }; |
| 226 | try segment.sections.ensureCapacity(alloc, inner.nsects); | |
| 226 | try segment.sections.ensureTotalCapacity(alloc, inner.nsects); | |
| 227 | 227 | |
| 228 | 228 | var i: usize = 0; |
| 229 | 229 | while (i < inner.nsects) : (i += 1) { |
src/link/Plan9.zig+332-64| ... | ... | @@ -20,6 +20,14 @@ const Allocator = std.mem.Allocator; |
| 20 | 20 | const log = std.log.scoped(.link); |
| 21 | 21 | const assert = std.debug.assert; |
| 22 | 22 | |
| 23 | const FnDeclOutput = struct { | |
| 24 | code: []const u8, | |
| 25 | /// this might have to be modified in the linker, so thats why its mutable | |
| 26 | lineinfo: []u8, | |
| 27 | start_line: u32, | |
| 28 | end_line: u32, | |
| 29 | }; | |
| 30 | ||
| 23 | 31 | base: link.File, |
| 24 | 32 | sixtyfour_bit: bool, |
| 25 | 33 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| ... | ... | @@ -27,16 +35,45 @@ bases: Bases, |
| 27 | 35 | |
| 28 | 36 | /// A symbol's value is just casted down when compiling |
| 29 | 37 | /// for a 32 bit target. |
| 38 | /// Does not represent the order or amount of symbols in the file | |
| 39 | /// it is just useful for storing symbols. Some other symbols are in | |
| 40 | /// file_segments. | |
| 30 | 41 | syms: std.ArrayListUnmanaged(aout.Sym) = .{}, |
| 31 | 42 | |
| 32 | fn_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{}, | |
| 43 | /// The plan9 a.out format requires segments of | |
| 44 | /// filenames to be deduplicated, so we use this map to | |
| 45 | /// de duplicate it. The value is the value of the path | |
| 46 | /// component | |
| 47 | file_segments: std.StringArrayHashMapUnmanaged(u16) = .{}, | |
| 48 | /// The value of a 'f' symbol increments by 1 every time, so that no 2 'f' | |
| 49 | /// symbols have the same value. | |
| 50 | file_segments_i: u16 = 1, | |
| 51 | ||
| 52 | path_arena: std.heap.ArenaAllocator, | |
| 53 | ||
| 54 | /// maps a file scope to a hash map of decl to codegen output | |
| 55 | /// this is useful for line debuginfo, since it makes sense to sort by file | |
| 56 | /// The debugger looks for the first file (aout.Sym.Type.z) preceeding the text symbol | |
| 57 | /// of the function to know what file it came from. | |
| 58 | /// If we group the decls by file, it makes it really easy to do this (put the symbol in the correct place) | |
| 59 | fn_decl_table: std.AutoArrayHashMapUnmanaged( | |
| 60 | *Module.Scope.File, | |
| 61 | struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(*Module.Decl, FnDeclOutput) = .{} }, | |
| 62 | ) = .{}, | |
| 33 | 63 | data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{}, |
| 34 | 64 | |
| 35 | 65 | hdr: aout.ExecHdr = undefined, |
| 36 | 66 | |
| 67 | magic: u32, | |
| 68 | ||
| 37 | 69 | entry_val: ?u64 = null, |
| 38 | 70 | |
| 39 | 71 | got_len: usize = 0, |
| 72 | // A list of all the free got indexes, so when making a new decl | |
| 73 | // don't make a new one, just use one from here. | |
| 74 | got_index_free_list: std.ArrayListUnmanaged(u64) = .{}, | |
| 75 | ||
| 76 | syms_index_free_list: std.ArrayListUnmanaged(u64) = .{}, | |
| 40 | 77 | |
| 41 | 78 | const Bases = struct { |
| 42 | 79 | text: u64, |
| ... | ... | @@ -103,8 +140,12 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { |
| 103 | 140 | 33...64 => true, |
| 104 | 141 | else => return error.UnsupportedP9Architecture, |
| 105 | 142 | }; |
| 143 | ||
| 144 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | |
| 145 | ||
| 106 | 146 | const self = try gpa.create(Plan9); |
| 107 | 147 | self.* = .{ |
| 148 | .path_arena = arena_allocator, | |
| 108 | 149 | .base = .{ |
| 109 | 150 | .tag = .plan9, |
| 110 | 151 | .options = options, |
| ... | ... | @@ -113,21 +154,102 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 { |
| 113 | 154 | }, |
| 114 | 155 | .sixtyfour_bit = sixtyfour_bit, |
| 115 | 156 | .bases = undefined, |
| 157 | .magic = try aout.magicFromArch(self.base.options.target.cpu.arch), | |
| 116 | 158 | }; |
| 159 | // a / will always be in a file path | |
| 160 | try self.file_segments.put(self.base.allocator, "/", 1); | |
| 117 | 161 | return self; |
| 118 | 162 | } |
| 119 | 163 | |
| 164 | fn putFn(self: *Plan9, decl: *Module.Decl, out: FnDeclOutput) !void { | |
| 165 | const gpa = self.base.allocator; | |
| 166 | const fn_map_res = try self.fn_decl_table.getOrPut(gpa, decl.namespace.file_scope); | |
| 167 | if (fn_map_res.found_existing) { | |
| 168 | try fn_map_res.value_ptr.functions.put(gpa, decl, out); | |
| 169 | } else { | |
| 170 | const file = decl.namespace.file_scope; | |
| 171 | const arena = &self.path_arena.allocator; | |
| 172 | // each file gets a symbol | |
| 173 | fn_map_res.value_ptr.* = .{ | |
| 174 | .sym_index = blk: { | |
| 175 | try self.syms.append(gpa, undefined); | |
| 176 | break :blk @intCast(u32, self.syms.items.len - 1); | |
| 177 | }, | |
| 178 | }; | |
| 179 | try fn_map_res.value_ptr.functions.put(gpa, decl, out); | |
| 180 | ||
| 181 | var a = std.ArrayList(u8).init(arena); | |
| 182 | errdefer a.deinit(); | |
| 183 | // every 'z' starts with 0 | |
| 184 | try a.append(0); | |
| 185 | // path component value of '/' | |
| 186 | try a.writer().writeIntBig(u16, 1); | |
| 187 | ||
| 188 | // getting the full file path | |
| 189 | var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 190 | const dir = file.pkg.root_src_directory.path orelse try std.os.getcwd(&buf); | |
| 191 | const sub_path = try std.fs.path.join(arena, &.{ dir, file.sub_file_path }); | |
| 192 | try self.addPathComponents(sub_path, &a); | |
| 193 | ||
| 194 | // null terminate | |
| 195 | try a.append(0); | |
| 196 | const final = a.toOwnedSlice(); | |
| 197 | self.syms.items[fn_map_res.value_ptr.sym_index] = .{ | |
| 198 | .type = .z, | |
| 199 | .value = 1, | |
| 200 | .name = final, | |
| 201 | }; | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !void { | |
| 206 | const sep = std.fs.path.sep; | |
| 207 | var it = std.mem.tokenize(u8, path, &.{sep}); | |
| 208 | while (it.next()) |component| { | |
| 209 | if (self.file_segments.get(component)) |num| { | |
| 210 | try a.writer().writeIntBig(u16, num); | |
| 211 | } else { | |
| 212 | self.file_segments_i += 1; | |
| 213 | try self.file_segments.put(self.base.allocator, component, self.file_segments_i); | |
| 214 | try a.writer().writeIntBig(u16, self.file_segments_i); | |
| 215 | } | |
| 216 | } | |
| 217 | } | |
| 218 | ||
| 120 | 219 | pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { |
| 121 | 220 | if (build_options.skip_non_native and builtin.object_format != .plan9) { |
| 122 | 221 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 123 | 222 | } |
| 124 | 223 | |
| 125 | 224 | const decl = func.owner_decl; |
| 225 | ||
| 226 | try self.seeDecl(decl); | |
| 126 | 227 | log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); |
| 127 | 228 | |
| 128 | 229 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 129 | 230 | defer code_buffer.deinit(); |
| 130 | const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ .none = .{} }); | |
| 231 | var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator); | |
| 232 | defer dbg_line_buffer.deinit(); | |
| 233 | var start_line: ?u32 = null; | |
| 234 | var end_line: u32 = undefined; | |
| 235 | var pcop_change_index: ?u32 = null; | |
| 236 | ||
| 237 | const res = try codegen.generateFunction( | |
| 238 | &self.base, | |
| 239 | decl.srcLoc(), | |
| 240 | func, | |
| 241 | air, | |
| 242 | liveness, | |
| 243 | &code_buffer, | |
| 244 | .{ | |
| 245 | .plan9 = .{ | |
| 246 | .dbg_line = &dbg_line_buffer, | |
| 247 | .end_line = &end_line, | |
| 248 | .start_line = &start_line, | |
| 249 | .pcop_change_index = &pcop_change_index, | |
| 250 | }, | |
| 251 | }, | |
| 252 | ); | |
| 131 | 253 | const code = switch (res) { |
| 132 | 254 | .appended => code_buffer.toOwnedSlice(), |
| 133 | 255 | .fail => |em| { |
| ... | ... | @@ -136,7 +258,13 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv |
| 136 | 258 | return; |
| 137 | 259 | }, |
| 138 | 260 | }; |
| 139 | try self.fn_decl_table.put(self.base.allocator, decl, code); | |
| 261 | const out: FnDeclOutput = .{ | |
| 262 | .code = code, | |
| 263 | .lineinfo = dbg_line_buffer.toOwnedSlice(), | |
| 264 | .start_line = start_line.?, | |
| 265 | .end_line = end_line, | |
| 266 | }; | |
| 267 | try self.putFn(decl, out); | |
| 140 | 268 | return self.updateFinish(decl); |
| 141 | 269 | } |
| 142 | 270 | |
| ... | ... | @@ -151,6 +279,8 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { |
| 151 | 279 | } |
| 152 | 280 | } |
| 153 | 281 | |
| 282 | try self.seeDecl(decl); | |
| 283 | ||
| 154 | 284 | log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); |
| 155 | 285 | |
| 156 | 286 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| ... | ... | @@ -192,8 +322,12 @@ fn updateFinish(self: *Plan9, decl: *Module.Decl) !void { |
| 192 | 322 | if (decl.link.plan9.sym_index) |s| { |
| 193 | 323 | self.syms.items[s] = sym; |
| 194 | 324 | } else { |
| 195 | try self.syms.append(self.base.allocator, sym); | |
| 196 | decl.link.plan9.sym_index = self.syms.items.len - 1; | |
| 325 | if (self.syms_index_free_list.popOrNull()) |i| { | |
| 326 | decl.link.plan9.sym_index = i; | |
| 327 | } else { | |
| 328 | try self.syms.append(self.base.allocator, sym); | |
| 329 | decl.link.plan9.sym_index = self.syms.items.len - 1; | |
| 330 | } | |
| 197 | 331 | } |
| 198 | 332 | } |
| 199 | 333 | |
| ... | ... | @@ -209,6 +343,30 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void { |
| 209 | 343 | return self.flushModule(comp); |
| 210 | 344 | } |
| 211 | 345 | |
| 346 | pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void { | |
| 347 | if (delta_line > 0 and delta_line < 65) { | |
| 348 | const toappend = @intCast(u8, delta_line); | |
| 349 | try l.append(toappend); | |
| 350 | } else if (delta_line < 0 and delta_line > -65) { | |
| 351 | const toadd: u8 = @intCast(u8, -delta_line + 64); | |
| 352 | try l.append(toadd); | |
| 353 | } else if (delta_line != 0) { | |
| 354 | try l.append(0); | |
| 355 | try l.writer().writeIntBig(i32, delta_line); | |
| 356 | } | |
| 357 | } | |
| 358 | ||
| 359 | fn declCount(self: *Plan9) u64 { | |
| 360 | var fn_decl_count: u64 = 0; | |
| 361 | var itf_files = self.fn_decl_table.iterator(); | |
| 362 | while (itf_files.next()) |ent| { | |
| 363 | // get the submap | |
| 364 | var submap = ent.value_ptr.functions; | |
| 365 | fn_decl_count += submap.count(); | |
| 366 | } | |
| 367 | return self.data_decl_table.count() + fn_decl_count; | |
| 368 | } | |
| 369 | ||
| 212 | 370 | pub fn flushModule(self: *Plan9, comp: *Compilation) !void { |
| 213 | 371 | if (build_options.skip_non_native and builtin.object_format != .plan9) { |
| 214 | 372 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| ... | ... | @@ -224,15 +382,13 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { |
| 224 | 382 | |
| 225 | 383 | const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; |
| 226 | 384 | |
| 227 | // TODO I changed this assert from == to >= but this code all needs to be audited; see | |
| 228 | // the comment in `freeDecl`. | |
| 229 | assert(self.got_len >= self.fn_decl_table.count() + self.data_decl_table.count()); | |
| 385 | assert(self.got_len == self.declCount() + self.got_index_free_list.items.len); | |
| 230 | 386 | const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8; |
| 231 | 387 | var got_table = try self.base.allocator.alloc(u8, got_size); |
| 232 | 388 | defer self.base.allocator.free(got_table); |
| 233 | 389 | |
| 234 | // + 2 for header, got, symbols | |
| 235 | var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.fn_decl_table.count() + self.data_decl_table.count() + 3); | |
| 390 | // + 4 for header, got, symbols, linecountinfo | |
| 391 | var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.declCount() + 4); | |
| 236 | 392 | defer self.base.allocator.free(iovecs); |
| 237 | 393 | |
| 238 | 394 | const file = self.base.file.?; |
| ... | ... | @@ -245,30 +401,52 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { |
| 245 | 401 | iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len }; |
| 246 | 402 | var iovecs_i: usize = 1; |
| 247 | 403 | var text_i: u64 = 0; |
| 404 | ||
| 405 | var linecountinfo = std.ArrayList(u8).init(self.base.allocator); | |
| 406 | defer linecountinfo.deinit(); | |
| 248 | 407 | // text |
| 249 | 408 | { |
| 250 | var it = self.fn_decl_table.iterator(); | |
| 251 | while (it.next()) |entry| { | |
| 252 | const decl = entry.key_ptr.*; | |
| 253 | const code = entry.value_ptr.*; | |
| 254 | log.debug("write text decl {*} ({s})", .{ decl, decl.name }); | |
| 255 | foff += code.len; | |
| 256 | iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len }; | |
| 257 | iovecs_i += 1; | |
| 258 | const off = self.getAddr(text_i, .t); | |
| 259 | text_i += code.len; | |
| 260 | decl.link.plan9.offset = off; | |
| 261 | if (!self.sixtyfour_bit) { | |
| 262 | mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off)); | |
| 263 | mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); | |
| 264 | } else { | |
| 265 | mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); | |
| 266 | } | |
| 267 | self.syms.items[decl.link.plan9.sym_index.?].value = off; | |
| 268 | if (mod.decl_exports.get(decl)) |exports| { | |
| 269 | try self.addDeclExports(mod, decl, exports); | |
| 409 | var linecount: u32 = 0; | |
| 410 | var it_file = self.fn_decl_table.iterator(); | |
| 411 | while (it_file.next()) |fentry| { | |
| 412 | var it = fentry.value_ptr.functions.iterator(); | |
| 413 | while (it.next()) |entry| { | |
| 414 | const decl = entry.key_ptr.*; | |
| 415 | const out = entry.value_ptr.*; | |
| 416 | log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line, out.end_line }); | |
| 417 | { | |
| 418 | // connect the previous decl to the next | |
| 419 | const delta_line = @intCast(i32, out.start_line) - @intCast(i32, linecount); | |
| 420 | ||
| 421 | try changeLine(&linecountinfo, delta_line); | |
| 422 | // TODO change the pc too (maybe?) | |
| 423 | ||
| 424 | // write out the actual info that was generated in codegen now | |
| 425 | try linecountinfo.appendSlice(out.lineinfo); | |
| 426 | linecount = out.end_line; | |
| 427 | } | |
| 428 | foff += out.code.len; | |
| 429 | iovecs[iovecs_i] = .{ .iov_base = out.code.ptr, .iov_len = out.code.len }; | |
| 430 | iovecs_i += 1; | |
| 431 | const off = self.getAddr(text_i, .t); | |
| 432 | text_i += out.code.len; | |
| 433 | decl.link.plan9.offset = off; | |
| 434 | if (!self.sixtyfour_bit) { | |
| 435 | mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off)); | |
| 436 | mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); | |
| 437 | } else { | |
| 438 | mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); | |
| 439 | } | |
| 440 | self.syms.items[decl.link.plan9.sym_index.?].value = off; | |
| 441 | if (mod.decl_exports.get(decl)) |exports| { | |
| 442 | try self.addDeclExports(mod, decl, exports); | |
| 443 | } | |
| 270 | 444 | } |
| 271 | 445 | } |
| 446 | if (linecountinfo.items.len & 1 == 1) { | |
| 447 | // just a nop to make it even, the plan9 linker does this | |
| 448 | try linecountinfo.append(129); | |
| 449 | } | |
| 272 | 450 | // etext symbol |
| 273 | 451 | self.syms.items[2].value = self.getAddr(text_i, .t); |
| 274 | 452 | } |
| ... | ... | @@ -306,20 +484,23 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { |
| 306 | 484 | // edata |
| 307 | 485 | self.syms.items[1].value = self.getAddr(0x0, .b); |
| 308 | 486 | var sym_buf = std.ArrayList(u8).init(self.base.allocator); |
| 309 | defer sym_buf.deinit(); | |
| 310 | 487 | try self.writeSyms(&sym_buf); |
| 311 | assert(2 + self.fn_decl_table.count() + self.data_decl_table.count() == iovecs_i); // we didn't write all the decls | |
| 312 | iovecs[iovecs_i] = .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len }; | |
| 488 | const syms = sym_buf.toOwnedSlice(); | |
| 489 | defer self.base.allocator.free(syms); | |
| 490 | assert(2 + self.declCount() == iovecs_i); // we didn't write all the decls | |
| 491 | iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len }; | |
| 492 | iovecs_i += 1; | |
| 493 | iovecs[iovecs_i] = .{ .iov_base = linecountinfo.items.ptr, .iov_len = linecountinfo.items.len }; | |
| 313 | 494 | iovecs_i += 1; |
| 314 | 495 | // generate the header |
| 315 | 496 | self.hdr = .{ |
| 316 | .magic = try aout.magicFromArch(self.base.options.target.cpu.arch), | |
| 497 | .magic = self.magic, | |
| 317 | 498 | .text = @intCast(u32, text_i), |
| 318 | 499 | .data = @intCast(u32, data_i), |
| 319 | .syms = @intCast(u32, sym_buf.items.len), | |
| 500 | .syms = @intCast(u32, syms.len), | |
| 320 | 501 | .bss = 0, |
| 321 | .pcsz = 0, | |
| 322 | 502 | .spsz = 0, |
| 503 | .pcsz = @intCast(u32, linecountinfo.items.len), | |
| 323 | 504 | .entry = @intCast(u32, self.entry_val.?), |
| 324 | 505 | }; |
| 325 | 506 | std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]); |
| ... | ... | @@ -360,18 +541,43 @@ fn addDeclExports( |
| 360 | 541 | } |
| 361 | 542 | |
| 362 | 543 | pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void { |
| 363 | // TODO this is not the correct check for being function body, | |
| 364 | // it could just be a function pointer. | |
| 365 | 544 | // TODO audit the lifetimes of decls table entries. It's possible to get |
| 366 | 545 | // allocateDeclIndexes and then freeDecl without any updateDecl in between. |
| 367 | 546 | // However that is planned to change, see the TODO comment in Module.zig |
| 368 | 547 | // in the deleteUnusedDecl function. |
| 369 | const is_fn = (decl.ty.zigTypeTag() == .Fn); | |
| 548 | const is_fn = (decl.val.tag() == .function); | |
| 370 | 549 | if (is_fn) { |
| 371 | _ = self.fn_decl_table.swapRemove(decl); | |
| 550 | var symidx_and_submap = | |
| 551 | self.fn_decl_table.get(decl.namespace.file_scope).?; | |
| 552 | var submap = symidx_and_submap.functions; | |
| 553 | _ = submap.swapRemove(decl); | |
| 554 | if (submap.count() == 0) { | |
| 555 | self.syms.items[symidx_and_submap.sym_index] = aout.Sym.undefined_symbol; | |
| 556 | self.syms_index_free_list.append(self.base.allocator, symidx_and_submap.sym_index) catch {}; | |
| 557 | submap.deinit(self.base.allocator); | |
| 558 | } | |
| 372 | 559 | } else { |
| 373 | 560 | _ = self.data_decl_table.swapRemove(decl); |
| 374 | 561 | } |
| 562 | if (decl.link.plan9.got_index) |i| { | |
| 563 | // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length | |
| 564 | self.got_index_free_list.append(self.base.allocator, i) catch {}; | |
| 565 | } | |
| 566 | if (decl.link.plan9.sym_index) |i| { | |
| 567 | self.syms_index_free_list.append(self.base.allocator, i) catch {}; | |
| 568 | self.syms.items[i] = aout.Sym.undefined_symbol; | |
| 569 | } | |
| 570 | } | |
| 571 | ||
| 572 | pub fn seeDecl(self: *Plan9, decl: *Module.Decl) !void { | |
| 573 | if (decl.link.plan9.got_index == null) { | |
| 574 | if (self.got_index_free_list.popOrNull()) |i| { | |
| 575 | decl.link.plan9.got_index = i; | |
| 576 | } else { | |
| 577 | self.got_len += 1; | |
| 578 | decl.link.plan9.got_index = self.got_len - 1; | |
| 579 | } | |
| 580 | } | |
| 375 | 581 | } |
| 376 | 582 | |
| 377 | 583 | pub fn updateDeclExports( |
| ... | ... | @@ -380,6 +586,7 @@ pub fn updateDeclExports( |
| 380 | 586 | decl: *Module.Decl, |
| 381 | 587 | exports: []const *Module.Export, |
| 382 | 588 | ) !void { |
| 589 | try self.seeDecl(decl); | |
| 383 | 590 | // we do all the things in flush |
| 384 | 591 | _ = self; |
| 385 | 592 | _ = module; |
| ... | ... | @@ -387,17 +594,29 @@ pub fn updateDeclExports( |
| 387 | 594 | _ = exports; |
| 388 | 595 | } |
| 389 | 596 | pub fn deinit(self: *Plan9) void { |
| 390 | var itf = self.fn_decl_table.iterator(); | |
| 391 | while (itf.next()) |entry| { | |
| 392 | self.base.allocator.free(entry.value_ptr.*); | |
| 597 | const gpa = self.base.allocator; | |
| 598 | var itf_files = self.fn_decl_table.iterator(); | |
| 599 | while (itf_files.next()) |ent| { | |
| 600 | // get the submap | |
| 601 | var submap = ent.value_ptr.functions; | |
| 602 | defer submap.deinit(gpa); | |
| 603 | var itf = submap.iterator(); | |
| 604 | while (itf.next()) |entry| { | |
| 605 | gpa.free(entry.value_ptr.code); | |
| 606 | gpa.free(entry.value_ptr.lineinfo); | |
| 607 | } | |
| 393 | 608 | } |
| 394 | self.fn_decl_table.deinit(self.base.allocator); | |
| 609 | self.fn_decl_table.deinit(gpa); | |
| 395 | 610 | var itd = self.data_decl_table.iterator(); |
| 396 | 611 | while (itd.next()) |entry| { |
| 397 | self.base.allocator.free(entry.value_ptr.*); | |
| 612 | gpa.free(entry.value_ptr.*); | |
| 398 | 613 | } |
| 399 | self.data_decl_table.deinit(self.base.allocator); | |
| 400 | self.syms.deinit(self.base.allocator); | |
| 614 | self.data_decl_table.deinit(gpa); | |
| 615 | self.syms.deinit(gpa); | |
| 616 | self.got_index_free_list.deinit(gpa); | |
| 617 | self.syms_index_free_list.deinit(gpa); | |
| 618 | self.file_segments.deinit(gpa); | |
| 619 | self.path_arena.deinit(); | |
| 401 | 620 | } |
| 402 | 621 | |
| 403 | 622 | pub const Export = ?usize; |
| ... | ... | @@ -407,7 +626,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 407 | 626 | return error.LLVMBackendDoesNotSupportPlan9; |
| 408 | 627 | assert(options.object_format == .plan9); |
| 409 | 628 | const file = try options.emit.?.directory.handle.createFile(sub_path, .{ |
| 410 | .truncate = false, | |
| 411 | 629 | .read = true, |
| 412 | 630 | .mode = link.determineMode(options), |
| 413 | 631 | }); |
| ... | ... | @@ -441,27 +659,77 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 441 | 659 | return self; |
| 442 | 660 | } |
| 443 | 661 | |
| 662 | pub fn writeSym(self: *Plan9, w: anytype, sym: aout.Sym) !void { | |
| 663 | log.debug("write sym.name: {s}", .{sym.name}); | |
| 664 | log.debug("write sym.value: {x}", .{sym.value}); | |
| 665 | if (sym.type == .bad) return; // we don't want to write free'd symbols | |
| 666 | if (!self.sixtyfour_bit) { | |
| 667 | try w.writeIntBig(u32, @intCast(u32, sym.value)); | |
| 668 | } else { | |
| 669 | try w.writeIntBig(u64, sym.value); | |
| 670 | } | |
| 671 | try w.writeByte(@enumToInt(sym.type)); | |
| 672 | try w.writeAll(sym.name); | |
| 673 | try w.writeByte(0); | |
| 674 | } | |
| 444 | 675 | pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { |
| 445 | 676 | const writer = buf.writer(); |
| 446 | for (self.syms.items) |sym| { | |
| 447 | log.debug("sym.name: {s}", .{sym.name}); | |
| 448 | log.debug("sym.value: {x}", .{sym.value}); | |
| 449 | if (mem.eql(u8, sym.name, "_start")) | |
| 450 | self.entry_val = sym.value; | |
| 451 | if (!self.sixtyfour_bit) { | |
| 452 | try writer.writeIntBig(u32, @intCast(u32, sym.value)); | |
| 453 | } else { | |
| 454 | try writer.writeIntBig(u64, sym.value); | |
| 677 | // write the f symbols | |
| 678 | { | |
| 679 | var it = self.file_segments.iterator(); | |
| 680 | while (it.next()) |entry| { | |
| 681 | try self.writeSym(writer, .{ | |
| 682 | .type = .f, | |
| 683 | .value = entry.value_ptr.*, | |
| 684 | .name = entry.key_ptr.*, | |
| 685 | }); | |
| 686 | } | |
| 687 | } | |
| 688 | // write the data symbols | |
| 689 | { | |
| 690 | var it = self.data_decl_table.iterator(); | |
| 691 | while (it.next()) |entry| { | |
| 692 | const decl = entry.key_ptr.*; | |
| 693 | const sym = self.syms.items[decl.link.plan9.sym_index.?]; | |
| 694 | try self.writeSym(writer, sym); | |
| 695 | if (self.base.options.module.?.decl_exports.get(decl)) |exports| { | |
| 696 | for (exports) |e| { | |
| 697 | try self.writeSym(writer, self.syms.items[e.link.plan9.?]); | |
| 698 | } | |
| 699 | } | |
| 700 | } | |
| 701 | } | |
| 702 | // text symbols are the hardest: | |
| 703 | // the file of a text symbol is the .z symbol before it | |
| 704 | // so we have to write everything in the right order | |
| 705 | { | |
| 706 | var it_file = self.fn_decl_table.iterator(); | |
| 707 | while (it_file.next()) |fentry| { | |
| 708 | var symidx_and_submap = fentry.value_ptr; | |
| 709 | // write the z symbol | |
| 710 | try self.writeSym(writer, self.syms.items[symidx_and_submap.sym_index]); | |
| 711 | ||
| 712 | // write all the decls come from the file of the z symbol | |
| 713 | var submap_it = symidx_and_submap.functions.iterator(); | |
| 714 | while (submap_it.next()) |entry| { | |
| 715 | const decl = entry.key_ptr.*; | |
| 716 | const sym = self.syms.items[decl.link.plan9.sym_index.?]; | |
| 717 | try self.writeSym(writer, sym); | |
| 718 | if (self.base.options.module.?.decl_exports.get(decl)) |exports| { | |
| 719 | for (exports) |e| { | |
| 720 | const s = self.syms.items[e.link.plan9.?]; | |
| 721 | if (mem.eql(u8, s.name, "_start")) | |
| 722 | self.entry_val = s.value; | |
| 723 | try self.writeSym(writer, s); | |
| 724 | } | |
| 725 | } | |
| 726 | } | |
| 455 | 727 | } |
| 456 | try writer.writeByte(@enumToInt(sym.type)); | |
| 457 | try writer.writeAll(sym.name); | |
| 458 | try writer.writeByte(0); | |
| 459 | 728 | } |
| 460 | 729 | } |
| 461 | 730 | |
| 731 | /// this will be removed, moved to updateFinish | |
| 462 | 732 | pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void { |
| 463 | if (decl.link.plan9.got_index == null) { | |
| 464 | self.got_len += 1; | |
| 465 | decl.link.plan9.got_index = self.got_len - 1; | |
| 466 | } | |
| 733 | _ = self; | |
| 734 | _ = decl; | |
| 467 | 735 | } |
src/link/Plan9/aout.zig+18-1| ... | ... | @@ -16,7 +16,7 @@ pub const ExecHdr = extern struct { |
| 16 | 16 | comptime { |
| 17 | 17 | assert(@sizeOf(@This()) == 32); |
| 18 | 18 | } |
| 19 | /// It is up to the caller to disgard the last 8 bytes if the header is not fat. | |
| 19 | /// It is up to the caller to discard the last 8 bytes if the header is not fat. | |
| 20 | 20 | pub fn toU8s(self: *@This()) [40]u8 { |
| 21 | 21 | var buf: [40]u8 = undefined; |
| 22 | 22 | var i: u8 = 0; |
| ... | ... | @@ -34,6 +34,12 @@ pub const Sym = struct { |
| 34 | 34 | type: Type, |
| 35 | 35 | name: []const u8, |
| 36 | 36 | |
| 37 | pub const undefined_symbol: Sym = .{ | |
| 38 | .value = undefined, | |
| 39 | .type = .bad, | |
| 40 | .name = "undefined_symbol", | |
| 41 | }; | |
| 42 | ||
| 37 | 43 | /// The type field is one of the following characters with the |
| 38 | 44 | /// high bit set: |
| 39 | 45 | /// T text segment symbol |
| ... | ... | @@ -65,6 +71,8 @@ pub const Sym = struct { |
| 65 | 71 | z = 0x80 | 'z', |
| 66 | 72 | Z = 0x80 | 'Z', |
| 67 | 73 | m = 0x80 | 'm', |
| 74 | /// represents an undefined symbol, to be removed in flush | |
| 75 | bad = 0, | |
| 68 | 76 | |
| 69 | 77 | pub fn toGlobal(self: Type) Type { |
| 70 | 78 | return switch (self) { |
| ... | ... | @@ -112,3 +120,12 @@ pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 { |
| 112 | 120 | else => error.ArchNotSupportedByPlan9, |
| 113 | 121 | }; |
| 114 | 122 | } |
| 123 | ||
| 124 | /// gets the quantization of pc for the arch | |
| 125 | pub fn getPCQuant(arch: std.Target.Cpu.Arch) !u8 { | |
| 126 | return switch (arch) { | |
| 127 | .i386, .x86_64 => 1, | |
| 128 | .powerpc, .powerpc64, .mips, .sparc, .arm, .aarch64 => 4, | |
| 129 | else => error.ArchNotSupportedByPlan9, | |
| 130 | }; | |
| 131 | } |
src/link/SpirV.zig+2-2| ... | ... | @@ -12,7 +12,7 @@ |
| 12 | 12 | //! - OpName and OpMemberName instructions. |
| 13 | 13 | //! - OpModuleProcessed instructions. |
| 14 | 14 | //! All annotation (decoration) instructions. |
| 15 | //! All type declaration instructions, constant instructions, global variable declarations, (preferrably) OpUndef instructions. | |
| 15 | //! All type declaration instructions, constant instructions, global variable declarations, (preferably) OpUndef instructions. | |
| 16 | 16 | //! All function declarations without a body (extern functions presumably). |
| 17 | 17 | //! All regular functions. |
| 18 | 18 | |
| ... | ... | @@ -93,7 +93,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 93 | 93 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForSpirV; // TODO: LLVM Doesn't support SpirV at all. |
| 94 | 94 | if (options.use_lld) return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all. |
| 95 | 95 | |
| 96 | // TODO: read the file and keep vaild parts instead of truncating | |
| 96 | // TODO: read the file and keep valid parts instead of truncating | |
| 97 | 97 | const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); |
| 98 | 98 | errdefer file.close(); |
| 99 | 99 |
src/link/Wasm.zig+10-6| ... | ... | @@ -35,7 +35,7 @@ llvm_object: ?*LlvmObject = null, |
| 35 | 35 | /// TODO: can/should we access some data structure in Module directly? |
| 36 | 36 | funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, |
| 37 | 37 | /// List of all extern function Decls to be written to the `import` section of the |
| 38 | /// wasm binary. The positin in the list defines the function index | |
| 38 | /// wasm binary. The position in the list defines the function index | |
| 39 | 39 | ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, |
| 40 | 40 | /// When importing objects from the host environment, a name must be supplied. |
| 41 | 41 | /// LLVM uses "env" by default when none is given. This would be a good default for Zig |
| ... | ... | @@ -121,7 +121,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 121 | 121 | const self = try createEmpty(allocator, options); |
| 122 | 122 | errdefer self.base.destroy(); |
| 123 | 123 | |
| 124 | self.llvm_object = try LlvmObject.create(allocator, options); | |
| 124 | self.llvm_object = try LlvmObject.create(allocator, sub_path, options); | |
| 125 | 125 | return self; |
| 126 | 126 | } |
| 127 | 127 | |
| ... | ... | @@ -172,8 +172,8 @@ pub fn deinit(self: *Wasm) void { |
| 172 | 172 | pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { |
| 173 | 173 | if (decl.link.wasm.init) return; |
| 174 | 174 | |
| 175 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); | |
| 176 | try self.symbols.ensureCapacity(self.base.allocator, self.symbols.items.len + 1); | |
| 175 | try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); | |
| 176 | try self.symbols.ensureUnusedCapacity(self.base.allocator, 1); | |
| 177 | 177 | |
| 178 | 178 | const block = &decl.link.wasm; |
| 179 | 179 | block.init = true; |
| ... | ... | @@ -339,6 +339,10 @@ pub fn updateDeclExports( |
| 339 | 339 | } |
| 340 | 340 | |
| 341 | 341 | pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { |
| 342 | if (build_options.have_llvm) { | |
| 343 | if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); | |
| 344 | } | |
| 345 | ||
| 342 | 346 | if (self.getFuncidx(decl)) |func_idx| { |
| 343 | 347 | switch (decl.val.tag()) { |
| 344 | 348 | .function => _ = self.funcs.swapRemove(func_idx), |
| ... | ... | @@ -710,7 +714,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 710 | 714 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); |
| 711 | 715 | |
| 712 | 716 | if (self.base.options.output_mode == .Obj) { |
| 713 | // LLD's WASM driver does not support the equvialent of `-r` so we do a simple file copy | |
| 717 | // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy | |
| 714 | 718 | // here. TODO: think carefully about how we can avoid this redundant operation when doing |
| 715 | 719 | // build-obj. See also the corresponding TODO in linkAsArchive. |
| 716 | 720 | const the_object_path = blk: { |
| ... | ... | @@ -752,7 +756,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
| 752 | 756 | |
| 753 | 757 | if (self.base.options.output_mode == .Exe) { |
| 754 | 758 | // Increase the default stack size to a more reasonable value of 1MB instead of |
| 755 | // the default of 1 Wasm page being 64KB, unless overriden by the user. | |
| 759 | // the default of 1 Wasm page being 64KB, unless overridden by the user. | |
| 756 | 760 | try argv.append("-z"); |
| 757 | 761 | const stack_size = self.base.options.stack_size_override orelse 1048576; |
| 758 | 762 | const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); |
src/main.zig+48-13| ... | ... | @@ -20,6 +20,10 @@ const translate_c = @import("translate_c.zig"); |
| 20 | 20 | const Cache = @import("Cache.zig"); |
| 21 | 21 | const target_util = @import("target.zig"); |
| 22 | 22 | const ThreadPool = @import("ThreadPool.zig"); |
| 23 | const crash_report = @import("crash_report.zig"); | |
| 24 | ||
| 25 | // Crash report needs to override the panic handler and other root decls | |
| 26 | pub usingnamespace crash_report.root_decls; | |
| 23 | 27 | |
| 24 | 28 | pub fn fatal(comptime format: []const u8, args: anytype) noreturn { |
| 25 | 29 | std.log.emerg(format, args); |
| ... | ... | @@ -134,6 +138,8 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{ |
| 134 | 138 | }){}; |
| 135 | 139 | |
| 136 | 140 | pub fn main() anyerror!void { |
| 141 | crash_report.initialize(); | |
| 142 | ||
| 137 | 143 | var gpa_need_deinit = false; |
| 138 | 144 | const gpa = gpa: { |
| 139 | 145 | if (!std.builtin.link_libc) { |
| ... | ... | @@ -1151,6 +1157,7 @@ fn buildOutputType( |
| 1151 | 1157 | var is_shared_lib = false; |
| 1152 | 1158 | var linker_args = std.ArrayList([]const u8).init(arena); |
| 1153 | 1159 | var it = ClangArgIterator.init(arena, all_args); |
| 1160 | var emit_llvm = false; | |
| 1154 | 1161 | while (it.has_next) { |
| 1155 | 1162 | it.next() catch |err| { |
| 1156 | 1163 | fatal("unable to parse command line parameters: {s}", .{@errorName(err)}); |
| ... | ... | @@ -1161,6 +1168,7 @@ fn buildOutputType( |
| 1161 | 1168 | .c => c_out_mode = .object, // -c |
| 1162 | 1169 | .asm_only => c_out_mode = .assembly, // -S |
| 1163 | 1170 | .preprocess_only => c_out_mode = .preprocessor, // -E |
| 1171 | .emit_llvm => emit_llvm = true, | |
| 1164 | 1172 | .other => { |
| 1165 | 1173 | try clang_argv.appendSlice(it.other_args); |
| 1166 | 1174 | }, |
| ... | ... | @@ -1518,22 +1526,42 @@ fn buildOutputType( |
| 1518 | 1526 | output_mode = if (is_shared_lib) .Lib else .Exe; |
| 1519 | 1527 | emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out; |
| 1520 | 1528 | enable_cache = true; |
| 1529 | if (emit_llvm) { | |
| 1530 | fatal("-emit-llvm cannot be used when linking", .{}); | |
| 1531 | } | |
| 1521 | 1532 | }, |
| 1522 | 1533 | .object => { |
| 1523 | 1534 | output_mode = .Obj; |
| 1524 | if (out_path) |p| { | |
| 1525 | emit_bin = .{ .yes = p }; | |
| 1535 | if (emit_llvm) { | |
| 1536 | emit_bin = .no; | |
| 1537 | if (out_path) |p| { | |
| 1538 | emit_llvm_bc = .{ .yes = p }; | |
| 1539 | } else { | |
| 1540 | emit_llvm_bc = .yes_default_path; | |
| 1541 | } | |
| 1526 | 1542 | } else { |
| 1527 | emit_bin = .yes_default_path; | |
| 1543 | if (out_path) |p| { | |
| 1544 | emit_bin = .{ .yes = p }; | |
| 1545 | } else { | |
| 1546 | emit_bin = .yes_default_path; | |
| 1547 | } | |
| 1528 | 1548 | } |
| 1529 | 1549 | }, |
| 1530 | 1550 | .assembly => { |
| 1531 | 1551 | output_mode = .Obj; |
| 1532 | 1552 | emit_bin = .no; |
| 1533 | if (out_path) |p| { | |
| 1534 | emit_asm = .{ .yes = p }; | |
| 1553 | if (emit_llvm) { | |
| 1554 | if (out_path) |p| { | |
| 1555 | emit_llvm_ir = .{ .yes = p }; | |
| 1556 | } else { | |
| 1557 | emit_llvm_ir = .yes_default_path; | |
| 1558 | } | |
| 1535 | 1559 | } else { |
| 1536 | emit_asm = .yes_default_path; | |
| 1560 | if (out_path) |p| { | |
| 1561 | emit_asm = .{ .yes = p }; | |
| 1562 | } else { | |
| 1563 | emit_asm = .yes_default_path; | |
| 1564 | } | |
| 1537 | 1565 | } |
| 1538 | 1566 | }, |
| 1539 | 1567 | .preprocessor => { |
| ... | ... | @@ -1682,22 +1710,22 @@ fn buildOutputType( |
| 1682 | 1710 | } else true; |
| 1683 | 1711 | if (!should_get_sdk_path) break :outer false; |
| 1684 | 1712 | if (try std.zig.system.darwin.getSDKPath(arena, target_info.target)) |sdk_path| { |
| 1685 | try clang_argv.ensureCapacity(clang_argv.items.len + 2); | |
| 1713 | try clang_argv.ensureUnusedCapacity(2); | |
| 1686 | 1714 | clang_argv.appendAssumeCapacity("-isysroot"); |
| 1687 | 1715 | clang_argv.appendAssumeCapacity(sdk_path); |
| 1688 | 1716 | break :outer true; |
| 1689 | 1717 | } else break :outer false; |
| 1690 | 1718 | } else false; |
| 1691 | 1719 | |
| 1692 | try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2); | |
| 1720 | try clang_argv.ensureUnusedCapacity(paths.include_dirs.items.len * 2); | |
| 1693 | 1721 | const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem"; |
| 1694 | 1722 | for (paths.include_dirs.items) |include_dir| { |
| 1695 | 1723 | clang_argv.appendAssumeCapacity(isystem_flag); |
| 1696 | 1724 | clang_argv.appendAssumeCapacity(include_dir); |
| 1697 | 1725 | } |
| 1698 | 1726 | |
| 1699 | try clang_argv.ensureCapacity(clang_argv.items.len + paths.framework_dirs.items.len * 2); | |
| 1700 | try framework_dirs.ensureCapacity(framework_dirs.items.len + paths.framework_dirs.items.len); | |
| 1727 | try clang_argv.ensureUnusedCapacity(paths.framework_dirs.items.len * 2); | |
| 1728 | try framework_dirs.ensureUnusedCapacity(paths.framework_dirs.items.len); | |
| 1701 | 1729 | const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework"; |
| 1702 | 1730 | for (paths.framework_dirs.items) |framework_dir| { |
| 1703 | 1731 | clang_argv.appendAssumeCapacity(iframework_flag); |
| ... | ... | @@ -2761,7 +2789,7 @@ pub fn cmdInit( |
| 2761 | 2789 | fatal("unable to read template file 'build.zig': {s}", .{@errorName(err)}); |
| 2762 | 2790 | }; |
| 2763 | 2791 | var modified_build_zig_contents = std.ArrayList(u8).init(arena); |
| 2764 | try modified_build_zig_contents.ensureCapacity(build_zig_contents.len); | |
| 2792 | try modified_build_zig_contents.ensureTotalCapacity(build_zig_contents.len); | |
| 2765 | 2793 | for (build_zig_contents) |c| { |
| 2766 | 2794 | if (c == '$') { |
| 2767 | 2795 | try modified_build_zig_contents.appendSlice(cwd_basename); |
| ... | ... | @@ -2778,6 +2806,12 @@ pub fn cmdInit( |
| 2778 | 2806 | error.FileNotFound => {}, |
| 2779 | 2807 | else => fatal("unable to test existence of build.zig: {s}\n", .{@errorName(err)}), |
| 2780 | 2808 | } |
| 2809 | if (fs.cwd().access("src" ++ s ++ "main.zig", .{})) |_| { | |
| 2810 | fatal("existing src" ++ s ++ "main.zig file would be overwritten", .{}); | |
| 2811 | } else |err| switch (err) { | |
| 2812 | error.FileNotFound => {}, | |
| 2813 | else => fatal("unable to test existence of src" ++ s ++ "main.zig: {s}\n", .{@errorName(err)}), | |
| 2814 | } | |
| 2781 | 2815 | var src_dir = try fs.cwd().makeOpenPath("src", .{}); |
| 2782 | 2816 | defer src_dir.close(); |
| 2783 | 2817 | |
| ... | ... | @@ -3442,7 +3476,7 @@ fn fmtPathFile( |
| 3442 | 3476 | |
| 3443 | 3477 | // As a heuristic, we make enough capacity for the same as the input source. |
| 3444 | 3478 | fmt.out_buffer.shrinkRetainingCapacity(0); |
| 3445 | try fmt.out_buffer.ensureCapacity(source_code.len); | |
| 3479 | try fmt.out_buffer.ensureTotalCapacity(source_code.len); | |
| 3446 | 3480 | |
| 3447 | 3481 | try tree.renderToArrayList(&fmt.out_buffer); |
| 3448 | 3482 | if (mem.eql(u8, fmt.out_buffer.items, source_code)) |
| ... | ... | @@ -3663,6 +3697,7 @@ pub const ClangArgIterator = struct { |
| 3663 | 3697 | no_red_zone, |
| 3664 | 3698 | strip, |
| 3665 | 3699 | exec_model, |
| 3700 | emit_llvm, | |
| 3666 | 3701 | }; |
| 3667 | 3702 | |
| 3668 | 3703 | const Args = struct { |
| ... | ... | @@ -4109,7 +4144,7 @@ pub fn cmdAstCheck( |
| 4109 | 4144 | // zig fmt: on |
| 4110 | 4145 | } |
| 4111 | 4146 | |
| 4112 | return Zir.renderAsTextToFile(gpa, &file, io.getStdOut()); | |
| 4147 | return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut()); | |
| 4113 | 4148 | } |
| 4114 | 4149 | |
| 4115 | 4150 | /// This is only enabled for debug builds. |
src/mingw.zig+2-1| ... | ... | @@ -312,7 +312,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { |
| 312 | 312 | if (try man.hit()) { |
| 313 | 313 | const digest = man.final(); |
| 314 | 314 | |
| 315 | try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1); | |
| 315 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); | |
| 316 | 316 | comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{ |
| 317 | 317 | .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ |
| 318 | 318 | "o", &digest, final_lib_basename, |
| ... | ... | @@ -857,6 +857,7 @@ const mingwex_generic_src = [_][]const u8{ |
| 857 | 857 | "stdio" ++ path.sep_str ++ "fopen64.c", |
| 858 | 858 | "stdio" ++ path.sep_str ++ "fseeko32.c", |
| 859 | 859 | "stdio" ++ path.sep_str ++ "fseeko64.c", |
| 860 | "stdio" ++ path.sep_str ++ "fseeki64.c", | |
| 860 | 861 | "stdio" ++ path.sep_str ++ "fsetpos64.c", |
| 861 | 862 | "stdio" ++ path.sep_str ++ "ftello.c", |
| 862 | 863 | "stdio" ++ path.sep_str ++ "ftello64.c", |
src/musl.zig+2-2| ... | ... | @@ -112,7 +112,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 112 | 112 | var source_table = std.StringArrayHashMap(Ext).init(comp.gpa); |
| 113 | 113 | defer source_table.deinit(); |
| 114 | 114 | |
| 115 | try source_table.ensureCapacity(compat_time32_files.len + src_files.len); | |
| 115 | try source_table.ensureTotalCapacity(compat_time32_files.len + src_files.len); | |
| 116 | 116 | |
| 117 | 117 | for (src_files) |src_file| { |
| 118 | 118 | try addSrcFile(arena, &source_table, src_file); |
| ... | ... | @@ -231,7 +231,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 231 | 231 | |
| 232 | 232 | try sub_compilation.updateSubCompilation(); |
| 233 | 233 | |
| 234 | try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1); | |
| 234 | try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); | |
| 235 | 235 | |
| 236 | 236 | const basename = try comp.gpa.dupe(u8, "libc.so"); |
| 237 | 237 | errdefer comp.gpa.free(basename); |
src/print_air.zig+81-1| ... | ... | @@ -104,12 +104,16 @@ const Writer = struct { |
| 104 | 104 | |
| 105 | 105 | .add, |
| 106 | 106 | .addwrap, |
| 107 | .add_sat, | |
| 107 | 108 | .sub, |
| 108 | 109 | .subwrap, |
| 110 | .sub_sat, | |
| 109 | 111 | .mul, |
| 110 | 112 | .mulwrap, |
| 113 | .mul_sat, | |
| 111 | 114 | .div, |
| 112 | 115 | .rem, |
| 116 | .mod, | |
| 113 | 117 | .ptr_add, |
| 114 | 118 | .ptr_sub, |
| 115 | 119 | .bit_and, |
| ... | ... | @@ -129,7 +133,10 @@ const Writer = struct { |
| 129 | 133 | .ptr_elem_val, |
| 130 | 134 | .ptr_ptr_elem_val, |
| 131 | 135 | .shl, |
| 136 | .shl_exact, | |
| 137 | .shl_sat, | |
| 132 | 138 | .shr, |
| 139 | .set_union_tag, | |
| 133 | 140 | => try w.writeBinOp(s, inst), |
| 134 | 141 | |
| 135 | 142 | .is_null, |
| ... | ... | @@ -156,7 +163,8 @@ const Writer = struct { |
| 156 | 163 | .not, |
| 157 | 164 | .bitcast, |
| 158 | 165 | .load, |
| 159 | .floatcast, | |
| 166 | .fptrunc, | |
| 167 | .fpext, | |
| 160 | 168 | .intcast, |
| 161 | 169 | .trunc, |
| 162 | 170 | .optional_payload, |
| ... | ... | @@ -175,6 +183,11 @@ const Writer = struct { |
| 175 | 183 | .struct_field_ptr_index_2, |
| 176 | 184 | .struct_field_ptr_index_3, |
| 177 | 185 | .array_to_slice, |
| 186 | .int_to_float, | |
| 187 | .float_to_int, | |
| 188 | .get_union_tag, | |
| 189 | .clz, | |
| 190 | .ctz, | |
| 178 | 191 | => try w.writeTyOp(s, inst), |
| 179 | 192 | |
| 180 | 193 | .block, |
| ... | ... | @@ -192,6 +205,15 @@ const Writer = struct { |
| 192 | 205 | .cond_br => try w.writeCondBr(s, inst), |
| 193 | 206 | .switch_br => try w.writeSwitchBr(s, inst), |
| 194 | 207 | .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst), |
| 208 | .fence => try w.writeFence(s, inst), | |
| 209 | .atomic_load => try w.writeAtomicLoad(s, inst), | |
| 210 | .atomic_store_unordered => try w.writeAtomicStore(s, inst, .Unordered), | |
| 211 | .atomic_store_monotonic => try w.writeAtomicStore(s, inst, .Monotonic), | |
| 212 | .atomic_store_release => try w.writeAtomicStore(s, inst, .Release), | |
| 213 | .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst), | |
| 214 | .atomic_rmw => try w.writeAtomicRmw(s, inst), | |
| 215 | .memcpy => try w.writeMemcpy(s, inst), | |
| 216 | .memset => try w.writeMemset(s, inst), | |
| 195 | 217 | } |
| 196 | 218 | } |
| 197 | 219 | |
| ... | ... | @@ -276,6 +298,64 @@ const Writer = struct { |
| 276 | 298 | }); |
| 277 | 299 | } |
| 278 | 300 | |
| 301 | fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | |
| 302 | const atomic_order = w.air.instructions.items(.data)[inst].fence; | |
| 303 | ||
| 304 | try s.print("{s}", .{@tagName(atomic_order)}); | |
| 305 | } | |
| 306 | ||
| 307 | fn writeAtomicLoad(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | |
| 308 | const atomic_load = w.air.instructions.items(.data)[inst].atomic_load; | |
| 309 | ||
| 310 | try w.writeOperand(s, inst, 0, atomic_load.ptr); | |
| 311 | try s.print(", {s}", .{@tagName(atomic_load.order)}); | |
| 312 | } | |
| 313 | ||
| 314 | fn writeAtomicStore( | |
| 315 | w: *Writer, | |
| 316 | s: anytype, | |
| 317 | inst: Air.Inst.Index, | |
| 318 | order: std.builtin.AtomicOrder, | |
| 319 | ) @TypeOf(s).Error!void { | |
| 320 | const bin_op = w.air.instructions.items(.data)[inst].bin_op; | |
| 321 | try w.writeOperand(s, inst, 0, bin_op.lhs); | |
| 322 | try s.writeAll(", "); | |
| 323 | try w.writeOperand(s, inst, 1, bin_op.rhs); | |
| 324 | try s.print(", {s}", .{@tagName(order)}); | |
| 325 | } | |
| 326 | ||
| 327 | fn writeAtomicRmw(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | |
| 328 | const pl_op = w.air.instructions.items(.data)[inst].pl_op; | |
| 329 | const extra = w.air.extraData(Air.AtomicRmw, pl_op.payload).data; | |
| 330 | ||
| 331 | try w.writeOperand(s, inst, 0, pl_op.operand); | |
| 332 | try s.writeAll(", "); | |
| 333 | try w.writeOperand(s, inst, 1, extra.operand); | |
| 334 | try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); | |
| 335 | } | |
| 336 | ||
| 337 | fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | |
| 338 | const pl_op = w.air.instructions.items(.data)[inst].pl_op; | |
| 339 | const extra = w.air.extraData(Air.Bin, pl_op.payload).data; | |
| 340 | ||
| 341 | try w.writeOperand(s, inst, 0, pl_op.operand); | |
| 342 | try s.writeAll(", "); | |
| 343 | try w.writeOperand(s, inst, 1, extra.lhs); | |
| 344 | try s.writeAll(", "); | |
| 345 | try w.writeOperand(s, inst, 2, extra.rhs); | |
| 346 | } | |
| 347 | ||
| 348 | fn writeMemcpy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | |
| 349 | const pl_op = w.air.instructions.items(.data)[inst].pl_op; | |
| 350 | const extra = w.air.extraData(Air.Bin, pl_op.payload).data; | |
| 351 | ||
| 352 | try w.writeOperand(s, inst, 0, pl_op.operand); | |
| 353 | try s.writeAll(", "); | |
| 354 | try w.writeOperand(s, inst, 1, extra.lhs); | |
| 355 | try s.writeAll(", "); | |
| 356 | try w.writeOperand(s, inst, 2, extra.rhs); | |
| 357 | } | |
| 358 | ||
| 279 | 359 | fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 280 | 360 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; |
| 281 | 361 | const val = w.air.values[ty_pl.payload]; |
src/print_zir.zig created+2040| ... | ... | @@ -0,0 +1,2040 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const Allocator = std.mem.Allocator; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const Ast = std.zig.Ast; | |
| 6 | ||
| 7 | const Zir = @import("Zir.zig"); | |
| 8 | const Module = @import("Module.zig"); | |
| 9 | const LazySrcLoc = Module.LazySrcLoc; | |
| 10 | ||
| 11 | /// Write human-readable, debug formatted ZIR code to a file. | |
| 12 | pub fn renderAsTextToFile( | |
| 13 | gpa: *Allocator, | |
| 14 | scope_file: *Module.Scope.File, | |
| 15 | fs_file: std.fs.File, | |
| 16 | ) !void { | |
| 17 | var arena = std.heap.ArenaAllocator.init(gpa); | |
| 18 | defer arena.deinit(); | |
| 19 | ||
| 20 | var writer: Writer = .{ | |
| 21 | .gpa = gpa, | |
| 22 | .arena = &arena.allocator, | |
| 23 | .file = scope_file, | |
| 24 | .code = scope_file.zir, | |
| 25 | .indent = 0, | |
| 26 | .parent_decl_node = 0, | |
| 27 | .recurse_decls = true, | |
| 28 | .recurse_blocks = true, | |
| 29 | }; | |
| 30 | ||
| 31 | var raw_stream = std.io.bufferedWriter(fs_file.writer()); | |
| 32 | const stream = raw_stream.writer(); | |
| 33 | ||
| 34 | const main_struct_inst = Zir.main_struct_inst; | |
| 35 | try stream.print("%{d} ", .{main_struct_inst}); | |
| 36 | try writer.writeInstToStream(stream, main_struct_inst); | |
| 37 | try stream.writeAll("\n"); | |
| 38 | const imports_index = scope_file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)]; | |
| 39 | if (imports_index != 0) { | |
| 40 | try stream.writeAll("Imports:\n"); | |
| 41 | ||
| 42 | const extra = scope_file.zir.extraData(Zir.Inst.Imports, imports_index); | |
| 43 | var import_i: u32 = 0; | |
| 44 | var extra_index = extra.end; | |
| 45 | ||
| 46 | while (import_i < extra.data.imports_len) : (import_i += 1) { | |
| 47 | const item = scope_file.zir.extraData(Zir.Inst.Imports.Item, extra_index); | |
| 48 | extra_index = item.end; | |
| 49 | ||
| 50 | const src: LazySrcLoc = .{ .token_abs = item.data.token }; | |
| 51 | const import_path = scope_file.zir.nullTerminatedString(item.data.name); | |
| 52 | try stream.print(" @import(\"{}\") ", .{ | |
| 53 | std.zig.fmtEscapes(import_path), | |
| 54 | }); | |
| 55 | try writer.writeSrc(stream, src); | |
| 56 | try stream.writeAll("\n"); | |
| 57 | } | |
| 58 | } | |
| 59 | ||
| 60 | try raw_stream.flush(); | |
| 61 | } | |
| 62 | ||
| 63 | pub fn renderInstructionContext( | |
| 64 | gpa: *Allocator, | |
| 65 | block: []const Zir.Inst.Index, | |
| 66 | block_index: usize, | |
| 67 | scope_file: *Module.Scope.File, | |
| 68 | parent_decl_node: Ast.Node.Index, | |
| 69 | indent: u32, | |
| 70 | stream: anytype, | |
| 71 | ) !void { | |
| 72 | var arena = std.heap.ArenaAllocator.init(gpa); | |
| 73 | defer arena.deinit(); | |
| 74 | ||
| 75 | var writer: Writer = .{ | |
| 76 | .gpa = gpa, | |
| 77 | .arena = &arena.allocator, | |
| 78 | .file = scope_file, | |
| 79 | .code = scope_file.zir, | |
| 80 | .indent = if (indent < 2) 2 else indent, | |
| 81 | .parent_decl_node = parent_decl_node, | |
| 82 | .recurse_decls = false, | |
| 83 | .recurse_blocks = true, | |
| 84 | }; | |
| 85 | ||
| 86 | try writer.writeBody(stream, block[0..block_index]); | |
| 87 | try stream.writeByteNTimes(' ', writer.indent - 2); | |
| 88 | try stream.print("> %{d} ", .{block[block_index]}); | |
| 89 | try writer.writeInstToStream(stream, block[block_index]); | |
| 90 | try stream.writeByte('\n'); | |
| 91 | if (block_index + 1 < block.len) { | |
| 92 | try writer.writeBody(stream, block[block_index + 1 ..]); | |
| 93 | } | |
| 94 | } | |
| 95 | ||
| 96 | pub fn renderSingleInstruction( | |
| 97 | gpa: *Allocator, | |
| 98 | inst: Zir.Inst.Index, | |
| 99 | scope_file: *Module.Scope.File, | |
| 100 | parent_decl_node: Ast.Node.Index, | |
| 101 | indent: u32, | |
| 102 | stream: anytype, | |
| 103 | ) !void { | |
| 104 | var arena = std.heap.ArenaAllocator.init(gpa); | |
| 105 | defer arena.deinit(); | |
| 106 | ||
| 107 | var writer: Writer = .{ | |
| 108 | .gpa = gpa, | |
| 109 | .arena = &arena.allocator, | |
| 110 | .file = scope_file, | |
| 111 | .code = scope_file.zir, | |
| 112 | .indent = indent, | |
| 113 | .parent_decl_node = parent_decl_node, | |
| 114 | .recurse_decls = false, | |
| 115 | .recurse_blocks = false, | |
| 116 | }; | |
| 117 | ||
| 118 | try stream.print("%{d} ", .{inst}); | |
| 119 | try writer.writeInstToStream(stream, inst); | |
| 120 | } | |
| 121 | ||
| 122 | const Writer = struct { | |
| 123 | gpa: *Allocator, | |
| 124 | arena: *Allocator, | |
| 125 | file: *Module.Scope.File, | |
| 126 | code: Zir, | |
| 127 | indent: u32, | |
| 128 | parent_decl_node: Ast.Node.Index, | |
| 129 | recurse_decls: bool, | |
| 130 | recurse_blocks: bool, | |
| 131 | ||
| 132 | fn relativeToNodeIndex(self: *Writer, offset: i32) Ast.Node.Index { | |
| 133 | return @bitCast(Ast.Node.Index, offset + @bitCast(i32, self.parent_decl_node)); | |
| 134 | } | |
| 135 | ||
| 136 | fn writeInstToStream( | |
| 137 | self: *Writer, | |
| 138 | stream: anytype, | |
| 139 | inst: Zir.Inst.Index, | |
| 140 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 141 | const tags = self.code.instructions.items(.tag); | |
| 142 | const tag = tags[inst]; | |
| 143 | try stream.print("= {s}(", .{@tagName(tags[inst])}); | |
| 144 | switch (tag) { | |
| 145 | .array_type, | |
| 146 | .as, | |
| 147 | .coerce_result_ptr, | |
| 148 | .elem_ptr, | |
| 149 | .elem_val, | |
| 150 | .store, | |
| 151 | .store_to_block_ptr, | |
| 152 | .store_to_inferred_ptr, | |
| 153 | .field_ptr_type, | |
| 154 | => try self.writeBin(stream, inst), | |
| 155 | ||
| 156 | .alloc, | |
| 157 | .alloc_mut, | |
| 158 | .alloc_comptime, | |
| 159 | .indexable_ptr_len, | |
| 160 | .anyframe_type, | |
| 161 | .bit_not, | |
| 162 | .bool_not, | |
| 163 | .negate, | |
| 164 | .negate_wrap, | |
| 165 | .load, | |
| 166 | .ensure_result_used, | |
| 167 | .ensure_result_non_error, | |
| 168 | .ret_node, | |
| 169 | .ret_load, | |
| 170 | .resolve_inferred_alloc, | |
| 171 | .optional_type, | |
| 172 | .optional_payload_safe, | |
| 173 | .optional_payload_unsafe, | |
| 174 | .optional_payload_safe_ptr, | |
| 175 | .optional_payload_unsafe_ptr, | |
| 176 | .err_union_payload_safe, | |
| 177 | .err_union_payload_unsafe, | |
| 178 | .err_union_payload_safe_ptr, | |
| 179 | .err_union_payload_unsafe_ptr, | |
| 180 | .err_union_code, | |
| 181 | .err_union_code_ptr, | |
| 182 | .is_non_null, | |
| 183 | .is_non_null_ptr, | |
| 184 | .is_non_err, | |
| 185 | .is_non_err_ptr, | |
| 186 | .typeof, | |
| 187 | .typeof_elem, | |
| 188 | .struct_init_empty, | |
| 189 | .type_info, | |
| 190 | .size_of, | |
| 191 | .bit_size_of, | |
| 192 | .typeof_log2_int_type, | |
| 193 | .log2_int_type, | |
| 194 | .ptr_to_int, | |
| 195 | .error_to_int, | |
| 196 | .int_to_error, | |
| 197 | .compile_error, | |
| 198 | .set_eval_branch_quota, | |
| 199 | .enum_to_int, | |
| 200 | .align_of, | |
| 201 | .bool_to_int, | |
| 202 | .embed_file, | |
| 203 | .error_name, | |
| 204 | .panic, | |
| 205 | .set_align_stack, | |
| 206 | .set_cold, | |
| 207 | .set_float_mode, | |
| 208 | .set_runtime_safety, | |
| 209 | .sqrt, | |
| 210 | .sin, | |
| 211 | .cos, | |
| 212 | .exp, | |
| 213 | .exp2, | |
| 214 | .log, | |
| 215 | .log2, | |
| 216 | .log10, | |
| 217 | .fabs, | |
| 218 | .floor, | |
| 219 | .ceil, | |
| 220 | .trunc, | |
| 221 | .round, | |
| 222 | .tag_name, | |
| 223 | .reify, | |
| 224 | .type_name, | |
| 225 | .frame_type, | |
| 226 | .frame_size, | |
| 227 | .clz, | |
| 228 | .ctz, | |
| 229 | .pop_count, | |
| 230 | .byte_swap, | |
| 231 | .bit_reverse, | |
| 232 | .elem_type, | |
| 233 | .@"resume", | |
| 234 | .@"await", | |
| 235 | .await_nosuspend, | |
| 236 | .fence, | |
| 237 | => try self.writeUnNode(stream, inst), | |
| 238 | ||
| 239 | .ref, | |
| 240 | .ret_coerce, | |
| 241 | .ensure_err_payload_void, | |
| 242 | .closure_capture, | |
| 243 | => try self.writeUnTok(stream, inst), | |
| 244 | ||
| 245 | .bool_br_and, | |
| 246 | .bool_br_or, | |
| 247 | => try self.writeBoolBr(stream, inst), | |
| 248 | ||
| 249 | .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), | |
| 250 | .ptr_type_simple => try self.writePtrTypeSimple(stream, inst), | |
| 251 | .ptr_type => try self.writePtrType(stream, inst), | |
| 252 | .int => try self.writeInt(stream, inst), | |
| 253 | .int_big => try self.writeIntBig(stream, inst), | |
| 254 | .float => try self.writeFloat(stream, inst), | |
| 255 | .float128 => try self.writeFloat128(stream, inst), | |
| 256 | .str => try self.writeStr(stream, inst), | |
| 257 | .int_type => try self.writeIntType(stream, inst), | |
| 258 | ||
| 259 | .@"break", | |
| 260 | .break_inline, | |
| 261 | => try self.writeBreak(stream, inst), | |
| 262 | .array_init, | |
| 263 | .array_init_ref, | |
| 264 | => try self.writeArrayInit(stream, inst), | |
| 265 | ||
| 266 | .elem_ptr_node, | |
| 267 | .elem_val_node, | |
| 268 | .slice_start, | |
| 269 | .slice_end, | |
| 270 | .slice_sentinel, | |
| 271 | .array_init_anon, | |
| 272 | .array_init_anon_ref, | |
| 273 | .union_init_ptr, | |
| 274 | .shuffle, | |
| 275 | .select, | |
| 276 | .mul_add, | |
| 277 | .builtin_call, | |
| 278 | .field_parent_ptr, | |
| 279 | .builtin_async_call, | |
| 280 | => try self.writePlNode(stream, inst), | |
| 281 | ||
| 282 | .struct_init, | |
| 283 | .struct_init_ref, | |
| 284 | => try self.writeStructInit(stream, inst), | |
| 285 | ||
| 286 | .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst), | |
| 287 | .atomic_store => try self.writeAtomicStore(stream, inst), | |
| 288 | .atomic_rmw => try self.writeAtomicRmw(stream, inst), | |
| 289 | .memcpy => try self.writeMemcpy(stream, inst), | |
| 290 | .memset => try self.writeMemset(stream, inst), | |
| 291 | ||
| 292 | .struct_init_anon, | |
| 293 | .struct_init_anon_ref, | |
| 294 | => try self.writeStructInitAnon(stream, inst), | |
| 295 | ||
| 296 | .field_type => try self.writeFieldType(stream, inst), | |
| 297 | .field_type_ref => try self.writeFieldTypeRef(stream, inst), | |
| 298 | ||
| 299 | .add, | |
| 300 | .addwrap, | |
| 301 | .add_sat, | |
| 302 | .array_cat, | |
| 303 | .array_mul, | |
| 304 | .mul, | |
| 305 | .mulwrap, | |
| 306 | .mul_sat, | |
| 307 | .sub, | |
| 308 | .subwrap, | |
| 309 | .sub_sat, | |
| 310 | .cmp_lt, | |
| 311 | .cmp_lte, | |
| 312 | .cmp_eq, | |
| 313 | .cmp_gte, | |
| 314 | .cmp_gt, | |
| 315 | .cmp_neq, | |
| 316 | .div, | |
| 317 | .has_decl, | |
| 318 | .has_field, | |
| 319 | .mod_rem, | |
| 320 | .shl, | |
| 321 | .shl_exact, | |
| 322 | .shl_sat, | |
| 323 | .shr, | |
| 324 | .shr_exact, | |
| 325 | .xor, | |
| 326 | .store_node, | |
| 327 | .error_union_type, | |
| 328 | .merge_error_sets, | |
| 329 | .bit_and, | |
| 330 | .bit_or, | |
| 331 | .float_to_int, | |
| 332 | .int_to_float, | |
| 333 | .int_to_ptr, | |
| 334 | .int_to_enum, | |
| 335 | .float_cast, | |
| 336 | .int_cast, | |
| 337 | .err_set_cast, | |
| 338 | .ptr_cast, | |
| 339 | .truncate, | |
| 340 | .align_cast, | |
| 341 | .div_exact, | |
| 342 | .div_floor, | |
| 343 | .div_trunc, | |
| 344 | .mod, | |
| 345 | .rem, | |
| 346 | .bit_offset_of, | |
| 347 | .offset_of, | |
| 348 | .splat, | |
| 349 | .reduce, | |
| 350 | .atomic_load, | |
| 351 | .bitcast, | |
| 352 | .bitcast_result_ptr, | |
| 353 | .vector_type, | |
| 354 | .maximum, | |
| 355 | .minimum, | |
| 356 | => try self.writePlNodeBin(stream, inst), | |
| 357 | ||
| 358 | .@"export" => try self.writePlNodeExport(stream, inst), | |
| 359 | .export_value => try self.writePlNodeExportValue(stream, inst), | |
| 360 | ||
| 361 | .call => try self.writePlNodeCall(stream, inst), | |
| 362 | ||
| 363 | .block, | |
| 364 | .block_inline, | |
| 365 | .suspend_block, | |
| 366 | .loop, | |
| 367 | .validate_struct_init_ptr, | |
| 368 | .validate_array_init_ptr, | |
| 369 | .c_import, | |
| 370 | => try self.writePlNodeBlock(stream, inst), | |
| 371 | ||
| 372 | .condbr, | |
| 373 | .condbr_inline, | |
| 374 | => try self.writePlNodeCondBr(stream, inst), | |
| 375 | ||
| 376 | .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent), | |
| 377 | .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon), | |
| 378 | .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func), | |
| 379 | ||
| 380 | .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none), | |
| 381 | .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), | |
| 382 | .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under), | |
| 383 | .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none), | |
| 384 | .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), | |
| 385 | .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under), | |
| 386 | ||
| 387 | .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none), | |
| 388 | .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"), | |
| 389 | .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under), | |
| 390 | .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none), | |
| 391 | .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"), | |
| 392 | .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under), | |
| 393 | ||
| 394 | .field_ptr, | |
| 395 | .field_val, | |
| 396 | .field_call_bind, | |
| 397 | => try self.writePlNodeField(stream, inst), | |
| 398 | ||
| 399 | .field_ptr_named, | |
| 400 | .field_val_named, | |
| 401 | .field_call_bind_named, | |
| 402 | => try self.writePlNodeFieldNamed(stream, inst), | |
| 403 | ||
| 404 | .as_node => try self.writeAs(stream, inst), | |
| 405 | ||
| 406 | .breakpoint, | |
| 407 | .repeat, | |
| 408 | .repeat_inline, | |
| 409 | .alloc_inferred, | |
| 410 | .alloc_inferred_mut, | |
| 411 | .alloc_inferred_comptime, | |
| 412 | => try self.writeNode(stream, inst), | |
| 413 | ||
| 414 | .error_value, | |
| 415 | .enum_literal, | |
| 416 | .decl_ref, | |
| 417 | .decl_val, | |
| 418 | .import, | |
| 419 | .ret_err_value, | |
| 420 | .ret_err_value_code, | |
| 421 | .param_anytype, | |
| 422 | .param_anytype_comptime, | |
| 423 | => try self.writeStrTok(stream, inst), | |
| 424 | ||
| 425 | .param, .param_comptime => try self.writeParam(stream, inst), | |
| 426 | ||
| 427 | .func => try self.writeFunc(stream, inst, false), | |
| 428 | .func_inferred => try self.writeFunc(stream, inst, true), | |
| 429 | ||
| 430 | .@"unreachable" => try self.writeUnreachable(stream, inst), | |
| 431 | ||
| 432 | .switch_capture, | |
| 433 | .switch_capture_ref, | |
| 434 | .switch_capture_multi, | |
| 435 | .switch_capture_multi_ref, | |
| 436 | .switch_capture_else, | |
| 437 | .switch_capture_else_ref, | |
| 438 | => try self.writeSwitchCapture(stream, inst), | |
| 439 | ||
| 440 | .dbg_stmt => try self.writeDbgStmt(stream, inst), | |
| 441 | ||
| 442 | .closure_get => try self.writeInstNode(stream, inst), | |
| 443 | ||
| 444 | .extended => try self.writeExtended(stream, inst), | |
| 445 | } | |
| 446 | } | |
| 447 | ||
| 448 | fn writeExtended(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 449 | const extended = self.code.instructions.items(.data)[inst].extended; | |
| 450 | try stream.print("{s}(", .{@tagName(extended.opcode)}); | |
| 451 | switch (extended.opcode) { | |
| 452 | .ret_ptr, | |
| 453 | .ret_type, | |
| 454 | .this, | |
| 455 | .ret_addr, | |
| 456 | .error_return_trace, | |
| 457 | .frame, | |
| 458 | .frame_address, | |
| 459 | .builtin_src, | |
| 460 | => try self.writeExtNode(stream, extended), | |
| 461 | ||
| 462 | .@"asm" => try self.writeAsm(stream, extended), | |
| 463 | .func => try self.writeFuncExtended(stream, extended), | |
| 464 | .variable => try self.writeVarExtended(stream, extended), | |
| 465 | .alloc => try self.writeAllocExtended(stream, extended), | |
| 466 | ||
| 467 | .compile_log, | |
| 468 | .typeof_peer, | |
| 469 | => try self.writeNodeMultiOp(stream, extended), | |
| 470 | ||
| 471 | .add_with_overflow, | |
| 472 | .sub_with_overflow, | |
| 473 | .mul_with_overflow, | |
| 474 | .shl_with_overflow, | |
| 475 | => try self.writeOverflowArithmetic(stream, extended), | |
| 476 | ||
| 477 | .struct_decl => try self.writeStructDecl(stream, extended), | |
| 478 | .union_decl => try self.writeUnionDecl(stream, extended), | |
| 479 | .enum_decl => try self.writeEnumDecl(stream, extended), | |
| 480 | .opaque_decl => try self.writeOpaqueDecl(stream, extended), | |
| 481 | ||
| 482 | .c_undef, .c_include => { | |
| 483 | const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; | |
| 484 | try self.writeInstRef(stream, inst_data.operand); | |
| 485 | try stream.writeAll(") "); | |
| 486 | }, | |
| 487 | ||
| 488 | .c_define => { | |
| 489 | const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; | |
| 490 | try self.writeInstRef(stream, inst_data.lhs); | |
| 491 | try stream.writeAll(", "); | |
| 492 | try self.writeInstRef(stream, inst_data.rhs); | |
| 493 | try stream.writeByte(')'); | |
| 494 | }, | |
| 495 | ||
| 496 | .builtin_extern, | |
| 497 | .wasm_memory_size, | |
| 498 | .wasm_memory_grow, | |
| 499 | => try stream.writeAll("TODO))"), | |
| 500 | } | |
| 501 | } | |
| 502 | ||
| 503 | fn writeExtNode(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 504 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; | |
| 505 | try stream.writeAll(")) "); | |
| 506 | try self.writeSrc(stream, src); | |
| 507 | } | |
| 508 | ||
| 509 | fn writeBin(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 510 | const inst_data = self.code.instructions.items(.data)[inst].bin; | |
| 511 | try self.writeInstRef(stream, inst_data.lhs); | |
| 512 | try stream.writeAll(", "); | |
| 513 | try self.writeInstRef(stream, inst_data.rhs); | |
| 514 | try stream.writeByte(')'); | |
| 515 | } | |
| 516 | ||
| 517 | fn writeUnNode( | |
| 518 | self: *Writer, | |
| 519 | stream: anytype, | |
| 520 | inst: Zir.Inst.Index, | |
| 521 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 522 | const inst_data = self.code.instructions.items(.data)[inst].un_node; | |
| 523 | try self.writeInstRef(stream, inst_data.operand); | |
| 524 | try stream.writeAll(") "); | |
| 525 | try self.writeSrc(stream, inst_data.src()); | |
| 526 | } | |
| 527 | ||
| 528 | fn writeUnTok( | |
| 529 | self: *Writer, | |
| 530 | stream: anytype, | |
| 531 | inst: Zir.Inst.Index, | |
| 532 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 533 | const inst_data = self.code.instructions.items(.data)[inst].un_tok; | |
| 534 | try self.writeInstRef(stream, inst_data.operand); | |
| 535 | try stream.writeAll(") "); | |
| 536 | try self.writeSrc(stream, inst_data.src()); | |
| 537 | } | |
| 538 | ||
| 539 | fn writeArrayTypeSentinel( | |
| 540 | self: *Writer, | |
| 541 | stream: anytype, | |
| 542 | inst: Zir.Inst.Index, | |
| 543 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 544 | const inst_data = self.code.instructions.items(.data)[inst].array_type_sentinel; | |
| 545 | _ = inst_data; | |
| 546 | try stream.writeAll("TODO)"); | |
| 547 | } | |
| 548 | ||
| 549 | fn writePtrTypeSimple( | |
| 550 | self: *Writer, | |
| 551 | stream: anytype, | |
| 552 | inst: Zir.Inst.Index, | |
| 553 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 554 | const inst_data = self.code.instructions.items(.data)[inst].ptr_type_simple; | |
| 555 | const str_allowzero = if (inst_data.is_allowzero) "allowzero, " else ""; | |
| 556 | const str_const = if (!inst_data.is_mutable) "const, " else ""; | |
| 557 | const str_volatile = if (inst_data.is_volatile) "volatile, " else ""; | |
| 558 | try self.writeInstRef(stream, inst_data.elem_type); | |
| 559 | try stream.print(", {s}{s}{s}{s})", .{ | |
| 560 | str_allowzero, | |
| 561 | str_const, | |
| 562 | str_volatile, | |
| 563 | @tagName(inst_data.size), | |
| 564 | }); | |
| 565 | } | |
| 566 | ||
| 567 | fn writePtrType( | |
| 568 | self: *Writer, | |
| 569 | stream: anytype, | |
| 570 | inst: Zir.Inst.Index, | |
| 571 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 572 | const inst_data = self.code.instructions.items(.data)[inst].ptr_type; | |
| 573 | _ = inst_data; | |
| 574 | try stream.writeAll("TODO)"); | |
| 575 | } | |
| 576 | ||
| 577 | fn writeInt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 578 | const inst_data = self.code.instructions.items(.data)[inst].int; | |
| 579 | try stream.print("{d})", .{inst_data}); | |
| 580 | } | |
| 581 | ||
| 582 | fn writeIntBig(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 583 | const inst_data = self.code.instructions.items(.data)[inst].str; | |
| 584 | const byte_count = inst_data.len * @sizeOf(std.math.big.Limb); | |
| 585 | const limb_bytes = self.code.string_bytes[inst_data.start..][0..byte_count]; | |
| 586 | // limb_bytes is not aligned properly; we must allocate and copy the bytes | |
| 587 | // in order to accomplish this. | |
| 588 | const limbs = try self.gpa.alloc(std.math.big.Limb, inst_data.len); | |
| 589 | defer self.gpa.free(limbs); | |
| 590 | ||
| 591 | mem.copy(u8, mem.sliceAsBytes(limbs), limb_bytes); | |
| 592 | const big_int: std.math.big.int.Const = .{ | |
| 593 | .limbs = limbs, | |
| 594 | .positive = true, | |
| 595 | }; | |
| 596 | const as_string = try big_int.toStringAlloc(self.gpa, 10, .lower); | |
| 597 | defer self.gpa.free(as_string); | |
| 598 | try stream.print("{s})", .{as_string}); | |
| 599 | } | |
| 600 | ||
| 601 | fn writeFloat(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 602 | const number = self.code.instructions.items(.data)[inst].float; | |
| 603 | try stream.print("{d})", .{number}); | |
| 604 | } | |
| 605 | ||
| 606 | fn writeFloat128(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 607 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 608 | const extra = self.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data; | |
| 609 | const src = inst_data.src(); | |
| 610 | const number = extra.get(); | |
| 611 | // TODO improve std.format to be able to print f128 values | |
| 612 | try stream.print("{d}) ", .{@floatCast(f64, number)}); | |
| 613 | try self.writeSrc(stream, src); | |
| 614 | } | |
| 615 | ||
| 616 | fn writeStr( | |
| 617 | self: *Writer, | |
| 618 | stream: anytype, | |
| 619 | inst: Zir.Inst.Index, | |
| 620 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 621 | const inst_data = self.code.instructions.items(.data)[inst].str; | |
| 622 | const str = inst_data.get(self.code); | |
| 623 | try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)}); | |
| 624 | } | |
| 625 | ||
| 626 | fn writePlNode(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 627 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 628 | try stream.writeAll("TODO) "); | |
| 629 | try self.writeSrc(stream, inst_data.src()); | |
| 630 | } | |
| 631 | ||
| 632 | fn writeParam(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 633 | const inst_data = self.code.instructions.items(.data)[inst].pl_tok; | |
| 634 | const extra = self.code.extraData(Zir.Inst.Param, inst_data.payload_index); | |
| 635 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; | |
| 636 | try stream.print("\"{}\", ", .{ | |
| 637 | std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)), | |
| 638 | }); | |
| 639 | try self.writeBracedBody(stream, body); | |
| 640 | try stream.writeAll(") "); | |
| 641 | try self.writeSrc(stream, inst_data.src()); | |
| 642 | } | |
| 643 | ||
| 644 | fn writePlNodeBin(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 645 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 646 | const extra = self.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | |
| 647 | try self.writeInstRef(stream, extra.lhs); | |
| 648 | try stream.writeAll(", "); | |
| 649 | try self.writeInstRef(stream, extra.rhs); | |
| 650 | try stream.writeAll(") "); | |
| 651 | try self.writeSrc(stream, inst_data.src()); | |
| 652 | } | |
| 653 | ||
| 654 | fn writePlNodeExport(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 655 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 656 | const extra = self.code.extraData(Zir.Inst.Export, inst_data.payload_index).data; | |
| 657 | const decl_name = self.code.nullTerminatedString(extra.decl_name); | |
| 658 | ||
| 659 | try self.writeInstRef(stream, extra.namespace); | |
| 660 | try stream.print(", {}, ", .{std.zig.fmtId(decl_name)}); | |
| 661 | try self.writeInstRef(stream, extra.options); | |
| 662 | try stream.writeAll(") "); | |
| 663 | try self.writeSrc(stream, inst_data.src()); | |
| 664 | } | |
| 665 | ||
| 666 | fn writePlNodeExportValue(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 667 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 668 | const extra = self.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data; | |
| 669 | ||
| 670 | try self.writeInstRef(stream, extra.operand); | |
| 671 | try stream.writeAll(", "); | |
| 672 | try self.writeInstRef(stream, extra.options); | |
| 673 | try stream.writeAll(") "); | |
| 674 | try self.writeSrc(stream, inst_data.src()); | |
| 675 | } | |
| 676 | ||
| 677 | fn writeStructInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 678 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 679 | const extra = self.code.extraData(Zir.Inst.StructInit, inst_data.payload_index); | |
| 680 | var field_i: u32 = 0; | |
| 681 | var extra_index = extra.end; | |
| 682 | ||
| 683 | while (field_i < extra.data.fields_len) : (field_i += 1) { | |
| 684 | const item = self.code.extraData(Zir.Inst.StructInit.Item, extra_index); | |
| 685 | extra_index = item.end; | |
| 686 | ||
| 687 | if (field_i != 0) { | |
| 688 | try stream.writeAll(", ["); | |
| 689 | } else { | |
| 690 | try stream.writeAll("["); | |
| 691 | } | |
| 692 | try self.writeInstIndex(stream, item.data.field_type); | |
| 693 | try stream.writeAll(", "); | |
| 694 | try self.writeInstRef(stream, item.data.init); | |
| 695 | try stream.writeAll("]"); | |
| 696 | } | |
| 697 | try stream.writeAll(") "); | |
| 698 | try self.writeSrc(stream, inst_data.src()); | |
| 699 | } | |
| 700 | ||
| 701 | fn writeCmpxchg(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 702 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 703 | const extra = self.code.extraData(Zir.Inst.Cmpxchg, inst_data.payload_index).data; | |
| 704 | ||
| 705 | try self.writeInstRef(stream, extra.ptr); | |
| 706 | try stream.writeAll(", "); | |
| 707 | try self.writeInstRef(stream, extra.expected_value); | |
| 708 | try stream.writeAll(", "); | |
| 709 | try self.writeInstRef(stream, extra.new_value); | |
| 710 | try stream.writeAll(", "); | |
| 711 | try self.writeInstRef(stream, extra.success_order); | |
| 712 | try stream.writeAll(", "); | |
| 713 | try self.writeInstRef(stream, extra.failure_order); | |
| 714 | try stream.writeAll(") "); | |
| 715 | try self.writeSrc(stream, inst_data.src()); | |
| 716 | } | |
| 717 | ||
| 718 | fn writeAtomicStore(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 719 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 720 | const extra = self.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; | |
| 721 | ||
| 722 | try self.writeInstRef(stream, extra.ptr); | |
| 723 | try stream.writeAll(", "); | |
| 724 | try self.writeInstRef(stream, extra.operand); | |
| 725 | try stream.writeAll(", "); | |
| 726 | try self.writeInstRef(stream, extra.ordering); | |
| 727 | try stream.writeAll(") "); | |
| 728 | try self.writeSrc(stream, inst_data.src()); | |
| 729 | } | |
| 730 | ||
| 731 | fn writeAtomicRmw(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 732 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 733 | const extra = self.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; | |
| 734 | ||
| 735 | try self.writeInstRef(stream, extra.ptr); | |
| 736 | try stream.writeAll(", "); | |
| 737 | try self.writeInstRef(stream, extra.operation); | |
| 738 | try stream.writeAll(", "); | |
| 739 | try self.writeInstRef(stream, extra.operand); | |
| 740 | try stream.writeAll(", "); | |
| 741 | try self.writeInstRef(stream, extra.ordering); | |
| 742 | try stream.writeAll(") "); | |
| 743 | try self.writeSrc(stream, inst_data.src()); | |
| 744 | } | |
| 745 | ||
| 746 | fn writeMemcpy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 747 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 748 | const extra = self.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data; | |
| 749 | ||
| 750 | try self.writeInstRef(stream, extra.dest); | |
| 751 | try stream.writeAll(", "); | |
| 752 | try self.writeInstRef(stream, extra.source); | |
| 753 | try stream.writeAll(", "); | |
| 754 | try self.writeInstRef(stream, extra.byte_count); | |
| 755 | try stream.writeAll(") "); | |
| 756 | try self.writeSrc(stream, inst_data.src()); | |
| 757 | } | |
| 758 | ||
| 759 | fn writeMemset(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 760 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 761 | const extra = self.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data; | |
| 762 | ||
| 763 | try self.writeInstRef(stream, extra.dest); | |
| 764 | try stream.writeAll(", "); | |
| 765 | try self.writeInstRef(stream, extra.byte); | |
| 766 | try stream.writeAll(", "); | |
| 767 | try self.writeInstRef(stream, extra.byte_count); | |
| 768 | try stream.writeAll(") "); | |
| 769 | try self.writeSrc(stream, inst_data.src()); | |
| 770 | } | |
| 771 | ||
| 772 | fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 773 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 774 | const extra = self.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); | |
| 775 | var field_i: u32 = 0; | |
| 776 | var extra_index = extra.end; | |
| 777 | ||
| 778 | while (field_i < extra.data.fields_len) : (field_i += 1) { | |
| 779 | const item = self.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); | |
| 780 | extra_index = item.end; | |
| 781 | ||
| 782 | const field_name = self.code.nullTerminatedString(item.data.field_name); | |
| 783 | ||
| 784 | const prefix = if (field_i != 0) ", [" else "["; | |
| 785 | try stream.print("{s}[{s}=", .{ prefix, field_name }); | |
| 786 | try self.writeInstRef(stream, item.data.init); | |
| 787 | try stream.writeAll("]"); | |
| 788 | } | |
| 789 | try stream.writeAll(") "); | |
| 790 | try self.writeSrc(stream, inst_data.src()); | |
| 791 | } | |
| 792 | ||
| 793 | fn writeFieldType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 794 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 795 | const extra = self.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; | |
| 796 | try self.writeInstRef(stream, extra.container_type); | |
| 797 | const field_name = self.code.nullTerminatedString(extra.name_start); | |
| 798 | try stream.print(", {s}) ", .{field_name}); | |
| 799 | try self.writeSrc(stream, inst_data.src()); | |
| 800 | } | |
| 801 | ||
| 802 | fn writeFieldTypeRef(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 803 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 804 | const extra = self.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data; | |
| 805 | try self.writeInstRef(stream, extra.container_type); | |
| 806 | try stream.writeAll(", "); | |
| 807 | try self.writeInstRef(stream, extra.field_name); | |
| 808 | try stream.writeAll(") "); | |
| 809 | try self.writeSrc(stream, inst_data.src()); | |
| 810 | } | |
| 811 | ||
| 812 | fn writeNodeMultiOp(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 813 | const extra = self.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); | |
| 814 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; | |
| 815 | const operands = self.code.refSlice(extra.end, extended.small); | |
| 816 | ||
| 817 | for (operands) |operand, i| { | |
| 818 | if (i != 0) try stream.writeAll(", "); | |
| 819 | try self.writeInstRef(stream, operand); | |
| 820 | } | |
| 821 | try stream.writeAll(")) "); | |
| 822 | try self.writeSrc(stream, src); | |
| 823 | } | |
| 824 | ||
| 825 | fn writeInstNode( | |
| 826 | self: *Writer, | |
| 827 | stream: anytype, | |
| 828 | inst: Zir.Inst.Index, | |
| 829 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 830 | const inst_data = self.code.instructions.items(.data)[inst].inst_node; | |
| 831 | try self.writeInstIndex(stream, inst_data.inst); | |
| 832 | try stream.writeAll(") "); | |
| 833 | try self.writeSrc(stream, inst_data.src()); | |
| 834 | } | |
| 835 | ||
| 836 | fn writeAsm(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 837 | const extra = self.code.extraData(Zir.Inst.Asm, extended.operand); | |
| 838 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; | |
| 839 | const outputs_len = @truncate(u5, extended.small); | |
| 840 | const inputs_len = @truncate(u5, extended.small >> 5); | |
| 841 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 842 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | |
| 843 | const asm_source = self.code.nullTerminatedString(extra.data.asm_source); | |
| 844 | ||
| 845 | try self.writeFlag(stream, "volatile, ", is_volatile); | |
| 846 | try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)}); | |
| 847 | try stream.writeAll(", "); | |
| 848 | ||
| 849 | var extra_i: usize = extra.end; | |
| 850 | var output_type_bits = extra.data.output_type_bits; | |
| 851 | { | |
| 852 | var i: usize = 0; | |
| 853 | while (i < outputs_len) : (i += 1) { | |
| 854 | const output = self.code.extraData(Zir.Inst.Asm.Output, extra_i); | |
| 855 | extra_i = output.end; | |
| 856 | ||
| 857 | const is_type = @truncate(u1, output_type_bits) != 0; | |
| 858 | output_type_bits >>= 1; | |
| 859 | ||
| 860 | const name = self.code.nullTerminatedString(output.data.name); | |
| 861 | const constraint = self.code.nullTerminatedString(output.data.constraint); | |
| 862 | try stream.print("output({}, \"{}\", ", .{ | |
| 863 | std.zig.fmtId(name), std.zig.fmtEscapes(constraint), | |
| 864 | }); | |
| 865 | try self.writeFlag(stream, "->", is_type); | |
| 866 | try self.writeInstRef(stream, output.data.operand); | |
| 867 | try stream.writeAll(")"); | |
| 868 | if (i + 1 < outputs_len) { | |
| 869 | try stream.writeAll("), "); | |
| 870 | } | |
| 871 | } | |
| 872 | } | |
| 873 | { | |
| 874 | var i: usize = 0; | |
| 875 | while (i < inputs_len) : (i += 1) { | |
| 876 | const input = self.code.extraData(Zir.Inst.Asm.Input, extra_i); | |
| 877 | extra_i = input.end; | |
| 878 | ||
| 879 | const name = self.code.nullTerminatedString(input.data.name); | |
| 880 | const constraint = self.code.nullTerminatedString(input.data.constraint); | |
| 881 | try stream.print("input({}, \"{}\", ", .{ | |
| 882 | std.zig.fmtId(name), std.zig.fmtEscapes(constraint), | |
| 883 | }); | |
| 884 | try self.writeInstRef(stream, input.data.operand); | |
| 885 | try stream.writeAll(")"); | |
| 886 | if (i + 1 < inputs_len) { | |
| 887 | try stream.writeAll(", "); | |
| 888 | } | |
| 889 | } | |
| 890 | } | |
| 891 | { | |
| 892 | var i: usize = 0; | |
| 893 | while (i < clobbers_len) : (i += 1) { | |
| 894 | const str_index = self.code.extra[extra_i]; | |
| 895 | extra_i += 1; | |
| 896 | const clobber = self.code.nullTerminatedString(str_index); | |
| 897 | try stream.print("{}", .{std.zig.fmtId(clobber)}); | |
| 898 | if (i + 1 < clobbers_len) { | |
| 899 | try stream.writeAll(", "); | |
| 900 | } | |
| 901 | } | |
| 902 | } | |
| 903 | try stream.writeAll(")) "); | |
| 904 | try self.writeSrc(stream, src); | |
| 905 | } | |
| 906 | ||
| 907 | fn writeOverflowArithmetic(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 908 | const extra = self.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data; | |
| 909 | const src: LazySrcLoc = .{ .node_offset = extra.node }; | |
| 910 | ||
| 911 | try self.writeInstRef(stream, extra.lhs); | |
| 912 | try stream.writeAll(", "); | |
| 913 | try self.writeInstRef(stream, extra.rhs); | |
| 914 | try stream.writeAll(", "); | |
| 915 | try self.writeInstRef(stream, extra.ptr); | |
| 916 | try stream.writeAll(")) "); | |
| 917 | try self.writeSrc(stream, src); | |
| 918 | } | |
| 919 | ||
| 920 | fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 921 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 922 | const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index); | |
| 923 | const args = self.code.refSlice(extra.end, extra.data.flags.args_len); | |
| 924 | ||
| 925 | if (extra.data.flags.ensure_result_used) { | |
| 926 | try stream.writeAll("nodiscard "); | |
| 927 | } | |
| 928 | try stream.print(".{s}, ", .{@tagName(@intToEnum(std.builtin.CallOptions.Modifier, extra.data.flags.packed_modifier))}); | |
| 929 | try self.writeInstRef(stream, extra.data.callee); | |
| 930 | try stream.writeAll(", ["); | |
| 931 | for (args) |arg, i| { | |
| 932 | if (i != 0) try stream.writeAll(", "); | |
| 933 | try self.writeInstRef(stream, arg); | |
| 934 | } | |
| 935 | try stream.writeAll("]) "); | |
| 936 | try self.writeSrc(stream, inst_data.src()); | |
| 937 | } | |
| 938 | ||
| 939 | fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 940 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 941 | try self.writePlNodeBlockWithoutSrc(stream, inst); | |
| 942 | try self.writeSrc(stream, inst_data.src()); | |
| 943 | } | |
| 944 | ||
| 945 | fn writePlNodeBlockWithoutSrc(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 946 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 947 | const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index); | |
| 948 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; | |
| 949 | try self.writeBracedBody(stream, body); | |
| 950 | try stream.writeAll(") "); | |
| 951 | } | |
| 952 | ||
| 953 | fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 954 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 955 | const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); | |
| 956 | const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; | |
| 957 | const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | |
| 958 | try self.writeInstRef(stream, extra.data.condition); | |
| 959 | try stream.writeAll(", "); | |
| 960 | try self.writeBracedBody(stream, then_body); | |
| 961 | try stream.writeAll(", "); | |
| 962 | try self.writeBracedBody(stream, else_body); | |
| 963 | try stream.writeAll(") "); | |
| 964 | try self.writeSrc(stream, inst_data.src()); | |
| 965 | } | |
| 966 | ||
| 967 | fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 968 | const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); | |
| 969 | ||
| 970 | var extra_index: usize = extended.operand; | |
| 971 | ||
| 972 | const src_node: ?i32 = if (small.has_src_node) blk: { | |
| 973 | const src_node = @bitCast(i32, self.code.extra[extra_index]); | |
| 974 | extra_index += 1; | |
| 975 | break :blk src_node; | |
| 976 | } else null; | |
| 977 | ||
| 978 | const body_len = if (small.has_body_len) blk: { | |
| 979 | const body_len = self.code.extra[extra_index]; | |
| 980 | extra_index += 1; | |
| 981 | break :blk body_len; | |
| 982 | } else 0; | |
| 983 | ||
| 984 | const fields_len = if (small.has_fields_len) blk: { | |
| 985 | const fields_len = self.code.extra[extra_index]; | |
| 986 | extra_index += 1; | |
| 987 | break :blk fields_len; | |
| 988 | } else 0; | |
| 989 | ||
| 990 | const decls_len = if (small.has_decls_len) blk: { | |
| 991 | const decls_len = self.code.extra[extra_index]; | |
| 992 | extra_index += 1; | |
| 993 | break :blk decls_len; | |
| 994 | } else 0; | |
| 995 | ||
| 996 | try self.writeFlag(stream, "known_has_bits, ", small.known_has_bits); | |
| 997 | try stream.print("{s}, {s}, ", .{ | |
| 998 | @tagName(small.name_strategy), @tagName(small.layout), | |
| 999 | }); | |
| 1000 | ||
| 1001 | if (decls_len == 0) { | |
| 1002 | try stream.writeAll("{}, "); | |
| 1003 | } else { | |
| 1004 | try stream.writeAll("{\n"); | |
| 1005 | self.indent += 2; | |
| 1006 | extra_index = try self.writeDecls(stream, decls_len, extra_index); | |
| 1007 | self.indent -= 2; | |
| 1008 | try stream.writeByteNTimes(' ', self.indent); | |
| 1009 | try stream.writeAll("}, "); | |
| 1010 | } | |
| 1011 | ||
| 1012 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 1013 | extra_index += body.len; | |
| 1014 | ||
| 1015 | if (fields_len == 0) { | |
| 1016 | assert(body.len == 0); | |
| 1017 | try stream.writeAll("{}, {})"); | |
| 1018 | } else { | |
| 1019 | const prev_parent_decl_node = self.parent_decl_node; | |
| 1020 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); | |
| 1021 | try self.writeBracedDecl(stream, body); | |
| 1022 | try stream.writeAll(", {\n"); | |
| 1023 | ||
| 1024 | self.indent += 2; | |
| 1025 | const bits_per_field = 4; | |
| 1026 | const fields_per_u32 = 32 / bits_per_field; | |
| 1027 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; | |
| 1028 | var bit_bag_index: usize = extra_index; | |
| 1029 | extra_index += bit_bags_count; | |
| 1030 | var cur_bit_bag: u32 = undefined; | |
| 1031 | var field_i: u32 = 0; | |
| 1032 | while (field_i < fields_len) : (field_i += 1) { | |
| 1033 | if (field_i % fields_per_u32 == 0) { | |
| 1034 | cur_bit_bag = self.code.extra[bit_bag_index]; | |
| 1035 | bit_bag_index += 1; | |
| 1036 | } | |
| 1037 | const has_align = @truncate(u1, cur_bit_bag) != 0; | |
| 1038 | cur_bit_bag >>= 1; | |
| 1039 | const has_default = @truncate(u1, cur_bit_bag) != 0; | |
| 1040 | cur_bit_bag >>= 1; | |
| 1041 | const is_comptime = @truncate(u1, cur_bit_bag) != 0; | |
| 1042 | cur_bit_bag >>= 1; | |
| 1043 | const unused = @truncate(u1, cur_bit_bag) != 0; | |
| 1044 | cur_bit_bag >>= 1; | |
| 1045 | ||
| 1046 | _ = unused; | |
| 1047 | ||
| 1048 | const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); | |
| 1049 | extra_index += 1; | |
| 1050 | const field_type = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1051 | extra_index += 1; | |
| 1052 | ||
| 1053 | try stream.writeByteNTimes(' ', self.indent); | |
| 1054 | try self.writeFlag(stream, "comptime ", is_comptime); | |
| 1055 | try stream.print("{}: ", .{std.zig.fmtId(field_name)}); | |
| 1056 | try self.writeInstRef(stream, field_type); | |
| 1057 | ||
| 1058 | if (has_align) { | |
| 1059 | const align_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1060 | extra_index += 1; | |
| 1061 | ||
| 1062 | try stream.writeAll(" align("); | |
| 1063 | try self.writeInstRef(stream, align_ref); | |
| 1064 | try stream.writeAll(")"); | |
| 1065 | } | |
| 1066 | if (has_default) { | |
| 1067 | const default_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1068 | extra_index += 1; | |
| 1069 | ||
| 1070 | try stream.writeAll(" = "); | |
| 1071 | try self.writeInstRef(stream, default_ref); | |
| 1072 | } | |
| 1073 | try stream.writeAll(",\n"); | |
| 1074 | } | |
| 1075 | ||
| 1076 | self.parent_decl_node = prev_parent_decl_node; | |
| 1077 | self.indent -= 2; | |
| 1078 | try stream.writeByteNTimes(' ', self.indent); | |
| 1079 | try stream.writeAll("})"); | |
| 1080 | } | |
| 1081 | try self.writeSrcNode(stream, src_node); | |
| 1082 | } | |
| 1083 | ||
| 1084 | fn writeUnionDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 1085 | const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); | |
| 1086 | ||
| 1087 | var extra_index: usize = extended.operand; | |
| 1088 | ||
| 1089 | const src_node: ?i32 = if (small.has_src_node) blk: { | |
| 1090 | const src_node = @bitCast(i32, self.code.extra[extra_index]); | |
| 1091 | extra_index += 1; | |
| 1092 | break :blk src_node; | |
| 1093 | } else null; | |
| 1094 | ||
| 1095 | const tag_type_ref = if (small.has_tag_type) blk: { | |
| 1096 | const tag_type_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1097 | extra_index += 1; | |
| 1098 | break :blk tag_type_ref; | |
| 1099 | } else .none; | |
| 1100 | ||
| 1101 | const body_len = if (small.has_body_len) blk: { | |
| 1102 | const body_len = self.code.extra[extra_index]; | |
| 1103 | extra_index += 1; | |
| 1104 | break :blk body_len; | |
| 1105 | } else 0; | |
| 1106 | ||
| 1107 | const fields_len = if (small.has_fields_len) blk: { | |
| 1108 | const fields_len = self.code.extra[extra_index]; | |
| 1109 | extra_index += 1; | |
| 1110 | break :blk fields_len; | |
| 1111 | } else 0; | |
| 1112 | ||
| 1113 | const decls_len = if (small.has_decls_len) blk: { | |
| 1114 | const decls_len = self.code.extra[extra_index]; | |
| 1115 | extra_index += 1; | |
| 1116 | break :blk decls_len; | |
| 1117 | } else 0; | |
| 1118 | ||
| 1119 | try stream.print("{s}, {s}, ", .{ | |
| 1120 | @tagName(small.name_strategy), @tagName(small.layout), | |
| 1121 | }); | |
| 1122 | try self.writeFlag(stream, "autoenum, ", small.auto_enum_tag); | |
| 1123 | ||
| 1124 | if (decls_len == 0) { | |
| 1125 | try stream.writeAll("{}, "); | |
| 1126 | } else { | |
| 1127 | try stream.writeAll("{\n"); | |
| 1128 | self.indent += 2; | |
| 1129 | extra_index = try self.writeDecls(stream, decls_len, extra_index); | |
| 1130 | self.indent -= 2; | |
| 1131 | try stream.writeByteNTimes(' ', self.indent); | |
| 1132 | try stream.writeAll("}, "); | |
| 1133 | } | |
| 1134 | ||
| 1135 | assert(fields_len != 0); | |
| 1136 | ||
| 1137 | if (tag_type_ref != .none) { | |
| 1138 | try self.writeInstRef(stream, tag_type_ref); | |
| 1139 | try stream.writeAll(", "); | |
| 1140 | } | |
| 1141 | ||
| 1142 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 1143 | extra_index += body.len; | |
| 1144 | ||
| 1145 | const prev_parent_decl_node = self.parent_decl_node; | |
| 1146 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); | |
| 1147 | try self.writeBracedDecl(stream, body); | |
| 1148 | try stream.writeAll(", {\n"); | |
| 1149 | ||
| 1150 | self.indent += 2; | |
| 1151 | const bits_per_field = 4; | |
| 1152 | const fields_per_u32 = 32 / bits_per_field; | |
| 1153 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; | |
| 1154 | const body_end = extra_index; | |
| 1155 | extra_index += bit_bags_count; | |
| 1156 | var bit_bag_index: usize = body_end; | |
| 1157 | var cur_bit_bag: u32 = undefined; | |
| 1158 | var field_i: u32 = 0; | |
| 1159 | while (field_i < fields_len) : (field_i += 1) { | |
| 1160 | if (field_i % fields_per_u32 == 0) { | |
| 1161 | cur_bit_bag = self.code.extra[bit_bag_index]; | |
| 1162 | bit_bag_index += 1; | |
| 1163 | } | |
| 1164 | const has_type = @truncate(u1, cur_bit_bag) != 0; | |
| 1165 | cur_bit_bag >>= 1; | |
| 1166 | const has_align = @truncate(u1, cur_bit_bag) != 0; | |
| 1167 | cur_bit_bag >>= 1; | |
| 1168 | const has_value = @truncate(u1, cur_bit_bag) != 0; | |
| 1169 | cur_bit_bag >>= 1; | |
| 1170 | const unused = @truncate(u1, cur_bit_bag) != 0; | |
| 1171 | cur_bit_bag >>= 1; | |
| 1172 | ||
| 1173 | _ = unused; | |
| 1174 | ||
| 1175 | const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); | |
| 1176 | extra_index += 1; | |
| 1177 | try stream.writeByteNTimes(' ', self.indent); | |
| 1178 | try stream.print("{}", .{std.zig.fmtId(field_name)}); | |
| 1179 | ||
| 1180 | if (has_type) { | |
| 1181 | const field_type = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1182 | extra_index += 1; | |
| 1183 | ||
| 1184 | try stream.writeAll(": "); | |
| 1185 | try self.writeInstRef(stream, field_type); | |
| 1186 | } | |
| 1187 | if (has_align) { | |
| 1188 | const align_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1189 | extra_index += 1; | |
| 1190 | ||
| 1191 | try stream.writeAll(" align("); | |
| 1192 | try self.writeInstRef(stream, align_ref); | |
| 1193 | try stream.writeAll(")"); | |
| 1194 | } | |
| 1195 | if (has_value) { | |
| 1196 | const default_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1197 | extra_index += 1; | |
| 1198 | ||
| 1199 | try stream.writeAll(" = "); | |
| 1200 | try self.writeInstRef(stream, default_ref); | |
| 1201 | } | |
| 1202 | try stream.writeAll(",\n"); | |
| 1203 | } | |
| 1204 | ||
| 1205 | self.parent_decl_node = prev_parent_decl_node; | |
| 1206 | self.indent -= 2; | |
| 1207 | try stream.writeByteNTimes(' ', self.indent); | |
| 1208 | try stream.writeAll("})"); | |
| 1209 | try self.writeSrcNode(stream, src_node); | |
| 1210 | } | |
| 1211 | ||
| 1212 | fn writeDecls(self: *Writer, stream: anytype, decls_len: u32, extra_start: usize) !usize { | |
| 1213 | const parent_decl_node = self.parent_decl_node; | |
| 1214 | const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; | |
| 1215 | var extra_index = extra_start + bit_bags_count; | |
| 1216 | var bit_bag_index: usize = extra_start; | |
| 1217 | var cur_bit_bag: u32 = undefined; | |
| 1218 | var decl_i: u32 = 0; | |
| 1219 | while (decl_i < decls_len) : (decl_i += 1) { | |
| 1220 | if (decl_i % 8 == 0) { | |
| 1221 | cur_bit_bag = self.code.extra[bit_bag_index]; | |
| 1222 | bit_bag_index += 1; | |
| 1223 | } | |
| 1224 | const is_pub = @truncate(u1, cur_bit_bag) != 0; | |
| 1225 | cur_bit_bag >>= 1; | |
| 1226 | const is_exported = @truncate(u1, cur_bit_bag) != 0; | |
| 1227 | cur_bit_bag >>= 1; | |
| 1228 | const has_align = @truncate(u1, cur_bit_bag) != 0; | |
| 1229 | cur_bit_bag >>= 1; | |
| 1230 | const has_section_or_addrspace = @truncate(u1, cur_bit_bag) != 0; | |
| 1231 | cur_bit_bag >>= 1; | |
| 1232 | ||
| 1233 | const sub_index = extra_index; | |
| 1234 | ||
| 1235 | const hash_u32s = self.code.extra[extra_index..][0..4]; | |
| 1236 | extra_index += 4; | |
| 1237 | const line = self.code.extra[extra_index]; | |
| 1238 | extra_index += 1; | |
| 1239 | const decl_name_index = self.code.extra[extra_index]; | |
| 1240 | extra_index += 1; | |
| 1241 | const decl_index = self.code.extra[extra_index]; | |
| 1242 | extra_index += 1; | |
| 1243 | const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: { | |
| 1244 | const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1245 | extra_index += 1; | |
| 1246 | break :inst inst; | |
| 1247 | }; | |
| 1248 | const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { | |
| 1249 | const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1250 | extra_index += 1; | |
| 1251 | break :inst inst; | |
| 1252 | }; | |
| 1253 | const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { | |
| 1254 | const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1255 | extra_index += 1; | |
| 1256 | break :inst inst; | |
| 1257 | }; | |
| 1258 | ||
| 1259 | const pub_str = if (is_pub) "pub " else ""; | |
| 1260 | const hash_bytes = @bitCast([16]u8, hash_u32s.*); | |
| 1261 | try stream.writeByteNTimes(' ', self.indent); | |
| 1262 | if (decl_name_index == 0) { | |
| 1263 | const name = if (is_exported) "usingnamespace" else "comptime"; | |
| 1264 | try stream.writeAll(pub_str); | |
| 1265 | try stream.writeAll(name); | |
| 1266 | } else if (decl_name_index == 1) { | |
| 1267 | try stream.writeAll("test"); | |
| 1268 | } else { | |
| 1269 | const raw_decl_name = self.code.nullTerminatedString(decl_name_index); | |
| 1270 | const decl_name = if (raw_decl_name.len == 0) | |
| 1271 | self.code.nullTerminatedString(decl_name_index + 1) | |
| 1272 | else | |
| 1273 | raw_decl_name; | |
| 1274 | const test_str = if (raw_decl_name.len == 0) "test " else ""; | |
| 1275 | const export_str = if (is_exported) "export " else ""; | |
| 1276 | try stream.print("[{d}] {s}{s}{s}{}", .{ | |
| 1277 | sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name), | |
| 1278 | }); | |
| 1279 | if (align_inst != .none) { | |
| 1280 | try stream.writeAll(" align("); | |
| 1281 | try self.writeInstRef(stream, align_inst); | |
| 1282 | try stream.writeAll(")"); | |
| 1283 | } | |
| 1284 | if (addrspace_inst != .none) { | |
| 1285 | try stream.writeAll(" addrspace("); | |
| 1286 | try self.writeInstRef(stream, addrspace_inst); | |
| 1287 | try stream.writeAll(")"); | |
| 1288 | } | |
| 1289 | if (section_inst != .none) { | |
| 1290 | try stream.writeAll(" linksection("); | |
| 1291 | try self.writeInstRef(stream, section_inst); | |
| 1292 | try stream.writeAll(")"); | |
| 1293 | } | |
| 1294 | } | |
| 1295 | ||
| 1296 | if (self.recurse_decls) { | |
| 1297 | const tag = self.code.instructions.items(.tag)[decl_index]; | |
| 1298 | try stream.print(" line({d}) hash({}): %{d} = {s}(", .{ | |
| 1299 | line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag), | |
| 1300 | }); | |
| 1301 | ||
| 1302 | const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node; | |
| 1303 | const sub_decl_node_off = decl_block_inst_data.src_node; | |
| 1304 | self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off); | |
| 1305 | try self.writePlNodeBlockWithoutSrc(stream, decl_index); | |
| 1306 | self.parent_decl_node = parent_decl_node; | |
| 1307 | try self.writeSrc(stream, decl_block_inst_data.src()); | |
| 1308 | try stream.writeAll("\n"); | |
| 1309 | } else { | |
| 1310 | try stream.print(" line({d}) hash({}): %{d} = ...\n", .{ | |
| 1311 | line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, | |
| 1312 | }); | |
| 1313 | } | |
| 1314 | } | |
| 1315 | return extra_index; | |
| 1316 | } | |
| 1317 | ||
| 1318 | fn writeEnumDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 1319 | const small = @bitCast(Zir.Inst.EnumDecl.Small, extended.small); | |
| 1320 | var extra_index: usize = extended.operand; | |
| 1321 | ||
| 1322 | const src_node: ?i32 = if (small.has_src_node) blk: { | |
| 1323 | const src_node = @bitCast(i32, self.code.extra[extra_index]); | |
| 1324 | extra_index += 1; | |
| 1325 | break :blk src_node; | |
| 1326 | } else null; | |
| 1327 | ||
| 1328 | const tag_type_ref = if (small.has_tag_type) blk: { | |
| 1329 | const tag_type_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1330 | extra_index += 1; | |
| 1331 | break :blk tag_type_ref; | |
| 1332 | } else .none; | |
| 1333 | ||
| 1334 | const body_len = if (small.has_body_len) blk: { | |
| 1335 | const body_len = self.code.extra[extra_index]; | |
| 1336 | extra_index += 1; | |
| 1337 | break :blk body_len; | |
| 1338 | } else 0; | |
| 1339 | ||
| 1340 | const fields_len = if (small.has_fields_len) blk: { | |
| 1341 | const fields_len = self.code.extra[extra_index]; | |
| 1342 | extra_index += 1; | |
| 1343 | break :blk fields_len; | |
| 1344 | } else 0; | |
| 1345 | ||
| 1346 | const decls_len = if (small.has_decls_len) blk: { | |
| 1347 | const decls_len = self.code.extra[extra_index]; | |
| 1348 | extra_index += 1; | |
| 1349 | break :blk decls_len; | |
| 1350 | } else 0; | |
| 1351 | ||
| 1352 | try stream.print("{s}, ", .{@tagName(small.name_strategy)}); | |
| 1353 | try self.writeFlag(stream, "nonexhaustive, ", small.nonexhaustive); | |
| 1354 | ||
| 1355 | if (decls_len == 0) { | |
| 1356 | try stream.writeAll("{}, "); | |
| 1357 | } else { | |
| 1358 | try stream.writeAll("{\n"); | |
| 1359 | self.indent += 2; | |
| 1360 | extra_index = try self.writeDecls(stream, decls_len, extra_index); | |
| 1361 | self.indent -= 2; | |
| 1362 | try stream.writeByteNTimes(' ', self.indent); | |
| 1363 | try stream.writeAll("}, "); | |
| 1364 | } | |
| 1365 | ||
| 1366 | if (tag_type_ref != .none) { | |
| 1367 | try self.writeInstRef(stream, tag_type_ref); | |
| 1368 | try stream.writeAll(", "); | |
| 1369 | } | |
| 1370 | ||
| 1371 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 1372 | extra_index += body.len; | |
| 1373 | ||
| 1374 | if (fields_len == 0) { | |
| 1375 | assert(body.len == 0); | |
| 1376 | try stream.writeAll("{}, {})"); | |
| 1377 | } else { | |
| 1378 | const prev_parent_decl_node = self.parent_decl_node; | |
| 1379 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); | |
| 1380 | try self.writeBracedDecl(stream, body); | |
| 1381 | try stream.writeAll(", {\n"); | |
| 1382 | ||
| 1383 | self.indent += 2; | |
| 1384 | const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; | |
| 1385 | const body_end = extra_index; | |
| 1386 | extra_index += bit_bags_count; | |
| 1387 | var bit_bag_index: usize = body_end; | |
| 1388 | var cur_bit_bag: u32 = undefined; | |
| 1389 | var field_i: u32 = 0; | |
| 1390 | while (field_i < fields_len) : (field_i += 1) { | |
| 1391 | if (field_i % 32 == 0) { | |
| 1392 | cur_bit_bag = self.code.extra[bit_bag_index]; | |
| 1393 | bit_bag_index += 1; | |
| 1394 | } | |
| 1395 | const has_tag_value = @truncate(u1, cur_bit_bag) != 0; | |
| 1396 | cur_bit_bag >>= 1; | |
| 1397 | ||
| 1398 | const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); | |
| 1399 | extra_index += 1; | |
| 1400 | ||
| 1401 | try stream.writeByteNTimes(' ', self.indent); | |
| 1402 | try stream.print("{}", .{std.zig.fmtId(field_name)}); | |
| 1403 | ||
| 1404 | if (has_tag_value) { | |
| 1405 | const tag_value_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1406 | extra_index += 1; | |
| 1407 | ||
| 1408 | try stream.writeAll(" = "); | |
| 1409 | try self.writeInstRef(stream, tag_value_ref); | |
| 1410 | } | |
| 1411 | try stream.writeAll(",\n"); | |
| 1412 | } | |
| 1413 | self.parent_decl_node = prev_parent_decl_node; | |
| 1414 | self.indent -= 2; | |
| 1415 | try stream.writeByteNTimes(' ', self.indent); | |
| 1416 | try stream.writeAll("})"); | |
| 1417 | } | |
| 1418 | try self.writeSrcNode(stream, src_node); | |
| 1419 | } | |
| 1420 | ||
| 1421 | fn writeOpaqueDecl( | |
| 1422 | self: *Writer, | |
| 1423 | stream: anytype, | |
| 1424 | extended: Zir.Inst.Extended.InstData, | |
| 1425 | ) !void { | |
| 1426 | const small = @bitCast(Zir.Inst.OpaqueDecl.Small, extended.small); | |
| 1427 | var extra_index: usize = extended.operand; | |
| 1428 | ||
| 1429 | const src_node: ?i32 = if (small.has_src_node) blk: { | |
| 1430 | const src_node = @bitCast(i32, self.code.extra[extra_index]); | |
| 1431 | extra_index += 1; | |
| 1432 | break :blk src_node; | |
| 1433 | } else null; | |
| 1434 | ||
| 1435 | const decls_len = if (small.has_decls_len) blk: { | |
| 1436 | const decls_len = self.code.extra[extra_index]; | |
| 1437 | extra_index += 1; | |
| 1438 | break :blk decls_len; | |
| 1439 | } else 0; | |
| 1440 | ||
| 1441 | try stream.print("{s}, ", .{@tagName(small.name_strategy)}); | |
| 1442 | ||
| 1443 | if (decls_len == 0) { | |
| 1444 | try stream.writeAll("{})"); | |
| 1445 | } else { | |
| 1446 | try stream.writeAll("{\n"); | |
| 1447 | self.indent += 2; | |
| 1448 | _ = try self.writeDecls(stream, decls_len, extra_index); | |
| 1449 | self.indent -= 2; | |
| 1450 | try stream.writeByteNTimes(' ', self.indent); | |
| 1451 | try stream.writeAll("})"); | |
| 1452 | } | |
| 1453 | try self.writeSrcNode(stream, src_node); | |
| 1454 | } | |
| 1455 | ||
| 1456 | fn writeErrorSetDecl( | |
| 1457 | self: *Writer, | |
| 1458 | stream: anytype, | |
| 1459 | inst: Zir.Inst.Index, | |
| 1460 | name_strategy: Zir.Inst.NameStrategy, | |
| 1461 | ) !void { | |
| 1462 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 1463 | const extra = self.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index); | |
| 1464 | const fields = self.code.extra[extra.end..][0..extra.data.fields_len]; | |
| 1465 | ||
| 1466 | try stream.print("{s}, ", .{@tagName(name_strategy)}); | |
| 1467 | ||
| 1468 | try stream.writeAll("{\n"); | |
| 1469 | self.indent += 2; | |
| 1470 | for (fields) |str_index| { | |
| 1471 | const name = self.code.nullTerminatedString(str_index); | |
| 1472 | try stream.writeByteNTimes(' ', self.indent); | |
| 1473 | try stream.print("{},\n", .{std.zig.fmtId(name)}); | |
| 1474 | } | |
| 1475 | self.indent -= 2; | |
| 1476 | try stream.writeByteNTimes(' ', self.indent); | |
| 1477 | try stream.writeAll("}) "); | |
| 1478 | ||
| 1479 | try self.writeSrc(stream, inst_data.src()); | |
| 1480 | } | |
| 1481 | ||
| 1482 | fn writePlNodeSwitchBr( | |
| 1483 | self: *Writer, | |
| 1484 | stream: anytype, | |
| 1485 | inst: Zir.Inst.Index, | |
| 1486 | special_prong: Zir.SpecialProng, | |
| 1487 | ) !void { | |
| 1488 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 1489 | const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); | |
| 1490 | const special: struct { | |
| 1491 | body: []const Zir.Inst.Index, | |
| 1492 | end: usize, | |
| 1493 | } = switch (special_prong) { | |
| 1494 | .none => .{ .body = &.{}, .end = extra.end }, | |
| 1495 | .under, .@"else" => blk: { | |
| 1496 | const body_len = self.code.extra[extra.end]; | |
| 1497 | const extra_body_start = extra.end + 1; | |
| 1498 | break :blk .{ | |
| 1499 | .body = self.code.extra[extra_body_start..][0..body_len], | |
| 1500 | .end = extra_body_start + body_len, | |
| 1501 | }; | |
| 1502 | }, | |
| 1503 | }; | |
| 1504 | ||
| 1505 | try self.writeInstRef(stream, extra.data.operand); | |
| 1506 | ||
| 1507 | self.indent += 2; | |
| 1508 | ||
| 1509 | if (special.body.len != 0) { | |
| 1510 | const prong_name = switch (special_prong) { | |
| 1511 | .@"else" => "else", | |
| 1512 | .under => "_", | |
| 1513 | else => unreachable, | |
| 1514 | }; | |
| 1515 | try stream.writeAll(",\n"); | |
| 1516 | try stream.writeByteNTimes(' ', self.indent); | |
| 1517 | try stream.print("{s} => ", .{prong_name}); | |
| 1518 | try self.writeBracedBody(stream, special.body); | |
| 1519 | } | |
| 1520 | ||
| 1521 | var extra_index: usize = special.end; | |
| 1522 | { | |
| 1523 | var scalar_i: usize = 0; | |
| 1524 | while (scalar_i < extra.data.cases_len) : (scalar_i += 1) { | |
| 1525 | const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1526 | extra_index += 1; | |
| 1527 | const body_len = self.code.extra[extra_index]; | |
| 1528 | extra_index += 1; | |
| 1529 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 1530 | extra_index += body_len; | |
| 1531 | ||
| 1532 | try stream.writeAll(",\n"); | |
| 1533 | try stream.writeByteNTimes(' ', self.indent); | |
| 1534 | try self.writeInstRef(stream, item_ref); | |
| 1535 | try stream.writeAll(" => "); | |
| 1536 | try self.writeBracedBody(stream, body); | |
| 1537 | } | |
| 1538 | } | |
| 1539 | ||
| 1540 | self.indent -= 2; | |
| 1541 | ||
| 1542 | try stream.writeAll(") "); | |
| 1543 | try self.writeSrc(stream, inst_data.src()); | |
| 1544 | } | |
| 1545 | ||
| 1546 | fn writePlNodeSwitchBlockMulti( | |
| 1547 | self: *Writer, | |
| 1548 | stream: anytype, | |
| 1549 | inst: Zir.Inst.Index, | |
| 1550 | special_prong: Zir.SpecialProng, | |
| 1551 | ) !void { | |
| 1552 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 1553 | const extra = self.code.extraData(Zir.Inst.SwitchBlockMulti, inst_data.payload_index); | |
| 1554 | const special: struct { | |
| 1555 | body: []const Zir.Inst.Index, | |
| 1556 | end: usize, | |
| 1557 | } = switch (special_prong) { | |
| 1558 | .none => .{ .body = &.{}, .end = extra.end }, | |
| 1559 | .under, .@"else" => blk: { | |
| 1560 | const body_len = self.code.extra[extra.end]; | |
| 1561 | const extra_body_start = extra.end + 1; | |
| 1562 | break :blk .{ | |
| 1563 | .body = self.code.extra[extra_body_start..][0..body_len], | |
| 1564 | .end = extra_body_start + body_len, | |
| 1565 | }; | |
| 1566 | }, | |
| 1567 | }; | |
| 1568 | ||
| 1569 | try self.writeInstRef(stream, extra.data.operand); | |
| 1570 | ||
| 1571 | self.indent += 2; | |
| 1572 | ||
| 1573 | if (special.body.len != 0) { | |
| 1574 | const prong_name = switch (special_prong) { | |
| 1575 | .@"else" => "else", | |
| 1576 | .under => "_", | |
| 1577 | else => unreachable, | |
| 1578 | }; | |
| 1579 | try stream.writeAll(",\n"); | |
| 1580 | try stream.writeByteNTimes(' ', self.indent); | |
| 1581 | try stream.print("{s} => ", .{prong_name}); | |
| 1582 | try self.writeBracedBody(stream, special.body); | |
| 1583 | } | |
| 1584 | ||
| 1585 | var extra_index: usize = special.end; | |
| 1586 | { | |
| 1587 | var scalar_i: usize = 0; | |
| 1588 | while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) { | |
| 1589 | const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1590 | extra_index += 1; | |
| 1591 | const body_len = self.code.extra[extra_index]; | |
| 1592 | extra_index += 1; | |
| 1593 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 1594 | extra_index += body_len; | |
| 1595 | ||
| 1596 | try stream.writeAll(",\n"); | |
| 1597 | try stream.writeByteNTimes(' ', self.indent); | |
| 1598 | try self.writeInstRef(stream, item_ref); | |
| 1599 | try stream.writeAll(" => "); | |
| 1600 | try self.writeBracedBody(stream, body); | |
| 1601 | } | |
| 1602 | } | |
| 1603 | { | |
| 1604 | var multi_i: usize = 0; | |
| 1605 | while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) { | |
| 1606 | const items_len = self.code.extra[extra_index]; | |
| 1607 | extra_index += 1; | |
| 1608 | const ranges_len = self.code.extra[extra_index]; | |
| 1609 | extra_index += 1; | |
| 1610 | const body_len = self.code.extra[extra_index]; | |
| 1611 | extra_index += 1; | |
| 1612 | const items = self.code.refSlice(extra_index, items_len); | |
| 1613 | extra_index += items_len; | |
| 1614 | ||
| 1615 | try stream.writeAll(",\n"); | |
| 1616 | try stream.writeByteNTimes(' ', self.indent); | |
| 1617 | ||
| 1618 | for (items) |item_ref, item_i| { | |
| 1619 | if (item_i != 0) try stream.writeAll(", "); | |
| 1620 | try self.writeInstRef(stream, item_ref); | |
| 1621 | } | |
| 1622 | ||
| 1623 | var range_i: usize = 0; | |
| 1624 | while (range_i < ranges_len) : (range_i += 1) { | |
| 1625 | const item_first = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1626 | extra_index += 1; | |
| 1627 | const item_last = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1628 | extra_index += 1; | |
| 1629 | ||
| 1630 | if (range_i != 0 or items.len != 0) { | |
| 1631 | try stream.writeAll(", "); | |
| 1632 | } | |
| 1633 | try self.writeInstRef(stream, item_first); | |
| 1634 | try stream.writeAll("..."); | |
| 1635 | try self.writeInstRef(stream, item_last); | |
| 1636 | } | |
| 1637 | ||
| 1638 | const body = self.code.extra[extra_index..][0..body_len]; | |
| 1639 | extra_index += body_len; | |
| 1640 | try stream.writeAll(" => "); | |
| 1641 | try self.writeBracedBody(stream, body); | |
| 1642 | } | |
| 1643 | } | |
| 1644 | ||
| 1645 | self.indent -= 2; | |
| 1646 | ||
| 1647 | try stream.writeAll(") "); | |
| 1648 | try self.writeSrc(stream, inst_data.src()); | |
| 1649 | } | |
| 1650 | ||
| 1651 | fn writePlNodeField(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1652 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 1653 | const extra = self.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; | |
| 1654 | const name = self.code.nullTerminatedString(extra.field_name_start); | |
| 1655 | try self.writeInstRef(stream, extra.lhs); | |
| 1656 | try stream.print(", \"{}\") ", .{std.zig.fmtEscapes(name)}); | |
| 1657 | try self.writeSrc(stream, inst_data.src()); | |
| 1658 | } | |
| 1659 | ||
| 1660 | fn writePlNodeFieldNamed(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1661 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 1662 | const extra = self.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; | |
| 1663 | try self.writeInstRef(stream, extra.lhs); | |
| 1664 | try stream.writeAll(", "); | |
| 1665 | try self.writeInstRef(stream, extra.field_name); | |
| 1666 | try stream.writeAll(") "); | |
| 1667 | try self.writeSrc(stream, inst_data.src()); | |
| 1668 | } | |
| 1669 | ||
| 1670 | fn writeAs(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1671 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 1672 | const extra = self.code.extraData(Zir.Inst.As, inst_data.payload_index).data; | |
| 1673 | try self.writeInstRef(stream, extra.dest_type); | |
| 1674 | try stream.writeAll(", "); | |
| 1675 | try self.writeInstRef(stream, extra.operand); | |
| 1676 | try stream.writeAll(") "); | |
| 1677 | try self.writeSrc(stream, inst_data.src()); | |
| 1678 | } | |
| 1679 | ||
| 1680 | fn writeNode( | |
| 1681 | self: *Writer, | |
| 1682 | stream: anytype, | |
| 1683 | inst: Zir.Inst.Index, | |
| 1684 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 1685 | const src_node = self.code.instructions.items(.data)[inst].node; | |
| 1686 | const src: LazySrcLoc = .{ .node_offset = src_node }; | |
| 1687 | try stream.writeAll(") "); | |
| 1688 | try self.writeSrc(stream, src); | |
| 1689 | } | |
| 1690 | ||
| 1691 | fn writeStrTok( | |
| 1692 | self: *Writer, | |
| 1693 | stream: anytype, | |
| 1694 | inst: Zir.Inst.Index, | |
| 1695 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 1696 | const inst_data = self.code.instructions.items(.data)[inst].str_tok; | |
| 1697 | const str = inst_data.get(self.code); | |
| 1698 | try stream.print("\"{}\") ", .{std.zig.fmtEscapes(str)}); | |
| 1699 | try self.writeSrc(stream, inst_data.src()); | |
| 1700 | } | |
| 1701 | ||
| 1702 | fn writeFunc( | |
| 1703 | self: *Writer, | |
| 1704 | stream: anytype, | |
| 1705 | inst: Zir.Inst.Index, | |
| 1706 | inferred_error_set: bool, | |
| 1707 | ) !void { | |
| 1708 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 1709 | const src = inst_data.src(); | |
| 1710 | const extra = self.code.extraData(Zir.Inst.Func, inst_data.payload_index); | |
| 1711 | var extra_index = extra.end; | |
| 1712 | ||
| 1713 | const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 1714 | extra_index += ret_ty_body.len; | |
| 1715 | ||
| 1716 | const body = self.code.extra[extra_index..][0..extra.data.body_len]; | |
| 1717 | extra_index += body.len; | |
| 1718 | ||
| 1719 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; | |
| 1720 | if (body.len != 0) { | |
| 1721 | src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; | |
| 1722 | } | |
| 1723 | return self.writeFuncCommon( | |
| 1724 | stream, | |
| 1725 | ret_ty_body, | |
| 1726 | inferred_error_set, | |
| 1727 | false, | |
| 1728 | false, | |
| 1729 | .none, | |
| 1730 | .none, | |
| 1731 | body, | |
| 1732 | src, | |
| 1733 | src_locs, | |
| 1734 | ); | |
| 1735 | } | |
| 1736 | ||
| 1737 | fn writeFuncExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 1738 | const extra = self.code.extraData(Zir.Inst.ExtendedFunc, extended.operand); | |
| 1739 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; | |
| 1740 | const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small); | |
| 1741 | ||
| 1742 | var extra_index: usize = extra.end; | |
| 1743 | if (small.has_lib_name) { | |
| 1744 | const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]); | |
| 1745 | extra_index += 1; | |
| 1746 | try stream.print("lib_name=\"{}\", ", .{std.zig.fmtEscapes(lib_name)}); | |
| 1747 | } | |
| 1748 | try self.writeFlag(stream, "test, ", small.is_test); | |
| 1749 | const cc: Zir.Inst.Ref = if (!small.has_cc) .none else blk: { | |
| 1750 | const cc = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1751 | extra_index += 1; | |
| 1752 | break :blk cc; | |
| 1753 | }; | |
| 1754 | const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: { | |
| 1755 | const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1756 | extra_index += 1; | |
| 1757 | break :blk align_inst; | |
| 1758 | }; | |
| 1759 | ||
| 1760 | const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 1761 | extra_index += ret_ty_body.len; | |
| 1762 | ||
| 1763 | const body = self.code.extra[extra_index..][0..extra.data.body_len]; | |
| 1764 | extra_index += body.len; | |
| 1765 | ||
| 1766 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; | |
| 1767 | if (body.len != 0) { | |
| 1768 | src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; | |
| 1769 | } | |
| 1770 | return self.writeFuncCommon( | |
| 1771 | stream, | |
| 1772 | ret_ty_body, | |
| 1773 | small.is_inferred_error, | |
| 1774 | small.is_var_args, | |
| 1775 | small.is_extern, | |
| 1776 | cc, | |
| 1777 | align_inst, | |
| 1778 | body, | |
| 1779 | src, | |
| 1780 | src_locs, | |
| 1781 | ); | |
| 1782 | } | |
| 1783 | ||
| 1784 | fn writeVarExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 1785 | const extra = self.code.extraData(Zir.Inst.ExtendedVar, extended.operand); | |
| 1786 | const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small); | |
| 1787 | ||
| 1788 | try self.writeInstRef(stream, extra.data.var_type); | |
| 1789 | ||
| 1790 | var extra_index: usize = extra.end; | |
| 1791 | if (small.has_lib_name) { | |
| 1792 | const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]); | |
| 1793 | extra_index += 1; | |
| 1794 | try stream.print(", lib_name=\"{}\"", .{std.zig.fmtEscapes(lib_name)}); | |
| 1795 | } | |
| 1796 | const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: { | |
| 1797 | const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1798 | extra_index += 1; | |
| 1799 | break :blk align_inst; | |
| 1800 | }; | |
| 1801 | const init_inst: Zir.Inst.Ref = if (!small.has_init) .none else blk: { | |
| 1802 | const init_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1803 | extra_index += 1; | |
| 1804 | break :blk init_inst; | |
| 1805 | }; | |
| 1806 | try self.writeFlag(stream, ", is_extern", small.is_extern); | |
| 1807 | try self.writeOptionalInstRef(stream, ", align=", align_inst); | |
| 1808 | try self.writeOptionalInstRef(stream, ", init=", init_inst); | |
| 1809 | try stream.writeAll("))"); | |
| 1810 | } | |
| 1811 | ||
| 1812 | fn writeAllocExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 1813 | const extra = self.code.extraData(Zir.Inst.AllocExtended, extended.operand); | |
| 1814 | const small = @bitCast(Zir.Inst.AllocExtended.Small, extended.small); | |
| 1815 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; | |
| 1816 | ||
| 1817 | var extra_index: usize = extra.end; | |
| 1818 | const type_inst: Zir.Inst.Ref = if (!small.has_type) .none else blk: { | |
| 1819 | const type_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1820 | extra_index += 1; | |
| 1821 | break :blk type_inst; | |
| 1822 | }; | |
| 1823 | const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: { | |
| 1824 | const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); | |
| 1825 | extra_index += 1; | |
| 1826 | break :blk align_inst; | |
| 1827 | }; | |
| 1828 | try self.writeFlag(stream, ",is_const", small.is_const); | |
| 1829 | try self.writeFlag(stream, ",is_comptime", small.is_comptime); | |
| 1830 | try self.writeOptionalInstRef(stream, ",ty=", type_inst); | |
| 1831 | try self.writeOptionalInstRef(stream, ",align=", align_inst); | |
| 1832 | try stream.writeAll(")) "); | |
| 1833 | try self.writeSrc(stream, src); | |
| 1834 | } | |
| 1835 | ||
| 1836 | fn writeBoolBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1837 | const inst_data = self.code.instructions.items(.data)[inst].bool_br; | |
| 1838 | const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index); | |
| 1839 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; | |
| 1840 | try self.writeInstRef(stream, inst_data.lhs); | |
| 1841 | try stream.writeAll(", "); | |
| 1842 | try self.writeBracedBody(stream, body); | |
| 1843 | } | |
| 1844 | ||
| 1845 | fn writeIntType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1846 | const int_type = self.code.instructions.items(.data)[inst].int_type; | |
| 1847 | const prefix: u8 = switch (int_type.signedness) { | |
| 1848 | .signed => 'i', | |
| 1849 | .unsigned => 'u', | |
| 1850 | }; | |
| 1851 | try stream.print("{c}{d}) ", .{ prefix, int_type.bit_count }); | |
| 1852 | try self.writeSrc(stream, int_type.src()); | |
| 1853 | } | |
| 1854 | ||
| 1855 | fn writeBreak(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1856 | const inst_data = self.code.instructions.items(.data)[inst].@"break"; | |
| 1857 | ||
| 1858 | try self.writeInstIndex(stream, inst_data.block_inst); | |
| 1859 | try stream.writeAll(", "); | |
| 1860 | try self.writeInstRef(stream, inst_data.operand); | |
| 1861 | try stream.writeAll(")"); | |
| 1862 | } | |
| 1863 | ||
| 1864 | fn writeArrayInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1865 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 1866 | ||
| 1867 | const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); | |
| 1868 | const args = self.code.refSlice(extra.end, extra.data.operands_len); | |
| 1869 | ||
| 1870 | try stream.writeAll(".{"); | |
| 1871 | for (args) |arg, i| { | |
| 1872 | if (i != 0) try stream.writeAll(", "); | |
| 1873 | try self.writeInstRef(stream, arg); | |
| 1874 | } | |
| 1875 | try stream.writeAll("})"); | |
| 1876 | } | |
| 1877 | ||
| 1878 | fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1879 | const inst_data = self.code.instructions.items(.data)[inst].@"unreachable"; | |
| 1880 | const safety_str = if (inst_data.safety) "safe" else "unsafe"; | |
| 1881 | try stream.print("{s}) ", .{safety_str}); | |
| 1882 | try self.writeSrc(stream, inst_data.src()); | |
| 1883 | } | |
| 1884 | ||
| 1885 | fn writeFuncCommon( | |
| 1886 | self: *Writer, | |
| 1887 | stream: anytype, | |
| 1888 | ret_ty_body: []const Zir.Inst.Index, | |
| 1889 | inferred_error_set: bool, | |
| 1890 | var_args: bool, | |
| 1891 | is_extern: bool, | |
| 1892 | cc: Zir.Inst.Ref, | |
| 1893 | align_inst: Zir.Inst.Ref, | |
| 1894 | body: []const Zir.Inst.Index, | |
| 1895 | src: LazySrcLoc, | |
| 1896 | src_locs: Zir.Inst.Func.SrcLocs, | |
| 1897 | ) !void { | |
| 1898 | if (ret_ty_body.len == 0) { | |
| 1899 | try stream.writeAll("ret_ty=void"); | |
| 1900 | } else { | |
| 1901 | try stream.writeAll("ret_ty="); | |
| 1902 | try self.writeBracedBody(stream, ret_ty_body); | |
| 1903 | } | |
| 1904 | ||
| 1905 | try self.writeOptionalInstRef(stream, ", cc=", cc); | |
| 1906 | try self.writeOptionalInstRef(stream, ", align=", align_inst); | |
| 1907 | try self.writeFlag(stream, ", vargs", var_args); | |
| 1908 | try self.writeFlag(stream, ", extern", is_extern); | |
| 1909 | try self.writeFlag(stream, ", inferror", inferred_error_set); | |
| 1910 | ||
| 1911 | try stream.writeAll(", body="); | |
| 1912 | try self.writeBracedBody(stream, body); | |
| 1913 | try stream.writeAll(") "); | |
| 1914 | if (body.len != 0) { | |
| 1915 | try stream.print("(lbrace={d}:{d},rbrace={d}:{d}) ", .{ | |
| 1916 | src_locs.lbrace_line, @truncate(u16, src_locs.columns), | |
| 1917 | src_locs.rbrace_line, @truncate(u16, src_locs.columns >> 16), | |
| 1918 | }); | |
| 1919 | } | |
| 1920 | try self.writeSrc(stream, src); | |
| 1921 | } | |
| 1922 | ||
| 1923 | fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1924 | const inst_data = self.code.instructions.items(.data)[inst].switch_capture; | |
| 1925 | try self.writeInstIndex(stream, inst_data.switch_inst); | |
| 1926 | try stream.print(", {d})", .{inst_data.prong_index}); | |
| 1927 | } | |
| 1928 | ||
| 1929 | fn writeDbgStmt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1930 | const inst_data = self.code.instructions.items(.data)[inst].dbg_stmt; | |
| 1931 | try stream.print("{d}, {d})", .{ inst_data.line, inst_data.column }); | |
| 1932 | } | |
| 1933 | ||
| 1934 | fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void { | |
| 1935 | var i: usize = @enumToInt(ref); | |
| 1936 | ||
| 1937 | if (i < Zir.Inst.Ref.typed_value_map.len) { | |
| 1938 | return stream.print("@{}", .{ref}); | |
| 1939 | } | |
| 1940 | i -= Zir.Inst.Ref.typed_value_map.len; | |
| 1941 | ||
| 1942 | return self.writeInstIndex(stream, @intCast(Zir.Inst.Index, i)); | |
| 1943 | } | |
| 1944 | ||
| 1945 | fn writeInstIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 1946 | _ = self; | |
| 1947 | return stream.print("%{d}", .{inst}); | |
| 1948 | } | |
| 1949 | ||
| 1950 | fn writeOptionalInstRef( | |
| 1951 | self: *Writer, | |
| 1952 | stream: anytype, | |
| 1953 | prefix: []const u8, | |
| 1954 | inst: Zir.Inst.Ref, | |
| 1955 | ) !void { | |
| 1956 | if (inst == .none) return; | |
| 1957 | try stream.writeAll(prefix); | |
| 1958 | try self.writeInstRef(stream, inst); | |
| 1959 | } | |
| 1960 | ||
| 1961 | fn writeFlag( | |
| 1962 | self: *Writer, | |
| 1963 | stream: anytype, | |
| 1964 | name: []const u8, | |
| 1965 | flag: bool, | |
| 1966 | ) !void { | |
| 1967 | _ = self; | |
| 1968 | if (!flag) return; | |
| 1969 | try stream.writeAll(name); | |
| 1970 | } | |
| 1971 | ||
| 1972 | fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void { | |
| 1973 | if (self.file.tree_loaded) { | |
| 1974 | const tree = self.file.tree; | |
| 1975 | const src_loc: Module.SrcLoc = .{ | |
| 1976 | .file_scope = self.file, | |
| 1977 | .parent_decl_node = self.parent_decl_node, | |
| 1978 | .lazy = src, | |
| 1979 | }; | |
| 1980 | const abs_byte_off = src_loc.byteOffset(self.gpa) catch unreachable; | |
| 1981 | const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off); | |
| 1982 | try stream.print("{s}:{d}:{d}", .{ | |
| 1983 | @tagName(src), delta_line.line + 1, delta_line.column + 1, | |
| 1984 | }); | |
| 1985 | } | |
| 1986 | } | |
| 1987 | ||
| 1988 | fn writeSrcNode(self: *Writer, stream: anytype, src_node: ?i32) !void { | |
| 1989 | const node_offset = src_node orelse return; | |
| 1990 | const src: LazySrcLoc = .{ .node_offset = node_offset }; | |
| 1991 | try stream.writeAll(" "); | |
| 1992 | return self.writeSrc(stream, src); | |
| 1993 | } | |
| 1994 | ||
| 1995 | fn writeBracedDecl(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { | |
| 1996 | try self.writeBracedBodyConditional(stream, body, self.recurse_decls); | |
| 1997 | } | |
| 1998 | ||
| 1999 | fn writeBracedBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { | |
| 2000 | try self.writeBracedBodyConditional(stream, body, self.recurse_blocks); | |
| 2001 | } | |
| 2002 | ||
| 2003 | fn writeBracedBodyConditional(self: *Writer, stream: anytype, body: []const Zir.Inst.Index, enabled: bool) !void { | |
| 2004 | if (body.len == 0) { | |
| 2005 | try stream.writeAll("{}"); | |
| 2006 | } else if (enabled) { | |
| 2007 | try stream.writeAll("{\n"); | |
| 2008 | self.indent += 2; | |
| 2009 | try self.writeBody(stream, body); | |
| 2010 | self.indent -= 2; | |
| 2011 | try stream.writeByteNTimes(' ', self.indent); | |
| 2012 | try stream.writeAll("}"); | |
| 2013 | } else if (body.len == 1) { | |
| 2014 | try stream.writeByte('{'); | |
| 2015 | try self.writeInstIndex(stream, body[0]); | |
| 2016 | try stream.writeByte('}'); | |
| 2017 | } else if (body.len == 2) { | |
| 2018 | try stream.writeByte('{'); | |
| 2019 | try self.writeInstIndex(stream, body[0]); | |
| 2020 | try stream.writeAll(", "); | |
| 2021 | try self.writeInstIndex(stream, body[1]); | |
| 2022 | try stream.writeByte('}'); | |
| 2023 | } else { | |
| 2024 | try stream.writeByte('{'); | |
| 2025 | try self.writeInstIndex(stream, body[0]); | |
| 2026 | try stream.writeAll(".."); | |
| 2027 | try self.writeInstIndex(stream, body[body.len - 1]); | |
| 2028 | try stream.writeByte('}'); | |
| 2029 | } | |
| 2030 | } | |
| 2031 | ||
| 2032 | fn writeBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { | |
| 2033 | for (body) |inst| { | |
| 2034 | try stream.writeByteNTimes(' ', self.indent); | |
| 2035 | try stream.print("%{d} ", .{inst}); | |
| 2036 | try self.writeInstToStream(stream, inst); | |
| 2037 | try stream.writeByte('\n'); | |
| 2038 | } | |
| 2039 | } | |
| 2040 | }; |
src/stage1/all_types.hpp+20-8| ... | ... | @@ -86,6 +86,14 @@ enum CallingConvention { |
| 86 | 86 | CallingConventionSysV |
| 87 | 87 | }; |
| 88 | 88 | |
| 89 | // Stage 1 supports only the generic address space | |
| 90 | enum AddressSpace { | |
| 91 | AddressSpaceGeneric, | |
| 92 | AddressSpaceGS, | |
| 93 | AddressSpaceFS, | |
| 94 | AddressSpaceSS, | |
| 95 | }; | |
| 96 | ||
| 89 | 97 | // This one corresponds to the builtin.zig enum. |
| 90 | 98 | enum BuiltinPtrSize { |
| 91 | 99 | BuiltinPtrSizeOne, |
| ... | ... | @@ -804,14 +812,18 @@ enum BinOpType { |
| 804 | 812 | BinOpTypeInvalid, |
| 805 | 813 | BinOpTypeAssign, |
| 806 | 814 | BinOpTypeAssignTimes, |
| 815 | BinOpTypeAssignTimesSat, | |
| 807 | 816 | BinOpTypeAssignTimesWrap, |
| 808 | 817 | BinOpTypeAssignDiv, |
| 809 | 818 | BinOpTypeAssignMod, |
| 810 | 819 | BinOpTypeAssignPlus, |
| 820 | BinOpTypeAssignPlusSat, | |
| 811 | 821 | BinOpTypeAssignPlusWrap, |
| 812 | 822 | BinOpTypeAssignMinus, |
| 823 | BinOpTypeAssignMinusSat, | |
| 813 | 824 | BinOpTypeAssignMinusWrap, |
| 814 | 825 | BinOpTypeAssignBitShiftLeft, |
| 826 | BinOpTypeAssignBitShiftLeftSat, | |
| 815 | 827 | BinOpTypeAssignBitShiftRight, |
| 816 | 828 | BinOpTypeAssignBitAnd, |
| 817 | 829 | BinOpTypeAssignBitXor, |
| ... | ... | @@ -828,12 +840,16 @@ enum BinOpType { |
| 828 | 840 | BinOpTypeBinXor, |
| 829 | 841 | BinOpTypeBinAnd, |
| 830 | 842 | BinOpTypeBitShiftLeft, |
| 843 | BinOpTypeBitShiftLeftSat, | |
| 831 | 844 | BinOpTypeBitShiftRight, |
| 832 | 845 | BinOpTypeAdd, |
| 846 | BinOpTypeAddSat, | |
| 833 | 847 | BinOpTypeAddWrap, |
| 834 | 848 | BinOpTypeSub, |
| 849 | BinOpTypeSubSat, | |
| 835 | 850 | BinOpTypeSubWrap, |
| 836 | 851 | BinOpTypeMult, |
| 852 | BinOpTypeMultSat, | |
| 837 | 853 | BinOpTypeMultWrap, |
| 838 | 854 | BinOpTypeDiv, |
| 839 | 855 | BinOpTypeMod, |
| ... | ... | @@ -1802,10 +1818,6 @@ enum BuiltinFnId { |
| 1802 | 1818 | BuiltinFnIdReduce, |
| 1803 | 1819 | BuiltinFnIdMaximum, |
| 1804 | 1820 | BuiltinFnIdMinimum, |
| 1805 | BuiltinFnIdSatAdd, | |
| 1806 | BuiltinFnIdSatSub, | |
| 1807 | BuiltinFnIdSatMul, | |
| 1808 | BuiltinFnIdSatShl, | |
| 1809 | 1821 | }; |
| 1810 | 1822 | |
| 1811 | 1823 | struct BuiltinFnEntry { |
| ... | ... | @@ -2950,10 +2962,10 @@ enum IrBinOp { |
| 2950 | 2962 | IrBinOpArrayMult, |
| 2951 | 2963 | IrBinOpMaximum, |
| 2952 | 2964 | IrBinOpMinimum, |
| 2953 | IrBinOpSatAdd, | |
| 2954 | IrBinOpSatSub, | |
| 2955 | IrBinOpSatMul, | |
| 2956 | IrBinOpSatShl, | |
| 2965 | IrBinOpAddSat, | |
| 2966 | IrBinOpSubSat, | |
| 2967 | IrBinOpMultSat, | |
| 2968 | IrBinOpShlSat, | |
| 2957 | 2969 | }; |
| 2958 | 2970 | |
| 2959 | 2971 | struct Stage1ZirInstBinOp { |
src/stage1/analyze.cpp+11-1| ... | ... | @@ -1019,6 +1019,16 @@ bool calling_convention_allows_zig_types(CallingConvention cc) { |
| 1019 | 1019 | zig_unreachable(); |
| 1020 | 1020 | } |
| 1021 | 1021 | |
| 1022 | const char *address_space_name(AddressSpace as) { | |
| 1023 | switch (as) { | |
| 1024 | case AddressSpaceGeneric: return "generic"; | |
| 1025 | case AddressSpaceGS: return "gs"; | |
| 1026 | case AddressSpaceFS: return "fs"; | |
| 1027 | case AddressSpaceSS: return "ss"; | |
| 1028 | } | |
| 1029 | zig_unreachable(); | |
| 1030 | } | |
| 1031 | ||
| 1022 | 1032 | ZigType *get_stack_trace_type(CodeGen *g) { |
| 1023 | 1033 | if (g->stack_trace_type == nullptr) { |
| 1024 | 1034 | g->stack_trace_type = get_builtin_type(g, "StackTrace"); |
| ... | ... | @@ -6794,7 +6804,7 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { |
| 6794 | 6804 | // Since this frame is async, an await might represent a suspend point, and |
| 6795 | 6805 | // therefore need to spill. It also needs to mark expr scopes as having to spill. |
| 6796 | 6806 | // For example: foo() + await z |
| 6797 | // The funtion call result of foo() must be spilled. | |
| 6807 | // The function call result of foo() must be spilled. | |
| 6798 | 6808 | for (size_t i = 0; i < fn->await_list.length; i += 1) { |
| 6799 | 6809 | Stage1AirInstAwait *await = fn->await_list.at(i); |
| 6800 | 6810 | if (await->is_nosuspend) { |
src/stage1/analyze.hpp+2| ... | ... | @@ -242,6 +242,8 @@ Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result); |
| 242 | 242 | bool calling_convention_allows_zig_types(CallingConvention cc); |
| 243 | 243 | const char *calling_convention_name(CallingConvention cc); |
| 244 | 244 | |
| 245 | const char *address_space_name(AddressSpace as); | |
| 246 | ||
| 245 | 247 | Error ATTRIBUTE_MUST_USE file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents); |
| 246 | 248 | |
| 247 | 249 | void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); |
src/stage1/astgen.cpp+16-60| ... | ... | @@ -3672,6 +3672,8 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod |
| 3672 | 3672 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMult), lval, result_loc); |
| 3673 | 3673 | case BinOpTypeAssignTimesWrap: |
| 3674 | 3674 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultWrap), lval, result_loc); |
| 3675 | case BinOpTypeAssignTimesSat: | |
| 3676 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultSat), lval, result_loc); | |
| 3675 | 3677 | case BinOpTypeAssignDiv: |
| 3676 | 3678 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc); |
| 3677 | 3679 | case BinOpTypeAssignMod: |
| ... | ... | @@ -3680,12 +3682,18 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod |
| 3680 | 3682 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAdd), lval, result_loc); |
| 3681 | 3683 | case BinOpTypeAssignPlusWrap: |
| 3682 | 3684 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddWrap), lval, result_loc); |
| 3685 | case BinOpTypeAssignPlusSat: | |
| 3686 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddSat), lval, result_loc); | |
| 3683 | 3687 | case BinOpTypeAssignMinus: |
| 3684 | 3688 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSub), lval, result_loc); |
| 3685 | 3689 | case BinOpTypeAssignMinusWrap: |
| 3686 | 3690 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubWrap), lval, result_loc); |
| 3691 | case BinOpTypeAssignMinusSat: | |
| 3692 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubSat), lval, result_loc); | |
| 3687 | 3693 | case BinOpTypeAssignBitShiftLeft: |
| 3688 | 3694 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc); |
| 3695 | case BinOpTypeAssignBitShiftLeftSat: | |
| 3696 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpShlSat), lval, result_loc); | |
| 3689 | 3697 | case BinOpTypeAssignBitShiftRight: |
| 3690 | 3698 | return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc); |
| 3691 | 3699 | case BinOpTypeAssignBitAnd: |
| ... | ... | @@ -3718,20 +3726,28 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod |
| 3718 | 3726 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBinAnd), lval, result_loc); |
| 3719 | 3727 | case BinOpTypeBitShiftLeft: |
| 3720 | 3728 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc); |
| 3729 | case BinOpTypeBitShiftLeftSat: | |
| 3730 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpShlSat), lval, result_loc); | |
| 3721 | 3731 | case BinOpTypeBitShiftRight: |
| 3722 | 3732 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc); |
| 3723 | 3733 | case BinOpTypeAdd: |
| 3724 | 3734 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAdd), lval, result_loc); |
| 3725 | 3735 | case BinOpTypeAddWrap: |
| 3726 | 3736 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddWrap), lval, result_loc); |
| 3737 | case BinOpTypeAddSat: | |
| 3738 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddSat), lval, result_loc); | |
| 3727 | 3739 | case BinOpTypeSub: |
| 3728 | 3740 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSub), lval, result_loc); |
| 3729 | 3741 | case BinOpTypeSubWrap: |
| 3730 | 3742 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubWrap), lval, result_loc); |
| 3743 | case BinOpTypeSubSat: | |
| 3744 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubSat), lval, result_loc); | |
| 3731 | 3745 | case BinOpTypeMult: |
| 3732 | 3746 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMult), lval, result_loc); |
| 3733 | 3747 | case BinOpTypeMultWrap: |
| 3734 | 3748 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultWrap), lval, result_loc); |
| 3749 | case BinOpTypeMultSat: | |
| 3750 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultSat), lval, result_loc); | |
| 3735 | 3751 | case BinOpTypeDiv: |
| 3736 | 3752 | return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc); |
| 3737 | 3753 | case BinOpTypeMod: |
| ... | ... | @@ -4704,66 +4720,6 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast |
| 4704 | 4720 | Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true); |
| 4705 | 4721 | return ir_lval_wrap(ag, scope, bin_op, lval, result_loc); |
| 4706 | 4722 | } |
| 4707 | case BuiltinFnIdSatAdd: | |
| 4708 | { | |
| 4709 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 4710 | Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope); | |
| 4711 | if (arg0_value == ag->codegen->invalid_inst_src) | |
| 4712 | return arg0_value; | |
| 4713 | ||
| 4714 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 4715 | Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope); | |
| 4716 | if (arg1_value == ag->codegen->invalid_inst_src) | |
| 4717 | return arg1_value; | |
| 4718 | ||
| 4719 | Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatAdd, arg0_value, arg1_value, true); | |
| 4720 | return ir_lval_wrap(ag, scope, bin_op, lval, result_loc); | |
| 4721 | } | |
| 4722 | case BuiltinFnIdSatSub: | |
| 4723 | { | |
| 4724 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 4725 | Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope); | |
| 4726 | if (arg0_value == ag->codegen->invalid_inst_src) | |
| 4727 | return arg0_value; | |
| 4728 | ||
| 4729 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 4730 | Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope); | |
| 4731 | if (arg1_value == ag->codegen->invalid_inst_src) | |
| 4732 | return arg1_value; | |
| 4733 | ||
| 4734 | Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatSub, arg0_value, arg1_value, true); | |
| 4735 | return ir_lval_wrap(ag, scope, bin_op, lval, result_loc); | |
| 4736 | } | |
| 4737 | case BuiltinFnIdSatMul: | |
| 4738 | { | |
| 4739 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 4740 | Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope); | |
| 4741 | if (arg0_value == ag->codegen->invalid_inst_src) | |
| 4742 | return arg0_value; | |
| 4743 | ||
| 4744 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 4745 | Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope); | |
| 4746 | if (arg1_value == ag->codegen->invalid_inst_src) | |
| 4747 | return arg1_value; | |
| 4748 | ||
| 4749 | Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatMul, arg0_value, arg1_value, true); | |
| 4750 | return ir_lval_wrap(ag, scope, bin_op, lval, result_loc); | |
| 4751 | } | |
| 4752 | case BuiltinFnIdSatShl: | |
| 4753 | { | |
| 4754 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 4755 | Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope); | |
| 4756 | if (arg0_value == ag->codegen->invalid_inst_src) | |
| 4757 | return arg0_value; | |
| 4758 | ||
| 4759 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 4760 | Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope); | |
| 4761 | if (arg1_value == ag->codegen->invalid_inst_src) | |
| 4762 | return arg1_value; | |
| 4763 | ||
| 4764 | Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatShl, arg0_value, arg1_value, true); | |
| 4765 | return ir_lval_wrap(ag, scope, bin_op, lval, result_loc); | |
| 4766 | } | |
| 4767 | 4723 | case BuiltinFnIdMemcpy: |
| 4768 | 4724 | { |
| 4769 | 4725 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
src/stage1/codegen.cpp+6-11| ... | ... | @@ -487,9 +487,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { |
| 487 | 487 | if (mangled_symbol_buf) buf_destroy(mangled_symbol_buf); |
| 488 | 488 | } |
| 489 | 489 | } else { |
| 490 | if (llvm_fn == nullptr) { | |
| 491 | llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type); | |
| 492 | } | |
| 490 | llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type); | |
| 493 | 491 | |
| 494 | 492 | for (size_t i = 1; i < fn->export_list.length; i += 1) { |
| 495 | 493 | GlobalExport *fn_export = &fn->export_list.items[i]; |
| ... | ... | @@ -3335,7 +3333,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, |
| 3335 | 3333 | } else { |
| 3336 | 3334 | zig_unreachable(); |
| 3337 | 3335 | } |
| 3338 | case IrBinOpSatAdd: | |
| 3336 | case IrBinOpAddSat: | |
| 3339 | 3337 | if (scalar_type->id == ZigTypeIdInt) { |
| 3340 | 3338 | if (scalar_type->data.integral.is_signed) { |
| 3341 | 3339 | return ZigLLVMBuildSAddSat(g->builder, op1_value, op2_value, ""); |
| ... | ... | @@ -3345,7 +3343,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, |
| 3345 | 3343 | } else { |
| 3346 | 3344 | zig_unreachable(); |
| 3347 | 3345 | } |
| 3348 | case IrBinOpSatSub: | |
| 3346 | case IrBinOpSubSat: | |
| 3349 | 3347 | if (scalar_type->id == ZigTypeIdInt) { |
| 3350 | 3348 | if (scalar_type->data.integral.is_signed) { |
| 3351 | 3349 | return ZigLLVMBuildSSubSat(g->builder, op1_value, op2_value, ""); |
| ... | ... | @@ -3355,7 +3353,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, |
| 3355 | 3353 | } else { |
| 3356 | 3354 | zig_unreachable(); |
| 3357 | 3355 | } |
| 3358 | case IrBinOpSatMul: | |
| 3356 | case IrBinOpMultSat: | |
| 3359 | 3357 | if (scalar_type->id == ZigTypeIdInt) { |
| 3360 | 3358 | if (scalar_type->data.integral.is_signed) { |
| 3361 | 3359 | return ZigLLVMBuildSMulFixSat(g->builder, op1_value, op2_value, ""); |
| ... | ... | @@ -3365,7 +3363,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, |
| 3365 | 3363 | } else { |
| 3366 | 3364 | zig_unreachable(); |
| 3367 | 3365 | } |
| 3368 | case IrBinOpSatShl: | |
| 3366 | case IrBinOpShlSat: | |
| 3369 | 3367 | if (scalar_type->id == ZigTypeIdInt) { |
| 3370 | 3368 | if (scalar_type->data.integral.is_signed) { |
| 3371 | 3369 | return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, ""); |
| ... | ... | @@ -9134,10 +9132,6 @@ static void define_builtin_fns(CodeGen *g) { |
| 9134 | 9132 | create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2); |
| 9135 | 9133 | create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2); |
| 9136 | 9134 | create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2); |
| 9137 | create_builtin_fn(g, BuiltinFnIdSatAdd, "addWithSaturation", 2); | |
| 9138 | create_builtin_fn(g, BuiltinFnIdSatSub, "subWithSaturation", 2); | |
| 9139 | create_builtin_fn(g, BuiltinFnIdSatMul, "mulWithSaturation", 2); | |
| 9140 | create_builtin_fn(g, BuiltinFnIdSatShl, "shlWithSaturation", 2); | |
| 9141 | 9135 | } |
| 9142 | 9136 | |
| 9143 | 9137 | static const char *bool_to_str(bool b) { |
| ... | ... | @@ -9323,6 +9317,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 9323 | 9317 | buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); |
| 9324 | 9318 | buf_appendf(contents, "pub const abi = std.Target.Abi.%s;\n", cur_abi); |
| 9325 | 9319 | buf_appendf(contents, "pub const cpu = std.Target.Cpu.baseline(.%s);\n", cur_arch); |
| 9320 | buf_appendf(contents, "pub const stage2_arch: std.Target.Cpu.Arch = .%s;\n", cur_arch); | |
| 9326 | 9321 | buf_appendf(contents, "pub const os = std.Target.Os.Tag.defaultVersionRange(.%s);\n", cur_os); |
| 9327 | 9322 | buf_appendf(contents, |
| 9328 | 9323 | "pub const target = std.Target{\n" |
src/stage1/ir.cpp+57-40| ... | ... | @@ -6374,7 +6374,7 @@ static Stage1AirInst *ir_analyze_enum_to_union(IrAnalyze *ira, Scope *scope, Ast |
| 6374 | 6374 | |
| 6375 | 6375 | if (target->value->type->data.enumeration.non_exhaustive) { |
| 6376 | 6376 | ir_add_error_node(ira, source_node, |
| 6377 | buf_sprintf("runtime cast to union '%s' from non-exhustive enum", | |
| 6377 | buf_sprintf("runtime cast to union '%s' from non-exhaustive enum", | |
| 6378 | 6378 | buf_ptr(&wanted_type->name))); |
| 6379 | 6379 | return ira->codegen->invalid_inst_gen; |
| 6380 | 6380 | } |
| ... | ... | @@ -9820,28 +9820,28 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s |
| 9820 | 9820 | float_min(out_val, op1_val, op2_val); |
| 9821 | 9821 | } |
| 9822 | 9822 | break; |
| 9823 | case IrBinOpSatAdd: | |
| 9823 | case IrBinOpAddSat: | |
| 9824 | 9824 | if (is_int) { |
| 9825 | 9825 | bigint_add_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); |
| 9826 | 9826 | } else { |
| 9827 | 9827 | zig_unreachable(); |
| 9828 | 9828 | } |
| 9829 | 9829 | break; |
| 9830 | case IrBinOpSatSub: | |
| 9830 | case IrBinOpSubSat: | |
| 9831 | 9831 | if (is_int) { |
| 9832 | 9832 | bigint_sub_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); |
| 9833 | 9833 | } else { |
| 9834 | 9834 | zig_unreachable(); |
| 9835 | 9835 | } |
| 9836 | 9836 | break; |
| 9837 | case IrBinOpSatMul: | |
| 9837 | case IrBinOpMultSat: | |
| 9838 | 9838 | if (is_int) { |
| 9839 | 9839 | bigint_mul_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); |
| 9840 | 9840 | } else { |
| 9841 | 9841 | zig_unreachable(); |
| 9842 | 9842 | } |
| 9843 | 9843 | break; |
| 9844 | case IrBinOpSatShl: | |
| 9844 | case IrBinOpShlSat: | |
| 9845 | 9845 | if (is_int) { |
| 9846 | 9846 | bigint_shl_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); |
| 9847 | 9847 | } else { |
| ... | ... | @@ -10069,10 +10069,10 @@ static bool ok_float_op(IrBinOp op) { |
| 10069 | 10069 | case IrBinOpBitShiftRightExact: |
| 10070 | 10070 | case IrBinOpAddWrap: |
| 10071 | 10071 | case IrBinOpSubWrap: |
| 10072 | case IrBinOpSatAdd: | |
| 10073 | case IrBinOpSatSub: | |
| 10074 | case IrBinOpSatMul: | |
| 10075 | case IrBinOpSatShl: | |
| 10072 | case IrBinOpAddSat: | |
| 10073 | case IrBinOpSubSat: | |
| 10074 | case IrBinOpMultSat: | |
| 10075 | case IrBinOpShlSat: | |
| 10076 | 10076 | case IrBinOpMultWrap: |
| 10077 | 10077 | case IrBinOpArrayCat: |
| 10078 | 10078 | case IrBinOpArrayMult: |
| ... | ... | @@ -11046,10 +11046,10 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns |
| 11046 | 11046 | case IrBinOpRemMod: |
| 11047 | 11047 | case IrBinOpMaximum: |
| 11048 | 11048 | case IrBinOpMinimum: |
| 11049 | case IrBinOpSatAdd: | |
| 11050 | case IrBinOpSatSub: | |
| 11051 | case IrBinOpSatMul: | |
| 11052 | case IrBinOpSatShl: | |
| 11049 | case IrBinOpAddSat: | |
| 11050 | case IrBinOpSubSat: | |
| 11051 | case IrBinOpMultSat: | |
| 11052 | case IrBinOpShlSat: | |
| 11053 | 11053 | return ir_analyze_bin_op_math(ira, bin_op_instruction); |
| 11054 | 11054 | case IrBinOpArrayCat: |
| 11055 | 11055 | return ir_analyze_array_cat(ira, bin_op_instruction); |
| ... | ... | @@ -15189,7 +15189,7 @@ static Stage1AirInst *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ |
| 15189 | 15189 | return ir_analyze_inferred_field_ptr(ira, field_name, scope, source_node, container_ptr, bare_type); |
| 15190 | 15190 | } |
| 15191 | 15191 | |
| 15192 | // Tracks wether we should return an undefined value of the correct type. | |
| 15192 | // Tracks whether we should return an undefined value of the correct type. | |
| 15193 | 15193 | // We do this if the container pointer is undefined and we are in a TypeOf call. |
| 15194 | 15194 | bool return_undef = container_ptr->value->special == ConstValSpecialUndef && \ |
| 15195 | 15195 | get_scope_typeof(scope) != nullptr; |
| ... | ... | @@ -15248,7 +15248,7 @@ static Stage1AirInst *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ |
| 15248 | 15248 | if (type_is_invalid(union_val->type)) |
| 15249 | 15249 | return ira->codegen->invalid_inst_gen; |
| 15250 | 15250 | |
| 15251 | // Reject undefined values unless we're intializing the union: | |
| 15251 | // Reject undefined values unless we're initializing the union: | |
| 15252 | 15252 | // a undefined union means also the tag is undefined, accessing |
| 15253 | 15253 | // its payload slot is UB. |
| 15254 | 15254 | const UndefAllowed allow_undef = initializing ? UndefOk : UndefBad; |
| ... | ... | @@ -16124,7 +16124,7 @@ static Stage1AirInst *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira, |
| 16124 | 16124 | |
| 16125 | 16125 | static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCtz *instruction) { |
| 16126 | 16126 | Error err; |
| 16127 | ||
| 16127 | ||
| 16128 | 16128 | ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); |
| 16129 | 16129 | if (type_is_invalid(int_type)) |
| 16130 | 16130 | return ira->codegen->invalid_inst_gen; |
| ... | ... | @@ -16166,7 +16166,7 @@ static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCt |
| 16166 | 16166 | return ira->codegen->invalid_inst_gen; |
| 16167 | 16167 | if (val->special == ConstValSpecialUndef) |
| 16168 | 16168 | return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int); |
| 16169 | ||
| 16169 | ||
| 16170 | 16170 | if (is_vector) { |
| 16171 | 16171 | ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type); |
| 16172 | 16172 | Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, smallest_vec_type); |
| ... | ... | @@ -16200,7 +16200,7 @@ static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCt |
| 16200 | 16200 | |
| 16201 | 16201 | static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstClz *instruction) { |
| 16202 | 16202 | Error err; |
| 16203 | ||
| 16203 | ||
| 16204 | 16204 | ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); |
| 16205 | 16205 | if (type_is_invalid(int_type)) |
| 16206 | 16206 | return ira->codegen->invalid_inst_gen; |
| ... | ... | @@ -16242,7 +16242,7 @@ static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstCl |
| 16242 | 16242 | return ira->codegen->invalid_inst_gen; |
| 16243 | 16243 | if (val->special == ConstValSpecialUndef) |
| 16244 | 16244 | return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int); |
| 16245 | ||
| 16245 | ||
| 16246 | 16246 | if (is_vector) { |
| 16247 | 16247 | ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type); |
| 16248 | 16248 | Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, smallest_vec_type); |
| ... | ... | @@ -16276,7 +16276,7 @@ static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstCl |
| 16276 | 16276 | |
| 16277 | 16277 | static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1ZirInstPopCount *instruction) { |
| 16278 | 16278 | Error err; |
| 16279 | ||
| 16279 | ||
| 16280 | 16280 | ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); |
| 16281 | 16281 | if (type_is_invalid(int_type)) |
| 16282 | 16282 | return ira->codegen->invalid_inst_gen; |
| ... | ... | @@ -16318,7 +16318,7 @@ static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1Zir |
| 16318 | 16318 | return ira->codegen->invalid_inst_gen; |
| 16319 | 16319 | if (val->special == ConstValSpecialUndef) |
| 16320 | 16320 | return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int); |
| 16321 | ||
| 16321 | ||
| 16322 | 16322 | if (is_vector) { |
| 16323 | 16323 | ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type); |
| 16324 | 16324 | Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, smallest_vec_type); |
| ... | ... | @@ -17904,7 +17904,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, Scope *scope, AstNode |
| 17904 | 17904 | result->special = ConstValSpecialStatic; |
| 17905 | 17905 | result->type = type_info_pointer_type; |
| 17906 | 17906 | |
| 17907 | ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 7); | |
| 17907 | ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 8); | |
| 17908 | 17908 | result->data.x_struct.fields = fields; |
| 17909 | 17909 | |
| 17910 | 17910 | // size: Size |
| ... | ... | @@ -17939,24 +17939,29 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, Scope *scope, AstNode |
| 17939 | 17939 | lazy_align_of->base.id = LazyValueIdAlignOf; |
| 17940 | 17940 | lazy_align_of->target_type = ir_const_type(ira, scope, source_node, attrs_type->data.pointer.child_type); |
| 17941 | 17941 | } |
| 17942 | // child: type | |
| 17943 | ensure_field_index(result->type, "child", 4); | |
| 17942 | // address_space: AddressSpace, | |
| 17943 | ensure_field_index(result->type, "address_space", 4); | |
| 17944 | 17944 | fields[4]->special = ConstValSpecialStatic; |
| 17945 | fields[4]->type = ira->codegen->builtin_types.entry_type; | |
| 17946 | fields[4]->data.x_type = attrs_type->data.pointer.child_type; | |
| 17947 | // is_allowzero: bool | |
| 17948 | ensure_field_index(result->type, "is_allowzero", 5); | |
| 17945 | fields[4]->type = get_builtin_type(ira->codegen, "AddressSpace"); | |
| 17946 | bigint_init_unsigned(&fields[4]->data.x_enum_tag, AddressSpaceGeneric); | |
| 17947 | // child: type | |
| 17948 | ensure_field_index(result->type, "child", 5); | |
| 17949 | 17949 | fields[5]->special = ConstValSpecialStatic; |
| 17950 | fields[5]->type = ira->codegen->builtin_types.entry_bool; | |
| 17951 | fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero; | |
| 17952 | // sentinel: anytype | |
| 17953 | ensure_field_index(result->type, "sentinel", 6); | |
| 17950 | fields[5]->type = ira->codegen->builtin_types.entry_type; | |
| 17951 | fields[5]->data.x_type = attrs_type->data.pointer.child_type; | |
| 17952 | // is_allowzero: bool | |
| 17953 | ensure_field_index(result->type, "is_allowzero", 6); | |
| 17954 | 17954 | fields[6]->special = ConstValSpecialStatic; |
| 17955 | fields[6]->type = ira->codegen->builtin_types.entry_bool; | |
| 17956 | fields[6]->data.x_bool = attrs_type->data.pointer.allow_zero; | |
| 17957 | // sentinel: anytype | |
| 17958 | ensure_field_index(result->type, "sentinel", 7); | |
| 17959 | fields[7]->special = ConstValSpecialStatic; | |
| 17955 | 17960 | if (attrs_type->data.pointer.sentinel != nullptr) { |
| 17956 | fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type); | |
| 17957 | set_optional_payload(fields[6], attrs_type->data.pointer.sentinel); | |
| 17961 | fields[7]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type); | |
| 17962 | set_optional_payload(fields[7], attrs_type->data.pointer.sentinel); | |
| 17958 | 17963 | } else { |
| 17959 | fields[6]->type = ira->codegen->builtin_types.entry_null; | |
| 17964 | fields[7]->type = ira->codegen->builtin_types.entry_null; | |
| 17960 | 17965 | } |
| 17961 | 17966 | |
| 17962 | 17967 | return result; |
| ... | ... | @@ -18465,7 +18470,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour |
| 18465 | 18470 | result->special = ConstValSpecialStatic; |
| 18466 | 18471 | result->type = ir_type_info_get_type(ira, "Fn", nullptr); |
| 18467 | 18472 | |
| 18468 | ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 6); | |
| 18473 | ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 7); | |
| 18469 | 18474 | result->data.x_struct.fields = fields; |
| 18470 | 18475 | |
| 18471 | 18476 | // calling_convention: TypeInfo.CallingConvention |
| ... | ... | @@ -18826,11 +18831,11 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_ |
| 18826 | 18831 | assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type)); |
| 18827 | 18832 | BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag); |
| 18828 | 18833 | PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index); |
| 18829 | ZigType *elem_type = get_const_field_meta_type(ira, source_node, payload, "child", 4); | |
| 18834 | ZigType *elem_type = get_const_field_meta_type(ira, source_node, payload, "child", 5); | |
| 18830 | 18835 | if (type_is_invalid(elem_type)) |
| 18831 | 18836 | return ira->codegen->invalid_inst_gen->value->type; |
| 18832 | 18837 | ZigValue *sentinel; |
| 18833 | if ((err = get_const_field_sentinel(ira, scope, source_node, payload, "sentinel", 6, | |
| 18838 | if ((err = get_const_field_sentinel(ira, scope, source_node, payload, "sentinel", 7, | |
| 18834 | 18839 | elem_type, &sentinel))) |
| 18835 | 18840 | { |
| 18836 | 18841 | return ira->codegen->invalid_inst_gen->value->type; |
| ... | ... | @@ -18845,6 +18850,19 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_ |
| 18845 | 18850 | if (alignment == nullptr) |
| 18846 | 18851 | return ira->codegen->invalid_inst_gen->value->type; |
| 18847 | 18852 | |
| 18853 | ZigValue *as_value = get_const_field(ira, source_node, payload, "address_space", 4); | |
| 18854 | if (as_value == nullptr) | |
| 18855 | return ira->codegen->invalid_inst_gen->value->type; | |
| 18856 | assert(as_value->special == ConstValSpecialStatic); | |
| 18857 | assert(as_value->type == get_builtin_type(ira->codegen, "AddressSpace")); | |
| 18858 | AddressSpace as = (AddressSpace)bigint_as_u32(&as_value->data.x_enum_tag); | |
| 18859 | if (as != AddressSpaceGeneric) { | |
| 18860 | ir_add_error_node(ira, source_node, buf_sprintf( | |
| 18861 | "address space '%s' not available in stage 1 compiler, must be .generic", | |
| 18862 | address_space_name(as))); | |
| 18863 | return ira->codegen->invalid_inst_gen->value->type; | |
| 18864 | } | |
| 18865 | ||
| 18848 | 18866 | bool is_const; |
| 18849 | 18867 | if ((err = get_const_field_bool(ira, source_node, payload, "is_const", 1, &is_const))) |
| 18850 | 18868 | return ira->codegen->invalid_inst_gen->value->type; |
| ... | ... | @@ -18857,13 +18875,12 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_ |
| 18857 | 18875 | } |
| 18858 | 18876 | |
| 18859 | 18877 | bool is_allowzero; |
| 18860 | if ((err = get_const_field_bool(ira, source_node, payload, "is_allowzero", 5, | |
| 18878 | if ((err = get_const_field_bool(ira, source_node, payload, "is_allowzero", 6, | |
| 18861 | 18879 | &is_allowzero))) |
| 18862 | 18880 | { |
| 18863 | 18881 | return ira->codegen->invalid_inst_gen->value->type; |
| 18864 | 18882 | } |
| 18865 | 18883 | |
| 18866 | ||
| 18867 | 18884 | ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, |
| 18868 | 18885 | elem_type, |
| 18869 | 18886 | is_const, |
src/stage1/ir_print.cpp+6-6| ... | ... | @@ -558,7 +558,7 @@ const char* ir_inst_gen_type_str(Stage1AirInstId id) { |
| 558 | 558 | case Stage1AirInstIdWasmMemoryGrow: |
| 559 | 559 | return "GenWasmMemoryGrow"; |
| 560 | 560 | case Stage1AirInstIdExtern: |
| 561 | return "GenExtrern"; | |
| 561 | return "GenExtern"; | |
| 562 | 562 | } |
| 563 | 563 | zig_unreachable(); |
| 564 | 564 | } |
| ... | ... | @@ -737,13 +737,13 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) { |
| 737 | 737 | return "@maximum"; |
| 738 | 738 | case IrBinOpMinimum: |
| 739 | 739 | return "@minimum"; |
| 740 | case IrBinOpSatAdd: | |
| 740 | case IrBinOpAddSat: | |
| 741 | 741 | return "@addWithSaturation"; |
| 742 | case IrBinOpSatSub: | |
| 742 | case IrBinOpSubSat: | |
| 743 | 743 | return "@subWithSaturation"; |
| 744 | case IrBinOpSatMul: | |
| 744 | case IrBinOpMultSat: | |
| 745 | 745 | return "@mulWithSaturation"; |
| 746 | case IrBinOpSatShl: | |
| 746 | case IrBinOpShlSat: | |
| 747 | 747 | return "@shlWithSaturation"; |
| 748 | 748 | } |
| 749 | 749 | zig_unreachable(); |
| ... | ... | @@ -829,7 +829,7 @@ static const char *cast_op_str(CastOp op) { |
| 829 | 829 | case CastOpIntToFloat: return "IntToFloat"; |
| 830 | 830 | case CastOpFloatToInt: return "FloatToInt"; |
| 831 | 831 | case CastOpBoolToInt: return "BoolToInt"; |
| 832 | case CastOpNumLitToConcrete: return "NumLitToConcrate"; | |
| 832 | case CastOpNumLitToConcrete: return "NumLitToConcrete"; | |
| 833 | 833 | case CastOpErrSet: return "ErrSet"; |
| 834 | 834 | case CastOpBitCast: return "BitCast"; |
| 835 | 835 | } |
src/stage1/os.hpp+2| ... | ... | @@ -33,6 +33,8 @@ |
| 33 | 33 | #define ZIG_OS_OPENBSD |
| 34 | 34 | #elif defined(__HAIKU__) |
| 35 | 35 | #define ZIG_OS_HAIKU |
| 36 | #elif defined(__sun) | |
| 37 | #define ZIG_OS_SOLARIS | |
| 36 | 38 | #else |
| 37 | 39 | #define ZIG_OS_UNKNOWN |
| 38 | 40 | #endif |
src/stage1/parser.cpp+17-1| ... | ... | @@ -2073,7 +2073,7 @@ static AstNode *ast_parse_field_init(ParseContext *pc) { |
| 2073 | 2073 | return nullptr; |
| 2074 | 2074 | } |
| 2075 | 2075 | if (eat_token_if(pc, TokenIdEq) == 0) { |
| 2076 | // Because ".Name" can also be intepreted as an enum literal, we should put back | |
| 2076 | // Because ".Name" can also be interpreted as an enum literal, we should put back | |
| 2077 | 2077 | // those two tokens again so that the parser can try to parse them as the enum |
| 2078 | 2078 | // literal later. |
| 2079 | 2079 | put_back_token(pc); |
| ... | ... | @@ -2381,6 +2381,7 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) { |
| 2381 | 2381 | // / PLUSEQUAL |
| 2382 | 2382 | // / MINUSEQUAL |
| 2383 | 2383 | // / LARROW2EQUAL |
| 2384 | // / LARROW2PIPEEQUAL | |
| 2384 | 2385 | // / RARROW2EQUAL |
| 2385 | 2386 | // / AMPERSANDEQUAL |
| 2386 | 2387 | // / CARETEQUAL |
| ... | ... | @@ -2388,6 +2389,9 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) { |
| 2388 | 2389 | // / ASTERISKPERCENTEQUAL |
| 2389 | 2390 | // / PLUSPERCENTEQUAL |
| 2390 | 2391 | // / MINUSPERCENTEQUAL |
| 2392 | // / ASTERISKPIPEEQUAL | |
| 2393 | // / PLUSPIPEEQUAL | |
| 2394 | // / MINUSPIPEEQUAL | |
| 2391 | 2395 | // / EQUAL |
| 2392 | 2396 | static AstNode *ast_parse_assign_op(ParseContext *pc) { |
| 2393 | 2397 | // In C, we have `T arr[N] = {[i] = T{}};` but it doesn't |
| ... | ... | @@ -2396,17 +2400,21 @@ static AstNode *ast_parse_assign_op(ParseContext *pc) { |
| 2396 | 2400 | table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd; |
| 2397 | 2401 | table[TokenIdBitOrEq] = BinOpTypeAssignBitOr; |
| 2398 | 2402 | table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft; |
| 2403 | table[TokenIdBitShiftLeftPipeEq] = BinOpTypeAssignBitShiftLeftSat; | |
| 2399 | 2404 | table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight; |
| 2400 | 2405 | table[TokenIdBitXorEq] = BinOpTypeAssignBitXor; |
| 2401 | 2406 | table[TokenIdDivEq] = BinOpTypeAssignDiv; |
| 2402 | 2407 | table[TokenIdEq] = BinOpTypeAssign; |
| 2403 | 2408 | table[TokenIdMinusEq] = BinOpTypeAssignMinus; |
| 2404 | 2409 | table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap; |
| 2410 | table[TokenIdMinusPipeEq] = BinOpTypeAssignMinusSat; | |
| 2405 | 2411 | table[TokenIdModEq] = BinOpTypeAssignMod; |
| 2406 | 2412 | table[TokenIdPlusEq] = BinOpTypeAssignPlus; |
| 2407 | 2413 | table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap; |
| 2414 | table[TokenIdPlusPipeEq] = BinOpTypeAssignPlusSat; | |
| 2408 | 2415 | table[TokenIdTimesEq] = BinOpTypeAssignTimes; |
| 2409 | 2416 | table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap; |
| 2417 | table[TokenIdTimesPipeEq] = BinOpTypeAssignTimesSat; | |
| 2410 | 2418 | |
| 2411 | 2419 | BinOpType op = table[pc->token_ids[pc->current_token]]; |
| 2412 | 2420 | if (op != BinOpTypeInvalid) { |
| ... | ... | @@ -2483,10 +2491,12 @@ static AstNode *ast_parse_bitwise_op(ParseContext *pc) { |
| 2483 | 2491 | |
| 2484 | 2492 | // BitShiftOp |
| 2485 | 2493 | // <- LARROW2 |
| 2494 | // / LARROW2PIPE | |
| 2486 | 2495 | // / RARROW2 |
| 2487 | 2496 | static AstNode *ast_parse_bit_shift_op(ParseContext *pc) { |
| 2488 | 2497 | BinOpType table[TokenIdCount] = {}; |
| 2489 | 2498 | table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft; |
| 2499 | table[TokenIdBitShiftLeftPipe] = BinOpTypeBitShiftLeftSat; | |
| 2490 | 2500 | table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight; |
| 2491 | 2501 | |
| 2492 | 2502 | BinOpType op = table[pc->token_ids[pc->current_token]]; |
| ... | ... | @@ -2506,6 +2516,8 @@ static AstNode *ast_parse_bit_shift_op(ParseContext *pc) { |
| 2506 | 2516 | // / PLUS2 |
| 2507 | 2517 | // / PLUSPERCENT |
| 2508 | 2518 | // / MINUSPERCENT |
| 2519 | // / PLUSPIPE | |
| 2520 | // / MINUSPIPE | |
| 2509 | 2521 | static AstNode *ast_parse_addition_op(ParseContext *pc) { |
| 2510 | 2522 | BinOpType table[TokenIdCount] = {}; |
| 2511 | 2523 | table[TokenIdPlus] = BinOpTypeAdd; |
| ... | ... | @@ -2513,6 +2525,8 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) { |
| 2513 | 2525 | table[TokenIdPlusPlus] = BinOpTypeArrayCat; |
| 2514 | 2526 | table[TokenIdPlusPercent] = BinOpTypeAddWrap; |
| 2515 | 2527 | table[TokenIdMinusPercent] = BinOpTypeSubWrap; |
| 2528 | table[TokenIdPlusPipe] = BinOpTypeAddSat; | |
| 2529 | table[TokenIdMinusPipe] = BinOpTypeSubSat; | |
| 2516 | 2530 | |
| 2517 | 2531 | BinOpType op = table[pc->token_ids[pc->current_token]]; |
| 2518 | 2532 | if (op != BinOpTypeInvalid) { |
| ... | ... | @@ -2532,6 +2546,7 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) { |
| 2532 | 2546 | // / PERCENT |
| 2533 | 2547 | // / ASTERISK2 |
| 2534 | 2548 | // / ASTERISKPERCENT |
| 2549 | // / ASTERISKPIPE | |
| 2535 | 2550 | static AstNode *ast_parse_multiply_op(ParseContext *pc) { |
| 2536 | 2551 | BinOpType table[TokenIdCount] = {}; |
| 2537 | 2552 | table[TokenIdBarBar] = BinOpTypeMergeErrorSets; |
| ... | ... | @@ -2540,6 +2555,7 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc) { |
| 2540 | 2555 | table[TokenIdPercent] = BinOpTypeMod; |
| 2541 | 2556 | table[TokenIdStarStar] = BinOpTypeArrayMult; |
| 2542 | 2557 | table[TokenIdTimesPercent] = BinOpTypeMultWrap; |
| 2558 | table[TokenIdTimesPipe] = BinOpTypeMultSat; | |
| 2543 | 2559 | |
| 2544 | 2560 | BinOpType op = table[pc->token_ids[pc->current_token]]; |
| 2545 | 2561 | if (op != BinOpTypeInvalid) { |
src/stage1/target.cpp+4-1| ... | ... | @@ -401,6 +401,9 @@ Error target_parse_os(Os *out_os, const char *os_ptr, size_t os_len) { |
| 401 | 401 | #elif defined(ZIG_OS_HAIKU) |
| 402 | 402 | *out_os = OsHaiku; |
| 403 | 403 | return ErrorNone; |
| 404 | #elif defined(ZIG_OS_SOLARIS) | |
| 405 | *out_os = OsSolaris; | |
| 406 | return ErrorNone; | |
| 404 | 407 | #else |
| 405 | 408 | zig_panic("stage1 is unable to detect native target for this OS"); |
| 406 | 409 | #endif |
| ... | ... | @@ -674,6 +677,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { |
| 674 | 677 | case OsOpenBSD: |
| 675 | 678 | case OsWASI: |
| 676 | 679 | case OsHaiku: |
| 680 | case OsSolaris: | |
| 677 | 681 | case OsEmscripten: |
| 678 | 682 | case OsPlan9: |
| 679 | 683 | switch (id) { |
| ... | ... | @@ -732,7 +736,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { |
| 732 | 736 | case OsCloudABI: |
| 733 | 737 | case OsKFreeBSD: |
| 734 | 738 | case OsLv2: |
| 735 | case OsSolaris: | |
| 736 | 739 | case OsZOS: |
| 737 | 740 | case OsMinix: |
| 738 | 741 | case OsRTEMS: |
src/stage1/tokenizer.cpp+84| ... | ... | @@ -226,8 +226,10 @@ enum TokenizeState { |
| 226 | 226 | TokenizeState_pipe, |
| 227 | 227 | TokenizeState_minus, |
| 228 | 228 | TokenizeState_minus_percent, |
| 229 | TokenizeState_minus_pipe, | |
| 229 | 230 | TokenizeState_asterisk, |
| 230 | 231 | TokenizeState_asterisk_percent, |
| 232 | TokenizeState_asterisk_pipe, | |
| 231 | 233 | TokenizeState_slash, |
| 232 | 234 | TokenizeState_line_comment_start, |
| 233 | 235 | TokenizeState_line_comment, |
| ... | ... | @@ -257,8 +259,10 @@ enum TokenizeState { |
| 257 | 259 | TokenizeState_percent, |
| 258 | 260 | TokenizeState_plus, |
| 259 | 261 | TokenizeState_plus_percent, |
| 262 | TokenizeState_plus_pipe, | |
| 260 | 263 | TokenizeState_angle_bracket_left, |
| 261 | 264 | TokenizeState_angle_bracket_angle_bracket_left, |
| 265 | TokenizeState_angle_bracket_angle_bracket_left_pipe, | |
| 262 | 266 | TokenizeState_angle_bracket_right, |
| 263 | 267 | TokenizeState_angle_bracket_angle_bracket_right, |
| 264 | 268 | TokenizeState_period, |
| ... | ... | @@ -548,6 +552,9 @@ void tokenize(const char *source, Tokenization *out) { |
| 548 | 552 | case '%': |
| 549 | 553 | t.state = TokenizeState_asterisk_percent; |
| 550 | 554 | break; |
| 555 | case '|': | |
| 556 | t.state = TokenizeState_asterisk_pipe; | |
| 557 | break; | |
| 551 | 558 | default: |
| 552 | 559 | t.state = TokenizeState_start; |
| 553 | 560 | continue; |
| ... | ... | @@ -568,6 +575,21 @@ void tokenize(const char *source, Tokenization *out) { |
| 568 | 575 | continue; |
| 569 | 576 | } |
| 570 | 577 | break; |
| 578 | case TokenizeState_asterisk_pipe: | |
| 579 | switch (c) { | |
| 580 | case 0: | |
| 581 | t.out->ids.last() = TokenIdTimesPipe; | |
| 582 | goto eof; | |
| 583 | case '=': | |
| 584 | t.out->ids.last() = TokenIdTimesPipeEq; | |
| 585 | t.state = TokenizeState_start; | |
| 586 | break; | |
| 587 | default: | |
| 588 | t.out->ids.last() = TokenIdTimesPipe; | |
| 589 | t.state = TokenizeState_start; | |
| 590 | continue; | |
| 591 | } | |
| 592 | break; | |
| 571 | 593 | case TokenizeState_percent: |
| 572 | 594 | switch (c) { |
| 573 | 595 | case 0: |
| ... | ... | @@ -596,6 +618,9 @@ void tokenize(const char *source, Tokenization *out) { |
| 596 | 618 | case '%': |
| 597 | 619 | t.state = TokenizeState_plus_percent; |
| 598 | 620 | break; |
| 621 | case '|': | |
| 622 | t.state = TokenizeState_plus_pipe; | |
| 623 | break; | |
| 599 | 624 | default: |
| 600 | 625 | t.state = TokenizeState_start; |
| 601 | 626 | continue; |
| ... | ... | @@ -616,6 +641,21 @@ void tokenize(const char *source, Tokenization *out) { |
| 616 | 641 | continue; |
| 617 | 642 | } |
| 618 | 643 | break; |
| 644 | case TokenizeState_plus_pipe: | |
| 645 | switch (c) { | |
| 646 | case 0: | |
| 647 | t.out->ids.last() = TokenIdPlusPipe; | |
| 648 | goto eof; | |
| 649 | case '=': | |
| 650 | t.out->ids.last() = TokenIdPlusPipeEq; | |
| 651 | t.state = TokenizeState_start; | |
| 652 | break; | |
| 653 | default: | |
| 654 | t.out->ids.last() = TokenIdPlusPipe; | |
| 655 | t.state = TokenizeState_start; | |
| 656 | continue; | |
| 657 | } | |
| 658 | break; | |
| 619 | 659 | case TokenizeState_caret: |
| 620 | 660 | switch (c) { |
| 621 | 661 | case 0: |
| ... | ... | @@ -891,6 +931,9 @@ void tokenize(const char *source, Tokenization *out) { |
| 891 | 931 | case '%': |
| 892 | 932 | t.state = TokenizeState_minus_percent; |
| 893 | 933 | break; |
| 934 | case '|': | |
| 935 | t.state = TokenizeState_minus_pipe; | |
| 936 | break; | |
| 894 | 937 | default: |
| 895 | 938 | t.state = TokenizeState_start; |
| 896 | 939 | continue; |
| ... | ... | @@ -911,6 +954,21 @@ void tokenize(const char *source, Tokenization *out) { |
| 911 | 954 | continue; |
| 912 | 955 | } |
| 913 | 956 | break; |
| 957 | case TokenizeState_minus_pipe: | |
| 958 | switch (c) { | |
| 959 | case 0: | |
| 960 | t.out->ids.last() = TokenIdMinusPipe; | |
| 961 | goto eof; | |
| 962 | case '=': | |
| 963 | t.out->ids.last() = TokenIdMinusPipeEq; | |
| 964 | t.state = TokenizeState_start; | |
| 965 | break; | |
| 966 | default: | |
| 967 | t.out->ids.last() = TokenIdMinusPipe; | |
| 968 | t.state = TokenizeState_start; | |
| 969 | continue; | |
| 970 | } | |
| 971 | break; | |
| 914 | 972 | case TokenizeState_angle_bracket_left: |
| 915 | 973 | switch (c) { |
| 916 | 974 | case 0: |
| ... | ... | @@ -936,12 +994,30 @@ void tokenize(const char *source, Tokenization *out) { |
| 936 | 994 | t.out->ids.last() = TokenIdBitShiftLeftEq; |
| 937 | 995 | t.state = TokenizeState_start; |
| 938 | 996 | break; |
| 997 | case '|': | |
| 998 | t.state = TokenizeState_angle_bracket_angle_bracket_left_pipe; | |
| 999 | break; | |
| 939 | 1000 | default: |
| 940 | 1001 | t.out->ids.last() = TokenIdBitShiftLeft; |
| 941 | 1002 | t.state = TokenizeState_start; |
| 942 | 1003 | continue; |
| 943 | 1004 | } |
| 944 | 1005 | break; |
| 1006 | case TokenizeState_angle_bracket_angle_bracket_left_pipe: | |
| 1007 | switch (c) { | |
| 1008 | case 0: | |
| 1009 | t.out->ids.last() = TokenIdBitShiftLeftPipe; | |
| 1010 | goto eof; | |
| 1011 | case '=': | |
| 1012 | t.out->ids.last() = TokenIdBitShiftLeftPipeEq; | |
| 1013 | t.state = TokenizeState_start; | |
| 1014 | break; | |
| 1015 | default: | |
| 1016 | t.out->ids.last() = TokenIdBitShiftLeftPipe; | |
| 1017 | t.state = TokenizeState_start; | |
| 1018 | continue; | |
| 1019 | } | |
| 1020 | break; | |
| 945 | 1021 | case TokenizeState_angle_bracket_right: |
| 946 | 1022 | switch (c) { |
| 947 | 1023 | case 0: |
| ... | ... | @@ -1437,6 +1513,8 @@ const char * token_name(TokenId id) { |
| 1437 | 1513 | case TokenIdBitOrEq: return "|="; |
| 1438 | 1514 | case TokenIdBitShiftLeft: return "<<"; |
| 1439 | 1515 | case TokenIdBitShiftLeftEq: return "<<="; |
| 1516 | case TokenIdBitShiftLeftPipe: return "<<|"; | |
| 1517 | case TokenIdBitShiftLeftPipeEq: return "<<|="; | |
| 1440 | 1518 | case TokenIdBitShiftRight: return ">>"; |
| 1441 | 1519 | case TokenIdBitShiftRightEq: return ">>="; |
| 1442 | 1520 | case TokenIdBitXorEq: return "^="; |
| ... | ... | @@ -1521,12 +1599,16 @@ const char * token_name(TokenId id) { |
| 1521 | 1599 | case TokenIdMinusEq: return "-="; |
| 1522 | 1600 | case TokenIdMinusPercent: return "-%"; |
| 1523 | 1601 | case TokenIdMinusPercentEq: return "-%="; |
| 1602 | case TokenIdMinusPipe: return "-|"; | |
| 1603 | case TokenIdMinusPipeEq: return "-|="; | |
| 1524 | 1604 | case TokenIdModEq: return "%="; |
| 1525 | 1605 | case TokenIdPercent: return "%"; |
| 1526 | 1606 | case TokenIdPlus: return "+"; |
| 1527 | 1607 | case TokenIdPlusEq: return "+="; |
| 1528 | 1608 | case TokenIdPlusPercent: return "+%"; |
| 1529 | 1609 | case TokenIdPlusPercentEq: return "+%="; |
| 1610 | case TokenIdPlusPipe: return "+|"; | |
| 1611 | case TokenIdPlusPipeEq: return "+|="; | |
| 1530 | 1612 | case TokenIdPlusPlus: return "++"; |
| 1531 | 1613 | case TokenIdRBrace: return "}"; |
| 1532 | 1614 | case TokenIdRBracket: return "]"; |
| ... | ... | @@ -1542,6 +1624,8 @@ const char * token_name(TokenId id) { |
| 1542 | 1624 | case TokenIdTimesEq: return "*="; |
| 1543 | 1625 | case TokenIdTimesPercent: return "*%"; |
| 1544 | 1626 | case TokenIdTimesPercentEq: return "*%="; |
| 1627 | case TokenIdTimesPipe: return "*|"; | |
| 1628 | case TokenIdTimesPipeEq: return "*|="; | |
| 1545 | 1629 | case TokenIdBuiltin: return "Builtin"; |
| 1546 | 1630 | case TokenIdCount: |
| 1547 | 1631 | zig_unreachable(); |
src/stage1/tokenizer.hpp+8| ... | ... | @@ -23,6 +23,8 @@ enum TokenId : uint8_t { |
| 23 | 23 | TokenIdBitOrEq, |
| 24 | 24 | TokenIdBitShiftLeft, |
| 25 | 25 | TokenIdBitShiftLeftEq, |
| 26 | TokenIdBitShiftLeftPipe, | |
| 27 | TokenIdBitShiftLeftPipeEq, | |
| 26 | 28 | TokenIdBitShiftRight, |
| 27 | 29 | TokenIdBitShiftRightEq, |
| 28 | 30 | TokenIdBitXorEq, |
| ... | ... | @@ -108,12 +110,16 @@ enum TokenId : uint8_t { |
| 108 | 110 | TokenIdMinusEq, |
| 109 | 111 | TokenIdMinusPercent, |
| 110 | 112 | TokenIdMinusPercentEq, |
| 113 | TokenIdMinusPipe, | |
| 114 | TokenIdMinusPipeEq, | |
| 111 | 115 | TokenIdModEq, |
| 112 | 116 | TokenIdPercent, |
| 113 | 117 | TokenIdPlus, |
| 114 | 118 | TokenIdPlusEq, |
| 115 | 119 | TokenIdPlusPercent, |
| 116 | 120 | TokenIdPlusPercentEq, |
| 121 | TokenIdPlusPipe, | |
| 122 | TokenIdPlusPipeEq, | |
| 117 | 123 | TokenIdPlusPlus, |
| 118 | 124 | TokenIdRBrace, |
| 119 | 125 | TokenIdRBracket, |
| ... | ... | @@ -129,6 +135,8 @@ enum TokenId : uint8_t { |
| 129 | 135 | TokenIdTimesEq, |
| 130 | 136 | TokenIdTimesPercent, |
| 131 | 137 | TokenIdTimesPercentEq, |
| 138 | TokenIdTimesPipe, | |
| 139 | TokenIdTimesPipeEq, | |
| 132 | 140 | |
| 133 | 141 | TokenIdCount, |
| 134 | 142 | }; |
src/target.zig+25| ... | ... | @@ -438,6 +438,13 @@ pub fn libcFullLinkFlags(target: std.Target) []const []const u8 { |
| 438 | 438 | "-lc", |
| 439 | 439 | "-lutil", |
| 440 | 440 | }, |
| 441 | .solaris => &[_][]const u8{ | |
| 442 | "-lm", | |
| 443 | "-lsocket", | |
| 444 | "-lnsl", | |
| 445 | // Solaris releases after 10 merged the threading libraries into libc. | |
| 446 | "-lc", | |
| 447 | }, | |
| 441 | 448 | .haiku => &[_][]const u8{ |
| 442 | 449 | "-lm", |
| 443 | 450 | "-lroot", |
| ... | ... | @@ -550,3 +557,21 @@ pub fn largestAtomicBits(target: std.Target) u32 { |
| 550 | 557 | .x86_64 => 128, |
| 551 | 558 | }; |
| 552 | 559 | } |
| 560 | ||
| 561 | pub fn defaultAddressSpace( | |
| 562 | target: std.Target, | |
| 563 | context: enum { | |
| 564 | /// Query the default address space for global constant values. | |
| 565 | global_constant, | |
| 566 | /// Query the default address space for global mutable values. | |
| 567 | global_mutable, | |
| 568 | /// Query the default address space for function-local values. | |
| 569 | local, | |
| 570 | /// Query the default address space for functions themselves. | |
| 571 | function, | |
| 572 | }, | |
| 573 | ) std.builtin.AddressSpace { | |
| 574 | _ = target; | |
| 575 | _ = context; | |
| 576 | return .generic; | |
| 577 | } |
src/translate_c.zig+1-1| ... | ... | @@ -4882,7 +4882,7 @@ fn finishTransFnProto( |
| 4882 | 4882 | var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa); |
| 4883 | 4883 | defer fn_params.deinit(); |
| 4884 | 4884 | const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0; |
| 4885 | try fn_params.ensureCapacity(param_count); | |
| 4885 | try fn_params.ensureTotalCapacity(param_count); | |
| 4886 | 4886 | |
| 4887 | 4887 | var i: usize = 0; |
| 4888 | 4888 | while (i < param_count) : (i += 1) { |
src/translate_c/ast.zig+12-9| ... | ... | @@ -728,13 +728,13 @@ pub fn render(gpa: *Allocator, nodes: []const Node) !std.zig.Ast { |
| 728 | 728 | |
| 729 | 729 | // Estimate that each top level node has 10 child nodes. |
| 730 | 730 | const estimated_node_count = nodes.len * 10; |
| 731 | try ctx.nodes.ensureCapacity(gpa, estimated_node_count); | |
| 731 | try ctx.nodes.ensureTotalCapacity(gpa, estimated_node_count); | |
| 732 | 732 | // Estimate that each each node has 2 tokens. |
| 733 | 733 | const estimated_tokens_count = estimated_node_count * 2; |
| 734 | try ctx.tokens.ensureCapacity(gpa, estimated_tokens_count); | |
| 734 | try ctx.tokens.ensureTotalCapacity(gpa, estimated_tokens_count); | |
| 735 | 735 | // Estimate that each each token is 3 bytes long. |
| 736 | 736 | const estimated_buf_len = estimated_tokens_count * 3; |
| 737 | try ctx.buf.ensureCapacity(estimated_buf_len); | |
| 737 | try ctx.buf.ensureTotalCapacity(estimated_buf_len); | |
| 738 | 738 | |
| 739 | 739 | ctx.nodes.appendAssumeCapacity(.{ |
| 740 | 740 | .tag = .root, |
| ... | ... | @@ -839,7 +839,7 @@ const Context = struct { |
| 839 | 839 | |
| 840 | 840 | fn addExtra(c: *Context, extra: anytype) Allocator.Error!NodeIndex { |
| 841 | 841 | const fields = std.meta.fields(@TypeOf(extra)); |
| 842 | try c.extra_data.ensureCapacity(c.gpa, c.extra_data.items.len + fields.len); | |
| 842 | try c.extra_data.ensureUnusedCapacity(c.gpa, fields.len); | |
| 843 | 843 | const result = @intCast(u32, c.extra_data.items.len); |
| 844 | 844 | inline for (fields) |field| { |
| 845 | 845 | comptime std.debug.assert(field.field_type == NodeIndex); |
| ... | ... | @@ -1462,10 +1462,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1462 | 1462 | .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="), |
| 1463 | 1463 | .div => return renderBinOpGrouped(c, node, .div, .slash, "/"), |
| 1464 | 1464 | .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="), |
| 1465 | .shl => return renderBinOpGrouped(c, node, .bit_shift_left, .angle_bracket_angle_bracket_left, "<<"), | |
| 1466 | .shl_assign => return renderBinOp(c, node, .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal, "<<="), | |
| 1467 | .shr => return renderBinOpGrouped(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"), | |
| 1468 | .shr_assign => return renderBinOp(c, node, .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal, ">>="), | |
| 1465 | .shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"), | |
| 1466 | .shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="), | |
| 1467 | .shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"), | |
| 1468 | .shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="), | |
| 1469 | 1469 | .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"), |
| 1470 | 1470 | .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="), |
| 1471 | 1471 | .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"), |
| ... | ... | @@ -2614,6 +2614,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex { |
| 2614 | 2614 | .type_node = type_node, |
| 2615 | 2615 | .align_node = align_node, |
| 2616 | 2616 | .section_node = section_node, |
| 2617 | .addrspace_node = 0, | |
| 2617 | 2618 | }), |
| 2618 | 2619 | .rhs = init_node, |
| 2619 | 2620 | }, |
| ... | ... | @@ -2705,6 +2706,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { |
| 2705 | 2706 | .lhs = try c.addExtra(std.zig.Ast.Node.FnProtoOne{ |
| 2706 | 2707 | .param = params.items[0], |
| 2707 | 2708 | .align_expr = align_expr, |
| 2709 | .addrspace_expr = 0, // TODO | |
| 2708 | 2710 | .section_expr = section_expr, |
| 2709 | 2711 | .callconv_expr = callconv_expr, |
| 2710 | 2712 | }), |
| ... | ... | @@ -2720,6 +2722,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { |
| 2720 | 2722 | .params_start = span.start, |
| 2721 | 2723 | .params_end = span.end, |
| 2722 | 2724 | .align_expr = align_expr, |
| 2725 | .addrspace_expr = 0, // TODO | |
| 2723 | 2726 | .section_expr = section_expr, |
| 2724 | 2727 | .callconv_expr = callconv_expr, |
| 2725 | 2728 | }), |
| ... | ... | @@ -2797,7 +2800,7 @@ fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.Ar |
| 2797 | 2800 | _ = try c.addToken(.l_paren, "("); |
| 2798 | 2801 | var rendered = std.ArrayList(NodeIndex).init(c.gpa); |
| 2799 | 2802 | errdefer rendered.deinit(); |
| 2800 | try rendered.ensureCapacity(std.math.max(params.len, 1)); | |
| 2803 | try rendered.ensureTotalCapacity(std.math.max(params.len, 1)); | |
| 2801 | 2804 | |
| 2802 | 2805 | for (params) |param, i| { |
| 2803 | 2806 | if (i != 0) _ = try c.addToken(.comma, ","); |
src/type.zig+449-108| ... | ... | @@ -124,9 +124,11 @@ pub const Type = extern union { |
| 124 | 124 | .enum_full, |
| 125 | 125 | .enum_nonexhaustive, |
| 126 | 126 | .enum_simple, |
| 127 | .enum_numbered, | |
| 127 | 128 | .atomic_order, |
| 128 | 129 | .atomic_rmw_op, |
| 129 | 130 | .calling_convention, |
| 131 | .address_space, | |
| 130 | 132 | .float_mode, |
| 131 | 133 | .reduce_op, |
| 132 | 134 | => return .Enum, |
| ... | ... | @@ -136,6 +138,7 @@ pub const Type = extern union { |
| 136 | 138 | .type_info, |
| 137 | 139 | => return .Union, |
| 138 | 140 | |
| 141 | .bound_fn => unreachable, | |
| 139 | 142 | .var_args_param => unreachable, // can be any type |
| 140 | 143 | } |
| 141 | 144 | } |
| ... | ... | @@ -288,6 +291,7 @@ pub const Type = extern union { |
| 288 | 291 | .pointee_type = Type.initTag(.comptime_int), |
| 289 | 292 | .sentinel = null, |
| 290 | 293 | .@"align" = 0, |
| 294 | .@"addrspace" = .generic, | |
| 291 | 295 | .bit_offset = 0, |
| 292 | 296 | .host_size = 0, |
| 293 | 297 | .@"allowzero" = false, |
| ... | ... | @@ -299,6 +303,7 @@ pub const Type = extern union { |
| 299 | 303 | .pointee_type = Type.initTag(.u8), |
| 300 | 304 | .sentinel = null, |
| 301 | 305 | .@"align" = 0, |
| 306 | .@"addrspace" = .generic, | |
| 302 | 307 | .bit_offset = 0, |
| 303 | 308 | .host_size = 0, |
| 304 | 309 | .@"allowzero" = false, |
| ... | ... | @@ -310,6 +315,7 @@ pub const Type = extern union { |
| 310 | 315 | .pointee_type = self.castPointer().?.data, |
| 311 | 316 | .sentinel = null, |
| 312 | 317 | .@"align" = 0, |
| 318 | .@"addrspace" = .generic, | |
| 313 | 319 | .bit_offset = 0, |
| 314 | 320 | .host_size = 0, |
| 315 | 321 | .@"allowzero" = false, |
| ... | ... | @@ -321,6 +327,7 @@ pub const Type = extern union { |
| 321 | 327 | .pointee_type = self.castPointer().?.data, |
| 322 | 328 | .sentinel = null, |
| 323 | 329 | .@"align" = 0, |
| 330 | .@"addrspace" = .generic, | |
| 324 | 331 | .bit_offset = 0, |
| 325 | 332 | .host_size = 0, |
| 326 | 333 | .@"allowzero" = false, |
| ... | ... | @@ -332,6 +339,7 @@ pub const Type = extern union { |
| 332 | 339 | .pointee_type = self.castPointer().?.data, |
| 333 | 340 | .sentinel = null, |
| 334 | 341 | .@"align" = 0, |
| 342 | .@"addrspace" = .generic, | |
| 335 | 343 | .bit_offset = 0, |
| 336 | 344 | .host_size = 0, |
| 337 | 345 | .@"allowzero" = false, |
| ... | ... | @@ -343,6 +351,7 @@ pub const Type = extern union { |
| 343 | 351 | .pointee_type = Type.initTag(.u8), |
| 344 | 352 | .sentinel = null, |
| 345 | 353 | .@"align" = 0, |
| 354 | .@"addrspace" = .generic, | |
| 346 | 355 | .bit_offset = 0, |
| 347 | 356 | .host_size = 0, |
| 348 | 357 | .@"allowzero" = false, |
| ... | ... | @@ -354,6 +363,7 @@ pub const Type = extern union { |
| 354 | 363 | .pointee_type = self.castPointer().?.data, |
| 355 | 364 | .sentinel = null, |
| 356 | 365 | .@"align" = 0, |
| 366 | .@"addrspace" = .generic, | |
| 357 | 367 | .bit_offset = 0, |
| 358 | 368 | .host_size = 0, |
| 359 | 369 | .@"allowzero" = false, |
| ... | ... | @@ -365,6 +375,7 @@ pub const Type = extern union { |
| 365 | 375 | .pointee_type = Type.initTag(.u8), |
| 366 | 376 | .sentinel = null, |
| 367 | 377 | .@"align" = 0, |
| 378 | .@"addrspace" = .generic, | |
| 368 | 379 | .bit_offset = 0, |
| 369 | 380 | .host_size = 0, |
| 370 | 381 | .@"allowzero" = false, |
| ... | ... | @@ -376,6 +387,7 @@ pub const Type = extern union { |
| 376 | 387 | .pointee_type = self.castPointer().?.data, |
| 377 | 388 | .sentinel = null, |
| 378 | 389 | .@"align" = 0, |
| 390 | .@"addrspace" = .generic, | |
| 379 | 391 | .bit_offset = 0, |
| 380 | 392 | .host_size = 0, |
| 381 | 393 | .@"allowzero" = false, |
| ... | ... | @@ -387,6 +399,7 @@ pub const Type = extern union { |
| 387 | 399 | .pointee_type = self.castPointer().?.data, |
| 388 | 400 | .sentinel = null, |
| 389 | 401 | .@"align" = 0, |
| 402 | .@"addrspace" = .generic, | |
| 390 | 403 | .bit_offset = 0, |
| 391 | 404 | .host_size = 0, |
| 392 | 405 | .@"allowzero" = false, |
| ... | ... | @@ -398,6 +411,7 @@ pub const Type = extern union { |
| 398 | 411 | .pointee_type = self.castPointer().?.data, |
| 399 | 412 | .sentinel = null, |
| 400 | 413 | .@"align" = 0, |
| 414 | .@"addrspace" = .generic, | |
| 401 | 415 | .bit_offset = 0, |
| 402 | 416 | .host_size = 0, |
| 403 | 417 | .@"allowzero" = false, |
| ... | ... | @@ -409,6 +423,7 @@ pub const Type = extern union { |
| 409 | 423 | .pointee_type = self.castPointer().?.data, |
| 410 | 424 | .sentinel = null, |
| 411 | 425 | .@"align" = 0, |
| 426 | .@"addrspace" = .generic, | |
| 412 | 427 | .bit_offset = 0, |
| 413 | 428 | .host_size = 0, |
| 414 | 429 | .@"allowzero" = false, |
| ... | ... | @@ -461,6 +476,8 @@ pub const Type = extern union { |
| 461 | 476 | return false; |
| 462 | 477 | if (info_a.host_size != info_b.host_size) |
| 463 | 478 | return false; |
| 479 | if (info_a.@"addrspace" != info_b.@"addrspace") | |
| 480 | return false; | |
| 464 | 481 | |
| 465 | 482 | const sentinel_a = info_a.sentinel; |
| 466 | 483 | const sentinel_b = info_b.sentinel; |
| ... | ... | @@ -587,8 +604,8 @@ pub const Type = extern union { |
| 587 | 604 | } |
| 588 | 605 | return false; |
| 589 | 606 | }, |
| 607 | .Float => return a.tag() == b.tag(), | |
| 590 | 608 | .Opaque, |
| 591 | .Float, | |
| 592 | 609 | .BoundFn, |
| 593 | 610 | .Frame, |
| 594 | 611 | => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }), |
| ... | ... | @@ -746,6 +763,7 @@ pub const Type = extern union { |
| 746 | 763 | .atomic_order, |
| 747 | 764 | .atomic_rmw_op, |
| 748 | 765 | .calling_convention, |
| 766 | .address_space, | |
| 749 | 767 | .float_mode, |
| 750 | 768 | .reduce_op, |
| 751 | 769 | .call_options, |
| ... | ... | @@ -754,6 +772,7 @@ pub const Type = extern union { |
| 754 | 772 | .type_info, |
| 755 | 773 | .@"anyframe", |
| 756 | 774 | .generic_poison, |
| 775 | .bound_fn, | |
| 757 | 776 | => unreachable, |
| 758 | 777 | |
| 759 | 778 | .array_u8, |
| ... | ... | @@ -835,6 +854,7 @@ pub const Type = extern union { |
| 835 | 854 | .pointee_type = try payload.pointee_type.copy(allocator), |
| 836 | 855 | .sentinel = sent, |
| 837 | 856 | .@"align" = payload.@"align", |
| 857 | .@"addrspace" = payload.@"addrspace", | |
| 838 | 858 | .bit_offset = payload.bit_offset, |
| 839 | 859 | .host_size = payload.host_size, |
| 840 | 860 | .@"allowzero" = payload.@"allowzero", |
| ... | ... | @@ -857,6 +877,7 @@ pub const Type = extern union { |
| 857 | 877 | .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), |
| 858 | 878 | .@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union), |
| 859 | 879 | .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple), |
| 880 | .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered), | |
| 860 | 881 | .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull), |
| 861 | 882 | .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque), |
| 862 | 883 | } |
| ... | ... | @@ -917,6 +938,7 @@ pub const Type = extern union { |
| 917 | 938 | .comptime_float, |
| 918 | 939 | .noreturn, |
| 919 | 940 | .var_args_param, |
| 941 | .bound_fn, | |
| 920 | 942 | => return writer.writeAll(@tagName(t)), |
| 921 | 943 | |
| 922 | 944 | .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"), |
| ... | ... | @@ -941,6 +963,10 @@ pub const Type = extern union { |
| 941 | 963 | const enum_simple = ty.castTag(.enum_simple).?.data; |
| 942 | 964 | return enum_simple.owner_decl.renderFullyQualifiedName(writer); |
| 943 | 965 | }, |
| 966 | .enum_numbered => { | |
| 967 | const enum_numbered = ty.castTag(.enum_numbered).?.data; | |
| 968 | return enum_numbered.owner_decl.renderFullyQualifiedName(writer); | |
| 969 | }, | |
| 944 | 970 | .@"opaque" => { |
| 945 | 971 | // TODO use declaration name |
| 946 | 972 | return writer.writeAll("opaque {}"); |
| ... | ... | @@ -958,6 +984,7 @@ pub const Type = extern union { |
| 958 | 984 | .atomic_order => return writer.writeAll("std.builtin.AtomicOrder"), |
| 959 | 985 | .atomic_rmw_op => return writer.writeAll("std.builtin.AtomicRmwOp"), |
| 960 | 986 | .calling_convention => return writer.writeAll("std.builtin.CallingConvention"), |
| 987 | .address_space => return writer.writeAll("std.builtin.AddressSpace"), | |
| 961 | 988 | .float_mode => return writer.writeAll("std.builtin.FloatMode"), |
| 962 | 989 | .reduce_op => return writer.writeAll("std.builtin.ReduceOp"), |
| 963 | 990 | .call_options => return writer.writeAll("std.builtin.CallOptions"), |
| ... | ... | @@ -1111,6 +1138,9 @@ pub const Type = extern union { |
| 1111 | 1138 | } |
| 1112 | 1139 | try writer.writeAll(") "); |
| 1113 | 1140 | } |
| 1141 | if (payload.@"addrspace" != .generic) { | |
| 1142 | try writer.print("addrspace(.{s}) ", .{@tagName(payload.@"addrspace")}); | |
| 1143 | } | |
| 1114 | 1144 | if (!payload.mutable) try writer.writeAll("const "); |
| 1115 | 1145 | if (payload.@"volatile") try writer.writeAll("volatile "); |
| 1116 | 1146 | if (payload.@"allowzero") try writer.writeAll("allowzero "); |
| ... | ... | @@ -1186,6 +1216,7 @@ pub const Type = extern union { |
| 1186 | 1216 | .atomic_order, |
| 1187 | 1217 | .atomic_rmw_op, |
| 1188 | 1218 | .calling_convention, |
| 1219 | .address_space, | |
| 1189 | 1220 | .float_mode, |
| 1190 | 1221 | .reduce_op, |
| 1191 | 1222 | .call_options, |
| ... | ... | @@ -1220,6 +1251,7 @@ pub const Type = extern union { |
| 1220 | 1251 | .var_args_param => unreachable, |
| 1221 | 1252 | .inferred_alloc_mut => unreachable, |
| 1222 | 1253 | .inferred_alloc_const => unreachable, |
| 1254 | .bound_fn => unreachable, | |
| 1223 | 1255 | |
| 1224 | 1256 | .array_u8, |
| 1225 | 1257 | .array_u8_sentinel_0, |
| ... | ... | @@ -1246,6 +1278,7 @@ pub const Type = extern union { |
| 1246 | 1278 | .@"union", |
| 1247 | 1279 | .union_tagged, |
| 1248 | 1280 | .enum_simple, |
| 1281 | .enum_numbered, | |
| 1249 | 1282 | .enum_full, |
| 1250 | 1283 | .enum_nonexhaustive, |
| 1251 | 1284 | => false, // TODO some of these should be `true` depending on their child types |
| ... | ... | @@ -1301,6 +1334,7 @@ pub const Type = extern union { |
| 1301 | 1334 | .atomic_order => return Value.initTag(.atomic_order_type), |
| 1302 | 1335 | .atomic_rmw_op => return Value.initTag(.atomic_rmw_op_type), |
| 1303 | 1336 | .calling_convention => return Value.initTag(.calling_convention_type), |
| 1337 | .address_space => return Value.initTag(.address_space_type), | |
| 1304 | 1338 | .float_mode => return Value.initTag(.float_mode_type), |
| 1305 | 1339 | .reduce_op => return Value.initTag(.reduce_op_type), |
| 1306 | 1340 | .call_options => return Value.initTag(.call_options_type), |
| ... | ... | @@ -1313,6 +1347,8 @@ pub const Type = extern union { |
| 1313 | 1347 | } |
| 1314 | 1348 | } |
| 1315 | 1349 | |
| 1350 | /// For structs and unions, if the type does not have their fields resolved | |
| 1351 | /// this will return `false`. | |
| 1316 | 1352 | pub fn hasCodeGenBits(self: Type) bool { |
| 1317 | 1353 | return switch (self.tag()) { |
| 1318 | 1354 | .u1, |
| ... | ... | @@ -1343,10 +1379,6 @@ pub const Type = extern union { |
| 1343 | 1379 | .f128, |
| 1344 | 1380 | .bool, |
| 1345 | 1381 | .anyerror, |
| 1346 | .fn_noreturn_no_args, | |
| 1347 | .fn_void_no_args, | |
| 1348 | .fn_naked_noreturn_no_args, | |
| 1349 | .fn_ccc_void_no_args, | |
| 1350 | 1382 | .single_const_pointer_to_comptime_int, |
| 1351 | 1383 | .const_slice_u8, |
| 1352 | 1384 | .array_u8_sentinel_0, |
| ... | ... | @@ -1362,6 +1394,7 @@ pub const Type = extern union { |
| 1362 | 1394 | .atomic_order, |
| 1363 | 1395 | .atomic_rmw_op, |
| 1364 | 1396 | .calling_convention, |
| 1397 | .address_space, | |
| 1365 | 1398 | .float_mode, |
| 1366 | 1399 | .reduce_op, |
| 1367 | 1400 | .call_options, |
| ... | ... | @@ -1373,15 +1406,17 @@ pub const Type = extern union { |
| 1373 | 1406 | |
| 1374 | 1407 | .function => !self.castTag(.function).?.data.is_generic, |
| 1375 | 1408 | |
| 1409 | .fn_noreturn_no_args, | |
| 1410 | .fn_void_no_args, | |
| 1411 | .fn_naked_noreturn_no_args, | |
| 1412 | .fn_ccc_void_no_args, | |
| 1413 | => true, | |
| 1414 | ||
| 1376 | 1415 | .@"struct" => { |
| 1377 | // TODO introduce lazy value mechanism | |
| 1378 | 1416 | const struct_obj = self.castTag(.@"struct").?.data; |
| 1379 | 1417 | if (struct_obj.known_has_bits) { |
| 1380 | 1418 | return true; |
| 1381 | 1419 | } |
| 1382 | assert(struct_obj.status == .have_field_types or | |
| 1383 | struct_obj.status == .layout_wip or | |
| 1384 | struct_obj.status == .have_layout); | |
| 1385 | 1420 | for (struct_obj.fields.values()) |value| { |
| 1386 | 1421 | if (value.ty.hasCodeGenBits()) |
| 1387 | 1422 | return true; |
| ... | ... | @@ -1397,7 +1432,7 @@ pub const Type = extern union { |
| 1397 | 1432 | const enum_simple = self.castTag(.enum_simple).?.data; |
| 1398 | 1433 | return enum_simple.fields.count() >= 2; |
| 1399 | 1434 | }, |
| 1400 | .enum_nonexhaustive => { | |
| 1435 | .enum_numbered, .enum_nonexhaustive => { | |
| 1401 | 1436 | var buffer: Payload.Bits = undefined; |
| 1402 | 1437 | const int_tag_ty = self.intTagType(&buffer); |
| 1403 | 1438 | return int_tag_ty.hasCodeGenBits(); |
| ... | ... | @@ -1448,6 +1483,7 @@ pub const Type = extern union { |
| 1448 | 1483 | .empty_struct_literal, |
| 1449 | 1484 | .@"opaque", |
| 1450 | 1485 | .type_info, |
| 1486 | .bound_fn, | |
| 1451 | 1487 | => false, |
| 1452 | 1488 | |
| 1453 | 1489 | .inferred_alloc_const => unreachable, |
| ... | ... | @@ -1458,7 +1494,9 @@ pub const Type = extern union { |
| 1458 | 1494 | } |
| 1459 | 1495 | |
| 1460 | 1496 | pub fn isNoReturn(self: Type) bool { |
| 1461 | const definitely_correct_result = self.zigTypeTag() == .NoReturn; | |
| 1497 | const definitely_correct_result = | |
| 1498 | self.tag_if_small_enough != .bound_fn and | |
| 1499 | self.zigTypeTag() == .NoReturn; | |
| 1462 | 1500 | const fast_result = self.tag_if_small_enough == Tag.noreturn; |
| 1463 | 1501 | assert(fast_result == definitely_correct_result); |
| 1464 | 1502 | return fast_result; |
| ... | ... | @@ -1496,6 +1534,30 @@ pub const Type = extern union { |
| 1496 | 1534 | } |
| 1497 | 1535 | } |
| 1498 | 1536 | |
| 1537 | pub fn ptrAddressSpace(self: Type) std.builtin.AddressSpace { | |
| 1538 | return switch (self.tag()) { | |
| 1539 | .single_const_pointer_to_comptime_int, | |
| 1540 | .const_slice_u8, | |
| 1541 | .single_const_pointer, | |
| 1542 | .single_mut_pointer, | |
| 1543 | .many_const_pointer, | |
| 1544 | .many_mut_pointer, | |
| 1545 | .c_const_pointer, | |
| 1546 | .c_mut_pointer, | |
| 1547 | .const_slice, | |
| 1548 | .mut_slice, | |
| 1549 | .inferred_alloc_const, | |
| 1550 | .inferred_alloc_mut, | |
| 1551 | .manyptr_u8, | |
| 1552 | .manyptr_const_u8, | |
| 1553 | => .generic, | |
| 1554 | ||
| 1555 | .pointer => self.castTag(.pointer).?.data.@"addrspace", | |
| 1556 | ||
| 1557 | else => unreachable, | |
| 1558 | }; | |
| 1559 | } | |
| 1560 | ||
| 1499 | 1561 | /// Asserts that hasCodeGenBits() is true. |
| 1500 | 1562 | pub fn abiAlignment(self: Type, target: Target) u32 { |
| 1501 | 1563 | return switch (self.tag()) { |
| ... | ... | @@ -1508,6 +1570,7 @@ pub const Type = extern union { |
| 1508 | 1570 | .atomic_order, |
| 1509 | 1571 | .atomic_rmw_op, |
| 1510 | 1572 | .calling_convention, |
| 1573 | .address_space, | |
| 1511 | 1574 | .float_mode, |
| 1512 | 1575 | .reduce_op, |
| 1513 | 1576 | .call_options, |
| ... | ... | @@ -1583,7 +1646,11 @@ pub const Type = extern union { |
| 1583 | 1646 | |
| 1584 | 1647 | .int_signed, .int_unsigned => { |
| 1585 | 1648 | const bits: u16 = self.cast(Payload.Bits).?.data; |
| 1586 | return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); | |
| 1649 | if (bits <= 8) return 1; | |
| 1650 | if (bits <= 16) return 2; | |
| 1651 | if (bits <= 32) return 4; | |
| 1652 | if (bits <= 64) return 8; | |
| 1653 | return 16; | |
| 1587 | 1654 | }, |
| 1588 | 1655 | |
| 1589 | 1656 | .optional => { |
| ... | ... | @@ -1629,7 +1696,7 @@ pub const Type = extern union { |
| 1629 | 1696 | assert(biggest != 0); |
| 1630 | 1697 | return biggest; |
| 1631 | 1698 | }, |
| 1632 | .enum_full, .enum_nonexhaustive, .enum_simple => { | |
| 1699 | .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => { | |
| 1633 | 1700 | var buffer: Payload.Bits = undefined; |
| 1634 | 1701 | const int_tag_ty = self.intTagType(&buffer); |
| 1635 | 1702 | return int_tag_ty.abiAlignment(target); |
| ... | ... | @@ -1676,6 +1743,7 @@ pub const Type = extern union { |
| 1676 | 1743 | .@"opaque", |
| 1677 | 1744 | .var_args_param, |
| 1678 | 1745 | .type_info, |
| 1746 | .bound_fn, | |
| 1679 | 1747 | => unreachable, |
| 1680 | 1748 | |
| 1681 | 1749 | .generic_poison => unreachable, |
| ... | ... | @@ -1708,13 +1776,30 @@ pub const Type = extern union { |
| 1708 | 1776 | .var_args_param => unreachable, |
| 1709 | 1777 | .generic_poison => unreachable, |
| 1710 | 1778 | .type_info => unreachable, |
| 1779 | .bound_fn => unreachable, | |
| 1711 | 1780 | |
| 1712 | 1781 | .@"struct" => { |
| 1713 | 1782 | const s = self.castTag(.@"struct").?.data; |
| 1714 | 1783 | assert(s.status == .have_layout); |
| 1715 | @panic("TODO abiSize struct"); | |
| 1784 | const is_packed = s.layout == .Packed; | |
| 1785 | if (is_packed) @panic("TODO packed structs"); | |
| 1786 | var size: u64 = 0; | |
| 1787 | for (s.fields.values()) |field| { | |
| 1788 | if (!field.ty.hasCodeGenBits()) continue; | |
| 1789 | ||
| 1790 | const field_align = a: { | |
| 1791 | if (field.abi_align.tag() == .abi_align_default) { | |
| 1792 | break :a field.ty.abiAlignment(target); | |
| 1793 | } else { | |
| 1794 | break :a field.abi_align.toUnsignedInt(); | |
| 1795 | } | |
| 1796 | }; | |
| 1797 | size = std.mem.alignForwardGeneric(u64, size, field_align); | |
| 1798 | size += field.ty.abiSize(target); | |
| 1799 | } | |
| 1800 | return size; | |
| 1716 | 1801 | }, |
| 1717 | .enum_simple, .enum_full, .enum_nonexhaustive => { | |
| 1802 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { | |
| 1718 | 1803 | var buffer: Payload.Bits = undefined; |
| 1719 | 1804 | const int_tag_ty = self.intTagType(&buffer); |
| 1720 | 1805 | return int_tag_ty.abiSize(target); |
| ... | ... | @@ -1730,6 +1815,7 @@ pub const Type = extern union { |
| 1730 | 1815 | .atomic_order, |
| 1731 | 1816 | .atomic_rmw_op, |
| 1732 | 1817 | .calling_convention, |
| 1818 | .address_space, | |
| 1733 | 1819 | .float_mode, |
| 1734 | 1820 | .reduce_op, |
| 1735 | 1821 | .call_options, |
| ... | ... | @@ -1818,6 +1904,7 @@ pub const Type = extern union { |
| 1818 | 1904 | |
| 1819 | 1905 | .int_signed, .int_unsigned => { |
| 1820 | 1906 | const bits: u16 = self.cast(Payload.Bits).?.data; |
| 1907 | if (bits == 0) return 0; | |
| 1821 | 1908 | return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); |
| 1822 | 1909 | }, |
| 1823 | 1910 | |
| ... | ... | @@ -1875,11 +1962,12 @@ pub const Type = extern union { |
| 1875 | 1962 | .@"opaque" => unreachable, |
| 1876 | 1963 | .var_args_param => unreachable, |
| 1877 | 1964 | .generic_poison => unreachable, |
| 1965 | .bound_fn => unreachable, | |
| 1878 | 1966 | |
| 1879 | 1967 | .@"struct" => { |
| 1880 | 1968 | @panic("TODO bitSize struct"); |
| 1881 | 1969 | }, |
| 1882 | .enum_simple, .enum_full, .enum_nonexhaustive => { | |
| 1970 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { | |
| 1883 | 1971 | var buffer: Payload.Bits = undefined; |
| 1884 | 1972 | const int_tag_ty = self.intTagType(&buffer); |
| 1885 | 1973 | return int_tag_ty.bitSize(target); |
| ... | ... | @@ -2014,6 +2102,7 @@ pub const Type = extern union { |
| 2014 | 2102 | .atomic_order, |
| 2015 | 2103 | .atomic_rmw_op, |
| 2016 | 2104 | .calling_convention, |
| 2105 | .address_space, | |
| 2017 | 2106 | .float_mode, |
| 2018 | 2107 | .reduce_op, |
| 2019 | 2108 | .call_options, |
| ... | ... | @@ -2024,23 +2113,6 @@ pub const Type = extern union { |
| 2024 | 2113 | }; |
| 2025 | 2114 | } |
| 2026 | 2115 | |
| 2027 | /// Asserts the type is an enum. | |
| 2028 | pub fn intTagType(self: Type, buffer: *Payload.Bits) Type { | |
| 2029 | switch (self.tag()) { | |
| 2030 | .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty, | |
| 2031 | .enum_simple => { | |
| 2032 | const enum_simple = self.castTag(.enum_simple).?.data; | |
| 2033 | const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count()); | |
| 2034 | buffer.* = .{ | |
| 2035 | .base = .{ .tag = .int_unsigned }, | |
| 2036 | .data = bits, | |
| 2037 | }; | |
| 2038 | return Type.initPayload(&buffer.base); | |
| 2039 | }, | |
| 2040 | else => unreachable, | |
| 2041 | } | |
| 2042 | } | |
| 2043 | ||
| 2044 | 2116 | pub fn isSinglePointer(self: Type) bool { |
| 2045 | 2117 | return switch (self.tag()) { |
| 2046 | 2118 | .single_const_pointer, |
| ... | ... | @@ -2100,42 +2172,82 @@ pub const Type = extern union { |
| 2100 | 2172 | }; |
| 2101 | 2173 | } |
| 2102 | 2174 | |
| 2103 | pub fn slicePtrFieldType(self: Type, buffer: *Payload.ElemType) Type { | |
| 2175 | pub const SlicePtrFieldTypeBuffer = union { | |
| 2176 | elem_type: Payload.ElemType, | |
| 2177 | pointer: Payload.Pointer, | |
| 2178 | }; | |
| 2179 | ||
| 2180 | pub fn slicePtrFieldType(self: Type, buffer: *SlicePtrFieldTypeBuffer) Type { | |
| 2104 | 2181 | switch (self.tag()) { |
| 2105 | 2182 | .const_slice_u8 => return Type.initTag(.manyptr_const_u8), |
| 2106 | 2183 | |
| 2107 | 2184 | .const_slice => { |
| 2108 | 2185 | const elem_type = self.castTag(.const_slice).?.data; |
| 2109 | 2186 | buffer.* = .{ |
| 2110 | .base = .{ .tag = .many_const_pointer }, | |
| 2111 | .data = elem_type, | |
| 2187 | .elem_type = .{ | |
| 2188 | .base = .{ .tag = .many_const_pointer }, | |
| 2189 | .data = elem_type, | |
| 2190 | }, | |
| 2112 | 2191 | }; |
| 2113 | return Type.initPayload(&buffer.base); | |
| 2192 | return Type.initPayload(&buffer.elem_type.base); | |
| 2114 | 2193 | }, |
| 2115 | 2194 | .mut_slice => { |
| 2116 | 2195 | const elem_type = self.castTag(.mut_slice).?.data; |
| 2117 | 2196 | buffer.* = .{ |
| 2118 | .base = .{ .tag = .many_mut_pointer }, | |
| 2119 | .data = elem_type, | |
| 2197 | .elem_type = .{ | |
| 2198 | .base = .{ .tag = .many_mut_pointer }, | |
| 2199 | .data = elem_type, | |
| 2200 | }, | |
| 2120 | 2201 | }; |
| 2121 | return Type.initPayload(&buffer.base); | |
| 2202 | return Type.initPayload(&buffer.elem_type.base); | |
| 2122 | 2203 | }, |
| 2123 | 2204 | |
| 2124 | 2205 | .pointer => { |
| 2125 | 2206 | const payload = self.castTag(.pointer).?.data; |
| 2126 | 2207 | assert(payload.size == .Slice); |
| 2127 | if (payload.mutable) { | |
| 2208 | ||
| 2209 | if (payload.sentinel != null or | |
| 2210 | payload.@"align" != 0 or | |
| 2211 | payload.@"addrspace" != .generic or | |
| 2212 | payload.bit_offset != 0 or | |
| 2213 | payload.host_size != 0 or | |
| 2214 | payload.@"allowzero" or | |
| 2215 | payload.@"volatile") | |
| 2216 | { | |
| 2128 | 2217 | buffer.* = .{ |
| 2129 | .base = .{ .tag = .many_mut_pointer }, | |
| 2130 | .data = payload.pointee_type, | |
| 2218 | .pointer = .{ | |
| 2219 | .data = .{ | |
| 2220 | .pointee_type = payload.pointee_type, | |
| 2221 | .sentinel = payload.sentinel, | |
| 2222 | .@"align" = payload.@"align", | |
| 2223 | .@"addrspace" = payload.@"addrspace", | |
| 2224 | .bit_offset = payload.bit_offset, | |
| 2225 | .host_size = payload.host_size, | |
| 2226 | .@"allowzero" = payload.@"allowzero", | |
| 2227 | .mutable = payload.mutable, | |
| 2228 | .@"volatile" = payload.@"volatile", | |
| 2229 | .size = .Many, | |
| 2230 | }, | |
| 2231 | }, | |
| 2131 | 2232 | }; |
| 2233 | return Type.initPayload(&buffer.pointer.base); | |
| 2234 | } else if (payload.mutable) { | |
| 2235 | buffer.* = .{ | |
| 2236 | .elem_type = .{ | |
| 2237 | .base = .{ .tag = .many_mut_pointer }, | |
| 2238 | .data = payload.pointee_type, | |
| 2239 | }, | |
| 2240 | }; | |
| 2241 | return Type.initPayload(&buffer.elem_type.base); | |
| 2132 | 2242 | } else { |
| 2133 | 2243 | buffer.* = .{ |
| 2134 | .base = .{ .tag = .many_const_pointer }, | |
| 2135 | .data = payload.pointee_type, | |
| 2244 | .elem_type = .{ | |
| 2245 | .base = .{ .tag = .many_const_pointer }, | |
| 2246 | .data = payload.pointee_type, | |
| 2247 | }, | |
| 2136 | 2248 | }; |
| 2249 | return Type.initPayload(&buffer.elem_type.base); | |
| 2137 | 2250 | } |
| 2138 | return Type.initPayload(&buffer.base); | |
| 2139 | 2251 | }, |
| 2140 | 2252 | |
| 2141 | 2253 | else => unreachable, |
| ... | ... | @@ -2191,6 +2303,44 @@ pub const Type = extern union { |
| 2191 | 2303 | }; |
| 2192 | 2304 | } |
| 2193 | 2305 | |
| 2306 | pub fn isPtrAtRuntime(self: Type) bool { | |
| 2307 | switch (self.tag()) { | |
| 2308 | .c_const_pointer, | |
| 2309 | .c_mut_pointer, | |
| 2310 | .many_const_pointer, | |
| 2311 | .many_mut_pointer, | |
| 2312 | .manyptr_const_u8, | |
| 2313 | .manyptr_u8, | |
| 2314 | .optional_single_const_pointer, | |
| 2315 | .optional_single_mut_pointer, | |
| 2316 | .single_const_pointer, | |
| 2317 | .single_const_pointer_to_comptime_int, | |
| 2318 | .single_mut_pointer, | |
| 2319 | => return true, | |
| 2320 | ||
| 2321 | .pointer => switch (self.castTag(.pointer).?.data.size) { | |
| 2322 | .Slice => return false, | |
| 2323 | .One, .Many, .C => return true, | |
| 2324 | }, | |
| 2325 | ||
| 2326 | .optional => { | |
| 2327 | var buf: Payload.ElemType = undefined; | |
| 2328 | const child_type = self.optionalChild(&buf); | |
| 2329 | // optionals of zero sized pointers behave like bools | |
| 2330 | if (!child_type.hasCodeGenBits()) return false; | |
| 2331 | if (child_type.zigTypeTag() != .Pointer) return false; | |
| 2332 | ||
| 2333 | const info = child_type.ptrInfo().data; | |
| 2334 | switch (info.size) { | |
| 2335 | .Slice, .C => return false, | |
| 2336 | .Many, .One => return !info.@"allowzero", | |
| 2337 | } | |
| 2338 | }, | |
| 2339 | ||
| 2340 | else => return false, | |
| 2341 | } | |
| 2342 | } | |
| 2343 | ||
| 2194 | 2344 | /// Asserts that the type is an optional |
| 2195 | 2345 | pub fn isPtrLikeOptional(self: Type) bool { |
| 2196 | 2346 | switch (self.tag()) { |
| ... | ... | @@ -2203,8 +2353,13 @@ pub const Type = extern union { |
| 2203 | 2353 | const child_type = self.optionalChild(&buf); |
| 2204 | 2354 | // optionals of zero sized pointers behave like bools |
| 2205 | 2355 | if (!child_type.hasCodeGenBits()) return false; |
| 2356 | if (child_type.zigTypeTag() != .Pointer) return false; | |
| 2206 | 2357 | |
| 2207 | return child_type.zigTypeTag() == .Pointer and !child_type.isCPtr(); | |
| 2358 | const info = child_type.ptrInfo().data; | |
| 2359 | switch (info.size) { | |
| 2360 | .Slice, .C => return false, | |
| 2361 | .Many, .One => return !info.@"allowzero", | |
| 2362 | } | |
| 2208 | 2363 | }, |
| 2209 | 2364 | else => unreachable, |
| 2210 | 2365 | } |
| ... | ... | @@ -2252,12 +2407,14 @@ pub const Type = extern union { |
| 2252 | 2407 | }; |
| 2253 | 2408 | } |
| 2254 | 2409 | |
| 2255 | /// Asserts the type is a pointer or array type. | |
| 2256 | pub fn elemType(self: Type) Type { | |
| 2257 | return switch (self.tag()) { | |
| 2258 | .vector => self.castTag(.vector).?.data.elem_type, | |
| 2259 | .array => self.castTag(.array).?.data.elem_type, | |
| 2260 | .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type, | |
| 2410 | /// For *[N]T, returns [N]T. | |
| 2411 | /// For *T, returns T. | |
| 2412 | /// For [*]T, returns T. | |
| 2413 | pub fn childType(ty: Type) Type { | |
| 2414 | return switch (ty.tag()) { | |
| 2415 | .vector => ty.castTag(.vector).?.data.elem_type, | |
| 2416 | .array => ty.castTag(.array).?.data.elem_type, | |
| 2417 | .array_sentinel => ty.castTag(.array_sentinel).?.data.elem_type, | |
| 2261 | 2418 | .single_const_pointer, |
| 2262 | 2419 | .single_mut_pointer, |
| 2263 | 2420 | .many_const_pointer, |
| ... | ... | @@ -2266,7 +2423,48 @@ pub const Type = extern union { |
| 2266 | 2423 | .c_mut_pointer, |
| 2267 | 2424 | .const_slice, |
| 2268 | 2425 | .mut_slice, |
| 2269 | => self.castPointer().?.data, | |
| 2426 | => ty.castPointer().?.data, | |
| 2427 | ||
| 2428 | .array_u8, | |
| 2429 | .array_u8_sentinel_0, | |
| 2430 | .const_slice_u8, | |
| 2431 | .manyptr_u8, | |
| 2432 | .manyptr_const_u8, | |
| 2433 | => Type.initTag(.u8), | |
| 2434 | ||
| 2435 | .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), | |
| 2436 | .pointer => ty.castTag(.pointer).?.data.pointee_type, | |
| 2437 | ||
| 2438 | else => unreachable, | |
| 2439 | }; | |
| 2440 | } | |
| 2441 | ||
| 2442 | /// Asserts the type is a pointer or array type. | |
| 2443 | /// TODO this is deprecated in favor of `childType`. | |
| 2444 | pub const elemType = childType; | |
| 2445 | ||
| 2446 | /// For *[N]T, returns T. | |
| 2447 | /// For ?*T, returns T. | |
| 2448 | /// For ?*[N]T, returns T. | |
| 2449 | /// For ?[*]T, returns T. | |
| 2450 | /// For *T, returns T. | |
| 2451 | /// For [*]T, returns T. | |
| 2452 | pub fn elemType2(ty: Type) Type { | |
| 2453 | return switch (ty.tag()) { | |
| 2454 | .vector => ty.castTag(.vector).?.data.elem_type, | |
| 2455 | .array => ty.castTag(.array).?.data.elem_type, | |
| 2456 | .array_sentinel => ty.castTag(.array_sentinel).?.data.elem_type, | |
| 2457 | .many_const_pointer, | |
| 2458 | .many_mut_pointer, | |
| 2459 | .c_const_pointer, | |
| 2460 | .c_mut_pointer, | |
| 2461 | .const_slice, | |
| 2462 | .mut_slice, | |
| 2463 | => ty.castPointer().?.data, | |
| 2464 | ||
| 2465 | .single_const_pointer, | |
| 2466 | .single_mut_pointer, | |
| 2467 | => ty.castPointer().?.data.shallowElemType(), | |
| 2270 | 2468 | |
| 2271 | 2469 | .array_u8, |
| 2272 | 2470 | .array_u8_sentinel_0, |
| ... | ... | @@ -2276,12 +2474,29 @@ pub const Type = extern union { |
| 2276 | 2474 | => Type.initTag(.u8), |
| 2277 | 2475 | |
| 2278 | 2476 | .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), |
| 2279 | .pointer => self.castTag(.pointer).?.data.pointee_type, | |
| 2477 | .pointer => { | |
| 2478 | const info = ty.castTag(.pointer).?.data; | |
| 2479 | const child_ty = info.pointee_type; | |
| 2480 | if (info.size == .One) { | |
| 2481 | return child_ty.shallowElemType(); | |
| 2482 | } else { | |
| 2483 | return child_ty; | |
| 2484 | } | |
| 2485 | }, | |
| 2486 | ||
| 2487 | // TODO handle optionals | |
| 2280 | 2488 | |
| 2281 | 2489 | else => unreachable, |
| 2282 | 2490 | }; |
| 2283 | 2491 | } |
| 2284 | 2492 | |
| 2493 | fn shallowElemType(child_ty: Type) Type { | |
| 2494 | return switch (child_ty.zigTypeTag()) { | |
| 2495 | .Array, .Vector => child_ty.childType(), | |
| 2496 | else => child_ty, | |
| 2497 | }; | |
| 2498 | } | |
| 2499 | ||
| 2285 | 2500 | /// Asserts that the type is an optional. |
| 2286 | 2501 | /// Resulting `Type` will have inner memory referencing `buf`. |
| 2287 | 2502 | pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type { |
| ... | ... | @@ -2320,6 +2535,21 @@ pub const Type = extern union { |
| 2320 | 2535 | } |
| 2321 | 2536 | } |
| 2322 | 2537 | |
| 2538 | /// Returns the tag type of a union, if the type is a union and it has a tag type. | |
| 2539 | /// Otherwise, returns `null`. | |
| 2540 | pub fn unionTagType(ty: Type) ?Type { | |
| 2541 | return switch (ty.tag()) { | |
| 2542 | .union_tagged => ty.castTag(.union_tagged).?.data.tag_ty, | |
| 2543 | else => null, | |
| 2544 | }; | |
| 2545 | } | |
| 2546 | ||
| 2547 | pub fn unionFieldType(ty: Type, enum_tag: Value) Type { | |
| 2548 | const union_obj = ty.cast(Payload.Union).?.data; | |
| 2549 | const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag).?; | |
| 2550 | return union_obj.fields.values()[index].ty; | |
| 2551 | } | |
| 2552 | ||
| 2323 | 2553 | /// Asserts that the type is an error union. |
| 2324 | 2554 | pub fn errorUnionPayload(self: Type) Type { |
| 2325 | 2555 | return switch (self.tag()) { |
| ... | ... | @@ -2476,7 +2706,8 @@ pub const Type = extern union { |
| 2476 | 2706 | }; |
| 2477 | 2707 | } |
| 2478 | 2708 | |
| 2479 | pub fn isFloat(self: Type) bool { | |
| 2709 | /// Returns `false` for `comptime_float`. | |
| 2710 | pub fn isRuntimeFloat(self: Type) bool { | |
| 2480 | 2711 | return switch (self.tag()) { |
| 2481 | 2712 | .f16, |
| 2482 | 2713 | .f32, |
| ... | ... | @@ -2489,13 +2720,29 @@ pub const Type = extern union { |
| 2489 | 2720 | }; |
| 2490 | 2721 | } |
| 2491 | 2722 | |
| 2492 | /// Asserts the type is a fixed-size float. | |
| 2723 | /// Returns `true` for `comptime_float`. | |
| 2724 | pub fn isAnyFloat(self: Type) bool { | |
| 2725 | return switch (self.tag()) { | |
| 2726 | .f16, | |
| 2727 | .f32, | |
| 2728 | .f64, | |
| 2729 | .f128, | |
| 2730 | .c_longdouble, | |
| 2731 | .comptime_float, | |
| 2732 | => true, | |
| 2733 | ||
| 2734 | else => false, | |
| 2735 | }; | |
| 2736 | } | |
| 2737 | ||
| 2738 | /// Asserts the type is a fixed-size float or comptime_float. | |
| 2739 | /// Returns 128 for comptime_float types. | |
| 2493 | 2740 | pub fn floatBits(self: Type, target: Target) u16 { |
| 2494 | 2741 | return switch (self.tag()) { |
| 2495 | 2742 | .f16 => 16, |
| 2496 | 2743 | .f32 => 32, |
| 2497 | 2744 | .f64 => 64, |
| 2498 | .f128 => 128, | |
| 2745 | .f128, .comptime_float => 128, | |
| 2499 | 2746 | .c_longdouble => CType.longdouble.sizeInBits(target), |
| 2500 | 2747 | |
| 2501 | 2748 | else => unreachable, |
| ... | ... | @@ -2728,6 +2975,7 @@ pub const Type = extern union { |
| 2728 | 2975 | .atomic_order, |
| 2729 | 2976 | .atomic_rmw_op, |
| 2730 | 2977 | .calling_convention, |
| 2978 | .address_space, | |
| 2731 | 2979 | .float_mode, |
| 2732 | 2980 | .reduce_op, |
| 2733 | 2981 | .call_options, |
| ... | ... | @@ -2743,6 +2991,7 @@ pub const Type = extern union { |
| 2743 | 2991 | .single_const_pointer, |
| 2744 | 2992 | .single_mut_pointer, |
| 2745 | 2993 | .pointer, |
| 2994 | .bound_fn, | |
| 2746 | 2995 | => return null, |
| 2747 | 2996 | |
| 2748 | 2997 | .@"struct" => { |
| ... | ... | @@ -2772,6 +3021,7 @@ pub const Type = extern union { |
| 2772 | 3021 | } |
| 2773 | 3022 | }, |
| 2774 | 3023 | .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty, |
| 3024 | .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty, | |
| 2775 | 3025 | .@"union" => { |
| 2776 | 3026 | return null; // TODO |
| 2777 | 3027 | }, |
| ... | ... | @@ -2832,7 +3082,7 @@ pub const Type = extern union { |
| 2832 | 3082 | } |
| 2833 | 3083 | |
| 2834 | 3084 | /// Asserts that self.zigTypeTag() == .Int. |
| 2835 | pub fn minInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value { | |
| 3085 | pub fn minInt(self: Type, arena: *Allocator, target: Target) !Value { | |
| 2836 | 3086 | assert(self.zigTypeTag() == .Int); |
| 2837 | 3087 | const info = self.intInfo(target); |
| 2838 | 3088 | |
| ... | ... | @@ -2842,35 +3092,35 @@ pub const Type = extern union { |
| 2842 | 3092 | |
| 2843 | 3093 | if ((info.bits - 1) <= std.math.maxInt(u6)) { |
| 2844 | 3094 | const n: i64 = -(@as(i64, 1) << @truncate(u6, info.bits - 1)); |
| 2845 | return Value.Tag.int_i64.create(&arena.allocator, n); | |
| 3095 | return Value.Tag.int_i64.create(arena, n); | |
| 2846 | 3096 | } |
| 2847 | 3097 | |
| 2848 | var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1); | |
| 3098 | var res = try std.math.big.int.Managed.initSet(arena, 1); | |
| 2849 | 3099 | try res.shiftLeft(res, info.bits - 1); |
| 2850 | 3100 | res.negate(); |
| 2851 | 3101 | |
| 2852 | 3102 | const res_const = res.toConst(); |
| 2853 | 3103 | if (res_const.positive) { |
| 2854 | return Value.Tag.int_big_positive.create(&arena.allocator, res_const.limbs); | |
| 3104 | return Value.Tag.int_big_positive.create(arena, res_const.limbs); | |
| 2855 | 3105 | } else { |
| 2856 | return Value.Tag.int_big_negative.create(&arena.allocator, res_const.limbs); | |
| 3106 | return Value.Tag.int_big_negative.create(arena, res_const.limbs); | |
| 2857 | 3107 | } |
| 2858 | 3108 | } |
| 2859 | 3109 | |
| 2860 | 3110 | /// Asserts that self.zigTypeTag() == .Int. |
| 2861 | pub fn maxInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value { | |
| 3111 | pub fn maxInt(self: Type, arena: *Allocator, target: Target) !Value { | |
| 2862 | 3112 | assert(self.zigTypeTag() == .Int); |
| 2863 | 3113 | const info = self.intInfo(target); |
| 2864 | 3114 | |
| 2865 | 3115 | if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) { |
| 2866 | 3116 | const n: i64 = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1; |
| 2867 | return Value.Tag.int_i64.create(&arena.allocator, n); | |
| 3117 | return Value.Tag.int_i64.create(arena, n); | |
| 2868 | 3118 | } else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) { |
| 2869 | 3119 | const n: u64 = (@as(u64, 1) << @truncate(u6, info.bits)) - 1; |
| 2870 | return Value.Tag.int_u64.create(&arena.allocator, n); | |
| 3120 | return Value.Tag.int_u64.create(arena, n); | |
| 2871 | 3121 | } |
| 2872 | 3122 | |
| 2873 | var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1); | |
| 3123 | var res = try std.math.big.int.Managed.initSet(arena, 1); | |
| 2874 | 3124 | try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed)); |
| 2875 | 3125 | const one = std.math.big.int.Const{ |
| 2876 | 3126 | .limbs = &[_]std.math.big.Limb{1}, |
| ... | ... | @@ -2880,37 +3130,27 @@ pub const Type = extern union { |
| 2880 | 3130 | |
| 2881 | 3131 | const res_const = res.toConst(); |
| 2882 | 3132 | if (res_const.positive) { |
| 2883 | return Value.Tag.int_big_positive.create(&arena.allocator, res_const.limbs); | |
| 3133 | return Value.Tag.int_big_positive.create(arena, res_const.limbs); | |
| 2884 | 3134 | } else { |
| 2885 | return Value.Tag.int_big_negative.create(&arena.allocator, res_const.limbs); | |
| 3135 | return Value.Tag.int_big_negative.create(arena, res_const.limbs); | |
| 2886 | 3136 | } |
| 2887 | 3137 | } |
| 2888 | 3138 | |
| 2889 | /// Returns the integer tag type of the enum. | |
| 2890 | pub fn enumTagType(ty: Type, buffer: *Payload.Bits) Type { | |
| 2891 | switch (ty.tag()) { | |
| 2892 | .enum_full, .enum_nonexhaustive => { | |
| 2893 | const enum_full = ty.cast(Payload.EnumFull).?.data; | |
| 2894 | return enum_full.tag_ty; | |
| 2895 | }, | |
| 3139 | /// Asserts the type is an enum or a union. | |
| 3140 | /// TODO support unions | |
| 3141 | pub fn intTagType(self: Type, buffer: *Payload.Bits) Type { | |
| 3142 | switch (self.tag()) { | |
| 3143 | .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty, | |
| 3144 | .enum_numbered => return self.castTag(.enum_numbered).?.data.tag_ty, | |
| 2896 | 3145 | .enum_simple => { |
| 2897 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 3146 | const enum_simple = self.castTag(.enum_simple).?.data; | |
| 3147 | const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count()); | |
| 2898 | 3148 | buffer.* = .{ |
| 2899 | 3149 | .base = .{ .tag = .int_unsigned }, |
| 2900 | .data = std.math.log2_int_ceil(usize, enum_simple.fields.count()), | |
| 3150 | .data = bits, | |
| 2901 | 3151 | }; |
| 2902 | 3152 | return Type.initPayload(&buffer.base); |
| 2903 | 3153 | }, |
| 2904 | .atomic_order, | |
| 2905 | .atomic_rmw_op, | |
| 2906 | .calling_convention, | |
| 2907 | .float_mode, | |
| 2908 | .reduce_op, | |
| 2909 | .call_options, | |
| 2910 | .export_options, | |
| 2911 | .extern_options, | |
| 2912 | => @panic("TODO resolve std.builtin types"), | |
| 2913 | ||
| 2914 | 3154 | else => unreachable, |
| 2915 | 3155 | } |
| 2916 | 3156 | } |
| ... | ... | @@ -2928,13 +3168,12 @@ pub const Type = extern union { |
| 2928 | 3168 | const enum_full = ty.cast(Payload.EnumFull).?.data; |
| 2929 | 3169 | return enum_full.fields.count(); |
| 2930 | 3170 | }, |
| 2931 | .enum_simple => { | |
| 2932 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 2933 | return enum_simple.fields.count(); | |
| 2934 | }, | |
| 3171 | .enum_simple => return ty.castTag(.enum_simple).?.data.fields.count(), | |
| 3172 | .enum_numbered => return ty.castTag(.enum_numbered).?.data.fields.count(), | |
| 2935 | 3173 | .atomic_order, |
| 2936 | 3174 | .atomic_rmw_op, |
| 2937 | 3175 | .calling_convention, |
| 3176 | .address_space, | |
| 2938 | 3177 | .float_mode, |
| 2939 | 3178 | .reduce_op, |
| 2940 | 3179 | .call_options, |
| ... | ... | @@ -2956,9 +3195,14 @@ pub const Type = extern union { |
| 2956 | 3195 | const enum_simple = ty.castTag(.enum_simple).?.data; |
| 2957 | 3196 | return enum_simple.fields.keys()[field_index]; |
| 2958 | 3197 | }, |
| 3198 | .enum_numbered => { | |
| 3199 | const enum_numbered = ty.castTag(.enum_numbered).?.data; | |
| 3200 | return enum_numbered.fields.keys()[field_index]; | |
| 3201 | }, | |
| 2959 | 3202 | .atomic_order, |
| 2960 | 3203 | .atomic_rmw_op, |
| 2961 | 3204 | .calling_convention, |
| 3205 | .address_space, | |
| 2962 | 3206 | .float_mode, |
| 2963 | 3207 | .reduce_op, |
| 2964 | 3208 | .call_options, |
| ... | ... | @@ -2979,9 +3223,14 @@ pub const Type = extern union { |
| 2979 | 3223 | const enum_simple = ty.castTag(.enum_simple).?.data; |
| 2980 | 3224 | return enum_simple.fields.getIndex(field_name); |
| 2981 | 3225 | }, |
| 3226 | .enum_numbered => { | |
| 3227 | const enum_numbered = ty.castTag(.enum_numbered).?.data; | |
| 3228 | return enum_numbered.fields.getIndex(field_name); | |
| 3229 | }, | |
| 2982 | 3230 | .atomic_order, |
| 2983 | 3231 | .atomic_rmw_op, |
| 2984 | 3232 | .calling_convention, |
| 3233 | .address_space, | |
| 2985 | 3234 | .float_mode, |
| 2986 | 3235 | .reduce_op, |
| 2987 | 3236 | .call_options, |
| ... | ... | @@ -3021,6 +3270,15 @@ pub const Type = extern union { |
| 3021 | 3270 | return enum_full.values.getIndexContext(enum_tag, .{ .ty = tag_ty }); |
| 3022 | 3271 | } |
| 3023 | 3272 | }, |
| 3273 | .enum_numbered => { | |
| 3274 | const enum_obj = ty.castTag(.enum_numbered).?.data; | |
| 3275 | const tag_ty = enum_obj.tag_ty; | |
| 3276 | if (enum_obj.values.count() == 0) { | |
| 3277 | return S.fieldWithRange(tag_ty, enum_tag, enum_obj.fields.count()); | |
| 3278 | } else { | |
| 3279 | return enum_obj.values.getIndexContext(enum_tag, .{ .ty = tag_ty }); | |
| 3280 | } | |
| 3281 | }, | |
| 3024 | 3282 | .enum_simple => { |
| 3025 | 3283 | const enum_simple = ty.castTag(.enum_simple).?.data; |
| 3026 | 3284 | const fields_len = enum_simple.fields.count(); |
| ... | ... | @@ -3035,6 +3293,7 @@ pub const Type = extern union { |
| 3035 | 3293 | .atomic_order, |
| 3036 | 3294 | .atomic_rmw_op, |
| 3037 | 3295 | .calling_convention, |
| 3296 | .address_space, | |
| 3038 | 3297 | .float_mode, |
| 3039 | 3298 | .reduce_op, |
| 3040 | 3299 | .call_options, |
| ... | ... | @@ -3071,6 +3330,7 @@ pub const Type = extern union { |
| 3071 | 3330 | const enum_full = ty.cast(Payload.EnumFull).?.data; |
| 3072 | 3331 | return enum_full.srcLoc(); |
| 3073 | 3332 | }, |
| 3333 | .enum_numbered => return ty.castTag(.enum_numbered).?.data.srcLoc(), | |
| 3074 | 3334 | .enum_simple => { |
| 3075 | 3335 | const enum_simple = ty.castTag(.enum_simple).?.data; |
| 3076 | 3336 | return enum_simple.srcLoc(); |
| ... | ... | @@ -3090,6 +3350,7 @@ pub const Type = extern union { |
| 3090 | 3350 | .atomic_order, |
| 3091 | 3351 | .atomic_rmw_op, |
| 3092 | 3352 | .calling_convention, |
| 3353 | .address_space, | |
| 3093 | 3354 | .float_mode, |
| 3094 | 3355 | .reduce_op, |
| 3095 | 3356 | .call_options, |
| ... | ... | @@ -3107,6 +3368,7 @@ pub const Type = extern union { |
| 3107 | 3368 | const enum_full = ty.cast(Payload.EnumFull).?.data; |
| 3108 | 3369 | return enum_full.owner_decl; |
| 3109 | 3370 | }, |
| 3371 | .enum_numbered => return ty.castTag(.enum_numbered).?.data.owner_decl, | |
| 3110 | 3372 | .enum_simple => { |
| 3111 | 3373 | const enum_simple = ty.castTag(.enum_simple).?.data; |
| 3112 | 3374 | return enum_simple.owner_decl; |
| ... | ... | @@ -3127,6 +3389,7 @@ pub const Type = extern union { |
| 3127 | 3389 | .atomic_order, |
| 3128 | 3390 | .atomic_rmw_op, |
| 3129 | 3391 | .calling_convention, |
| 3392 | .address_space, | |
| 3130 | 3393 | .float_mode, |
| 3131 | 3394 | .reduce_op, |
| 3132 | 3395 | .call_options, |
| ... | ... | @@ -3163,6 +3426,15 @@ pub const Type = extern union { |
| 3163 | 3426 | return enum_full.values.containsContext(int, .{ .ty = tag_ty }); |
| 3164 | 3427 | } |
| 3165 | 3428 | }, |
| 3429 | .enum_numbered => { | |
| 3430 | const enum_obj = ty.castTag(.enum_numbered).?.data; | |
| 3431 | const tag_ty = enum_obj.tag_ty; | |
| 3432 | if (enum_obj.values.count() == 0) { | |
| 3433 | return S.intInRange(tag_ty, int, enum_obj.fields.count()); | |
| 3434 | } else { | |
| 3435 | return enum_obj.values.containsContext(int, .{ .ty = tag_ty }); | |
| 3436 | } | |
| 3437 | }, | |
| 3166 | 3438 | .enum_simple => { |
| 3167 | 3439 | const enum_simple = ty.castTag(.enum_simple).?.data; |
| 3168 | 3440 | const fields_len = enum_simple.fields.count(); |
| ... | ... | @@ -3177,6 +3449,7 @@ pub const Type = extern union { |
| 3177 | 3449 | .atomic_order, |
| 3178 | 3450 | .atomic_rmw_op, |
| 3179 | 3451 | .calling_convention, |
| 3452 | .address_space, | |
| 3180 | 3453 | .float_mode, |
| 3181 | 3454 | .reduce_op, |
| 3182 | 3455 | .call_options, |
| ... | ... | @@ -3237,6 +3510,7 @@ pub const Type = extern union { |
| 3237 | 3510 | atomic_order, |
| 3238 | 3511 | atomic_rmw_op, |
| 3239 | 3512 | calling_convention, |
| 3513 | address_space, | |
| 3240 | 3514 | float_mode, |
| 3241 | 3515 | reduce_op, |
| 3242 | 3516 | call_options, |
| ... | ... | @@ -3254,7 +3528,7 @@ pub const Type = extern union { |
| 3254 | 3528 | anyerror_void_error_union, |
| 3255 | 3529 | generic_poison, |
| 3256 | 3530 | /// This is a special type for variadic parameters of a function call. |
| 3257 | /// Casts to it will validate that the type can be passed to a c calling convetion function. | |
| 3531 | /// Casts to it will validate that the type can be passed to a c calling convention function. | |
| 3258 | 3532 | var_args_param, |
| 3259 | 3533 | /// Same as `empty_struct` except it has an empty namespace. |
| 3260 | 3534 | empty_struct_literal, |
| ... | ... | @@ -3264,6 +3538,7 @@ pub const Type = extern union { |
| 3264 | 3538 | inferred_alloc_mut, |
| 3265 | 3539 | /// Same as `inferred_alloc_mut` but the local is `var` not `const`. |
| 3266 | 3540 | inferred_alloc_const, // See last_no_payload_tag below. |
| 3541 | bound_fn, | |
| 3267 | 3542 | // After this, the tag requires a payload. |
| 3268 | 3543 | |
| 3269 | 3544 | array_u8, |
| ... | ... | @@ -3298,10 +3573,11 @@ pub const Type = extern union { |
| 3298 | 3573 | @"union", |
| 3299 | 3574 | union_tagged, |
| 3300 | 3575 | enum_simple, |
| 3576 | enum_numbered, | |
| 3301 | 3577 | enum_full, |
| 3302 | 3578 | enum_nonexhaustive, |
| 3303 | 3579 | |
| 3304 | pub const last_no_payload_tag = Tag.inferred_alloc_const; | |
| 3580 | pub const last_no_payload_tag = Tag.bound_fn; | |
| 3305 | 3581 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| 3306 | 3582 | |
| 3307 | 3583 | pub fn Type(comptime t: Tag) type { |
| ... | ... | @@ -3360,6 +3636,7 @@ pub const Type = extern union { |
| 3360 | 3636 | .atomic_order, |
| 3361 | 3637 | .atomic_rmw_op, |
| 3362 | 3638 | .calling_convention, |
| 3639 | .address_space, | |
| 3363 | 3640 | .float_mode, |
| 3364 | 3641 | .reduce_op, |
| 3365 | 3642 | .call_options, |
| ... | ... | @@ -3367,6 +3644,7 @@ pub const Type = extern union { |
| 3367 | 3644 | .extern_options, |
| 3368 | 3645 | .type_info, |
| 3369 | 3646 | .@"anyframe", |
| 3647 | .bound_fn, | |
| 3370 | 3648 | => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), |
| 3371 | 3649 | |
| 3372 | 3650 | .array_u8, |
| ... | ... | @@ -3405,6 +3683,7 @@ pub const Type = extern union { |
| 3405 | 3683 | .@"union", .union_tagged => Payload.Union, |
| 3406 | 3684 | .enum_full, .enum_nonexhaustive => Payload.EnumFull, |
| 3407 | 3685 | .enum_simple => Payload.EnumSimple, |
| 3686 | .enum_numbered => Payload.EnumNumbered, | |
| 3408 | 3687 | .empty_struct => Payload.ContainerScope, |
| 3409 | 3688 | }; |
| 3410 | 3689 | } |
| ... | ... | @@ -3415,12 +3694,12 @@ pub const Type = extern union { |
| 3415 | 3694 | } |
| 3416 | 3695 | |
| 3417 | 3696 | pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!file_struct.Type { |
| 3418 | const ptr = try ally.create(t.Type()); | |
| 3419 | ptr.* = .{ | |
| 3697 | const p = try ally.create(t.Type()); | |
| 3698 | p.* = .{ | |
| 3420 | 3699 | .base = .{ .tag = t }, |
| 3421 | 3700 | .data = data, |
| 3422 | 3701 | }; |
| 3423 | return file_struct.Type{ .ptr_otherwise = &ptr.base }; | |
| 3702 | return file_struct.Type{ .ptr_otherwise = &p.base }; | |
| 3424 | 3703 | } |
| 3425 | 3704 | |
| 3426 | 3705 | pub fn Data(comptime t: Tag) type { |
| ... | ... | @@ -3510,18 +3789,23 @@ pub const Type = extern union { |
| 3510 | 3789 | pub const base_tag = Tag.pointer; |
| 3511 | 3790 | |
| 3512 | 3791 | base: Payload = Payload{ .tag = base_tag }, |
| 3513 | data: struct { | |
| 3792 | data: Data, | |
| 3793 | ||
| 3794 | pub const Data = struct { | |
| 3514 | 3795 | pointee_type: Type, |
| 3515 | sentinel: ?Value, | |
| 3796 | sentinel: ?Value = null, | |
| 3516 | 3797 | /// If zero use pointee_type.AbiAlign() |
| 3517 | @"align": u32, | |
| 3518 | bit_offset: u16, | |
| 3519 | host_size: u16, | |
| 3520 | @"allowzero": bool, | |
| 3521 | mutable: bool, | |
| 3522 | @"volatile": bool, | |
| 3523 | size: std.builtin.TypeInfo.Pointer.Size, | |
| 3524 | }, | |
| 3798 | @"align": u32 = 0, | |
| 3799 | /// See src/target.zig defaultAddressSpace function for how to obtain | |
| 3800 | /// an appropriate value for this field. | |
| 3801 | @"addrspace": std.builtin.AddressSpace, | |
| 3802 | bit_offset: u16 = 0, | |
| 3803 | host_size: u16 = 0, | |
| 3804 | @"allowzero": bool = false, | |
| 3805 | mutable: bool = true, // TODO change this to const, not mutable | |
| 3806 | @"volatile": bool = false, | |
| 3807 | size: std.builtin.TypeInfo.Pointer.Size = .One, | |
| 3808 | }; | |
| 3525 | 3809 | }; |
| 3526 | 3810 | |
| 3527 | 3811 | pub const ErrorUnion = struct { |
| ... | ... | @@ -3576,7 +3860,64 @@ pub const Type = extern union { |
| 3576 | 3860 | base: Payload = .{ .tag = .enum_simple }, |
| 3577 | 3861 | data: *Module.EnumSimple, |
| 3578 | 3862 | }; |
| 3863 | ||
| 3864 | pub const EnumNumbered = struct { | |
| 3865 | base: Payload = .{ .tag = .enum_numbered }, | |
| 3866 | data: *Module.EnumNumbered, | |
| 3867 | }; | |
| 3579 | 3868 | }; |
| 3869 | ||
| 3870 | pub const @"bool" = initTag(.bool); | |
| 3871 | pub const @"usize" = initTag(.usize); | |
| 3872 | pub const @"comptime_int" = initTag(.comptime_int); | |
| 3873 | ||
| 3874 | pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type { | |
| 3875 | assert(d.host_size == 0 or d.bit_offset < d.host_size * 8); | |
| 3876 | ||
| 3877 | if (d.sentinel != null or d.@"align" != 0 or d.@"addrspace" != .generic or | |
| 3878 | d.bit_offset != 0 or d.host_size != 0 or d.@"allowzero" or d.@"volatile") | |
| 3879 | { | |
| 3880 | return Type.Tag.pointer.create(arena, d); | |
| 3881 | } | |
| 3882 | ||
| 3883 | if (!d.mutable and d.size == .Slice and d.pointee_type.eql(Type.initTag(.u8))) { | |
| 3884 | return Type.initTag(.const_slice_u8); | |
| 3885 | } | |
| 3886 | ||
| 3887 | // TODO stage1 type inference bug | |
| 3888 | const T = Type.Tag; | |
| 3889 | ||
| 3890 | const type_payload = try arena.create(Type.Payload.ElemType); | |
| 3891 | type_payload.* = .{ | |
| 3892 | .base = .{ | |
| 3893 | .tag = switch (d.size) { | |
| 3894 | .One => if (d.mutable) T.single_mut_pointer else T.single_const_pointer, | |
| 3895 | .Many => if (d.mutable) T.many_mut_pointer else T.many_const_pointer, | |
| 3896 | .C => if (d.mutable) T.c_mut_pointer else T.c_const_pointer, | |
| 3897 | .Slice => if (d.mutable) T.mut_slice else T.const_slice, | |
| 3898 | }, | |
| 3899 | }, | |
| 3900 | .data = d.pointee_type, | |
| 3901 | }; | |
| 3902 | return Type.initPayload(&type_payload.base); | |
| 3903 | } | |
| 3904 | ||
| 3905 | pub fn smallestUnsignedInt(arena: *Allocator, max: u64) !Type { | |
| 3906 | const bits = bits: { | |
| 3907 | if (max == 0) break :bits 0; | |
| 3908 | const base = std.math.log2(max); | |
| 3909 | const upper = (@as(u64, 1) << @intCast(u6, base)) - 1; | |
| 3910 | break :bits base + @boolToInt(upper < max); | |
| 3911 | }; | |
| 3912 | return switch (@intCast(u16, bits)) { | |
| 3913 | 1 => initTag(.u1), | |
| 3914 | 8 => initTag(.u8), | |
| 3915 | 16 => initTag(.u16), | |
| 3916 | 32 => initTag(.u32), | |
| 3917 | 64 => initTag(.u64), | |
| 3918 | else => |b| return Tag.int_unsigned.create(arena, b), | |
| 3919 | }; | |
| 3920 | } | |
| 3580 | 3921 | }; |
| 3581 | 3922 | |
| 3582 | 3923 | pub const CType = enum { |
| ... | ... | @@ -3633,6 +3974,7 @@ pub const CType = enum { |
| 3633 | 3974 | .wasi, |
| 3634 | 3975 | .emscripten, |
| 3635 | 3976 | .plan9, |
| 3977 | .solaris, | |
| 3636 | 3978 | => switch (self) { |
| 3637 | 3979 | .short, |
| 3638 | 3980 | .ushort, |
| ... | ... | @@ -3684,7 +4026,6 @@ pub const CType = enum { |
| 3684 | 4026 | .fuchsia, |
| 3685 | 4027 | .kfreebsd, |
| 3686 | 4028 | .lv2, |
| 3687 | .solaris, | |
| 3688 | 4029 | .zos, |
| 3689 | 4030 | .haiku, |
| 3690 | 4031 | .minix, |
src/value.zig+543-41| ... | ... | @@ -63,6 +63,7 @@ pub const Value = extern union { |
| 63 | 63 | atomic_order_type, |
| 64 | 64 | atomic_rmw_op_type, |
| 65 | 65 | calling_convention_type, |
| 66 | address_space_type, | |
| 66 | 67 | float_mode_type, |
| 67 | 68 | reduce_op_type, |
| 68 | 69 | call_options_type, |
| ... | ... | @@ -158,6 +159,10 @@ pub const Value = extern union { |
| 158 | 159 | /// Used to coordinate alloc_inferred, store_to_inferred_ptr, and resolve_inferred_alloc |
| 159 | 160 | /// instructions for comptime code. |
| 160 | 161 | inferred_alloc_comptime, |
| 162 | /// Used sometimes as the result of field_call_bind. This value is always temporary, | |
| 163 | /// and refers directly to the air. It will never be referenced by the air itself. | |
| 164 | /// TODO: This is probably a bad encoding, maybe put temp data in the sema instead. | |
| 165 | bound_fn, | |
| 161 | 166 | |
| 162 | 167 | pub const last_no_payload_tag = Tag.empty_array; |
| 163 | 168 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | ... | @@ -226,6 +231,7 @@ pub const Value = extern union { |
| 226 | 231 | .atomic_order_type, |
| 227 | 232 | .atomic_rmw_op_type, |
| 228 | 233 | .calling_convention_type, |
| 234 | .address_space_type, | |
| 229 | 235 | .float_mode_type, |
| 230 | 236 | .reduce_op_type, |
| 231 | 237 | .call_options_type, |
| ... | ... | @@ -277,6 +283,7 @@ pub const Value = extern union { |
| 277 | 283 | .inferred_alloc => Payload.InferredAlloc, |
| 278 | 284 | .@"struct" => Payload.Struct, |
| 279 | 285 | .@"union" => Payload.Union, |
| 286 | .bound_fn => Payload.BoundFn, | |
| 280 | 287 | }; |
| 281 | 288 | } |
| 282 | 289 | |
| ... | ... | @@ -412,6 +419,7 @@ pub const Value = extern union { |
| 412 | 419 | .atomic_order_type, |
| 413 | 420 | .atomic_rmw_op_type, |
| 414 | 421 | .calling_convention_type, |
| 422 | .address_space_type, | |
| 415 | 423 | .float_mode_type, |
| 416 | 424 | .reduce_op_type, |
| 417 | 425 | .call_options_type, |
| ... | ... | @@ -419,6 +427,7 @@ pub const Value = extern union { |
| 419 | 427 | .extern_options_type, |
| 420 | 428 | .type_info_type, |
| 421 | 429 | .generic_poison, |
| 430 | .bound_fn, | |
| 422 | 431 | => unreachable, |
| 423 | 432 | |
| 424 | 433 | .ty => { |
| ... | ... | @@ -625,6 +634,7 @@ pub const Value = extern union { |
| 625 | 634 | .atomic_order_type => return out_stream.writeAll("std.builtin.AtomicOrder"), |
| 626 | 635 | .atomic_rmw_op_type => return out_stream.writeAll("std.builtin.AtomicRmwOp"), |
| 627 | 636 | .calling_convention_type => return out_stream.writeAll("std.builtin.CallingConvention"), |
| 637 | .address_space_type => return out_stream.writeAll("std.builtin.AddressSpace"), | |
| 628 | 638 | .float_mode_type => return out_stream.writeAll("std.builtin.FloatMode"), |
| 629 | 639 | .reduce_op_type => return out_stream.writeAll("std.builtin.ReduceOp"), |
| 630 | 640 | .call_options_type => return out_stream.writeAll("std.builtin.CallOptions"), |
| ... | ... | @@ -712,6 +722,10 @@ pub const Value = extern union { |
| 712 | 722 | try out_stream.writeAll("(opt_payload_ptr)"); |
| 713 | 723 | val = val.castTag(.opt_payload_ptr).?.data; |
| 714 | 724 | }, |
| 725 | .bound_fn => { | |
| 726 | const bound_func = val.castTag(.bound_fn).?.data; | |
| 727 | return out_stream.print("(bound_fn %{}(%{})", .{ bound_func.func_inst, bound_func.arg0_inst }); | |
| 728 | }, | |
| 715 | 729 | }; |
| 716 | 730 | } |
| 717 | 731 | |
| ... | ... | @@ -792,6 +806,7 @@ pub const Value = extern union { |
| 792 | 806 | .atomic_order_type => Type.initTag(.atomic_order), |
| 793 | 807 | .atomic_rmw_op_type => Type.initTag(.atomic_rmw_op), |
| 794 | 808 | .calling_convention_type => Type.initTag(.calling_convention), |
| 809 | .address_space_type => Type.initTag(.address_space), | |
| 795 | 810 | .float_mode_type => Type.initTag(.float_mode), |
| 796 | 811 | .reduce_op_type => Type.initTag(.reduce_op), |
| 797 | 812 | .call_options_type => Type.initTag(.call_options), |
| ... | ... | @@ -932,7 +947,7 @@ pub const Value = extern union { |
| 932 | 947 | /// Asserts that the value is a float or an integer. |
| 933 | 948 | pub fn toFloat(self: Value, comptime T: type) T { |
| 934 | 949 | return switch (self.tag()) { |
| 935 | .float_16 => @panic("TODO soft float"), | |
| 950 | .float_16 => @floatCast(T, self.castTag(.float_16).?.data), | |
| 936 | 951 | .float_32 => @floatCast(T, self.castTag(.float_32).?.data), |
| 937 | 952 | .float_64 => @floatCast(T, self.castTag(.float_64).?.data), |
| 938 | 953 | .float_128 => @floatCast(T, self.castTag(.float_128).?.data), |
| ... | ... | @@ -947,6 +962,45 @@ pub const Value = extern union { |
| 947 | 962 | }; |
| 948 | 963 | } |
| 949 | 964 | |
| 965 | pub fn clz(val: Value, ty: Type, target: Target) u64 { | |
| 966 | const ty_bits = ty.intInfo(target).bits; | |
| 967 | switch (val.tag()) { | |
| 968 | .zero, .bool_false => return ty_bits, | |
| 969 | .one, .bool_true => return ty_bits - 1, | |
| 970 | ||
| 971 | .int_u64 => { | |
| 972 | const big = @clz(u64, val.castTag(.int_u64).?.data); | |
| 973 | return big + ty_bits - 64; | |
| 974 | }, | |
| 975 | .int_i64 => { | |
| 976 | @panic("TODO implement i64 Value clz"); | |
| 977 | }, | |
| 978 | .int_big_positive => { | |
| 979 | // TODO: move this code into std lib big ints | |
| 980 | const bigint = val.castTag(.int_big_positive).?.asBigInt(); | |
| 981 | // Limbs are stored in little-endian order but we need | |
| 982 | // to iterate big-endian. | |
| 983 | var total_limb_lz: u64 = 0; | |
| 984 | var i: usize = bigint.limbs.len; | |
| 985 | const bits_per_limb = @sizeOf(std.math.big.Limb) * 8; | |
| 986 | while (i != 0) { | |
| 987 | i -= 1; | |
| 988 | const limb = bigint.limbs[i]; | |
| 989 | const this_limb_lz = @clz(std.math.big.Limb, limb); | |
| 990 | total_limb_lz += this_limb_lz; | |
| 991 | if (this_limb_lz != bits_per_limb) break; | |
| 992 | } | |
| 993 | const total_limb_bits = bigint.limbs.len * bits_per_limb; | |
| 994 | return total_limb_lz + ty_bits - total_limb_bits; | |
| 995 | }, | |
| 996 | .int_big_negative => { | |
| 997 | @panic("TODO implement int_big_negative Value clz"); | |
| 998 | }, | |
| 999 | ||
| 1000 | else => unreachable, | |
| 1001 | } | |
| 1002 | } | |
| 1003 | ||
| 950 | 1004 | /// Asserts the value is an integer and not undefined. |
| 951 | 1005 | /// Returns the number of bits the value requires to represent stored in twos complement form. |
| 952 | 1006 | pub fn intBitCountTwosComp(self: Value) usize { |
| ... | ... | @@ -1036,31 +1090,15 @@ pub const Value = extern union { |
| 1036 | 1090 | } |
| 1037 | 1091 | } |
| 1038 | 1092 | |
| 1039 | /// Converts an integer or a float to a float. | |
| 1040 | /// Returns `error.Overflow` if the value does not fit in the new type. | |
| 1041 | pub fn floatCast(self: Value, allocator: *Allocator, dest_ty: Type) !Value { | |
| 1093 | /// Converts an integer or a float to a float. May result in a loss of information. | |
| 1094 | /// Caller can find out by equality checking the result against the operand. | |
| 1095 | pub fn floatCast(self: Value, arena: *Allocator, dest_ty: Type) !Value { | |
| 1042 | 1096 | switch (dest_ty.tag()) { |
| 1043 | .f16 => { | |
| 1044 | @panic("TODO add __trunctfhf2 to compiler-rt"); | |
| 1045 | //const res = try Value.Tag.float_16.create(allocator, self.toFloat(f16)); | |
| 1046 | //if (!self.eql(res)) | |
| 1047 | // return error.Overflow; | |
| 1048 | //return res; | |
| 1049 | }, | |
| 1050 | .f32 => { | |
| 1051 | const res = try Value.Tag.float_32.create(allocator, self.toFloat(f32)); | |
| 1052 | if (!self.eql(res, dest_ty)) | |
| 1053 | return error.Overflow; | |
| 1054 | return res; | |
| 1055 | }, | |
| 1056 | .f64 => { | |
| 1057 | const res = try Value.Tag.float_64.create(allocator, self.toFloat(f64)); | |
| 1058 | if (!self.eql(res, dest_ty)) | |
| 1059 | return error.Overflow; | |
| 1060 | return res; | |
| 1061 | }, | |
| 1097 | .f16 => return Value.Tag.float_16.create(arena, self.toFloat(f16)), | |
| 1098 | .f32 => return Value.Tag.float_32.create(arena, self.toFloat(f32)), | |
| 1099 | .f64 => return Value.Tag.float_64.create(arena, self.toFloat(f64)), | |
| 1062 | 1100 | .f128, .comptime_float, .c_longdouble => { |
| 1063 | return Value.Tag.float_128.create(allocator, self.toFloat(f128)); | |
| 1101 | return Value.Tag.float_128.create(arena, self.toFloat(f128)); | |
| 1064 | 1102 | }, |
| 1065 | 1103 | else => unreachable, |
| 1066 | 1104 | } |
| ... | ... | @@ -1286,7 +1324,12 @@ pub const Value = extern union { |
| 1286 | 1324 | } |
| 1287 | 1325 | }, |
| 1288 | 1326 | .Union => { |
| 1289 | @panic("TODO implement hashing union values"); | |
| 1327 | const union_obj = val.castTag(.@"union").?.data; | |
| 1328 | if (ty.unionTagType()) |tag_ty| { | |
| 1329 | union_obj.tag.hash(tag_ty, hasher); | |
| 1330 | } | |
| 1331 | const active_field_ty = ty.unionFieldType(union_obj.tag); | |
| 1332 | union_obj.val.hash(active_field_ty, hasher); | |
| 1290 | 1333 | }, |
| 1291 | 1334 | .Fn => { |
| 1292 | 1335 | @panic("TODO implement hashing function values"); |
| ... | ... | @@ -1442,6 +1485,14 @@ pub const Value = extern union { |
| 1442 | 1485 | } |
| 1443 | 1486 | } |
| 1444 | 1487 | |
| 1488 | pub fn unionTag(val: Value) Value { | |
| 1489 | switch (val.tag()) { | |
| 1490 | .undef => return val, | |
| 1491 | .@"union" => return val.castTag(.@"union").?.data.tag, | |
| 1492 | else => unreachable, | |
| 1493 | } | |
| 1494 | } | |
| 1495 | ||
| 1445 | 1496 | /// Returns a pointer to the element value at the index. |
| 1446 | 1497 | pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value { |
| 1447 | 1498 | if (self.castTag(.elem_ptr)) |elem_ptr| { |
| ... | ... | @@ -1524,6 +1575,341 @@ pub const Value = extern union { |
| 1524 | 1575 | }; |
| 1525 | 1576 | } |
| 1526 | 1577 | |
| 1578 | pub fn intToFloat(val: Value, allocator: *Allocator, dest_ty: Type, target: Target) !Value { | |
| 1579 | switch (val.tag()) { | |
| 1580 | .undef, .zero, .one => return val, | |
| 1581 | .int_u64 => { | |
| 1582 | return intToFloatInner(val.castTag(.int_u64).?.data, allocator, dest_ty, target); | |
| 1583 | }, | |
| 1584 | .int_i64 => { | |
| 1585 | return intToFloatInner(val.castTag(.int_i64).?.data, allocator, dest_ty, target); | |
| 1586 | }, | |
| 1587 | .int_big_positive, .int_big_negative => @panic("big int to float"), | |
| 1588 | else => unreachable, | |
| 1589 | } | |
| 1590 | } | |
| 1591 | ||
| 1592 | fn intToFloatInner(x: anytype, arena: *Allocator, dest_ty: Type, target: Target) !Value { | |
| 1593 | switch (dest_ty.floatBits(target)) { | |
| 1594 | 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)), | |
| 1595 | 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)), | |
| 1596 | 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)), | |
| 1597 | 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)), | |
| 1598 | else => unreachable, | |
| 1599 | } | |
| 1600 | } | |
| 1601 | ||
| 1602 | /// Supports both floats and ints; handles undefined. | |
| 1603 | pub fn numberAddWrap( | |
| 1604 | lhs: Value, | |
| 1605 | rhs: Value, | |
| 1606 | ty: Type, | |
| 1607 | arena: *Allocator, | |
| 1608 | target: Target, | |
| 1609 | ) !Value { | |
| 1610 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1611 | ||
| 1612 | if (ty.isAnyFloat()) { | |
| 1613 | return floatAdd(lhs, rhs, ty, arena); | |
| 1614 | } | |
| 1615 | const result = try intAdd(lhs, rhs, arena); | |
| 1616 | ||
| 1617 | const max = try ty.maxInt(arena, target); | |
| 1618 | if (compare(result, .gt, max, ty)) { | |
| 1619 | @panic("TODO comptime wrapping integer addition"); | |
| 1620 | } | |
| 1621 | ||
| 1622 | const min = try ty.minInt(arena, target); | |
| 1623 | if (compare(result, .lt, min, ty)) { | |
| 1624 | @panic("TODO comptime wrapping integer addition"); | |
| 1625 | } | |
| 1626 | ||
| 1627 | return result; | |
| 1628 | } | |
| 1629 | ||
| 1630 | /// Supports integers only; asserts neither operand is undefined. | |
| 1631 | pub fn intAddSat( | |
| 1632 | lhs: Value, | |
| 1633 | rhs: Value, | |
| 1634 | ty: Type, | |
| 1635 | arena: *Allocator, | |
| 1636 | target: Target, | |
| 1637 | ) !Value { | |
| 1638 | assert(!lhs.isUndef()); | |
| 1639 | assert(!rhs.isUndef()); | |
| 1640 | ||
| 1641 | const result = try intAdd(lhs, rhs, arena); | |
| 1642 | ||
| 1643 | const max = try ty.maxInt(arena, target); | |
| 1644 | if (compare(result, .gt, max, ty)) { | |
| 1645 | return max; | |
| 1646 | } | |
| 1647 | ||
| 1648 | const min = try ty.minInt(arena, target); | |
| 1649 | if (compare(result, .lt, min, ty)) { | |
| 1650 | return min; | |
| 1651 | } | |
| 1652 | ||
| 1653 | return result; | |
| 1654 | } | |
| 1655 | ||
| 1656 | /// Supports both floats and ints; handles undefined. | |
| 1657 | pub fn numberSubWrap( | |
| 1658 | lhs: Value, | |
| 1659 | rhs: Value, | |
| 1660 | ty: Type, | |
| 1661 | arena: *Allocator, | |
| 1662 | target: Target, | |
| 1663 | ) !Value { | |
| 1664 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1665 | ||
| 1666 | if (ty.isAnyFloat()) { | |
| 1667 | return floatSub(lhs, rhs, ty, arena); | |
| 1668 | } | |
| 1669 | const result = try intSub(lhs, rhs, arena); | |
| 1670 | ||
| 1671 | const max = try ty.maxInt(arena, target); | |
| 1672 | if (compare(result, .gt, max, ty)) { | |
| 1673 | @panic("TODO comptime wrapping integer subtraction"); | |
| 1674 | } | |
| 1675 | ||
| 1676 | const min = try ty.minInt(arena, target); | |
| 1677 | if (compare(result, .lt, min, ty)) { | |
| 1678 | @panic("TODO comptime wrapping integer subtraction"); | |
| 1679 | } | |
| 1680 | ||
| 1681 | return result; | |
| 1682 | } | |
| 1683 | ||
| 1684 | /// Supports integers only; asserts neither operand is undefined. | |
| 1685 | pub fn intSubSat( | |
| 1686 | lhs: Value, | |
| 1687 | rhs: Value, | |
| 1688 | ty: Type, | |
| 1689 | arena: *Allocator, | |
| 1690 | target: Target, | |
| 1691 | ) !Value { | |
| 1692 | assert(!lhs.isUndef()); | |
| 1693 | assert(!rhs.isUndef()); | |
| 1694 | ||
| 1695 | const result = try intSub(lhs, rhs, arena); | |
| 1696 | ||
| 1697 | const max = try ty.maxInt(arena, target); | |
| 1698 | if (compare(result, .gt, max, ty)) { | |
| 1699 | return max; | |
| 1700 | } | |
| 1701 | ||
| 1702 | const min = try ty.minInt(arena, target); | |
| 1703 | if (compare(result, .lt, min, ty)) { | |
| 1704 | return min; | |
| 1705 | } | |
| 1706 | ||
| 1707 | return result; | |
| 1708 | } | |
| 1709 | ||
| 1710 | /// Supports both floats and ints; handles undefined. | |
| 1711 | pub fn numberMulWrap( | |
| 1712 | lhs: Value, | |
| 1713 | rhs: Value, | |
| 1714 | ty: Type, | |
| 1715 | arena: *Allocator, | |
| 1716 | target: Target, | |
| 1717 | ) !Value { | |
| 1718 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1719 | ||
| 1720 | if (ty.isAnyFloat()) { | |
| 1721 | return floatMul(lhs, rhs, ty, arena); | |
| 1722 | } | |
| 1723 | const result = try intMul(lhs, rhs, arena); | |
| 1724 | ||
| 1725 | const max = try ty.maxInt(arena, target); | |
| 1726 | if (compare(result, .gt, max, ty)) { | |
| 1727 | @panic("TODO comptime wrapping integer multiplication"); | |
| 1728 | } | |
| 1729 | ||
| 1730 | const min = try ty.minInt(arena, target); | |
| 1731 | if (compare(result, .lt, min, ty)) { | |
| 1732 | @panic("TODO comptime wrapping integer multiplication"); | |
| 1733 | } | |
| 1734 | ||
| 1735 | return result; | |
| 1736 | } | |
| 1737 | ||
| 1738 | /// Supports integers only; asserts neither operand is undefined. | |
| 1739 | pub fn intMulSat( | |
| 1740 | lhs: Value, | |
| 1741 | rhs: Value, | |
| 1742 | ty: Type, | |
| 1743 | arena: *Allocator, | |
| 1744 | target: Target, | |
| 1745 | ) !Value { | |
| 1746 | assert(!lhs.isUndef()); | |
| 1747 | assert(!rhs.isUndef()); | |
| 1748 | ||
| 1749 | const result = try intMul(lhs, rhs, arena); | |
| 1750 | ||
| 1751 | const max = try ty.maxInt(arena, target); | |
| 1752 | if (compare(result, .gt, max, ty)) { | |
| 1753 | return max; | |
| 1754 | } | |
| 1755 | ||
| 1756 | const min = try ty.minInt(arena, target); | |
| 1757 | if (compare(result, .lt, min, ty)) { | |
| 1758 | return min; | |
| 1759 | } | |
| 1760 | ||
| 1761 | return result; | |
| 1762 | } | |
| 1763 | ||
| 1764 | /// Supports both floats and ints; handles undefined. | |
| 1765 | pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value { | |
| 1766 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1767 | ||
| 1768 | // TODO is this a performance issue? maybe we should try the operation without | |
| 1769 | // resorting to BigInt first. | |
| 1770 | var lhs_space: Value.BigIntSpace = undefined; | |
| 1771 | var rhs_space: Value.BigIntSpace = undefined; | |
| 1772 | const lhs_bigint = lhs.toBigInt(&lhs_space); | |
| 1773 | const rhs_bigint = rhs.toBigInt(&rhs_space); | |
| 1774 | const limbs = try arena.alloc( | |
| 1775 | std.math.big.Limb, | |
| 1776 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 1777 | ); | |
| 1778 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 1779 | ||
| 1780 | switch (lhs_bigint.order(rhs_bigint)) { | |
| 1781 | .lt => result_bigint.copy(rhs_bigint), | |
| 1782 | .gt, .eq => result_bigint.copy(lhs_bigint), | |
| 1783 | } | |
| 1784 | ||
| 1785 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | |
| 1786 | ||
| 1787 | if (result_bigint.positive) { | |
| 1788 | return Value.Tag.int_big_positive.create(arena, result_limbs); | |
| 1789 | } else { | |
| 1790 | return Value.Tag.int_big_negative.create(arena, result_limbs); | |
| 1791 | } | |
| 1792 | } | |
| 1793 | ||
| 1794 | /// Supports both floats and ints; handles undefined. | |
| 1795 | pub fn numberMin(lhs: Value, rhs: Value, arena: *Allocator) !Value { | |
| 1796 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1797 | ||
| 1798 | // TODO is this a performance issue? maybe we should try the operation without | |
| 1799 | // resorting to BigInt first. | |
| 1800 | var lhs_space: Value.BigIntSpace = undefined; | |
| 1801 | var rhs_space: Value.BigIntSpace = undefined; | |
| 1802 | const lhs_bigint = lhs.toBigInt(&lhs_space); | |
| 1803 | const rhs_bigint = rhs.toBigInt(&rhs_space); | |
| 1804 | const limbs = try arena.alloc( | |
| 1805 | std.math.big.Limb, | |
| 1806 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 1807 | ); | |
| 1808 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 1809 | ||
| 1810 | switch (lhs_bigint.order(rhs_bigint)) { | |
| 1811 | .lt => result_bigint.copy(lhs_bigint), | |
| 1812 | .gt, .eq => result_bigint.copy(rhs_bigint), | |
| 1813 | } | |
| 1814 | ||
| 1815 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | |
| 1816 | ||
| 1817 | if (result_bigint.positive) { | |
| 1818 | return Value.Tag.int_big_positive.create(arena, result_limbs); | |
| 1819 | } else { | |
| 1820 | return Value.Tag.int_big_negative.create(arena, result_limbs); | |
| 1821 | } | |
| 1822 | } | |
| 1823 | ||
| 1824 | /// operands must be integers; handles undefined. | |
| 1825 | pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: *Allocator) !Value { | |
| 1826 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1827 | ||
| 1828 | // TODO is this a performance issue? maybe we should try the operation without | |
| 1829 | // resorting to BigInt first. | |
| 1830 | var lhs_space: Value.BigIntSpace = undefined; | |
| 1831 | var rhs_space: Value.BigIntSpace = undefined; | |
| 1832 | const lhs_bigint = lhs.toBigInt(&lhs_space); | |
| 1833 | const rhs_bigint = rhs.toBigInt(&rhs_space); | |
| 1834 | const limbs = try arena.alloc( | |
| 1835 | std.math.big.Limb, | |
| 1836 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 1837 | ); | |
| 1838 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 1839 | result_bigint.bitAnd(lhs_bigint, rhs_bigint); | |
| 1840 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | |
| 1841 | ||
| 1842 | if (result_bigint.positive) { | |
| 1843 | return Value.Tag.int_big_positive.create(arena, result_limbs); | |
| 1844 | } else { | |
| 1845 | return Value.Tag.int_big_negative.create(arena, result_limbs); | |
| 1846 | } | |
| 1847 | } | |
| 1848 | ||
| 1849 | /// operands must be integers; handles undefined. | |
| 1850 | pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: *Allocator, target: Target) !Value { | |
| 1851 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1852 | ||
| 1853 | const anded = try bitwiseAnd(lhs, rhs, arena); | |
| 1854 | ||
| 1855 | const all_ones = if (ty.isSignedInt()) | |
| 1856 | try Value.Tag.int_i64.create(arena, -1) | |
| 1857 | else | |
| 1858 | try ty.maxInt(arena, target); | |
| 1859 | ||
| 1860 | return bitwiseXor(anded, all_ones, arena); | |
| 1861 | } | |
| 1862 | ||
| 1863 | /// operands must be integers; handles undefined. | |
| 1864 | pub fn bitwiseOr(lhs: Value, rhs: Value, arena: *Allocator) !Value { | |
| 1865 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1866 | ||
| 1867 | // TODO is this a performance issue? maybe we should try the operation without | |
| 1868 | // resorting to BigInt first. | |
| 1869 | var lhs_space: Value.BigIntSpace = undefined; | |
| 1870 | var rhs_space: Value.BigIntSpace = undefined; | |
| 1871 | const lhs_bigint = lhs.toBigInt(&lhs_space); | |
| 1872 | const rhs_bigint = rhs.toBigInt(&rhs_space); | |
| 1873 | const limbs = try arena.alloc( | |
| 1874 | std.math.big.Limb, | |
| 1875 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 1876 | ); | |
| 1877 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 1878 | result_bigint.bitOr(lhs_bigint, rhs_bigint); | |
| 1879 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | |
| 1880 | ||
| 1881 | if (result_bigint.positive) { | |
| 1882 | return Value.Tag.int_big_positive.create(arena, result_limbs); | |
| 1883 | } else { | |
| 1884 | return Value.Tag.int_big_negative.create(arena, result_limbs); | |
| 1885 | } | |
| 1886 | } | |
| 1887 | ||
| 1888 | /// operands must be integers; handles undefined. | |
| 1889 | pub fn bitwiseXor(lhs: Value, rhs: Value, arena: *Allocator) !Value { | |
| 1890 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | |
| 1891 | ||
| 1892 | // TODO is this a performance issue? maybe we should try the operation without | |
| 1893 | // resorting to BigInt first. | |
| 1894 | var lhs_space: Value.BigIntSpace = undefined; | |
| 1895 | var rhs_space: Value.BigIntSpace = undefined; | |
| 1896 | const lhs_bigint = lhs.toBigInt(&lhs_space); | |
| 1897 | const rhs_bigint = rhs.toBigInt(&rhs_space); | |
| 1898 | const limbs = try arena.alloc( | |
| 1899 | std.math.big.Limb, | |
| 1900 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 1901 | ); | |
| 1902 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 1903 | result_bigint.bitXor(lhs_bigint, rhs_bigint); | |
| 1904 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | |
| 1905 | ||
| 1906 | if (result_bigint.positive) { | |
| 1907 | return Value.Tag.int_big_positive.create(arena, result_limbs); | |
| 1908 | } else { | |
| 1909 | return Value.Tag.int_big_negative.create(arena, result_limbs); | |
| 1910 | } | |
| 1911 | } | |
| 1912 | ||
| 1527 | 1913 | pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value { |
| 1528 | 1914 | // TODO is this a performance issue? maybe we should try the operation without |
| 1529 | 1915 | // resorting to BigInt first. |
| ... | ... | @@ -1599,6 +1985,82 @@ pub const Value = extern union { |
| 1599 | 1985 | } |
| 1600 | 1986 | } |
| 1601 | 1987 | |
| 1988 | pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | |
| 1989 | // TODO is this a performance issue? maybe we should try the operation without | |
| 1990 | // resorting to BigInt first. | |
| 1991 | var lhs_space: Value.BigIntSpace = undefined; | |
| 1992 | var rhs_space: Value.BigIntSpace = undefined; | |
| 1993 | const lhs_bigint = lhs.toBigInt(&lhs_space); | |
| 1994 | const rhs_bigint = rhs.toBigInt(&rhs_space); | |
| 1995 | const limbs_q = try allocator.alloc( | |
| 1996 | std.math.big.Limb, | |
| 1997 | lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1, | |
| 1998 | ); | |
| 1999 | const limbs_r = try allocator.alloc( | |
| 2000 | std.math.big.Limb, | |
| 2001 | lhs_bigint.limbs.len, | |
| 2002 | ); | |
| 2003 | const limbs_buffer = try allocator.alloc( | |
| 2004 | std.math.big.Limb, | |
| 2005 | std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 2006 | ); | |
| 2007 | var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; | |
| 2008 | var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; | |
| 2009 | result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null); | |
| 2010 | const result_limbs = result_r.limbs[0..result_r.len]; | |
| 2011 | ||
| 2012 | if (result_r.positive) { | |
| 2013 | return Value.Tag.int_big_positive.create(allocator, result_limbs); | |
| 2014 | } else { | |
| 2015 | return Value.Tag.int_big_negative.create(allocator, result_limbs); | |
| 2016 | } | |
| 2017 | } | |
| 2018 | ||
| 2019 | pub fn intMod(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | |
| 2020 | // TODO is this a performance issue? maybe we should try the operation without | |
| 2021 | // resorting to BigInt first. | |
| 2022 | var lhs_space: Value.BigIntSpace = undefined; | |
| 2023 | var rhs_space: Value.BigIntSpace = undefined; | |
| 2024 | const lhs_bigint = lhs.toBigInt(&lhs_space); | |
| 2025 | const rhs_bigint = rhs.toBigInt(&rhs_space); | |
| 2026 | const limbs_q = try allocator.alloc( | |
| 2027 | std.math.big.Limb, | |
| 2028 | lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1, | |
| 2029 | ); | |
| 2030 | const limbs_r = try allocator.alloc( | |
| 2031 | std.math.big.Limb, | |
| 2032 | lhs_bigint.limbs.len, | |
| 2033 | ); | |
| 2034 | const limbs_buffer = try allocator.alloc( | |
| 2035 | std.math.big.Limb, | |
| 2036 | std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 2037 | ); | |
| 2038 | var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; | |
| 2039 | var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; | |
| 2040 | result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null); | |
| 2041 | const result_limbs = result_r.limbs[0..result_r.len]; | |
| 2042 | ||
| 2043 | if (result_r.positive) { | |
| 2044 | return Value.Tag.int_big_positive.create(allocator, result_limbs); | |
| 2045 | } else { | |
| 2046 | return Value.Tag.int_big_negative.create(allocator, result_limbs); | |
| 2047 | } | |
| 2048 | } | |
| 2049 | ||
| 2050 | pub fn floatRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | |
| 2051 | _ = lhs; | |
| 2052 | _ = rhs; | |
| 2053 | _ = allocator; | |
| 2054 | @panic("TODO implement Value.floatRem"); | |
| 2055 | } | |
| 2056 | ||
| 2057 | pub fn floatMod(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | |
| 2058 | _ = lhs; | |
| 2059 | _ = rhs; | |
| 2060 | _ = allocator; | |
| 2061 | @panic("TODO implement Value.floatMod"); | |
| 2062 | } | |
| 2063 | ||
| 1602 | 2064 | pub fn intMul(lhs: Value, rhs: Value, allocator: *Allocator) !Value { |
| 1603 | 2065 | // TODO is this a performance issue? maybe we should try the operation without |
| 1604 | 2066 | // resorting to BigInt first. |
| ... | ... | @@ -1634,6 +2096,31 @@ pub const Value = extern union { |
| 1634 | 2096 | return Tag.int_u64.create(arena, truncated); |
| 1635 | 2097 | } |
| 1636 | 2098 | |
| 2099 | pub fn shl(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | |
| 2100 | // TODO is this a performance issue? maybe we should try the operation without | |
| 2101 | // resorting to BigInt first. | |
| 2102 | var lhs_space: Value.BigIntSpace = undefined; | |
| 2103 | const lhs_bigint = lhs.toBigInt(&lhs_space); | |
| 2104 | const shift = rhs.toUnsignedInt(); | |
| 2105 | const limbs = try allocator.alloc( | |
| 2106 | std.math.big.Limb, | |
| 2107 | lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1, | |
| 2108 | ); | |
| 2109 | var result_bigint = BigIntMutable{ | |
| 2110 | .limbs = limbs, | |
| 2111 | .positive = undefined, | |
| 2112 | .len = undefined, | |
| 2113 | }; | |
| 2114 | result_bigint.shiftLeft(lhs_bigint, shift); | |
| 2115 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | |
| 2116 | ||
| 2117 | if (result_bigint.positive) { | |
| 2118 | return Value.Tag.int_big_positive.create(allocator, result_limbs); | |
| 2119 | } else { | |
| 2120 | return Value.Tag.int_big_negative.create(allocator, result_limbs); | |
| 2121 | } | |
| 2122 | } | |
| 2123 | ||
| 1637 | 2124 | pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value { |
| 1638 | 2125 | // TODO is this a performance issue? maybe we should try the operation without |
| 1639 | 2126 | // resorting to BigInt first. |
| ... | ... | @@ -1667,10 +2154,9 @@ pub const Value = extern union { |
| 1667 | 2154 | ) !Value { |
| 1668 | 2155 | switch (float_type.tag()) { |
| 1669 | 2156 | .f16 => { |
| 1670 | @panic("TODO add __trunctfhf2 to compiler-rt"); | |
| 1671 | //const lhs_val = lhs.toFloat(f16); | |
| 1672 | //const rhs_val = rhs.toFloat(f16); | |
| 1673 | //return Value.Tag.float_16.create(arena, lhs_val + rhs_val); | |
| 2157 | const lhs_val = lhs.toFloat(f16); | |
| 2158 | const rhs_val = rhs.toFloat(f16); | |
| 2159 | return Value.Tag.float_16.create(arena, lhs_val + rhs_val); | |
| 1674 | 2160 | }, |
| 1675 | 2161 | .f32 => { |
| 1676 | 2162 | const lhs_val = lhs.toFloat(f32); |
| ... | ... | @@ -1699,10 +2185,9 @@ pub const Value = extern union { |
| 1699 | 2185 | ) !Value { |
| 1700 | 2186 | switch (float_type.tag()) { |
| 1701 | 2187 | .f16 => { |
| 1702 | @panic("TODO add __trunctfhf2 to compiler-rt"); | |
| 1703 | //const lhs_val = lhs.toFloat(f16); | |
| 1704 | //const rhs_val = rhs.toFloat(f16); | |
| 1705 | //return Value.Tag.float_16.create(arena, lhs_val - rhs_val); | |
| 2188 | const lhs_val = lhs.toFloat(f16); | |
| 2189 | const rhs_val = rhs.toFloat(f16); | |
| 2190 | return Value.Tag.float_16.create(arena, lhs_val - rhs_val); | |
| 1706 | 2191 | }, |
| 1707 | 2192 | .f32 => { |
| 1708 | 2193 | const lhs_val = lhs.toFloat(f32); |
| ... | ... | @@ -1731,10 +2216,9 @@ pub const Value = extern union { |
| 1731 | 2216 | ) !Value { |
| 1732 | 2217 | switch (float_type.tag()) { |
| 1733 | 2218 | .f16 => { |
| 1734 | @panic("TODO add __trunctfhf2 to compiler-rt"); | |
| 1735 | //const lhs_val = lhs.toFloat(f16); | |
| 1736 | //const rhs_val = rhs.toFloat(f16); | |
| 1737 | //return Value.Tag.float_16.create(arena, lhs_val / rhs_val); | |
| 2219 | const lhs_val = lhs.toFloat(f16); | |
| 2220 | const rhs_val = rhs.toFloat(f16); | |
| 2221 | return Value.Tag.float_16.create(arena, lhs_val / rhs_val); | |
| 1738 | 2222 | }, |
| 1739 | 2223 | .f32 => { |
| 1740 | 2224 | const lhs_val = lhs.toFloat(f32); |
| ... | ... | @@ -1763,10 +2247,9 @@ pub const Value = extern union { |
| 1763 | 2247 | ) !Value { |
| 1764 | 2248 | switch (float_type.tag()) { |
| 1765 | 2249 | .f16 => { |
| 1766 | @panic("TODO add __trunctfhf2 to compiler-rt"); | |
| 1767 | //const lhs_val = lhs.toFloat(f16); | |
| 1768 | //const rhs_val = rhs.toFloat(f16); | |
| 1769 | //return Value.Tag.float_16.create(arena, lhs_val * rhs_val); | |
| 2250 | const lhs_val = lhs.toFloat(f16); | |
| 2251 | const rhs_val = rhs.toFloat(f16); | |
| 2252 | return Value.Tag.float_16.create(arena, lhs_val * rhs_val); | |
| 1770 | 2253 | }, |
| 1771 | 2254 | .f32 => { |
| 1772 | 2255 | const lhs_val = lhs.toFloat(f32); |
| ... | ... | @@ -1972,6 +2455,16 @@ pub const Value = extern union { |
| 1972 | 2455 | val: Value, |
| 1973 | 2456 | }, |
| 1974 | 2457 | }; |
| 2458 | ||
| 2459 | pub const BoundFn = struct { | |
| 2460 | pub const base_tag = Tag.bound_fn; | |
| 2461 | ||
| 2462 | base: Payload = Payload{ .tag = base_tag }, | |
| 2463 | data: struct { | |
| 2464 | func_inst: Air.Inst.Ref, | |
| 2465 | arg0_inst: Air.Inst.Ref, | |
| 2466 | }, | |
| 2467 | }; | |
| 1975 | 2468 | }; |
| 1976 | 2469 | |
| 1977 | 2470 | /// Big enough to fit any non-BigInt value |
| ... | ... | @@ -1980,4 +2473,13 @@ pub const Value = extern union { |
| 1980 | 2473 | /// are possible without using an allocator. |
| 1981 | 2474 | limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb, |
| 1982 | 2475 | }; |
| 2476 | ||
| 2477 | pub const zero = initTag(.zero); | |
| 2478 | pub const one = initTag(.one); | |
| 2479 | pub const negative_one: Value = .{ .ptr_otherwise = &negative_one_payload.base }; | |
| 2480 | }; | |
| 2481 | ||
| 2482 | var negative_one_payload: Value.Payload.I64 = .{ | |
| 2483 | .base = .{ .tag = .int_i64 }, | |
| 2484 | .data = -1, | |
| 1983 | 2485 | }; |
src/windows_com.hpp+1-1| ... | ... | @@ -352,7 +352,7 @@ extern "C" { |
| 352 | 352 | 	/// <summary> |
| 353 | 353 | 	/// Gets product-specific properties. |
| 354 | 354 | 	/// </summary> |
| 355 | 	/// <param name="ppPropeties">A pointer to an instance of <see cref="ISetupPropertyStore"/>. This may be NULL if no properties are defined.</param> | |
| 355 | 	/// <param name="ppProperties">A pointer to an instance of <see cref="ISetupPropertyStore"/>. This may be NULL if no properties are defined.</param> | |
| 356 | 356 | 	/// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns> |
| 357 | 357 | 	STDMETHOD(GetProperties)( |
| 358 | 358 | 		_Outptr_result_maybenull_ ISetupPropertyStore** ppProperties |
src/zig_clang.cpp+4-4| ... | ... | @@ -1691,10 +1691,10 @@ void ZigClang_detect_enum_ConstantExprKind(clang::Expr::ConstantExprKind x) { |
| 1691 | 1691 | break; |
| 1692 | 1692 | } |
| 1693 | 1693 | } |
| 1694 | static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_Normal == clang::Expr::ConstantExprKind::Normal, ""); | |
| 1695 | static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_NonClassTemplateArgument == clang::Expr::ConstantExprKind::NonClassTemplateArgument, ""); | |
| 1696 | static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_ClassTemplateArgument == clang::Expr::ConstantExprKind::ClassTemplateArgument, ""); | |
| 1697 | static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_ImmediateInvocation == clang::Expr::ConstantExprKind::ImmediateInvocation, ""); | |
| 1694 | static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_Normal == clang::Expr::ConstantExprKind::Normal, ""); | |
| 1695 | static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_NonClassTemplateArgument == clang::Expr::ConstantExprKind::NonClassTemplateArgument, ""); | |
| 1696 | static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_ClassTemplateArgument == clang::Expr::ConstantExprKind::ClassTemplateArgument, ""); | |
| 1697 | static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_ImmediateInvocation == clang::Expr::ConstantExprKind::ImmediateInvocation, ""); | |
| 1698 | 1698 | |
| 1699 | 1699 | |
| 1700 | 1700 | static_assert(sizeof(ZigClangAPValue) == sizeof(clang::APValue), ""); |
src/zig_clang.h+4-4| ... | ... | @@ -1011,10 +1011,10 @@ enum ZigClangPreprocessedEntity_EntityKind { |
| 1011 | 1011 | }; |
| 1012 | 1012 | |
| 1013 | 1013 | enum ZigClangExpr_ConstantExprKind { |
| 1014 | ZigClangExpr_ContantExprKind_Normal, | |
| 1015 | ZigClangExpr_ContantExprKind_NonClassTemplateArgument, | |
| 1016 | ZigClangExpr_ContantExprKind_ClassTemplateArgument, | |
| 1017 | ZigClangExpr_ContantExprKind_ImmediateInvocation, | |
| 1014 | ZigClangExpr_ConstantExprKind_Normal, | |
| 1015 | ZigClangExpr_ConstantExprKind_NonClassTemplateArgument, | |
| 1016 | ZigClangExpr_ConstantExprKind_ClassTemplateArgument, | |
| 1017 | ZigClangExpr_ConstantExprKind_ImmediateInvocation, | |
| 1018 | 1018 | }; |
| 1019 | 1019 | |
| 1020 | 1020 | enum ZigClangUnaryExprOrTypeTrait_Kind { |
src/zig_llvm-ar.cpp+1-1| ... | ... | @@ -232,7 +232,7 @@ static std::string ArchiveName; |
| 232 | 232 | static std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; |
| 233 | 233 | static std::vector<std::unique_ptr<object::Archive>> Archives; |
| 234 | 234 | |
| 235 | // This variable holds the list of member files to proecess, as given | |
| 235 | // This variable holds the list of member files to process, as given | |
| 236 | 236 | // on the command line. |
| 237 | 237 | static std::vector<StringRef> Members; |
| 238 | 238 |
src/zig_llvm.cpp+5| ... | ... | @@ -412,6 +412,11 @@ ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref) { |
| 412 | 412 | return wrap(Type::getTokenTy(*unwrap(context_ref))); |
| 413 | 413 | } |
| 414 | 414 | |
| 415 | LLVMValueRef ZigLLVMAddFunctionInAddressSpace(LLVMModuleRef M, const char *Name, LLVMTypeRef FunctionTy, unsigned AddressSpace) { | |
| 416 | Function* func = Function::Create(unwrap<FunctionType>(FunctionTy), GlobalValue::ExternalLinkage, AddressSpace, Name, unwrap(M)); | |
| 417 | return wrap(func); | |
| 418 | } | |
| 419 | ||
| 415 | 420 | LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, |
| 416 | 421 | unsigned NumArgs, ZigLLVM_CallingConv CC, ZigLLVM_CallAttr attr, const char *Name) |
| 417 | 422 | { |
src/zig_llvm.h+3| ... | ... | @@ -65,6 +65,9 @@ ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, co |
| 65 | 65 | |
| 66 | 66 | ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref); |
| 67 | 67 | |
| 68 | ZIG_EXTERN_C LLVMValueRef ZigLLVMAddFunctionInAddressSpace(LLVMModuleRef M, const char *Name, | |
| 69 | LLVMTypeRef FunctionTy, unsigned AddressSpace); | |
| 70 | ||
| 68 | 71 | enum ZigLLVM_CallingConv { |
| 69 | 72 | ZigLLVM_C = 0, |
| 70 | 73 | ZigLLVM_Fast = 8, |
test/behavior.zig+43-38| ... | ... | @@ -2,19 +2,38 @@ const builtin = @import("builtin"); |
| 2 | 2 | |
| 3 | 3 | test { |
| 4 | 4 | // Tests that pass for both. |
| 5 | _ = @import("behavior/bool.zig"); | |
| 5 | _ = @import("behavior/array.zig"); | |
| 6 | _ = @import("behavior/atomics.zig"); | |
| 6 | 7 | _ = @import("behavior/basic.zig"); |
| 7 | _ = @import("behavior/generics.zig"); | |
| 8 | _ = @import("behavior/bool.zig"); | |
| 9 | _ = @import("behavior/bugs/655.zig"); | |
| 10 | _ = @import("behavior/bugs/1277.zig"); | |
| 11 | _ = @import("behavior/bugs/1741.zig"); | |
| 12 | _ = @import("behavior/bugs/2346.zig"); | |
| 13 | _ = @import("behavior/bugs/2692.zig"); | |
| 14 | _ = @import("behavior/bugs/4769_a.zig"); | |
| 15 | _ = @import("behavior/bugs/4769_b.zig"); | |
| 16 | _ = @import("behavior/bugs/6850.zig"); | |
| 17 | _ = @import("behavior/bugs/9584.zig"); | |
| 18 | _ = @import("behavior/cast.zig"); | |
| 8 | 19 | _ = @import("behavior/eval.zig"); |
| 9 | _ = @import("behavior/pointers.zig"); | |
| 20 | _ = @import("behavior/generics.zig"); | |
| 10 | 21 | _ = @import("behavior/if.zig"); |
| 11 | _ = @import("behavior/cast.zig"); | |
| 12 | _ = @import("behavior/array.zig"); | |
| 22 | _ = @import("behavior/math.zig"); | |
| 23 | _ = @import("behavior/member_func.zig"); | |
| 24 | _ = @import("behavior/pointers.zig"); | |
| 25 | _ = @import("behavior/sizeof_and_typeof.zig"); | |
| 26 | _ = @import("behavior/struct.zig"); | |
| 27 | _ = @import("behavior/this.zig"); | |
| 28 | _ = @import("behavior/translate_c_macros.zig"); | |
| 29 | _ = @import("behavior/union.zig"); | |
| 13 | 30 | _ = @import("behavior/usingnamespace.zig"); |
| 14 | _ = @import("behavior/atomics.zig"); | |
| 31 | _ = @import("behavior/widening.zig"); | |
| 15 | 32 | |
| 16 | if (!builtin.zig_is_stage2) { | |
| 17 | // Tests that only pass for stage1. | |
| 33 | if (builtin.zig_is_stage2) { | |
| 34 | // When all comptime_memory.zig tests pass, #9646 can be closed. | |
| 35 | // _ = @import("behavior/comptime_memory.zig"); | |
| 36 | } else { | |
| 18 | 37 | _ = @import("behavior/align.zig"); |
| 19 | 38 | _ = @import("behavior/alignof.zig"); |
| 20 | 39 | _ = @import("behavior/array_stage1.zig"); |
| ... | ... | @@ -22,16 +41,25 @@ test { |
| 22 | 41 | _ = @import("behavior/asm.zig"); |
| 23 | 42 | _ = @import("behavior/async_fn.zig"); |
| 24 | 43 | } |
| 25 | _ = @import("behavior/atomics_stage1.zig"); | |
| 26 | 44 | _ = @import("behavior/await_struct.zig"); |
| 27 | 45 | _ = @import("behavior/bit_shifting.zig"); |
| 28 | 46 | _ = @import("behavior/bitcast.zig"); |
| 29 | 47 | _ = @import("behavior/bitreverse.zig"); |
| 48 | _ = @import("behavior/bugs/394.zig"); | |
| 49 | _ = @import("behavior/bugs/421.zig"); | |
| 50 | _ = @import("behavior/bugs/529.zig"); | |
| 51 | _ = @import("behavior/bugs/624.zig"); | |
| 52 | _ = @import("behavior/bugs/656.zig"); | |
| 53 | _ = @import("behavior/bugs/679.zig"); | |
| 54 | _ = @import("behavior/bugs/704.zig"); | |
| 55 | _ = @import("behavior/bugs/718.zig"); | |
| 56 | _ = @import("behavior/bugs/726.zig"); | |
| 57 | _ = @import("behavior/bugs/828.zig"); | |
| 58 | _ = @import("behavior/bugs/920.zig"); | |
| 30 | 59 | _ = @import("behavior/bugs/1025.zig"); |
| 31 | 60 | _ = @import("behavior/bugs/1076.zig"); |
| 32 | 61 | _ = @import("behavior/bugs/1111.zig"); |
| 33 | 62 | _ = @import("behavior/bugs/1120.zig"); |
| 34 | _ = @import("behavior/bugs/1277.zig"); | |
| 35 | 63 | _ = @import("behavior/bugs/1310.zig"); |
| 36 | 64 | _ = @import("behavior/bugs/1322.zig"); |
| 37 | 65 | _ = @import("behavior/bugs/1381.zig"); |
| ... | ... | @@ -41,14 +69,11 @@ test { |
| 41 | 69 | _ = @import("behavior/bugs/1500.zig"); |
| 42 | 70 | _ = @import("behavior/bugs/1607.zig"); |
| 43 | 71 | _ = @import("behavior/bugs/1735.zig"); |
| 44 | _ = @import("behavior/bugs/1741.zig"); | |
| 45 | 72 | _ = @import("behavior/bugs/1851.zig"); |
| 46 | 73 | _ = @import("behavior/bugs/1914.zig"); |
| 47 | 74 | _ = @import("behavior/bugs/2006.zig"); |
| 48 | 75 | _ = @import("behavior/bugs/2114.zig"); |
| 49 | _ = @import("behavior/bugs/2346.zig"); | |
| 50 | 76 | _ = @import("behavior/bugs/2578.zig"); |
| 51 | _ = @import("behavior/bugs/2692.zig"); | |
| 52 | 77 | _ = @import("behavior/bugs/2889.zig"); |
| 53 | 78 | _ = @import("behavior/bugs/3007.zig"); |
| 54 | 79 | _ = @import("behavior/bugs/3046.zig"); |
| ... | ... | @@ -60,8 +85,6 @@ test { |
| 60 | 85 | _ = @import("behavior/bugs/3779.zig"); |
| 61 | 86 | _ = @import("behavior/bugs/4328.zig"); |
| 62 | 87 | _ = @import("behavior/bugs/4560.zig"); |
| 63 | _ = @import("behavior/bugs/4769_a.zig"); | |
| 64 | _ = @import("behavior/bugs/4769_b.zig"); | |
| 65 | 88 | _ = @import("behavior/bugs/4954.zig"); |
| 66 | 89 | _ = @import("behavior/bugs/5398.zig"); |
| 67 | 90 | _ = @import("behavior/bugs/5413.zig"); |
| ... | ... | @@ -69,30 +92,14 @@ test { |
| 69 | 92 | _ = @import("behavior/bugs/5487.zig"); |
| 70 | 93 | _ = @import("behavior/bugs/6456.zig"); |
| 71 | 94 | _ = @import("behavior/bugs/6781.zig"); |
| 72 | _ = @import("behavior/bugs/6850.zig"); | |
| 73 | 95 | _ = @import("behavior/bugs/7027.zig"); |
| 74 | 96 | _ = @import("behavior/bugs/7047.zig"); |
| 75 | 97 | _ = @import("behavior/bugs/7003.zig"); |
| 76 | 98 | _ = @import("behavior/bugs/7250.zig"); |
| 77 | _ = @import("behavior/bugs/9584.zig"); | |
| 78 | _ = @import("behavior/bugs/394.zig"); | |
| 79 | _ = @import("behavior/bugs/421.zig"); | |
| 80 | _ = @import("behavior/bugs/529.zig"); | |
| 81 | _ = @import("behavior/bugs/624.zig"); | |
| 82 | _ = @import("behavior/bugs/655.zig"); | |
| 83 | _ = @import("behavior/bugs/656.zig"); | |
| 84 | _ = @import("behavior/bugs/679.zig"); | |
| 85 | _ = @import("behavior/bugs/704.zig"); | |
| 86 | _ = @import("behavior/bugs/718.zig"); | |
| 87 | _ = @import("behavior/bugs/726.zig"); | |
| 88 | _ = @import("behavior/bugs/828.zig"); | |
| 89 | _ = @import("behavior/bugs/920.zig"); | |
| 90 | 99 | _ = @import("behavior/byteswap.zig"); |
| 91 | 100 | _ = @import("behavior/byval_arg_var.zig"); |
| 92 | 101 | _ = @import("behavior/call.zig"); |
| 93 | 102 | _ = @import("behavior/cast_stage1.zig"); |
| 94 | // When these tests pass, #9646 can be closed. | |
| 95 | // _ = @import("behavior/comptime_memory.zig"); | |
| 96 | 103 | _ = @import("behavior/const_slice_child.zig"); |
| 97 | 104 | _ = @import("behavior/defer.zig"); |
| 98 | 105 | _ = @import("behavior/enum.zig"); |
| ... | ... | @@ -113,7 +120,7 @@ test { |
| 113 | 120 | _ = @import("behavior/incomplete_struct_param_tld.zig"); |
| 114 | 121 | _ = @import("behavior/inttoptr.zig"); |
| 115 | 122 | _ = @import("behavior/ir_block_deps.zig"); |
| 116 | _ = @import("behavior/math.zig"); | |
| 123 | _ = @import("behavior/math_stage1.zig"); | |
| 117 | 124 | _ = @import("behavior/maximum_minimum.zig"); |
| 118 | 125 | _ = @import("behavior/merge_error_sets.zig"); |
| 119 | 126 | _ = @import("behavior/misc.zig"); |
| ... | ... | @@ -130,16 +137,15 @@ test { |
| 130 | 137 | _ = @import("behavior/saturating_arithmetic.zig"); |
| 131 | 138 | _ = @import("behavior/shuffle.zig"); |
| 132 | 139 | _ = @import("behavior/select.zig"); |
| 133 | _ = @import("behavior/sizeof_and_typeof.zig"); | |
| 140 | _ = @import("behavior/sizeof_and_typeof_stage1.zig"); | |
| 134 | 141 | _ = @import("behavior/slice.zig"); |
| 135 | 142 | _ = @import("behavior/slice_sentinel_comptime.zig"); |
| 136 | _ = @import("behavior/struct.zig"); | |
| 143 | _ = @import("behavior/struct_stage1.zig"); | |
| 137 | 144 | _ = @import("behavior/struct_contains_null_ptr_itself.zig"); |
| 138 | 145 | _ = @import("behavior/struct_contains_slice_of_itself.zig"); |
| 139 | 146 | _ = @import("behavior/switch.zig"); |
| 140 | 147 | _ = @import("behavior/switch_prong_err_enum.zig"); |
| 141 | 148 | _ = @import("behavior/switch_prong_implicit_cast.zig"); |
| 142 | _ = @import("behavior/this.zig"); | |
| 143 | 149 | _ = @import("behavior/truncate.zig"); |
| 144 | 150 | _ = @import("behavior/try.zig"); |
| 145 | 151 | _ = @import("behavior/tuple.zig"); |
| ... | ... | @@ -148,7 +154,7 @@ test { |
| 148 | 154 | _ = @import("behavior/typename.zig"); |
| 149 | 155 | _ = @import("behavior/undefined.zig"); |
| 150 | 156 | _ = @import("behavior/underscore.zig"); |
| 151 | _ = @import("behavior/union.zig"); | |
| 157 | _ = @import("behavior/union_stage1.zig"); | |
| 152 | 158 | _ = @import("behavior/usingnamespace_stage1.zig"); |
| 153 | 159 | _ = @import("behavior/var_args.zig"); |
| 154 | 160 | _ = @import("behavior/vector.zig"); |
| ... | ... | @@ -157,8 +163,7 @@ test { |
| 157 | 163 | _ = @import("behavior/wasm.zig"); |
| 158 | 164 | } |
| 159 | 165 | _ = @import("behavior/while.zig"); |
| 160 | _ = @import("behavior/widening.zig"); | |
| 161 | 166 | _ = @import("behavior/src.zig"); |
| 162 | _ = @import("behavior/translate_c_macros.zig"); | |
| 167 | _ = @import("behavior/translate_c_macros_stage1.zig"); | |
| 163 | 168 | } |
| 164 | 169 | } |
test/behavior/array.zig+6| ... | ... | @@ -27,3 +27,9 @@ test "arrays" { |
| 27 | 27 | fn getArrayLen(a: []const u32) usize { |
| 28 | 28 | return a.len; |
| 29 | 29 | } |
| 30 | ||
| 31 | test "array init with mult" { | |
| 32 | const a = 'a'; | |
| 33 | var i: [8]u8 = [2]u8{ a, 'b' } ** 4; | |
| 34 | try expect(std.mem.eql(u8, &i, "abababab")); | |
| 35 | } |
test/behavior/array_stage1.zig+1-1| ... | ... | @@ -140,7 +140,7 @@ fn testArrayByValAtComptime(b: [2]u8) u8 { |
| 140 | 140 | return b[0]; |
| 141 | 141 | } |
| 142 | 142 | |
| 143 | test "comptime evalutating function that takes array by value" { | |
| 143 | test "comptime evaluating function that takes array by value" { | |
| 144 | 144 | const arr = [_]u8{ 0, 1 }; |
| 145 | 145 | _ = comptime testArrayByValAtComptime(arr); |
| 146 | 146 | _ = comptime testArrayByValAtComptime(arr); |
test/behavior/atomics.zig+195| ... | ... | @@ -24,3 +24,198 @@ fn testCmpxchg() !void { |
| 24 | 24 | try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null); |
| 25 | 25 | try expect(x == 42); |
| 26 | 26 | } |
| 27 | ||
| 28 | test "fence" { | |
| 29 | var x: i32 = 1234; | |
| 30 | @fence(.SeqCst); | |
| 31 | x = 5678; | |
| 32 | } | |
| 33 | ||
| 34 | test "atomicrmw and atomicload" { | |
| 35 | var data: u8 = 200; | |
| 36 | try testAtomicRmw(&data); | |
| 37 | try expect(data == 42); | |
| 38 | try testAtomicLoad(&data); | |
| 39 | } | |
| 40 | ||
| 41 | fn testAtomicRmw(ptr: *u8) !void { | |
| 42 | const prev_value = @atomicRmw(u8, ptr, .Xchg, 42, .SeqCst); | |
| 43 | try expect(prev_value == 200); | |
| 44 | comptime { | |
| 45 | var x: i32 = 1234; | |
| 46 | const y: i32 = 12345; | |
| 47 | try expect(@atomicLoad(i32, &x, .SeqCst) == 1234); | |
| 48 | try expect(@atomicLoad(i32, &y, .SeqCst) == 12345); | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | fn testAtomicLoad(ptr: *u8) !void { | |
| 53 | const x = @atomicLoad(u8, ptr, .SeqCst); | |
| 54 | try expect(x == 42); | |
| 55 | } | |
| 56 | ||
| 57 | test "cmpxchg with ptr" { | |
| 58 | var data1: i32 = 1234; | |
| 59 | var data2: i32 = 5678; | |
| 60 | var data3: i32 = 9101; | |
| 61 | var x: *i32 = &data1; | |
| 62 | if (@cmpxchgWeak(*i32, &x, &data2, &data3, .SeqCst, .SeqCst)) |x1| { | |
| 63 | try expect(x1 == &data1); | |
| 64 | } else { | |
| 65 | @panic("cmpxchg should have failed"); | |
| 66 | } | |
| 67 | ||
| 68 | while (@cmpxchgWeak(*i32, &x, &data1, &data3, .SeqCst, .SeqCst)) |x1| { | |
| 69 | try expect(x1 == &data1); | |
| 70 | } | |
| 71 | try expect(x == &data3); | |
| 72 | ||
| 73 | try expect(@cmpxchgStrong(*i32, &x, &data3, &data2, .SeqCst, .SeqCst) == null); | |
| 74 | try expect(x == &data2); | |
| 75 | } | |
| 76 | ||
| 77 | test "cmpxchg with ignored result" { | |
| 78 | var x: i32 = 1234; | |
| 79 | ||
| 80 | _ = @cmpxchgStrong(i32, &x, 1234, 5678, .Monotonic, .Monotonic); | |
| 81 | ||
| 82 | try expect(5678 == x); | |
| 83 | } | |
| 84 | ||
| 85 | test "128-bit cmpxchg" { | |
| 86 | try test_u128_cmpxchg(); | |
| 87 | comptime try test_u128_cmpxchg(); | |
| 88 | } | |
| 89 | ||
| 90 | fn test_u128_cmpxchg() !void { | |
| 91 | if (builtin.zig_is_stage2) { | |
| 92 | if (builtin.stage2_arch != .x86_64) return error.SkipZigTest; | |
| 93 | if (!builtin.stage2_x86_cx16) return error.SkipZigTest; | |
| 94 | } else { | |
| 95 | if (builtin.cpu.arch != .x86_64) return error.SkipZigTest; | |
| 96 | if (comptime !std.Target.x86.featureSetHas(builtin.cpu.features, .cx16)) return error.SkipZigTest; | |
| 97 | } | |
| 98 | ||
| 99 | var x: u128 = 1234; | |
| 100 | if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { | |
| 101 | try expect(x1 == 1234); | |
| 102 | } else { | |
| 103 | @panic("cmpxchg should have failed"); | |
| 104 | } | |
| 105 | ||
| 106 | while (@cmpxchgWeak(u128, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { | |
| 107 | try expect(x1 == 1234); | |
| 108 | } | |
| 109 | try expect(x == 5678); | |
| 110 | ||
| 111 | try expect(@cmpxchgStrong(u128, &x, 5678, 42, .SeqCst, .SeqCst) == null); | |
| 112 | try expect(x == 42); | |
| 113 | } | |
| 114 | ||
| 115 | var a_global_variable = @as(u32, 1234); | |
| 116 | ||
| 117 | test "cmpxchg on a global variable" { | |
| 118 | _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic); | |
| 119 | try expect(a_global_variable == 42); | |
| 120 | } | |
| 121 | ||
| 122 | test "atomic load and rmw with enum" { | |
| 123 | const Value = enum(u8) { a, b, c }; | |
| 124 | var x = Value.a; | |
| 125 | ||
| 126 | try expect(@atomicLoad(Value, &x, .SeqCst) != .b); | |
| 127 | ||
| 128 | _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst); | |
| 129 | try expect(@atomicLoad(Value, &x, .SeqCst) == .c); | |
| 130 | try expect(@atomicLoad(Value, &x, .SeqCst) != .a); | |
| 131 | try expect(@atomicLoad(Value, &x, .SeqCst) != .b); | |
| 132 | } | |
| 133 | ||
| 134 | test "atomic store" { | |
| 135 | var x: u32 = 0; | |
| 136 | @atomicStore(u32, &x, 1, .SeqCst); | |
| 137 | try expect(@atomicLoad(u32, &x, .SeqCst) == 1); | |
| 138 | @atomicStore(u32, &x, 12345678, .SeqCst); | |
| 139 | try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); | |
| 140 | } | |
| 141 | ||
| 142 | test "atomic store comptime" { | |
| 143 | comptime try testAtomicStore(); | |
| 144 | try testAtomicStore(); | |
| 145 | } | |
| 146 | ||
| 147 | fn testAtomicStore() !void { | |
| 148 | var x: u32 = 0; | |
| 149 | @atomicStore(u32, &x, 1, .SeqCst); | |
| 150 | try expect(@atomicLoad(u32, &x, .SeqCst) == 1); | |
| 151 | @atomicStore(u32, &x, 12345678, .SeqCst); | |
| 152 | try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); | |
| 153 | } | |
| 154 | ||
| 155 | test "atomicrmw with floats" { | |
| 156 | try testAtomicRmwFloat(); | |
| 157 | comptime try testAtomicRmwFloat(); | |
| 158 | } | |
| 159 | ||
| 160 | fn testAtomicRmwFloat() !void { | |
| 161 | var x: f32 = 0; | |
| 162 | try expect(x == 0); | |
| 163 | _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst); | |
| 164 | try expect(x == 1); | |
| 165 | _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst); | |
| 166 | try expect(x == 6); | |
| 167 | _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst); | |
| 168 | try expect(x == 4); | |
| 169 | } | |
| 170 | ||
| 171 | test "atomicrmw with ints" { | |
| 172 | try testAtomicRmwInt(); | |
| 173 | comptime try testAtomicRmwInt(); | |
| 174 | } | |
| 175 | ||
| 176 | fn testAtomicRmwInt() !void { | |
| 177 | var x: u8 = 1; | |
| 178 | var res = @atomicRmw(u8, &x, .Xchg, 3, .SeqCst); | |
| 179 | try expect(x == 3 and res == 1); | |
| 180 | _ = @atomicRmw(u8, &x, .Add, 3, .SeqCst); | |
| 181 | try expect(x == 6); | |
| 182 | _ = @atomicRmw(u8, &x, .Sub, 1, .SeqCst); | |
| 183 | try expect(x == 5); | |
| 184 | _ = @atomicRmw(u8, &x, .And, 4, .SeqCst); | |
| 185 | try expect(x == 4); | |
| 186 | _ = @atomicRmw(u8, &x, .Nand, 4, .SeqCst); | |
| 187 | try expect(x == 0xfb); | |
| 188 | _ = @atomicRmw(u8, &x, .Or, 6, .SeqCst); | |
| 189 | try expect(x == 0xff); | |
| 190 | _ = @atomicRmw(u8, &x, .Xor, 2, .SeqCst); | |
| 191 | try expect(x == 0xfd); | |
| 192 | ||
| 193 | _ = @atomicRmw(u8, &x, .Max, 1, .SeqCst); | |
| 194 | try expect(x == 0xfd); | |
| 195 | _ = @atomicRmw(u8, &x, .Min, 1, .SeqCst); | |
| 196 | try expect(x == 1); | |
| 197 | } | |
| 198 | ||
| 199 | test "atomics with different types" { | |
| 200 | try testAtomicsWithType(bool, true, false); | |
| 201 | ||
| 202 | try testAtomicsWithType(u1, 0, 1); | |
| 203 | try testAtomicsWithType(i4, 0, 1); | |
| 204 | try testAtomicsWithType(u5, 0, 1); | |
| 205 | try testAtomicsWithType(i15, 0, 1); | |
| 206 | try testAtomicsWithType(u24, 0, 1); | |
| 207 | ||
| 208 | try testAtomicsWithType(u0, 0, 0); | |
| 209 | try testAtomicsWithType(i0, 0, 0); | |
| 210 | } | |
| 211 | ||
| 212 | fn testAtomicsWithType(comptime T: type, a: T, b: T) !void { | |
| 213 | var x: T = b; | |
| 214 | @atomicStore(T, &x, a, .SeqCst); | |
| 215 | try expect(x == a); | |
| 216 | try expect(@atomicLoad(T, &x, .SeqCst) == a); | |
| 217 | try expect(@atomicRmw(T, &x, .Xchg, b, .SeqCst) == a); | |
| 218 | try expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst) == null); | |
| 219 | if (@sizeOf(T) != 0) | |
| 220 | try expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst).? == a); | |
| 221 | } |
test/behavior/atomics_stage1.zig deleted-194| ... | ... | @@ -1,194 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const expectEqual = std.testing.expectEqual; | |
| 4 | const builtin = @import("builtin"); | |
| 5 | ||
| 6 | test "fence" { | |
| 7 | var x: i32 = 1234; | |
| 8 | @fence(.SeqCst); | |
| 9 | x = 5678; | |
| 10 | } | |
| 11 | ||
| 12 | test "atomicrmw and atomicload" { | |
| 13 | var data: u8 = 200; | |
| 14 | try testAtomicRmw(&data); | |
| 15 | try expect(data == 42); | |
| 16 | try testAtomicLoad(&data); | |
| 17 | } | |
| 18 | ||
| 19 | fn testAtomicRmw(ptr: *u8) !void { | |
| 20 | const prev_value = @atomicRmw(u8, ptr, .Xchg, 42, .SeqCst); | |
| 21 | try expect(prev_value == 200); | |
| 22 | comptime { | |
| 23 | var x: i32 = 1234; | |
| 24 | const y: i32 = 12345; | |
| 25 | try expect(@atomicLoad(i32, &x, .SeqCst) == 1234); | |
| 26 | try expect(@atomicLoad(i32, &y, .SeqCst) == 12345); | |
| 27 | } | |
| 28 | } | |
| 29 | ||
| 30 | fn testAtomicLoad(ptr: *u8) !void { | |
| 31 | const x = @atomicLoad(u8, ptr, .SeqCst); | |
| 32 | try expect(x == 42); | |
| 33 | } | |
| 34 | ||
| 35 | test "cmpxchg with ptr" { | |
| 36 | var data1: i32 = 1234; | |
| 37 | var data2: i32 = 5678; | |
| 38 | var data3: i32 = 9101; | |
| 39 | var x: *i32 = &data1; | |
| 40 | if (@cmpxchgWeak(*i32, &x, &data2, &data3, .SeqCst, .SeqCst)) |x1| { | |
| 41 | try expect(x1 == &data1); | |
| 42 | } else { | |
| 43 | @panic("cmpxchg should have failed"); | |
| 44 | } | |
| 45 | ||
| 46 | while (@cmpxchgWeak(*i32, &x, &data1, &data3, .SeqCst, .SeqCst)) |x1| { | |
| 47 | try expect(x1 == &data1); | |
| 48 | } | |
| 49 | try expect(x == &data3); | |
| 50 | ||
| 51 | try expect(@cmpxchgStrong(*i32, &x, &data3, &data2, .SeqCst, .SeqCst) == null); | |
| 52 | try expect(x == &data2); | |
| 53 | } | |
| 54 | ||
| 55 | test "128-bit cmpxchg" { | |
| 56 | try test_u128_cmpxchg(); | |
| 57 | comptime try test_u128_cmpxchg(); | |
| 58 | } | |
| 59 | ||
| 60 | fn test_u128_cmpxchg() !void { | |
| 61 | if (std.Target.current.cpu.arch != .x86_64) return error.SkipZigTest; | |
| 62 | if (comptime !std.Target.x86.featureSetHas(std.Target.current.cpu.features, .cx16)) return error.SkipZigTest; | |
| 63 | ||
| 64 | var x: u128 = 1234; | |
| 65 | if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { | |
| 66 | try expect(x1 == 1234); | |
| 67 | } else { | |
| 68 | @panic("cmpxchg should have failed"); | |
| 69 | } | |
| 70 | ||
| 71 | while (@cmpxchgWeak(u128, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { | |
| 72 | try expect(x1 == 1234); | |
| 73 | } | |
| 74 | try expect(x == 5678); | |
| 75 | ||
| 76 | try expect(@cmpxchgStrong(u128, &x, 5678, 42, .SeqCst, .SeqCst) == null); | |
| 77 | try expect(x == 42); | |
| 78 | } | |
| 79 | ||
| 80 | test "cmpxchg with ignored result" { | |
| 81 | var x: i32 = 1234; | |
| 82 | ||
| 83 | _ = @cmpxchgStrong(i32, &x, 1234, 5678, .Monotonic, .Monotonic); | |
| 84 | ||
| 85 | try expectEqual(@as(i32, 5678), x); | |
| 86 | } | |
| 87 | ||
| 88 | var a_global_variable = @as(u32, 1234); | |
| 89 | ||
| 90 | test "cmpxchg on a global variable" { | |
| 91 | _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic); | |
| 92 | try expectEqual(@as(u32, 42), a_global_variable); | |
| 93 | } | |
| 94 | ||
| 95 | test "atomic load and rmw with enum" { | |
| 96 | const Value = enum(u8) { | |
| 97 | a, | |
| 98 | b, | |
| 99 | c, | |
| 100 | }; | |
| 101 | var x = Value.a; | |
| 102 | ||
| 103 | try expect(@atomicLoad(Value, &x, .SeqCst) != .b); | |
| 104 | ||
| 105 | _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst); | |
| 106 | try expect(@atomicLoad(Value, &x, .SeqCst) == .c); | |
| 107 | try expect(@atomicLoad(Value, &x, .SeqCst) != .a); | |
| 108 | try expect(@atomicLoad(Value, &x, .SeqCst) != .b); | |
| 109 | } | |
| 110 | ||
| 111 | test "atomic store" { | |
| 112 | var x: u32 = 0; | |
| 113 | @atomicStore(u32, &x, 1, .SeqCst); | |
| 114 | try expect(@atomicLoad(u32, &x, .SeqCst) == 1); | |
| 115 | @atomicStore(u32, &x, 12345678, .SeqCst); | |
| 116 | try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); | |
| 117 | } | |
| 118 | ||
| 119 | test "atomic store comptime" { | |
| 120 | comptime try testAtomicStore(); | |
| 121 | try testAtomicStore(); | |
| 122 | } | |
| 123 | ||
| 124 | fn testAtomicStore() !void { | |
| 125 | var x: u32 = 0; | |
| 126 | @atomicStore(u32, &x, 1, .SeqCst); | |
| 127 | try expect(@atomicLoad(u32, &x, .SeqCst) == 1); | |
| 128 | @atomicStore(u32, &x, 12345678, .SeqCst); | |
| 129 | try expect(@atomicLoad(u32, &x, .SeqCst) == 12345678); | |
| 130 | } | |
| 131 | ||
| 132 | test "atomicrmw with floats" { | |
| 133 | try testAtomicRmwFloat(); | |
| 134 | comptime try testAtomicRmwFloat(); | |
| 135 | } | |
| 136 | ||
| 137 | fn testAtomicRmwFloat() !void { | |
| 138 | var x: f32 = 0; | |
| 139 | try expect(x == 0); | |
| 140 | _ = @atomicRmw(f32, &x, .Xchg, 1, .SeqCst); | |
| 141 | try expect(x == 1); | |
| 142 | _ = @atomicRmw(f32, &x, .Add, 5, .SeqCst); | |
| 143 | try expect(x == 6); | |
| 144 | _ = @atomicRmw(f32, &x, .Sub, 2, .SeqCst); | |
| 145 | try expect(x == 4); | |
| 146 | } | |
| 147 | ||
| 148 | test "atomicrmw with ints" { | |
| 149 | try testAtomicRmwInt(); | |
| 150 | comptime try testAtomicRmwInt(); | |
| 151 | } | |
| 152 | ||
| 153 | fn testAtomicRmwInt() !void { | |
| 154 | var x: u8 = 1; | |
| 155 | var res = @atomicRmw(u8, &x, .Xchg, 3, .SeqCst); | |
| 156 | try expect(x == 3 and res == 1); | |
| 157 | _ = @atomicRmw(u8, &x, .Add, 3, .SeqCst); | |
| 158 | try expect(x == 6); | |
| 159 | _ = @atomicRmw(u8, &x, .Sub, 1, .SeqCst); | |
| 160 | try expect(x == 5); | |
| 161 | _ = @atomicRmw(u8, &x, .And, 4, .SeqCst); | |
| 162 | try expect(x == 4); | |
| 163 | _ = @atomicRmw(u8, &x, .Nand, 4, .SeqCst); | |
| 164 | try expect(x == 0xfb); | |
| 165 | _ = @atomicRmw(u8, &x, .Or, 6, .SeqCst); | |
| 166 | try expect(x == 0xff); | |
| 167 | _ = @atomicRmw(u8, &x, .Xor, 2, .SeqCst); | |
| 168 | try expect(x == 0xfd); | |
| 169 | ||
| 170 | _ = @atomicRmw(u8, &x, .Max, 1, .SeqCst); | |
| 171 | try expect(x == 0xfd); | |
| 172 | _ = @atomicRmw(u8, &x, .Min, 1, .SeqCst); | |
| 173 | try expect(x == 1); | |
| 174 | } | |
| 175 | ||
| 176 | test "atomics with different types" { | |
| 177 | try testAtomicsWithType(bool, true, false); | |
| 178 | inline for (.{ u1, i4, u5, i15, u24 }) |T| { | |
| 179 | try testAtomicsWithType(T, 0, 1); | |
| 180 | } | |
| 181 | try testAtomicsWithType(u0, 0, 0); | |
| 182 | try testAtomicsWithType(i0, 0, 0); | |
| 183 | } | |
| 184 | ||
| 185 | fn testAtomicsWithType(comptime T: type, a: T, b: T) !void { | |
| 186 | var x: T = b; | |
| 187 | @atomicStore(T, &x, a, .SeqCst); | |
| 188 | try expect(x == a); | |
| 189 | try expect(@atomicLoad(T, &x, .SeqCst) == a); | |
| 190 | try expect(@atomicRmw(T, &x, .Xchg, b, .SeqCst) == a); | |
| 191 | try expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst) == null); | |
| 192 | if (@sizeOf(T) != 0) | |
| 193 | try expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst).? == a); | |
| 194 | } |
test/behavior/basic.zig+18| ... | ... | @@ -170,3 +170,21 @@ test "string concatenation" { |
| 170 | 170 | test "array mult operator" { |
| 171 | 171 | try expect(mem.eql(u8, "ab" ** 5, "ababababab")); |
| 172 | 172 | } |
| 173 | ||
| 174 | test "memcpy and memset intrinsics" { | |
| 175 | try testMemcpyMemset(); | |
| 176 | // TODO add comptime test coverage | |
| 177 | //comptime try testMemcpyMemset(); | |
| 178 | } | |
| 179 | ||
| 180 | fn testMemcpyMemset() !void { | |
| 181 | var foo: [20]u8 = undefined; | |
| 182 | var bar: [20]u8 = undefined; | |
| 183 | ||
| 184 | @memset(&foo, 'A', foo.len); | |
| 185 | @memcpy(&bar, &foo, bar.len); | |
| 186 | ||
| 187 | try expect(bar[0] == 'A'); | |
| 188 | try expect(bar[11] == 'A'); | |
| 189 | try expect(bar[19] == 'A'); | |
| 190 | } |
test/behavior/bugs/1735.zig+1-1| ... | ... | @@ -40,7 +40,7 @@ const a = struct { |
| 40 | 40 | } |
| 41 | 41 | }; |
| 42 | 42 | |
| 43 | test "intialization" { | |
| 43 | test "initialization" { | |
| 44 | 44 | var t = a.init(); |
| 45 | 45 | try std.testing.expect(t.foo.len == 0); |
| 46 | 46 | } |
test/behavior/eval.zig+28| ... | ... | @@ -155,3 +155,31 @@ fn MakeType(comptime T: type) type { |
| 155 | 155 | field: T, |
| 156 | 156 | }; |
| 157 | 157 | } |
| 158 | ||
| 159 | test "try to trick eval with runtime if" { | |
| 160 | try expect(testTryToTrickEvalWithRuntimeIf(true) == 10); | |
| 161 | } | |
| 162 | ||
| 163 | fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { | |
| 164 | comptime var i: usize = 0; | |
| 165 | inline while (i < 10) : (i += 1) { | |
| 166 | const result = if (b) false else true; | |
| 167 | _ = result; | |
| 168 | } | |
| 169 | comptime { | |
| 170 | return i; | |
| 171 | } | |
| 172 | } | |
| 173 | ||
| 174 | test "@setEvalBranchQuota" { | |
| 175 | comptime { | |
| 176 | // 1001 for the loop and then 1 more for the expect fn call | |
| 177 | @setEvalBranchQuota(1002); | |
| 178 | var i = 0; | |
| 179 | var sum = 0; | |
| 180 | while (i < 1001) : (i += 1) { | |
| 181 | sum += i; | |
| 182 | } | |
| 183 | try expect(sum == 500500); | |
| 184 | } | |
| 185 | } |
test/behavior/eval_stage1.zig+1-29| ... | ... | @@ -88,7 +88,7 @@ var st_init_str_foo = StInitStrFoo{ |
| 88 | 88 | .y = true, |
| 89 | 89 | }; |
| 90 | 90 | |
| 91 | test "statically initalized array literal" { | |
| 91 | test "statically initialized array literal" { | |
| 92 | 92 | const y: [4]u8 = st_init_arr_lit_x; |
| 93 | 93 | try expect(y[3] == 4); |
| 94 | 94 | } |
| ... | ... | @@ -109,21 +109,6 @@ test "const slice" { |
| 109 | 109 | } |
| 110 | 110 | } |
| 111 | 111 | |
| 112 | test "try to trick eval with runtime if" { | |
| 113 | try expect(testTryToTrickEvalWithRuntimeIf(true) == 10); | |
| 114 | } | |
| 115 | ||
| 116 | fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { | |
| 117 | comptime var i: usize = 0; | |
| 118 | inline while (i < 10) : (i += 1) { | |
| 119 | const result = if (b) false else true; | |
| 120 | _ = result; | |
| 121 | } | |
| 122 | comptime { | |
| 123 | return i; | |
| 124 | } | |
| 125 | } | |
| 126 | ||
| 127 | 112 | test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" { |
| 128 | 113 | var runtime = [1]i32{3}; |
| 129 | 114 | comptime var i: usize = 0; |
| ... | ... | @@ -276,19 +261,6 @@ fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void { |
| 276 | 261 | try expect(ptr1 == ptr2); |
| 277 | 262 | } |
| 278 | 263 | |
| 279 | test "@setEvalBranchQuota" { | |
| 280 | comptime { | |
| 281 | // 1001 for the loop and then 1 more for the expect fn call | |
| 282 | @setEvalBranchQuota(1002); | |
| 283 | var i = 0; | |
| 284 | var sum = 0; | |
| 285 | while (i < 1001) : (i += 1) { | |
| 286 | sum += i; | |
| 287 | } | |
| 288 | try expect(sum == 500500); | |
| 289 | } | |
| 290 | } | |
| 291 | ||
| 292 | 264 | test "float literal at compile time not lossy" { |
| 293 | 265 | try expect(16777216.0 + 1.0 == 16777217.0); |
| 294 | 266 | try expect(9007199254740992.0 + 1.0 == 9007199254740993.0); |
test/behavior/generics.zig+16| ... | ... | @@ -118,3 +118,19 @@ pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type { |
| 118 | 118 | prealloc_items: [STATIC_SIZE]T, |
| 119 | 119 | }; |
| 120 | 120 | } |
| 121 | ||
| 122 | test "const decls in struct" { | |
| 123 | try expect(GenericDataThing(3).count_plus_one == 4); | |
| 124 | } | |
| 125 | fn GenericDataThing(comptime count: isize) type { | |
| 126 | return struct { | |
| 127 | const count_plus_one = count + 1; | |
| 128 | }; | |
| 129 | } | |
| 130 | ||
| 131 | test "use generic param in generic param" { | |
| 132 | try expect(aGenericFn(i32, 3, 4) == 7); | |
| 133 | } | |
| 134 | fn aGenericFn(comptime T: type, comptime a: T, b: T) T { | |
| 135 | return a + b; | |
| 136 | } |
test/behavior/generics_stage1.zig-16| ... | ... | @@ -26,22 +26,6 @@ fn GenNode(comptime T: type) type { |
| 26 | 26 | }; |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | test "const decls in struct" { | |
| 30 | try expect(GenericDataThing(3).count_plus_one == 4); | |
| 31 | } | |
| 32 | fn GenericDataThing(comptime count: isize) type { | |
| 33 | return struct { | |
| 34 | const count_plus_one = count + 1; | |
| 35 | }; | |
| 36 | } | |
| 37 | ||
| 38 | test "use generic param in generic param" { | |
| 39 | try expect(aGenericFn(i32, 3, 4) == 7); | |
| 40 | } | |
| 41 | fn aGenericFn(comptime T: type, comptime a: T, b: T) T { | |
| 42 | return a + b; | |
| 43 | } | |
| 44 | ||
| 45 | 29 | test "generic fn with implicit cast" { |
| 46 | 30 | try expect(getFirstByte(u8, &[_]u8{13}) == 13); |
| 47 | 31 | try expect(getFirstByte(u16, &[_]u16{ |
test/behavior/math.zig+33-699| ... | ... | @@ -6,171 +6,6 @@ const maxInt = std.math.maxInt; |
| 6 | 6 | const minInt = std.math.minInt; |
| 7 | 7 | const mem = std.mem; |
| 8 | 8 | |
| 9 | test "division" { | |
| 10 | try testDivision(); | |
| 11 | comptime try testDivision(); | |
| 12 | } | |
| 13 | fn testDivision() !void { | |
| 14 | try expect(div(u32, 13, 3) == 4); | |
| 15 | try expect(div(f16, 1.0, 2.0) == 0.5); | |
| 16 | try expect(div(f32, 1.0, 2.0) == 0.5); | |
| 17 | ||
| 18 | try expect(divExact(u32, 55, 11) == 5); | |
| 19 | try expect(divExact(i32, -55, 11) == -5); | |
| 20 | try expect(divExact(f16, 55.0, 11.0) == 5.0); | |
| 21 | try expect(divExact(f16, -55.0, 11.0) == -5.0); | |
| 22 | try expect(divExact(f32, 55.0, 11.0) == 5.0); | |
| 23 | try expect(divExact(f32, -55.0, 11.0) == -5.0); | |
| 24 | ||
| 25 | try expect(divFloor(i32, 5, 3) == 1); | |
| 26 | try expect(divFloor(i32, -5, 3) == -2); | |
| 27 | try expect(divFloor(f16, 5.0, 3.0) == 1.0); | |
| 28 | try expect(divFloor(f16, -5.0, 3.0) == -2.0); | |
| 29 | try expect(divFloor(f32, 5.0, 3.0) == 1.0); | |
| 30 | try expect(divFloor(f32, -5.0, 3.0) == -2.0); | |
| 31 | try expect(divFloor(i32, -0x80000000, -2) == 0x40000000); | |
| 32 | try expect(divFloor(i32, 0, -0x80000000) == 0); | |
| 33 | try expect(divFloor(i32, -0x40000001, 0x40000000) == -2); | |
| 34 | try expect(divFloor(i32, -0x80000000, 1) == -0x80000000); | |
| 35 | try expect(divFloor(i32, 10, 12) == 0); | |
| 36 | try expect(divFloor(i32, -14, 12) == -2); | |
| 37 | try expect(divFloor(i32, -2, 12) == -1); | |
| 38 | ||
| 39 | try expect(divTrunc(i32, 5, 3) == 1); | |
| 40 | try expect(divTrunc(i32, -5, 3) == -1); | |
| 41 | try expect(divTrunc(f16, 5.0, 3.0) == 1.0); | |
| 42 | try expect(divTrunc(f16, -5.0, 3.0) == -1.0); | |
| 43 | try expect(divTrunc(f32, 5.0, 3.0) == 1.0); | |
| 44 | try expect(divTrunc(f32, -5.0, 3.0) == -1.0); | |
| 45 | try expect(divTrunc(f64, 5.0, 3.0) == 1.0); | |
| 46 | try expect(divTrunc(f64, -5.0, 3.0) == -1.0); | |
| 47 | try expect(divTrunc(i32, 10, 12) == 0); | |
| 48 | try expect(divTrunc(i32, -14, 12) == -1); | |
| 49 | try expect(divTrunc(i32, -2, 12) == 0); | |
| 50 | ||
| 51 | try expect(mod(i32, 10, 12) == 10); | |
| 52 | try expect(mod(i32, -14, 12) == 10); | |
| 53 | try expect(mod(i32, -2, 12) == 10); | |
| 54 | ||
| 55 | comptime { | |
| 56 | try expect( | |
| 57 | 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, | |
| 58 | ); | |
| 59 | try expect( | |
| 60 | @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, | |
| 61 | ); | |
| 62 | try expect( | |
| 63 | 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, | |
| 64 | ); | |
| 65 | try expect( | |
| 66 | @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, | |
| 67 | ); | |
| 68 | try expect( | |
| 69 | @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, | |
| 70 | ); | |
| 71 | try expect( | |
| 72 | @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, | |
| 73 | ); | |
| 74 | try expect( | |
| 75 | 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, | |
| 76 | ); | |
| 77 | } | |
| 78 | } | |
| 79 | fn div(comptime T: type, a: T, b: T) T { | |
| 80 | return a / b; | |
| 81 | } | |
| 82 | fn divExact(comptime T: type, a: T, b: T) T { | |
| 83 | return @divExact(a, b); | |
| 84 | } | |
| 85 | fn divFloor(comptime T: type, a: T, b: T) T { | |
| 86 | return @divFloor(a, b); | |
| 87 | } | |
| 88 | fn divTrunc(comptime T: type, a: T, b: T) T { | |
| 89 | return @divTrunc(a, b); | |
| 90 | } | |
| 91 | fn mod(comptime T: type, a: T, b: T) T { | |
| 92 | return @mod(a, b); | |
| 93 | } | |
| 94 | ||
| 95 | test "@addWithOverflow" { | |
| 96 | var result: u8 = undefined; | |
| 97 | try expect(@addWithOverflow(u8, 250, 100, &result)); | |
| 98 | try expect(!@addWithOverflow(u8, 100, 150, &result)); | |
| 99 | try expect(result == 250); | |
| 100 | } | |
| 101 | ||
| 102 | // TODO test mulWithOverflow | |
| 103 | // TODO test subWithOverflow | |
| 104 | ||
| 105 | test "@shlWithOverflow" { | |
| 106 | var result: u16 = undefined; | |
| 107 | try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); | |
| 108 | try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); | |
| 109 | try expect(result == 0b1011111111111100); | |
| 110 | } | |
| 111 | ||
| 112 | test "@*WithOverflow with u0 values" { | |
| 113 | var result: u0 = undefined; | |
| 114 | try expect(!@addWithOverflow(u0, 0, 0, &result)); | |
| 115 | try expect(!@subWithOverflow(u0, 0, 0, &result)); | |
| 116 | try expect(!@mulWithOverflow(u0, 0, 0, &result)); | |
| 117 | try expect(!@shlWithOverflow(u0, 0, 0, &result)); | |
| 118 | } | |
| 119 | ||
| 120 | test "@clz" { | |
| 121 | try testClz(); | |
| 122 | comptime try testClz(); | |
| 123 | } | |
| 124 | ||
| 125 | fn testClz() !void { | |
| 126 | try expect(@clz(u8, 0b10001010) == 0); | |
| 127 | try expect(@clz(u8, 0b00001010) == 4); | |
| 128 | try expect(@clz(u8, 0b00011010) == 3); | |
| 129 | try expect(@clz(u8, 0b00000000) == 8); | |
| 130 | try expect(@clz(u128, 0xffffffffffffffff) == 64); | |
| 131 | try expect(@clz(u128, 0x10000000000000000) == 63); | |
| 132 | } | |
| 133 | ||
| 134 | test "@clz vectors" { | |
| 135 | try testClzVectors(); | |
| 136 | comptime try testClzVectors(); | |
| 137 | } | |
| 138 | ||
| 139 | fn testClzVectors() !void { | |
| 140 | @setEvalBranchQuota(10_000); | |
| 141 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 0))); | |
| 142 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00001010))), @splat(64, @as(u4, 4))); | |
| 143 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00011010))), @splat(64, @as(u4, 3))); | |
| 144 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8))); | |
| 145 | try expectEqual(@clz(u128, @splat(64, @as(u128, 0xffffffffffffffff))), @splat(64, @as(u8, 64))); | |
| 146 | try expectEqual(@clz(u128, @splat(64, @as(u128, 0x10000000000000000))), @splat(64, @as(u8, 63))); | |
| 147 | } | |
| 148 | ||
| 149 | test "@ctz" { | |
| 150 | try testCtz(); | |
| 151 | comptime try testCtz(); | |
| 152 | } | |
| 153 | ||
| 154 | fn testCtz() !void { | |
| 155 | try expect(@ctz(u8, 0b10100000) == 5); | |
| 156 | try expect(@ctz(u8, 0b10001010) == 1); | |
| 157 | try expect(@ctz(u8, 0b00000000) == 8); | |
| 158 | try expect(@ctz(u16, 0b00000000) == 16); | |
| 159 | } | |
| 160 | ||
| 161 | test "@ctz vectors" { | |
| 162 | try testClzVectors(); | |
| 163 | comptime try testClzVectors(); | |
| 164 | } | |
| 165 | ||
| 166 | fn testCtzVectors() !void { | |
| 167 | @setEvalBranchQuota(10_000); | |
| 168 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10100000))), @splat(64, @as(u4, 5))); | |
| 169 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 1))); | |
| 170 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8))); | |
| 171 | try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16))); | |
| 172 | } | |
| 173 | ||
| 174 | 9 | test "assignment operators" { |
| 175 | 10 | var i: u32 = 0; |
| 176 | 11 | i += 5; |
| ... | ... | @@ -219,108 +54,31 @@ fn assertFalse(b: bool) !void { |
| 219 | 54 | try expect(!b); |
| 220 | 55 | } |
| 221 | 56 | |
| 222 | test "const number literal" { | |
| 223 | const one = 1; | |
| 224 | const eleven = ten + one; | |
| 225 | ||
| 226 | try expect(eleven == 11); | |
| 227 | } | |
| 228 | const ten = 10; | |
| 229 | ||
| 230 | test "unsigned wrapping" { | |
| 231 | try testUnsignedWrappingEval(maxInt(u32)); | |
| 232 | comptime try testUnsignedWrappingEval(maxInt(u32)); | |
| 233 | } | |
| 234 | fn testUnsignedWrappingEval(x: u32) !void { | |
| 235 | const zero = x +% 1; | |
| 236 | try expect(zero == 0); | |
| 237 | const orig = zero -% 1; | |
| 238 | try expect(orig == maxInt(u32)); | |
| 239 | } | |
| 240 | ||
| 241 | test "signed wrapping" { | |
| 242 | try testSignedWrappingEval(maxInt(i32)); | |
| 243 | comptime try testSignedWrappingEval(maxInt(i32)); | |
| 244 | } | |
| 245 | fn testSignedWrappingEval(x: i32) !void { | |
| 246 | const min_val = x +% 1; | |
| 247 | try expect(min_val == minInt(i32)); | |
| 248 | const max_val = min_val -% 1; | |
| 249 | try expect(max_val == maxInt(i32)); | |
| 250 | } | |
| 251 | ||
| 252 | test "signed negation wrapping" { | |
| 253 | try testSignedNegationWrappingEval(minInt(i16)); | |
| 254 | comptime try testSignedNegationWrappingEval(minInt(i16)); | |
| 255 | } | |
| 256 | fn testSignedNegationWrappingEval(x: i16) !void { | |
| 257 | try expect(x == -32768); | |
| 258 | const neg = -%x; | |
| 259 | try expect(neg == -32768); | |
| 260 | } | |
| 261 | ||
| 262 | test "unsigned negation wrapping" { | |
| 263 | try testUnsignedNegationWrappingEval(1); | |
| 264 | comptime try testUnsignedNegationWrappingEval(1); | |
| 265 | } | |
| 266 | fn testUnsignedNegationWrappingEval(x: u16) !void { | |
| 267 | try expect(x == 1); | |
| 268 | const neg = -%x; | |
| 269 | try expect(neg == maxInt(u16)); | |
| 57 | test "@clz" { | |
| 58 | try testClz(); | |
| 59 | comptime try testClz(); | |
| 270 | 60 | } |
| 271 | 61 | |
| 272 | test "unsigned 64-bit division" { | |
| 273 | try test_u64_div(); | |
| 274 | comptime try test_u64_div(); | |
| 275 | } | |
| 276 | fn test_u64_div() !void { | |
| 277 | const result = divWithResult(1152921504606846976, 34359738365); | |
| 278 | try expect(result.quotient == 33554432); | |
| 279 | try expect(result.remainder == 100663296); | |
| 280 | } | |
| 281 | fn divWithResult(a: u64, b: u64) DivResult { | |
| 282 | return DivResult{ | |
| 283 | .quotient = a / b, | |
| 284 | .remainder = a % b, | |
| 285 | }; | |
| 286 | } | |
| 287 | const DivResult = struct { | |
| 288 | quotient: u64, | |
| 289 | remainder: u64, | |
| 290 | }; | |
| 291 | ||
| 292 | test "binary not" { | |
| 293 | try expect(comptime x: { | |
| 294 | break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101; | |
| 295 | }); | |
| 296 | try expect(comptime x: { | |
| 297 | break :x ~@as(u64, 2147483647) == 18446744071562067968; | |
| 298 | }); | |
| 299 | try testBinaryNot(0b1010101010101010); | |
| 62 | fn testClz() !void { | |
| 63 | try expect(testOneClz(u8, 0b10001010) == 0); | |
| 64 | try expect(testOneClz(u8, 0b00001010) == 4); | |
| 65 | try expect(testOneClz(u8, 0b00011010) == 3); | |
| 66 | try expect(testOneClz(u8, 0b00000000) == 8); | |
| 67 | try expect(testOneClz(u128, 0xffffffffffffffff) == 64); | |
| 68 | try expect(testOneClz(u128, 0x10000000000000000) == 63); | |
| 300 | 69 | } |
| 301 | 70 | |
| 302 | fn testBinaryNot(x: u16) !void { | |
| 303 | try expect(~x == 0b0101010101010101); | |
| 71 | fn testOneClz(comptime T: type, x: T) u32 { | |
| 72 | return @clz(T, x); | |
| 304 | 73 | } |
| 305 | 74 | |
| 306 | test "small int addition" { | |
| 307 | var x: u2 = 0; | |
| 308 | try expect(x == 0); | |
| 309 | ||
| 310 | x += 1; | |
| 311 | try expect(x == 1); | |
| 312 | ||
| 313 | x += 1; | |
| 314 | try expect(x == 2); | |
| 315 | ||
| 316 | x += 1; | |
| 317 | try expect(x == 3); | |
| 318 | ||
| 319 | var result: @TypeOf(x) = 3; | |
| 320 | try expect(@addWithOverflow(@TypeOf(x), x, 1, &result)); | |
| 75 | test "const number literal" { | |
| 76 | const one = 1; | |
| 77 | const eleven = ten + one; | |
| 321 | 78 | |
| 322 | try expect(result == 0); | |
| 79 | try expect(eleven == 11); | |
| 323 | 80 | } |
| 81 | const ten = 10; | |
| 324 | 82 | |
| 325 | 83 | test "float equality" { |
| 326 | 84 | const x: f64 = 0.012; |
| ... | ... | @@ -335,15 +93,6 @@ fn testFloatEqualityImpl(x: f64, y: f64) !void { |
| 335 | 93 | try expect(y == y2); |
| 336 | 94 | } |
| 337 | 95 | |
| 338 | test "allow signed integer division/remainder when values are comptime known and positive or exact" { | |
| 339 | try expect(5 / 3 == 1); | |
| 340 | try expect(-5 / -3 == 1); | |
| 341 | try expect(-6 / 3 == -2); | |
| 342 | ||
| 343 | try expect(5 % 3 == 2); | |
| 344 | try expect(-6 % 3 == 0); | |
| 345 | } | |
| 346 | ||
| 347 | 96 | test "hex float literal parsing" { |
| 348 | 97 | comptime try expect(0x1.0 == 1.0); |
| 349 | 98 | } |
| ... | ... | @@ -353,102 +102,10 @@ test "quad hex float literal parsing in range" { |
| 353 | 102 | const b = 0x1.dedafcff354b6ae9758763545432p-9; |
| 354 | 103 | const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534; |
| 355 | 104 | const d = 0x1.edcbff8ad76ab5bf46463233214fp-435; |
| 356 | if (false) { | |
| 357 | a; | |
| 358 | b; | |
| 359 | c; | |
| 360 | d; | |
| 361 | } | |
| 362 | } | |
| 363 | ||
| 364 | test "quad hex float literal parsing accurate" { | |
| 365 | const a: f128 = 0x1.1111222233334444555566667777p+0; | |
| 366 | ||
| 367 | // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. | |
| 368 | const expected: u128 = 0x3fff1111222233334444555566667777; | |
| 369 | try expect(@bitCast(u128, a) == expected); | |
| 370 | ||
| 371 | // non-normalized | |
| 372 | const b: f128 = 0x11.111222233334444555566667777p-4; | |
| 373 | try expect(@bitCast(u128, b) == expected); | |
| 374 | ||
| 375 | const S = struct { | |
| 376 | fn doTheTest() !void { | |
| 377 | { | |
| 378 | var f: f128 = 0x1.2eab345678439abcdefea56782346p+5; | |
| 379 | try expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234); | |
| 380 | } | |
| 381 | { | |
| 382 | var f: f128 = 0x1.edcb34a235253948765432134674fp-1; | |
| 383 | try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674); | |
| 384 | } | |
| 385 | { | |
| 386 | var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50; | |
| 387 | try expect(@bitCast(u128, f) == 0x3fcd353e45674d89abacc3a2ebf3ff50); | |
| 388 | } | |
| 389 | { | |
| 390 | var f: f128 = 0x1.ed8764648369535adf4be3214567fp-9; | |
| 391 | try expect(@bitCast(u128, f) == 0x3ff6ed8764648369535adf4be3214568); | |
| 392 | } | |
| 393 | const exp2ft = [_]f64{ | |
| 394 | 0x1.6a09e667f3bcdp-1, | |
| 395 | 0x1.7a11473eb0187p-1, | |
| 396 | 0x1.8ace5422aa0dbp-1, | |
| 397 | 0x1.9c49182a3f090p-1, | |
| 398 | 0x1.ae89f995ad3adp-1, | |
| 399 | 0x1.c199bdd85529cp-1, | |
| 400 | 0x1.d5818dcfba487p-1, | |
| 401 | 0x1.ea4afa2a490dap-1, | |
| 402 | 0x1.0000000000000p+0, | |
| 403 | 0x1.0b5586cf9890fp+0, | |
| 404 | 0x1.172b83c7d517bp+0, | |
| 405 | 0x1.2387a6e756238p+0, | |
| 406 | 0x1.306fe0a31b715p+0, | |
| 407 | 0x1.3dea64c123422p+0, | |
| 408 | 0x1.4bfdad5362a27p+0, | |
| 409 | 0x1.5ab07dd485429p+0, | |
| 410 | 0x1.8p23, | |
| 411 | 0x1.62e430p-1, | |
| 412 | 0x1.ebfbe0p-3, | |
| 413 | 0x1.c6b348p-5, | |
| 414 | 0x1.3b2c9cp-7, | |
| 415 | 0x1.0p127, | |
| 416 | -0x1.0p-149, | |
| 417 | }; | |
| 418 | ||
| 419 | const answers = [_]u64{ | |
| 420 | 0x3fe6a09e667f3bcd, | |
| 421 | 0x3fe7a11473eb0187, | |
| 422 | 0x3fe8ace5422aa0db, | |
| 423 | 0x3fe9c49182a3f090, | |
| 424 | 0x3feae89f995ad3ad, | |
| 425 | 0x3fec199bdd85529c, | |
| 426 | 0x3fed5818dcfba487, | |
| 427 | 0x3feea4afa2a490da, | |
| 428 | 0x3ff0000000000000, | |
| 429 | 0x3ff0b5586cf9890f, | |
| 430 | 0x3ff172b83c7d517b, | |
| 431 | 0x3ff2387a6e756238, | |
| 432 | 0x3ff306fe0a31b715, | |
| 433 | 0x3ff3dea64c123422, | |
| 434 | 0x3ff4bfdad5362a27, | |
| 435 | 0x3ff5ab07dd485429, | |
| 436 | 0x4168000000000000, | |
| 437 | 0x3fe62e4300000000, | |
| 438 | 0x3fcebfbe00000000, | |
| 439 | 0x3fac6b3480000000, | |
| 440 | 0x3f83b2c9c0000000, | |
| 441 | 0x47e0000000000000, | |
| 442 | 0xb6a0000000000000, | |
| 443 | }; | |
| 444 | ||
| 445 | for (exp2ft) |x, i| { | |
| 446 | try expect(@bitCast(u64, x) == answers[i]); | |
| 447 | } | |
| 448 | } | |
| 449 | }; | |
| 450 | try S.doTheTest(); | |
| 451 | comptime try S.doTheTest(); | |
| 105 | _ = a; | |
| 106 | _ = b; | |
| 107 | _ = c; | |
| 108 | _ = d; | |
| 452 | 109 | } |
| 453 | 110 | |
| 454 | 111 | test "underscore separator parsing" { |
| ... | ... | @@ -483,66 +140,9 @@ test "hex float literal within range" { |
| 483 | 140 | const a = 0x1.0p16383; |
| 484 | 141 | const b = 0x0.1p16387; |
| 485 | 142 | const c = 0x1.0p-16382; |
| 486 | if (false) { | |
| 487 | a; | |
| 488 | b; | |
| 489 | c; | |
| 490 | } | |
| 491 | } | |
| 492 | ||
| 493 | test "truncating shift left" { | |
| 494 | try testShlTrunc(maxInt(u16)); | |
| 495 | comptime try testShlTrunc(maxInt(u16)); | |
| 496 | } | |
| 497 | fn testShlTrunc(x: u16) !void { | |
| 498 | const shifted = x << 1; | |
| 499 | try expect(shifted == 65534); | |
| 500 | } | |
| 501 | ||
| 502 | test "truncating shift right" { | |
| 503 | try testShrTrunc(maxInt(u16)); | |
| 504 | comptime try testShrTrunc(maxInt(u16)); | |
| 505 | } | |
| 506 | fn testShrTrunc(x: u16) !void { | |
| 507 | const shifted = x >> 1; | |
| 508 | try expect(shifted == 32767); | |
| 509 | } | |
| 510 | ||
| 511 | test "exact shift left" { | |
| 512 | try testShlExact(0b00110101); | |
| 513 | comptime try testShlExact(0b00110101); | |
| 514 | } | |
| 515 | fn testShlExact(x: u8) !void { | |
| 516 | const shifted = @shlExact(x, 2); | |
| 517 | try expect(shifted == 0b11010100); | |
| 518 | } | |
| 519 | ||
| 520 | test "exact shift right" { | |
| 521 | try testShrExact(0b10110100); | |
| 522 | comptime try testShrExact(0b10110100); | |
| 523 | } | |
| 524 | fn testShrExact(x: u8) !void { | |
| 525 | const shifted = @shrExact(x, 2); | |
| 526 | try expect(shifted == 0b00101101); | |
| 527 | } | |
| 528 | ||
| 529 | test "shift left/right on u0 operand" { | |
| 530 | const S = struct { | |
| 531 | fn doTheTest() !void { | |
| 532 | var x: u0 = 0; | |
| 533 | var y: u0 = 0; | |
| 534 | try expectEqual(@as(u0, 0), x << 0); | |
| 535 | try expectEqual(@as(u0, 0), x >> 0); | |
| 536 | try expectEqual(@as(u0, 0), x << y); | |
| 537 | try expectEqual(@as(u0, 0), x >> y); | |
| 538 | try expectEqual(@as(u0, 0), @shlExact(x, 0)); | |
| 539 | try expectEqual(@as(u0, 0), @shrExact(x, 0)); | |
| 540 | try expectEqual(@as(u0, 0), @shlExact(x, y)); | |
| 541 | try expectEqual(@as(u0, 0), @shrExact(x, y)); | |
| 542 | } | |
| 543 | }; | |
| 544 | try S.doTheTest(); | |
| 545 | comptime try S.doTheTest(); | |
| 143 | _ = a; | |
| 144 | _ = b; | |
| 145 | _ = c; | |
| 546 | 146 | } |
| 547 | 147 | |
| 548 | 148 | test "comptime_int addition" { |
| ... | ... | @@ -600,11 +200,15 @@ test "xor" { |
| 600 | 200 | } |
| 601 | 201 | |
| 602 | 202 | fn test_xor() !void { |
| 603 | try expect(0xFF ^ 0x00 == 0xFF); | |
| 604 | try expect(0xF0 ^ 0x0F == 0xFF); | |
| 605 | try expect(0xFF ^ 0xF0 == 0x0F); | |
| 606 | try expect(0xFF ^ 0x0F == 0xF0); | |
| 607 | try expect(0xFF ^ 0xFF == 0x00); | |
| 203 | try testOneXor(0xFF, 0x00, 0xFF); | |
| 204 | try testOneXor(0xF0, 0x0F, 0xFF); | |
| 205 | try testOneXor(0xFF, 0xF0, 0x0F); | |
| 206 | try testOneXor(0xFF, 0x0F, 0xF0); | |
| 207 | try testOneXor(0xFF, 0xFF, 0x00); | |
| 208 | } | |
| 209 | ||
| 210 | fn testOneXor(a: u8, b: u8, c: u8) !void { | |
| 211 | try expect(a ^ b == c); | |
| 608 | 212 | } |
| 609 | 213 | |
| 610 | 214 | test "comptime_int xor" { |
| ... | ... | @@ -620,191 +224,6 @@ test "comptime_int xor" { |
| 620 | 224 | } |
| 621 | 225 | } |
| 622 | 226 | |
| 623 | test "f128" { | |
| 624 | try test_f128(); | |
| 625 | comptime try test_f128(); | |
| 626 | } | |
| 627 | ||
| 628 | fn make_f128(x: f128) f128 { | |
| 629 | return x; | |
| 630 | } | |
| 631 | ||
| 632 | fn test_f128() !void { | |
| 633 | try expect(@sizeOf(f128) == 16); | |
| 634 | try expect(make_f128(1.0) == 1.0); | |
| 635 | try expect(make_f128(1.0) != 1.1); | |
| 636 | try expect(make_f128(1.0) > 0.9); | |
| 637 | try expect(make_f128(1.0) >= 0.9); | |
| 638 | try expect(make_f128(1.0) >= 1.0); | |
| 639 | try should_not_be_zero(1.0); | |
| 640 | } | |
| 641 | ||
| 642 | fn should_not_be_zero(x: f128) !void { | |
| 643 | try expect(x != 0.0); | |
| 644 | } | |
| 645 | ||
| 646 | test "comptime float rem int" { | |
| 647 | comptime { | |
| 648 | var x = @as(f32, 1) % 2; | |
| 649 | try expect(x == 1.0); | |
| 650 | } | |
| 651 | } | |
| 652 | ||
| 653 | test "remainder division" { | |
| 654 | comptime try remdiv(f16); | |
| 655 | comptime try remdiv(f32); | |
| 656 | comptime try remdiv(f64); | |
| 657 | comptime try remdiv(f128); | |
| 658 | try remdiv(f16); | |
| 659 | try remdiv(f64); | |
| 660 | try remdiv(f128); | |
| 661 | } | |
| 662 | ||
| 663 | fn remdiv(comptime T: type) !void { | |
| 664 | try expect(@as(T, 1) == @as(T, 1) % @as(T, 2)); | |
| 665 | try expect(@as(T, 1) == @as(T, 7) % @as(T, 3)); | |
| 666 | } | |
| 667 | ||
| 668 | test "@sqrt" { | |
| 669 | try testSqrt(f64, 12.0); | |
| 670 | comptime try testSqrt(f64, 12.0); | |
| 671 | try testSqrt(f32, 13.0); | |
| 672 | comptime try testSqrt(f32, 13.0); | |
| 673 | try testSqrt(f16, 13.0); | |
| 674 | comptime try testSqrt(f16, 13.0); | |
| 675 | ||
| 676 | const x = 14.0; | |
| 677 | const y = x * x; | |
| 678 | const z = @sqrt(y); | |
| 679 | comptime try expect(z == x); | |
| 680 | } | |
| 681 | ||
| 682 | fn testSqrt(comptime T: type, x: T) !void { | |
| 683 | try expect(@sqrt(x * x) == x); | |
| 684 | } | |
| 685 | ||
| 686 | test "@fabs" { | |
| 687 | try testFabs(f128, 12.0); | |
| 688 | comptime try testFabs(f128, 12.0); | |
| 689 | try testFabs(f64, 12.0); | |
| 690 | comptime try testFabs(f64, 12.0); | |
| 691 | try testFabs(f32, 12.0); | |
| 692 | comptime try testFabs(f32, 12.0); | |
| 693 | try testFabs(f16, 12.0); | |
| 694 | comptime try testFabs(f16, 12.0); | |
| 695 | ||
| 696 | const x = 14.0; | |
| 697 | const y = -x; | |
| 698 | const z = @fabs(y); | |
| 699 | comptime try expectEqual(x, z); | |
| 700 | } | |
| 701 | ||
| 702 | fn testFabs(comptime T: type, x: T) !void { | |
| 703 | const y = -x; | |
| 704 | const z = @fabs(y); | |
| 705 | try expectEqual(x, z); | |
| 706 | } | |
| 707 | ||
| 708 | test "@floor" { | |
| 709 | // FIXME: Generates a floorl function call | |
| 710 | // testFloor(f128, 12.0); | |
| 711 | comptime try testFloor(f128, 12.0); | |
| 712 | try testFloor(f64, 12.0); | |
| 713 | comptime try testFloor(f64, 12.0); | |
| 714 | try testFloor(f32, 12.0); | |
| 715 | comptime try testFloor(f32, 12.0); | |
| 716 | try testFloor(f16, 12.0); | |
| 717 | comptime try testFloor(f16, 12.0); | |
| 718 | ||
| 719 | const x = 14.0; | |
| 720 | const y = x + 0.7; | |
| 721 | const z = @floor(y); | |
| 722 | comptime try expectEqual(x, z); | |
| 723 | } | |
| 724 | ||
| 725 | fn testFloor(comptime T: type, x: T) !void { | |
| 726 | const y = x + 0.6; | |
| 727 | const z = @floor(y); | |
| 728 | try expectEqual(x, z); | |
| 729 | } | |
| 730 | ||
| 731 | test "@ceil" { | |
| 732 | // FIXME: Generates a ceill function call | |
| 733 | //testCeil(f128, 12.0); | |
| 734 | comptime try testCeil(f128, 12.0); | |
| 735 | try testCeil(f64, 12.0); | |
| 736 | comptime try testCeil(f64, 12.0); | |
| 737 | try testCeil(f32, 12.0); | |
| 738 | comptime try testCeil(f32, 12.0); | |
| 739 | try testCeil(f16, 12.0); | |
| 740 | comptime try testCeil(f16, 12.0); | |
| 741 | ||
| 742 | const x = 14.0; | |
| 743 | const y = x - 0.7; | |
| 744 | const z = @ceil(y); | |
| 745 | comptime try expectEqual(x, z); | |
| 746 | } | |
| 747 | ||
| 748 | fn testCeil(comptime T: type, x: T) !void { | |
| 749 | const y = x - 0.8; | |
| 750 | const z = @ceil(y); | |
| 751 | try expectEqual(x, z); | |
| 752 | } | |
| 753 | ||
| 754 | test "@trunc" { | |
| 755 | // FIXME: Generates a truncl function call | |
| 756 | //testTrunc(f128, 12.0); | |
| 757 | comptime try testTrunc(f128, 12.0); | |
| 758 | try testTrunc(f64, 12.0); | |
| 759 | comptime try testTrunc(f64, 12.0); | |
| 760 | try testTrunc(f32, 12.0); | |
| 761 | comptime try testTrunc(f32, 12.0); | |
| 762 | try testTrunc(f16, 12.0); | |
| 763 | comptime try testTrunc(f16, 12.0); | |
| 764 | ||
| 765 | const x = 14.0; | |
| 766 | const y = x + 0.7; | |
| 767 | const z = @trunc(y); | |
| 768 | comptime try expectEqual(x, z); | |
| 769 | } | |
| 770 | ||
| 771 | fn testTrunc(comptime T: type, x: T) !void { | |
| 772 | { | |
| 773 | const y = x + 0.8; | |
| 774 | const z = @trunc(y); | |
| 775 | try expectEqual(x, z); | |
| 776 | } | |
| 777 | ||
| 778 | { | |
| 779 | const y = -x - 0.8; | |
| 780 | const z = @trunc(y); | |
| 781 | try expectEqual(-x, z); | |
| 782 | } | |
| 783 | } | |
| 784 | ||
| 785 | test "@round" { | |
| 786 | // FIXME: Generates a roundl function call | |
| 787 | //testRound(f128, 12.0); | |
| 788 | comptime try testRound(f128, 12.0); | |
| 789 | try testRound(f64, 12.0); | |
| 790 | comptime try testRound(f64, 12.0); | |
| 791 | try testRound(f32, 12.0); | |
| 792 | comptime try testRound(f32, 12.0); | |
| 793 | try testRound(f16, 12.0); | |
| 794 | comptime try testRound(f16, 12.0); | |
| 795 | ||
| 796 | const x = 14.0; | |
| 797 | const y = x + 0.4; | |
| 798 | const z = @round(y); | |
| 799 | comptime try expectEqual(x, z); | |
| 800 | } | |
| 801 | ||
| 802 | fn testRound(comptime T: type, x: T) !void { | |
| 803 | const y = x - 0.5; | |
| 804 | const z = @round(y); | |
| 805 | try expectEqual(x, z); | |
| 806 | } | |
| 807 | ||
| 808 | 227 | test "comptime_int param and return" { |
| 809 | 228 | const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); |
| 810 | 229 | try expect(a == 137114567242441932203689521744947848950); |
| ... | ... | @@ -816,88 +235,3 @@ test "comptime_int param and return" { |
| 816 | 235 | fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { |
| 817 | 236 | return a + b; |
| 818 | 237 | } |
| 819 | ||
| 820 | test "vector integer addition" { | |
| 821 | const S = struct { | |
| 822 | fn doTheTest() !void { | |
| 823 | var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; | |
| 824 | var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; | |
| 825 | var result = a + b; | |
| 826 | var result_array: [4]i32 = result; | |
| 827 | const expected = [_]i32{ 6, 8, 10, 12 }; | |
| 828 | try expectEqualSlices(i32, &expected, &result_array); | |
| 829 | } | |
| 830 | }; | |
| 831 | try S.doTheTest(); | |
| 832 | comptime try S.doTheTest(); | |
| 833 | } | |
| 834 | ||
| 835 | test "NaN comparison" { | |
| 836 | try testNanEqNan(f16); | |
| 837 | try testNanEqNan(f32); | |
| 838 | try testNanEqNan(f64); | |
| 839 | try testNanEqNan(f128); | |
| 840 | comptime try testNanEqNan(f16); | |
| 841 | comptime try testNanEqNan(f32); | |
| 842 | comptime try testNanEqNan(f64); | |
| 843 | comptime try testNanEqNan(f128); | |
| 844 | } | |
| 845 | ||
| 846 | fn testNanEqNan(comptime F: type) !void { | |
| 847 | var nan1 = std.math.nan(F); | |
| 848 | var nan2 = std.math.nan(F); | |
| 849 | try expect(nan1 != nan2); | |
| 850 | try expect(!(nan1 == nan2)); | |
| 851 | try expect(!(nan1 > nan2)); | |
| 852 | try expect(!(nan1 >= nan2)); | |
| 853 | try expect(!(nan1 < nan2)); | |
| 854 | try expect(!(nan1 <= nan2)); | |
| 855 | } | |
| 856 | ||
| 857 | test "128-bit multiplication" { | |
| 858 | var a: i128 = 3; | |
| 859 | var b: i128 = 2; | |
| 860 | var c = a * b; | |
| 861 | try expect(c == 6); | |
| 862 | } | |
| 863 | ||
| 864 | test "vector comparison" { | |
| 865 | const S = struct { | |
| 866 | fn doTheTest() !void { | |
| 867 | var a: std.meta.Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 }; | |
| 868 | var b: std.meta.Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 }; | |
| 869 | try expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false })); | |
| 870 | try expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false })); | |
| 871 | try expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false })); | |
| 872 | try expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true })); | |
| 873 | try expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true })); | |
| 874 | try expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true })); | |
| 875 | } | |
| 876 | }; | |
| 877 | try S.doTheTest(); | |
| 878 | comptime try S.doTheTest(); | |
| 879 | } | |
| 880 | ||
| 881 | test "compare undefined literal with comptime_int" { | |
| 882 | var x = undefined == 1; | |
| 883 | // x is now undefined with type bool | |
| 884 | x = true; | |
| 885 | try expect(x); | |
| 886 | } | |
| 887 | ||
| 888 | test "signed zeros are represented properly" { | |
| 889 | const S = struct { | |
| 890 | fn doTheTest() !void { | |
| 891 | inline for ([_]type{ f16, f32, f64, f128 }) |T| { | |
| 892 | const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | |
| 893 | var as_fp_val = -@as(T, 0.0); | |
| 894 | var as_uint_val = @bitCast(ST, as_fp_val); | |
| 895 | // Ensure the sign bit is set. | |
| 896 | try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); | |
| 897 | } | |
| 898 | } | |
| 899 | }; | |
| 900 | ||
| 901 | try S.doTheTest(); | |
| 902 | comptime try S.doTheTest(); | |
| 903 | } |
test/behavior/math_stage1.zig created+677| ... | ... | @@ -0,0 +1,677 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const expectEqual = std.testing.expectEqual; | |
| 4 | const expectEqualSlices = std.testing.expectEqualSlices; | |
| 5 | const maxInt = std.math.maxInt; | |
| 6 | const minInt = std.math.minInt; | |
| 7 | const mem = std.mem; | |
| 8 | ||
| 9 | test "division" { | |
| 10 | try testDivision(); | |
| 11 | comptime try testDivision(); | |
| 12 | } | |
| 13 | fn testDivision() !void { | |
| 14 | try expect(div(u32, 13, 3) == 4); | |
| 15 | try expect(div(f16, 1.0, 2.0) == 0.5); | |
| 16 | try expect(div(f32, 1.0, 2.0) == 0.5); | |
| 17 | ||
| 18 | try expect(divExact(u32, 55, 11) == 5); | |
| 19 | try expect(divExact(i32, -55, 11) == -5); | |
| 20 | try expect(divExact(f16, 55.0, 11.0) == 5.0); | |
| 21 | try expect(divExact(f16, -55.0, 11.0) == -5.0); | |
| 22 | try expect(divExact(f32, 55.0, 11.0) == 5.0); | |
| 23 | try expect(divExact(f32, -55.0, 11.0) == -5.0); | |
| 24 | ||
| 25 | try expect(divFloor(i32, 5, 3) == 1); | |
| 26 | try expect(divFloor(i32, -5, 3) == -2); | |
| 27 | try expect(divFloor(f16, 5.0, 3.0) == 1.0); | |
| 28 | try expect(divFloor(f16, -5.0, 3.0) == -2.0); | |
| 29 | try expect(divFloor(f32, 5.0, 3.0) == 1.0); | |
| 30 | try expect(divFloor(f32, -5.0, 3.0) == -2.0); | |
| 31 | try expect(divFloor(i32, -0x80000000, -2) == 0x40000000); | |
| 32 | try expect(divFloor(i32, 0, -0x80000000) == 0); | |
| 33 | try expect(divFloor(i32, -0x40000001, 0x40000000) == -2); | |
| 34 | try expect(divFloor(i32, -0x80000000, 1) == -0x80000000); | |
| 35 | try expect(divFloor(i32, 10, 12) == 0); | |
| 36 | try expect(divFloor(i32, -14, 12) == -2); | |
| 37 | try expect(divFloor(i32, -2, 12) == -1); | |
| 38 | ||
| 39 | try expect(divTrunc(i32, 5, 3) == 1); | |
| 40 | try expect(divTrunc(i32, -5, 3) == -1); | |
| 41 | try expect(divTrunc(f16, 5.0, 3.0) == 1.0); | |
| 42 | try expect(divTrunc(f16, -5.0, 3.0) == -1.0); | |
| 43 | try expect(divTrunc(f32, 5.0, 3.0) == 1.0); | |
| 44 | try expect(divTrunc(f32, -5.0, 3.0) == -1.0); | |
| 45 | try expect(divTrunc(f64, 5.0, 3.0) == 1.0); | |
| 46 | try expect(divTrunc(f64, -5.0, 3.0) == -1.0); | |
| 47 | try expect(divTrunc(i32, 10, 12) == 0); | |
| 48 | try expect(divTrunc(i32, -14, 12) == -1); | |
| 49 | try expect(divTrunc(i32, -2, 12) == 0); | |
| 50 | ||
| 51 | try expect(mod(i32, 10, 12) == 10); | |
| 52 | try expect(mod(i32, -14, 12) == 10); | |
| 53 | try expect(mod(i32, -2, 12) == 10); | |
| 54 | ||
| 55 | comptime { | |
| 56 | try expect( | |
| 57 | 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, | |
| 58 | ); | |
| 59 | try expect( | |
| 60 | @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, | |
| 61 | ); | |
| 62 | try expect( | |
| 63 | 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, | |
| 64 | ); | |
| 65 | try expect( | |
| 66 | @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, | |
| 67 | ); | |
| 68 | try expect( | |
| 69 | @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, | |
| 70 | ); | |
| 71 | try expect( | |
| 72 | @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, | |
| 73 | ); | |
| 74 | try expect( | |
| 75 | 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, | |
| 76 | ); | |
| 77 | } | |
| 78 | } | |
| 79 | fn div(comptime T: type, a: T, b: T) T { | |
| 80 | return a / b; | |
| 81 | } | |
| 82 | fn divExact(comptime T: type, a: T, b: T) T { | |
| 83 | return @divExact(a, b); | |
| 84 | } | |
| 85 | fn divFloor(comptime T: type, a: T, b: T) T { | |
| 86 | return @divFloor(a, b); | |
| 87 | } | |
| 88 | fn divTrunc(comptime T: type, a: T, b: T) T { | |
| 89 | return @divTrunc(a, b); | |
| 90 | } | |
| 91 | fn mod(comptime T: type, a: T, b: T) T { | |
| 92 | return @mod(a, b); | |
| 93 | } | |
| 94 | ||
| 95 | test "@addWithOverflow" { | |
| 96 | var result: u8 = undefined; | |
| 97 | try expect(@addWithOverflow(u8, 250, 100, &result)); | |
| 98 | try expect(!@addWithOverflow(u8, 100, 150, &result)); | |
| 99 | try expect(result == 250); | |
| 100 | } | |
| 101 | ||
| 102 | // TODO test mulWithOverflow | |
| 103 | // TODO test subWithOverflow | |
| 104 | ||
| 105 | test "@shlWithOverflow" { | |
| 106 | var result: u16 = undefined; | |
| 107 | try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); | |
| 108 | try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); | |
| 109 | try expect(result == 0b1011111111111100); | |
| 110 | } | |
| 111 | ||
| 112 | test "@*WithOverflow with u0 values" { | |
| 113 | var result: u0 = undefined; | |
| 114 | try expect(!@addWithOverflow(u0, 0, 0, &result)); | |
| 115 | try expect(!@subWithOverflow(u0, 0, 0, &result)); | |
| 116 | try expect(!@mulWithOverflow(u0, 0, 0, &result)); | |
| 117 | try expect(!@shlWithOverflow(u0, 0, 0, &result)); | |
| 118 | } | |
| 119 | ||
| 120 | test "@clz vectors" { | |
| 121 | try testClzVectors(); | |
| 122 | comptime try testClzVectors(); | |
| 123 | } | |
| 124 | ||
| 125 | fn testClzVectors() !void { | |
| 126 | @setEvalBranchQuota(10_000); | |
| 127 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 0))); | |
| 128 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00001010))), @splat(64, @as(u4, 4))); | |
| 129 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00011010))), @splat(64, @as(u4, 3))); | |
| 130 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8))); | |
| 131 | try expectEqual(@clz(u128, @splat(64, @as(u128, 0xffffffffffffffff))), @splat(64, @as(u8, 64))); | |
| 132 | try expectEqual(@clz(u128, @splat(64, @as(u128, 0x10000000000000000))), @splat(64, @as(u8, 63))); | |
| 133 | } | |
| 134 | ||
| 135 | test "@ctz" { | |
| 136 | try testCtz(); | |
| 137 | comptime try testCtz(); | |
| 138 | } | |
| 139 | ||
| 140 | fn testCtz() !void { | |
| 141 | try expect(@ctz(u8, 0b10100000) == 5); | |
| 142 | try expect(@ctz(u8, 0b10001010) == 1); | |
| 143 | try expect(@ctz(u8, 0b00000000) == 8); | |
| 144 | try expect(@ctz(u16, 0b00000000) == 16); | |
| 145 | } | |
| 146 | ||
| 147 | test "@ctz vectors" { | |
| 148 | try testClzVectors(); | |
| 149 | comptime try testClzVectors(); | |
| 150 | } | |
| 151 | ||
| 152 | fn testCtzVectors() !void { | |
| 153 | @setEvalBranchQuota(10_000); | |
| 154 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10100000))), @splat(64, @as(u4, 5))); | |
| 155 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 1))); | |
| 156 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8))); | |
| 157 | try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16))); | |
| 158 | } | |
| 159 | ||
| 160 | test "unsigned wrapping" { | |
| 161 | try testUnsignedWrappingEval(maxInt(u32)); | |
| 162 | comptime try testUnsignedWrappingEval(maxInt(u32)); | |
| 163 | } | |
| 164 | fn testUnsignedWrappingEval(x: u32) !void { | |
| 165 | const zero = x +% 1; | |
| 166 | try expect(zero == 0); | |
| 167 | const orig = zero -% 1; | |
| 168 | try expect(orig == maxInt(u32)); | |
| 169 | } | |
| 170 | ||
| 171 | test "signed wrapping" { | |
| 172 | try testSignedWrappingEval(maxInt(i32)); | |
| 173 | comptime try testSignedWrappingEval(maxInt(i32)); | |
| 174 | } | |
| 175 | fn testSignedWrappingEval(x: i32) !void { | |
| 176 | const min_val = x +% 1; | |
| 177 | try expect(min_val == minInt(i32)); | |
| 178 | const max_val = min_val -% 1; | |
| 179 | try expect(max_val == maxInt(i32)); | |
| 180 | } | |
| 181 | ||
| 182 | test "signed negation wrapping" { | |
| 183 | try testSignedNegationWrappingEval(minInt(i16)); | |
| 184 | comptime try testSignedNegationWrappingEval(minInt(i16)); | |
| 185 | } | |
| 186 | fn testSignedNegationWrappingEval(x: i16) !void { | |
| 187 | try expect(x == -32768); | |
| 188 | const neg = -%x; | |
| 189 | try expect(neg == -32768); | |
| 190 | } | |
| 191 | ||
| 192 | test "unsigned negation wrapping" { | |
| 193 | try testUnsignedNegationWrappingEval(1); | |
| 194 | comptime try testUnsignedNegationWrappingEval(1); | |
| 195 | } | |
| 196 | fn testUnsignedNegationWrappingEval(x: u16) !void { | |
| 197 | try expect(x == 1); | |
| 198 | const neg = -%x; | |
| 199 | try expect(neg == maxInt(u16)); | |
| 200 | } | |
| 201 | ||
| 202 | test "unsigned 64-bit division" { | |
| 203 | try test_u64_div(); | |
| 204 | comptime try test_u64_div(); | |
| 205 | } | |
| 206 | fn test_u64_div() !void { | |
| 207 | const result = divWithResult(1152921504606846976, 34359738365); | |
| 208 | try expect(result.quotient == 33554432); | |
| 209 | try expect(result.remainder == 100663296); | |
| 210 | } | |
| 211 | fn divWithResult(a: u64, b: u64) DivResult { | |
| 212 | return DivResult{ | |
| 213 | .quotient = a / b, | |
| 214 | .remainder = a % b, | |
| 215 | }; | |
| 216 | } | |
| 217 | const DivResult = struct { | |
| 218 | quotient: u64, | |
| 219 | remainder: u64, | |
| 220 | }; | |
| 221 | ||
| 222 | test "binary not" { | |
| 223 | try expect(comptime x: { | |
| 224 | break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101; | |
| 225 | }); | |
| 226 | try expect(comptime x: { | |
| 227 | break :x ~@as(u64, 2147483647) == 18446744071562067968; | |
| 228 | }); | |
| 229 | try testBinaryNot(0b1010101010101010); | |
| 230 | } | |
| 231 | ||
| 232 | fn testBinaryNot(x: u16) !void { | |
| 233 | try expect(~x == 0b0101010101010101); | |
| 234 | } | |
| 235 | ||
| 236 | test "small int addition" { | |
| 237 | var x: u2 = 0; | |
| 238 | try expect(x == 0); | |
| 239 | ||
| 240 | x += 1; | |
| 241 | try expect(x == 1); | |
| 242 | ||
| 243 | x += 1; | |
| 244 | try expect(x == 2); | |
| 245 | ||
| 246 | x += 1; | |
| 247 | try expect(x == 3); | |
| 248 | ||
| 249 | var result: @TypeOf(x) = 3; | |
| 250 | try expect(@addWithOverflow(@TypeOf(x), x, 1, &result)); | |
| 251 | ||
| 252 | try expect(result == 0); | |
| 253 | } | |
| 254 | ||
| 255 | test "allow signed integer division/remainder when values are comptime known and positive or exact" { | |
| 256 | try expect(5 / 3 == 1); | |
| 257 | try expect(-5 / -3 == 1); | |
| 258 | try expect(-6 / 3 == -2); | |
| 259 | ||
| 260 | try expect(5 % 3 == 2); | |
| 261 | try expect(-6 % 3 == 0); | |
| 262 | } | |
| 263 | ||
| 264 | test "quad hex float literal parsing accurate" { | |
| 265 | const a: f128 = 0x1.1111222233334444555566667777p+0; | |
| 266 | ||
| 267 | // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. | |
| 268 | const expected: u128 = 0x3fff1111222233334444555566667777; | |
| 269 | try expect(@bitCast(u128, a) == expected); | |
| 270 | ||
| 271 | // non-normalized | |
| 272 | const b: f128 = 0x11.111222233334444555566667777p-4; | |
| 273 | try expect(@bitCast(u128, b) == expected); | |
| 274 | ||
| 275 | const S = struct { | |
| 276 | fn doTheTest() !void { | |
| 277 | { | |
| 278 | var f: f128 = 0x1.2eab345678439abcdefea56782346p+5; | |
| 279 | try expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234); | |
| 280 | } | |
| 281 | { | |
| 282 | var f: f128 = 0x1.edcb34a235253948765432134674fp-1; | |
| 283 | try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674); | |
| 284 | } | |
| 285 | { | |
| 286 | var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50; | |
| 287 | try expect(@bitCast(u128, f) == 0x3fcd353e45674d89abacc3a2ebf3ff50); | |
| 288 | } | |
| 289 | { | |
| 290 | var f: f128 = 0x1.ed8764648369535adf4be3214567fp-9; | |
| 291 | try expect(@bitCast(u128, f) == 0x3ff6ed8764648369535adf4be3214568); | |
| 292 | } | |
| 293 | const exp2ft = [_]f64{ | |
| 294 | 0x1.6a09e667f3bcdp-1, | |
| 295 | 0x1.7a11473eb0187p-1, | |
| 296 | 0x1.8ace5422aa0dbp-1, | |
| 297 | 0x1.9c49182a3f090p-1, | |
| 298 | 0x1.ae89f995ad3adp-1, | |
| 299 | 0x1.c199bdd85529cp-1, | |
| 300 | 0x1.d5818dcfba487p-1, | |
| 301 | 0x1.ea4afa2a490dap-1, | |
| 302 | 0x1.0000000000000p+0, | |
| 303 | 0x1.0b5586cf9890fp+0, | |
| 304 | 0x1.172b83c7d517bp+0, | |
| 305 | 0x1.2387a6e756238p+0, | |
| 306 | 0x1.306fe0a31b715p+0, | |
| 307 | 0x1.3dea64c123422p+0, | |
| 308 | 0x1.4bfdad5362a27p+0, | |
| 309 | 0x1.5ab07dd485429p+0, | |
| 310 | 0x1.8p23, | |
| 311 | 0x1.62e430p-1, | |
| 312 | 0x1.ebfbe0p-3, | |
| 313 | 0x1.c6b348p-5, | |
| 314 | 0x1.3b2c9cp-7, | |
| 315 | 0x1.0p127, | |
| 316 | -0x1.0p-149, | |
| 317 | }; | |
| 318 | ||
| 319 | const answers = [_]u64{ | |
| 320 | 0x3fe6a09e667f3bcd, | |
| 321 | 0x3fe7a11473eb0187, | |
| 322 | 0x3fe8ace5422aa0db, | |
| 323 | 0x3fe9c49182a3f090, | |
| 324 | 0x3feae89f995ad3ad, | |
| 325 | 0x3fec199bdd85529c, | |
| 326 | 0x3fed5818dcfba487, | |
| 327 | 0x3feea4afa2a490da, | |
| 328 | 0x3ff0000000000000, | |
| 329 | 0x3ff0b5586cf9890f, | |
| 330 | 0x3ff172b83c7d517b, | |
| 331 | 0x3ff2387a6e756238, | |
| 332 | 0x3ff306fe0a31b715, | |
| 333 | 0x3ff3dea64c123422, | |
| 334 | 0x3ff4bfdad5362a27, | |
| 335 | 0x3ff5ab07dd485429, | |
| 336 | 0x4168000000000000, | |
| 337 | 0x3fe62e4300000000, | |
| 338 | 0x3fcebfbe00000000, | |
| 339 | 0x3fac6b3480000000, | |
| 340 | 0x3f83b2c9c0000000, | |
| 341 | 0x47e0000000000000, | |
| 342 | 0xb6a0000000000000, | |
| 343 | }; | |
| 344 | ||
| 345 | for (exp2ft) |x, i| { | |
| 346 | try expect(@bitCast(u64, x) == answers[i]); | |
| 347 | } | |
| 348 | } | |
| 349 | }; | |
| 350 | try S.doTheTest(); | |
| 351 | comptime try S.doTheTest(); | |
| 352 | } | |
| 353 | ||
| 354 | test "truncating shift left" { | |
| 355 | try testShlTrunc(maxInt(u16)); | |
| 356 | comptime try testShlTrunc(maxInt(u16)); | |
| 357 | } | |
| 358 | fn testShlTrunc(x: u16) !void { | |
| 359 | const shifted = x << 1; | |
| 360 | try expect(shifted == 65534); | |
| 361 | } | |
| 362 | ||
| 363 | test "truncating shift right" { | |
| 364 | try testShrTrunc(maxInt(u16)); | |
| 365 | comptime try testShrTrunc(maxInt(u16)); | |
| 366 | } | |
| 367 | fn testShrTrunc(x: u16) !void { | |
| 368 | const shifted = x >> 1; | |
| 369 | try expect(shifted == 32767); | |
| 370 | } | |
| 371 | ||
| 372 | test "exact shift left" { | |
| 373 | try testShlExact(0b00110101); | |
| 374 | comptime try testShlExact(0b00110101); | |
| 375 | } | |
| 376 | fn testShlExact(x: u8) !void { | |
| 377 | const shifted = @shlExact(x, 2); | |
| 378 | try expect(shifted == 0b11010100); | |
| 379 | } | |
| 380 | ||
| 381 | test "exact shift right" { | |
| 382 | try testShrExact(0b10110100); | |
| 383 | comptime try testShrExact(0b10110100); | |
| 384 | } | |
| 385 | fn testShrExact(x: u8) !void { | |
| 386 | const shifted = @shrExact(x, 2); | |
| 387 | try expect(shifted == 0b00101101); | |
| 388 | } | |
| 389 | ||
| 390 | test "shift left/right on u0 operand" { | |
| 391 | const S = struct { | |
| 392 | fn doTheTest() !void { | |
| 393 | var x: u0 = 0; | |
| 394 | var y: u0 = 0; | |
| 395 | try expectEqual(@as(u0, 0), x << 0); | |
| 396 | try expectEqual(@as(u0, 0), x >> 0); | |
| 397 | try expectEqual(@as(u0, 0), x << y); | |
| 398 | try expectEqual(@as(u0, 0), x >> y); | |
| 399 | try expectEqual(@as(u0, 0), @shlExact(x, 0)); | |
| 400 | try expectEqual(@as(u0, 0), @shrExact(x, 0)); | |
| 401 | try expectEqual(@as(u0, 0), @shlExact(x, y)); | |
| 402 | try expectEqual(@as(u0, 0), @shrExact(x, y)); | |
| 403 | } | |
| 404 | }; | |
| 405 | try S.doTheTest(); | |
| 406 | comptime try S.doTheTest(); | |
| 407 | } | |
| 408 | ||
| 409 | test "f128" { | |
| 410 | try test_f128(); | |
| 411 | comptime try test_f128(); | |
| 412 | } | |
| 413 | ||
| 414 | fn make_f128(x: f128) f128 { | |
| 415 | return x; | |
| 416 | } | |
| 417 | ||
| 418 | fn test_f128() !void { | |
| 419 | try expect(@sizeOf(f128) == 16); | |
| 420 | try expect(make_f128(1.0) == 1.0); | |
| 421 | try expect(make_f128(1.0) != 1.1); | |
| 422 | try expect(make_f128(1.0) > 0.9); | |
| 423 | try expect(make_f128(1.0) >= 0.9); | |
| 424 | try expect(make_f128(1.0) >= 1.0); | |
| 425 | try should_not_be_zero(1.0); | |
| 426 | } | |
| 427 | ||
| 428 | fn should_not_be_zero(x: f128) !void { | |
| 429 | try expect(x != 0.0); | |
| 430 | } | |
| 431 | ||
| 432 | test "comptime float rem int" { | |
| 433 | comptime { | |
| 434 | var x = @as(f32, 1) % 2; | |
| 435 | try expect(x == 1.0); | |
| 436 | } | |
| 437 | } | |
| 438 | ||
| 439 | test "remainder division" { | |
| 440 | comptime try remdiv(f16); | |
| 441 | comptime try remdiv(f32); | |
| 442 | comptime try remdiv(f64); | |
| 443 | comptime try remdiv(f128); | |
| 444 | try remdiv(f16); | |
| 445 | try remdiv(f64); | |
| 446 | try remdiv(f128); | |
| 447 | } | |
| 448 | ||
| 449 | fn remdiv(comptime T: type) !void { | |
| 450 | try expect(@as(T, 1) == @as(T, 1) % @as(T, 2)); | |
| 451 | try expect(@as(T, 1) == @as(T, 7) % @as(T, 3)); | |
| 452 | } | |
| 453 | ||
| 454 | test "@sqrt" { | |
| 455 | try testSqrt(f64, 12.0); | |
| 456 | comptime try testSqrt(f64, 12.0); | |
| 457 | try testSqrt(f32, 13.0); | |
| 458 | comptime try testSqrt(f32, 13.0); | |
| 459 | try testSqrt(f16, 13.0); | |
| 460 | comptime try testSqrt(f16, 13.0); | |
| 461 | ||
| 462 | const x = 14.0; | |
| 463 | const y = x * x; | |
| 464 | const z = @sqrt(y); | |
| 465 | comptime try expect(z == x); | |
| 466 | } | |
| 467 | ||
| 468 | fn testSqrt(comptime T: type, x: T) !void { | |
| 469 | try expect(@sqrt(x * x) == x); | |
| 470 | } | |
| 471 | ||
| 472 | test "@fabs" { | |
| 473 | try testFabs(f128, 12.0); | |
| 474 | comptime try testFabs(f128, 12.0); | |
| 475 | try testFabs(f64, 12.0); | |
| 476 | comptime try testFabs(f64, 12.0); | |
| 477 | try testFabs(f32, 12.0); | |
| 478 | comptime try testFabs(f32, 12.0); | |
| 479 | try testFabs(f16, 12.0); | |
| 480 | comptime try testFabs(f16, 12.0); | |
| 481 | ||
| 482 | const x = 14.0; | |
| 483 | const y = -x; | |
| 484 | const z = @fabs(y); | |
| 485 | comptime try expectEqual(x, z); | |
| 486 | } | |
| 487 | ||
| 488 | fn testFabs(comptime T: type, x: T) !void { | |
| 489 | const y = -x; | |
| 490 | const z = @fabs(y); | |
| 491 | try expectEqual(x, z); | |
| 492 | } | |
| 493 | ||
| 494 | test "@floor" { | |
| 495 | // FIXME: Generates a floorl function call | |
| 496 | // testFloor(f128, 12.0); | |
| 497 | comptime try testFloor(f128, 12.0); | |
| 498 | try testFloor(f64, 12.0); | |
| 499 | comptime try testFloor(f64, 12.0); | |
| 500 | try testFloor(f32, 12.0); | |
| 501 | comptime try testFloor(f32, 12.0); | |
| 502 | try testFloor(f16, 12.0); | |
| 503 | comptime try testFloor(f16, 12.0); | |
| 504 | ||
| 505 | const x = 14.0; | |
| 506 | const y = x + 0.7; | |
| 507 | const z = @floor(y); | |
| 508 | comptime try expectEqual(x, z); | |
| 509 | } | |
| 510 | ||
| 511 | fn testFloor(comptime T: type, x: T) !void { | |
| 512 | const y = x + 0.6; | |
| 513 | const z = @floor(y); | |
| 514 | try expectEqual(x, z); | |
| 515 | } | |
| 516 | ||
| 517 | test "@ceil" { | |
| 518 | // FIXME: Generates a ceill function call | |
| 519 | //testCeil(f128, 12.0); | |
| 520 | comptime try testCeil(f128, 12.0); | |
| 521 | try testCeil(f64, 12.0); | |
| 522 | comptime try testCeil(f64, 12.0); | |
| 523 | try testCeil(f32, 12.0); | |
| 524 | comptime try testCeil(f32, 12.0); | |
| 525 | try testCeil(f16, 12.0); | |
| 526 | comptime try testCeil(f16, 12.0); | |
| 527 | ||
| 528 | const x = 14.0; | |
| 529 | const y = x - 0.7; | |
| 530 | const z = @ceil(y); | |
| 531 | comptime try expectEqual(x, z); | |
| 532 | } | |
| 533 | ||
| 534 | fn testCeil(comptime T: type, x: T) !void { | |
| 535 | const y = x - 0.8; | |
| 536 | const z = @ceil(y); | |
| 537 | try expectEqual(x, z); | |
| 538 | } | |
| 539 | ||
| 540 | test "@trunc" { | |
| 541 | // FIXME: Generates a truncl function call | |
| 542 | //testTrunc(f128, 12.0); | |
| 543 | comptime try testTrunc(f128, 12.0); | |
| 544 | try testTrunc(f64, 12.0); | |
| 545 | comptime try testTrunc(f64, 12.0); | |
| 546 | try testTrunc(f32, 12.0); | |
| 547 | comptime try testTrunc(f32, 12.0); | |
| 548 | try testTrunc(f16, 12.0); | |
| 549 | comptime try testTrunc(f16, 12.0); | |
| 550 | ||
| 551 | const x = 14.0; | |
| 552 | const y = x + 0.7; | |
| 553 | const z = @trunc(y); | |
| 554 | comptime try expectEqual(x, z); | |
| 555 | } | |
| 556 | ||
| 557 | fn testTrunc(comptime T: type, x: T) !void { | |
| 558 | { | |
| 559 | const y = x + 0.8; | |
| 560 | const z = @trunc(y); | |
| 561 | try expectEqual(x, z); | |
| 562 | } | |
| 563 | ||
| 564 | { | |
| 565 | const y = -x - 0.8; | |
| 566 | const z = @trunc(y); | |
| 567 | try expectEqual(-x, z); | |
| 568 | } | |
| 569 | } | |
| 570 | ||
| 571 | test "@round" { | |
| 572 | // FIXME: Generates a roundl function call | |
| 573 | //testRound(f128, 12.0); | |
| 574 | comptime try testRound(f128, 12.0); | |
| 575 | try testRound(f64, 12.0); | |
| 576 | comptime try testRound(f64, 12.0); | |
| 577 | try testRound(f32, 12.0); | |
| 578 | comptime try testRound(f32, 12.0); | |
| 579 | try testRound(f16, 12.0); | |
| 580 | comptime try testRound(f16, 12.0); | |
| 581 | ||
| 582 | const x = 14.0; | |
| 583 | const y = x + 0.4; | |
| 584 | const z = @round(y); | |
| 585 | comptime try expectEqual(x, z); | |
| 586 | } | |
| 587 | ||
| 588 | fn testRound(comptime T: type, x: T) !void { | |
| 589 | const y = x - 0.5; | |
| 590 | const z = @round(y); | |
| 591 | try expectEqual(x, z); | |
| 592 | } | |
| 593 | ||
| 594 | test "vector integer addition" { | |
| 595 | const S = struct { | |
| 596 | fn doTheTest() !void { | |
| 597 | var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; | |
| 598 | var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; | |
| 599 | var result = a + b; | |
| 600 | var result_array: [4]i32 = result; | |
| 601 | const expected = [_]i32{ 6, 8, 10, 12 }; | |
| 602 | try expectEqualSlices(i32, &expected, &result_array); | |
| 603 | } | |
| 604 | }; | |
| 605 | try S.doTheTest(); | |
| 606 | comptime try S.doTheTest(); | |
| 607 | } | |
| 608 | ||
| 609 | test "NaN comparison" { | |
| 610 | try testNanEqNan(f16); | |
| 611 | try testNanEqNan(f32); | |
| 612 | try testNanEqNan(f64); | |
| 613 | try testNanEqNan(f128); | |
| 614 | comptime try testNanEqNan(f16); | |
| 615 | comptime try testNanEqNan(f32); | |
| 616 | comptime try testNanEqNan(f64); | |
| 617 | comptime try testNanEqNan(f128); | |
| 618 | } | |
| 619 | ||
| 620 | fn testNanEqNan(comptime F: type) !void { | |
| 621 | var nan1 = std.math.nan(F); | |
| 622 | var nan2 = std.math.nan(F); | |
| 623 | try expect(nan1 != nan2); | |
| 624 | try expect(!(nan1 == nan2)); | |
| 625 | try expect(!(nan1 > nan2)); | |
| 626 | try expect(!(nan1 >= nan2)); | |
| 627 | try expect(!(nan1 < nan2)); | |
| 628 | try expect(!(nan1 <= nan2)); | |
| 629 | } | |
| 630 | ||
| 631 | test "128-bit multiplication" { | |
| 632 | var a: i128 = 3; | |
| 633 | var b: i128 = 2; | |
| 634 | var c = a * b; | |
| 635 | try expect(c == 6); | |
| 636 | } | |
| 637 | ||
| 638 | test "vector comparison" { | |
| 639 | const S = struct { | |
| 640 | fn doTheTest() !void { | |
| 641 | var a: std.meta.Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 }; | |
| 642 | var b: std.meta.Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 }; | |
| 643 | try expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false })); | |
| 644 | try expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false })); | |
| 645 | try expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false })); | |
| 646 | try expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true })); | |
| 647 | try expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true })); | |
| 648 | try expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true })); | |
| 649 | } | |
| 650 | }; | |
| 651 | try S.doTheTest(); | |
| 652 | comptime try S.doTheTest(); | |
| 653 | } | |
| 654 | ||
| 655 | test "compare undefined literal with comptime_int" { | |
| 656 | var x = undefined == 1; | |
| 657 | // x is now undefined with type bool | |
| 658 | x = true; | |
| 659 | try expect(x); | |
| 660 | } | |
| 661 | ||
| 662 | test "signed zeros are represented properly" { | |
| 663 | const S = struct { | |
| 664 | fn doTheTest() !void { | |
| 665 | inline for ([_]type{ f16, f32, f64, f128 }) |T| { | |
| 666 | const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | |
| 667 | var as_fp_val = -@as(T, 0.0); | |
| 668 | var as_uint_val = @bitCast(ST, as_fp_val); | |
| 669 | // Ensure the sign bit is set. | |
| 670 | try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); | |
| 671 | } | |
| 672 | } | |
| 673 | }; | |
| 674 | ||
| 675 | try S.doTheTest(); | |
| 676 | comptime try S.doTheTest(); | |
| 677 | } |
test/behavior/member_func.zig created+103| ... | ... | @@ -0,0 +1,103 @@ |
| 1 | const expect = @import("std").testing.expect; | |
| 2 | ||
| 3 | const HasFuncs = struct { | |
| 4 | state: u32, | |
| 5 | func_field: fn (u32) u32, | |
| 6 | ||
| 7 | fn inc(self: *HasFuncs) void { | |
| 8 | self.state += 1; | |
| 9 | } | |
| 10 | ||
| 11 | fn get(self: HasFuncs) u32 { | |
| 12 | return self.state; | |
| 13 | } | |
| 14 | ||
| 15 | fn getPtr(self: *const HasFuncs) *const u32 { | |
| 16 | return &self.state; | |
| 17 | } | |
| 18 | ||
| 19 | fn one(_: u32) u32 { | |
| 20 | return 1; | |
| 21 | } | |
| 22 | fn two(_: u32) u32 { | |
| 23 | return 2; | |
| 24 | } | |
| 25 | }; | |
| 26 | ||
| 27 | test "standard field calls" { | |
| 28 | try expect(HasFuncs.one(0) == 1); | |
| 29 | try expect(HasFuncs.two(0) == 2); | |
| 30 | ||
| 31 | var v: HasFuncs = undefined; | |
| 32 | v.state = 0; | |
| 33 | v.func_field = HasFuncs.one; | |
| 34 | ||
| 35 | const pv = &v; | |
| 36 | const pcv: *const HasFuncs = pv; | |
| 37 | ||
| 38 | try expect(v.get() == 0); | |
| 39 | v.inc(); | |
| 40 | try expect(v.state == 1); | |
| 41 | try expect(v.get() == 1); | |
| 42 | ||
| 43 | pv.inc(); | |
| 44 | try expect(v.state == 2); | |
| 45 | try expect(pv.get() == 2); | |
| 46 | try expect(v.getPtr().* == 2); | |
| 47 | try expect(pcv.get() == 2); | |
| 48 | try expect(pcv.getPtr().* == 2); | |
| 49 | ||
| 50 | v.func_field = HasFuncs.one; | |
| 51 | try expect(v.func_field(0) == 1); | |
| 52 | try expect(pv.func_field(0) == 1); | |
| 53 | try expect(pcv.func_field(0) == 1); | |
| 54 | ||
| 55 | try expect(pcv.func_field(blk: { | |
| 56 | pv.func_field = HasFuncs.two; | |
| 57 | break :blk 0; | |
| 58 | }) == 1); | |
| 59 | ||
| 60 | v.func_field = HasFuncs.two; | |
| 61 | try expect(v.func_field(0) == 2); | |
| 62 | try expect(pv.func_field(0) == 2); | |
| 63 | try expect(pcv.func_field(0) == 2); | |
| 64 | } | |
| 65 | ||
| 66 | test "@field field calls" { | |
| 67 | try expect(@field(HasFuncs, "one")(0) == 1); | |
| 68 | try expect(@field(HasFuncs, "two")(0) == 2); | |
| 69 | ||
| 70 | var v: HasFuncs = undefined; | |
| 71 | v.state = 0; | |
| 72 | v.func_field = HasFuncs.one; | |
| 73 | ||
| 74 | const pv = &v; | |
| 75 | const pcv: *const HasFuncs = pv; | |
| 76 | ||
| 77 | try expect(@field(v, "get")() == 0); | |
| 78 | @field(v, "inc")(); | |
| 79 | try expect(v.state == 1); | |
| 80 | try expect(@field(v, "get")() == 1); | |
| 81 | ||
| 82 | @field(pv, "inc")(); | |
| 83 | try expect(v.state == 2); | |
| 84 | try expect(@field(pv, "get")() == 2); | |
| 85 | try expect(@field(v, "getPtr")().* == 2); | |
| 86 | try expect(@field(pcv, "get")() == 2); | |
| 87 | try expect(@field(pcv, "getPtr")().* == 2); | |
| 88 | ||
| 89 | v.func_field = HasFuncs.one; | |
| 90 | try expect(@field(v, "func_field")(0) == 1); | |
| 91 | try expect(@field(pv, "func_field")(0) == 1); | |
| 92 | try expect(@field(pcv, "func_field")(0) == 1); | |
| 93 | ||
| 94 | try expect(@field(pcv, "func_field")(blk: { | |
| 95 | pv.func_field = HasFuncs.two; | |
| 96 | break :blk 0; | |
| 97 | }) == 1); | |
| 98 | ||
| 99 | v.func_field = HasFuncs.two; | |
| 100 | try expect(@field(v, "func_field")(0) == 2); | |
| 101 | try expect(@field(pv, "func_field")(0) == 2); | |
| 102 | try expect(@field(pcv, "func_field")(0) == 2); | |
| 103 | } |
test/behavior/misc.zig-10| ... | ... | @@ -5,16 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | 7 | |
| 8 | test "memcpy and memset intrinsics" { | |
| 9 | var foo: [20]u8 = undefined; | |
| 10 | var bar: [20]u8 = undefined; | |
| 11 | ||
| 12 | @memset(&foo, 'A', foo.len); | |
| 13 | @memcpy(&bar, &foo, bar.len); | |
| 14 | ||
| 15 | if (bar[11] != 'A') unreachable; | |
| 16 | } | |
| 17 | ||
| 18 | 8 | test "slicing" { |
| 19 | 9 | var array: [20]i32 = undefined; |
| 20 | 10 |
test/behavior/saturating_arithmetic.zig+28-31| ... | ... | @@ -11,16 +11,28 @@ fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void { |
| 11 | 11 | const a = test_data[0]; |
| 12 | 12 | const b = test_data[1]; |
| 13 | 13 | const expected = test_data[2]; |
| 14 | const actual = switch (op) { | |
| 15 | .add => @addWithSaturation(a, b), | |
| 16 | .sub => @subWithSaturation(a, b), | |
| 17 | .mul => @mulWithSaturation(a, b), | |
| 18 | .shl => @shlWithSaturation(a, b), | |
| 19 | }; | |
| 20 | try expectEqual(expected, actual); | |
| 14 | { | |
| 15 | const actual = switch (op) { | |
| 16 | .add => a +| b, | |
| 17 | .sub => a -| b, | |
| 18 | .mul => a *| b, | |
| 19 | .shl => a <<| b, | |
| 20 | }; | |
| 21 | try expectEqual(expected, actual); | |
| 22 | } | |
| 23 | { | |
| 24 | var actual = a; | |
| 25 | switch (op) { | |
| 26 | .add => actual +|= b, | |
| 27 | .sub => actual -|= b, | |
| 28 | .mul => actual *|= b, | |
| 29 | .shl => actual <<|= b, | |
| 30 | } | |
| 31 | try expectEqual(expected, actual); | |
| 32 | } | |
| 21 | 33 | } |
| 22 | 34 | |
| 23 | test "@addWithSaturation" { | |
| 35 | test "saturating add" { | |
| 24 | 36 | const S = struct { |
| 25 | 37 | fn doTheTest() !void { |
| 26 | 38 | // .{a, b, expected a+b} |
| ... | ... | @@ -38,22 +50,16 @@ test "@addWithSaturation" { |
| 38 | 50 | try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) }); |
| 39 | 51 | |
| 40 | 52 | const u8x3 = std.meta.Vector(3, u8); |
| 41 | try expectEqual(u8x3{ 255, 255, 255 }, @addWithSaturation( | |
| 42 | u8x3{ 255, 254, 1 }, | |
| 43 | u8x3{ 1, 2, 255 }, | |
| 44 | )); | |
| 53 | try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 })); | |
| 45 | 54 | const i8x3 = std.meta.Vector(3, i8); |
| 46 | try expectEqual(i8x3{ 127, 127, 127 }, @addWithSaturation( | |
| 47 | i8x3{ 127, 126, 1 }, | |
| 48 | i8x3{ 1, 2, 127 }, | |
| 49 | )); | |
| 55 | try expectEqual(i8x3{ 127, 127, 127 }, (i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 })); | |
| 50 | 56 | } |
| 51 | 57 | }; |
| 52 | 58 | try S.doTheTest(); |
| 53 | 59 | comptime try S.doTheTest(); |
| 54 | 60 | } |
| 55 | 61 | |
| 56 | test "@subWithSaturation" { | |
| 62 | test "saturating subtraction" { | |
| 57 | 63 | const S = struct { |
| 58 | 64 | fn doTheTest() !void { |
| 59 | 65 | // .{a, b, expected a-b} |
| ... | ... | @@ -69,17 +75,14 @@ test "@subWithSaturation" { |
| 69 | 75 | try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 }); |
| 70 | 76 | |
| 71 | 77 | const u8x3 = std.meta.Vector(3, u8); |
| 72 | try expectEqual(u8x3{ 0, 0, 0 }, @subWithSaturation( | |
| 73 | u8x3{ 0, 0, 0 }, | |
| 74 | u8x3{ 255, 255, 255 }, | |
| 75 | )); | |
| 78 | try expectEqual(u8x3{ 0, 0, 0 }, (u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 })); | |
| 76 | 79 | } |
| 77 | 80 | }; |
| 78 | 81 | try S.doTheTest(); |
| 79 | 82 | comptime try S.doTheTest(); |
| 80 | 83 | } |
| 81 | 84 | |
| 82 | test "@mulWithSaturation" { | |
| 85 | test "saturating multiplication" { | |
| 83 | 86 | // TODO: once #9660 has been solved, remove this line |
| 84 | 87 | if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest; |
| 85 | 88 | |
| ... | ... | @@ -100,10 +103,7 @@ test "@mulWithSaturation" { |
| 100 | 103 | try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) }); |
| 101 | 104 | |
| 102 | 105 | const u8x3 = std.meta.Vector(3, u8); |
| 103 | try expectEqual(u8x3{ 255, 255, 255 }, @mulWithSaturation( | |
| 104 | u8x3{ 2, 2, 2 }, | |
| 105 | u8x3{ 255, 255, 255 }, | |
| 106 | )); | |
| 106 | try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 })); | |
| 107 | 107 | } |
| 108 | 108 | }; |
| 109 | 109 | |
| ... | ... | @@ -111,7 +111,7 @@ test "@mulWithSaturation" { |
| 111 | 111 | comptime try S.doTheTest(); |
| 112 | 112 | } |
| 113 | 113 | |
| 114 | test "@shlWithSaturation" { | |
| 114 | test "saturating shift-left" { | |
| 115 | 115 | const S = struct { |
| 116 | 116 | fn doTheTest() !void { |
| 117 | 117 | // .{a, b, expected a<<b} |
| ... | ... | @@ -128,10 +128,7 @@ test "@shlWithSaturation" { |
| 128 | 128 | try testSaturatingOp(.shl, u8, .{ 255, 1, 255 }); |
| 129 | 129 | |
| 130 | 130 | const u8x3 = std.meta.Vector(3, u8); |
| 131 | try expectEqual(u8x3{ 255, 255, 255 }, @shlWithSaturation( | |
| 132 | u8x3{ 255, 255, 255 }, | |
| 133 | u8x3{ 1, 1, 1 }, | |
| 134 | )); | |
| 131 | try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 })); | |
| 135 | 132 | } |
| 136 | 133 | }; |
| 137 | 134 | try S.doTheTest(); |
test/behavior/sizeof_and_typeof.zig-214| ... | ... | @@ -10,118 +10,6 @@ test "@sizeOf and @TypeOf" { |
| 10 | 10 | const x: u16 = 13; |
| 11 | 11 | const z: @TypeOf(x) = 19; |
| 12 | 12 | |
| 13 | const A = struct { | |
| 14 | a: u8, | |
| 15 | b: u32, | |
| 16 | c: u8, | |
| 17 | d: u3, | |
| 18 | e: u5, | |
| 19 | f: u16, | |
| 20 | g: u16, | |
| 21 | h: u9, | |
| 22 | i: u7, | |
| 23 | }; | |
| 24 | ||
| 25 | const P = packed struct { | |
| 26 | a: u8, | |
| 27 | b: u32, | |
| 28 | c: u8, | |
| 29 | d: u3, | |
| 30 | e: u5, | |
| 31 | f: u16, | |
| 32 | g: u16, | |
| 33 | h: u9, | |
| 34 | i: u7, | |
| 35 | }; | |
| 36 | ||
| 37 | test "@offsetOf" { | |
| 38 | // Packed structs have fixed memory layout | |
| 39 | try expect(@offsetOf(P, "a") == 0); | |
| 40 | try expect(@offsetOf(P, "b") == 1); | |
| 41 | try expect(@offsetOf(P, "c") == 5); | |
| 42 | try expect(@offsetOf(P, "d") == 6); | |
| 43 | try expect(@offsetOf(P, "e") == 6); | |
| 44 | try expect(@offsetOf(P, "f") == 7); | |
| 45 | try expect(@offsetOf(P, "g") == 9); | |
| 46 | try expect(@offsetOf(P, "h") == 11); | |
| 47 | try expect(@offsetOf(P, "i") == 12); | |
| 48 | ||
| 49 | // Normal struct fields can be moved/padded | |
| 50 | var a: A = undefined; | |
| 51 | try expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a")); | |
| 52 | try expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(A, "b")); | |
| 53 | try expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(A, "c")); | |
| 54 | try expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @offsetOf(A, "d")); | |
| 55 | try expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @offsetOf(A, "e")); | |
| 56 | try expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @offsetOf(A, "f")); | |
| 57 | try expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @offsetOf(A, "g")); | |
| 58 | try expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @offsetOf(A, "h")); | |
| 59 | try expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @offsetOf(A, "i")); | |
| 60 | } | |
| 61 | ||
| 62 | test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" { | |
| 63 | const p3a_len = 3; | |
| 64 | const P3 = packed struct { | |
| 65 | a: [p3a_len]u8, | |
| 66 | b: usize, | |
| 67 | }; | |
| 68 | try std.testing.expectEqual(0, @offsetOf(P3, "a")); | |
| 69 | try std.testing.expectEqual(p3a_len, @offsetOf(P3, "b")); | |
| 70 | ||
| 71 | const p5a_len = 5; | |
| 72 | const P5 = packed struct { | |
| 73 | a: [p5a_len]u8, | |
| 74 | b: usize, | |
| 75 | }; | |
| 76 | try std.testing.expectEqual(0, @offsetOf(P5, "a")); | |
| 77 | try std.testing.expectEqual(p5a_len, @offsetOf(P5, "b")); | |
| 78 | ||
| 79 | const p6a_len = 6; | |
| 80 | const P6 = packed struct { | |
| 81 | a: [p6a_len]u8, | |
| 82 | b: usize, | |
| 83 | }; | |
| 84 | try std.testing.expectEqual(0, @offsetOf(P6, "a")); | |
| 85 | try std.testing.expectEqual(p6a_len, @offsetOf(P6, "b")); | |
| 86 | ||
| 87 | const p7a_len = 7; | |
| 88 | const P7 = packed struct { | |
| 89 | a: [p7a_len]u8, | |
| 90 | b: usize, | |
| 91 | }; | |
| 92 | try std.testing.expectEqual(0, @offsetOf(P7, "a")); | |
| 93 | try std.testing.expectEqual(p7a_len, @offsetOf(P7, "b")); | |
| 94 | ||
| 95 | const p9a_len = 9; | |
| 96 | const P9 = packed struct { | |
| 97 | a: [p9a_len]u8, | |
| 98 | b: usize, | |
| 99 | }; | |
| 100 | try std.testing.expectEqual(0, @offsetOf(P9, "a")); | |
| 101 | try std.testing.expectEqual(p9a_len, @offsetOf(P9, "b")); | |
| 102 | ||
| 103 | // 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases | |
| 104 | } | |
| 105 | ||
| 106 | test "@bitOffsetOf" { | |
| 107 | // Packed structs have fixed memory layout | |
| 108 | try expect(@bitOffsetOf(P, "a") == 0); | |
| 109 | try expect(@bitOffsetOf(P, "b") == 8); | |
| 110 | try expect(@bitOffsetOf(P, "c") == 40); | |
| 111 | try expect(@bitOffsetOf(P, "d") == 48); | |
| 112 | try expect(@bitOffsetOf(P, "e") == 51); | |
| 113 | try expect(@bitOffsetOf(P, "f") == 56); | |
| 114 | try expect(@bitOffsetOf(P, "g") == 72); | |
| 115 | ||
| 116 | try expect(@offsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); | |
| 117 | try expect(@offsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); | |
| 118 | try expect(@offsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); | |
| 119 | try expect(@offsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); | |
| 120 | try expect(@offsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); | |
| 121 | try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); | |
| 122 | try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); | |
| 123 | } | |
| 124 | ||
| 125 | 13 | test "@sizeOf on compile-time types" { |
| 126 | 14 | try expect(@sizeOf(comptime_int) == 0); |
| 127 | 15 | try expect(@sizeOf(comptime_float) == 0); |
| ... | ... | @@ -129,34 +17,6 @@ test "@sizeOf on compile-time types" { |
| 129 | 17 | try expect(@sizeOf(@TypeOf(type)) == 0); |
| 130 | 18 | } |
| 131 | 19 | |
| 132 | test "@sizeOf(T) == 0 doesn't force resolving struct size" { | |
| 133 | const S = struct { | |
| 134 | const Foo = struct { | |
| 135 | y: if (@sizeOf(Foo) == 0) u64 else u32, | |
| 136 | }; | |
| 137 | const Bar = struct { | |
| 138 | x: i32, | |
| 139 | y: if (0 == @sizeOf(Bar)) u64 else u32, | |
| 140 | }; | |
| 141 | }; | |
| 142 | ||
| 143 | try expect(@sizeOf(S.Foo) == 4); | |
| 144 | try expect(@sizeOf(S.Bar) == 8); | |
| 145 | } | |
| 146 | ||
| 147 | test "@TypeOf() has no runtime side effects" { | |
| 148 | const S = struct { | |
| 149 | fn foo(comptime T: type, ptr: *T) T { | |
| 150 | ptr.* += 1; | |
| 151 | return ptr.*; | |
| 152 | } | |
| 153 | }; | |
| 154 | var data: i32 = 0; | |
| 155 | const T = @TypeOf(S.foo(i32, &data)); | |
| 156 | comptime try expect(T == i32); | |
| 157 | try expect(data == 0); | |
| 158 | } | |
| 159 | ||
| 160 | 20 | test "@TypeOf() with multiple arguments" { |
| 161 | 21 | { |
| 162 | 22 | var var_1: u32 = undefined; |
| ... | ... | @@ -180,19 +40,6 @@ test "@TypeOf() with multiple arguments" { |
| 180 | 40 | } |
| 181 | 41 | } |
| 182 | 42 | |
| 183 | test "branching logic inside @TypeOf" { | |
| 184 | const S = struct { | |
| 185 | var data: i32 = 0; | |
| 186 | fn foo() anyerror!i32 { | |
| 187 | data += 1; | |
| 188 | return undefined; | |
| 189 | } | |
| 190 | }; | |
| 191 | const T = @TypeOf(S.foo() catch undefined); | |
| 192 | comptime try expect(T == i32); | |
| 193 | try expect(S.data == 0); | |
| 194 | } | |
| 195 | ||
| 196 | 43 | fn fn1(alpha: bool) void { |
| 197 | 44 | const n: usize = 7; |
| 198 | 45 | _ = if (alpha) n else @sizeOf(usize); |
| ... | ... | @@ -201,64 +48,3 @@ fn fn1(alpha: bool) void { |
| 201 | 48 | test "lazy @sizeOf result is checked for definedness" { |
| 202 | 49 | _ = fn1; |
| 203 | 50 | } |
| 204 | ||
| 205 | test "@bitSizeOf" { | |
| 206 | try expect(@bitSizeOf(u2) == 2); | |
| 207 | try expect(@bitSizeOf(u8) == @sizeOf(u8) * 8); | |
| 208 | try expect(@bitSizeOf(struct { | |
| 209 | a: u2, | |
| 210 | }) == 8); | |
| 211 | try expect(@bitSizeOf(packed struct { | |
| 212 | a: u2, | |
| 213 | }) == 2); | |
| 214 | } | |
| 215 | ||
| 216 | test "@sizeOf comparison against zero" { | |
| 217 | const S0 = struct { | |
| 218 | f: *@This(), | |
| 219 | }; | |
| 220 | const U0 = union { | |
| 221 | f: *@This(), | |
| 222 | }; | |
| 223 | const S1 = struct { | |
| 224 | fn H(comptime T: type) type { | |
| 225 | return struct { | |
| 226 | x: T, | |
| 227 | }; | |
| 228 | } | |
| 229 | f0: H(*@This()), | |
| 230 | f1: H(**@This()), | |
| 231 | f2: H(***@This()), | |
| 232 | }; | |
| 233 | const U1 = union { | |
| 234 | fn H(comptime T: type) type { | |
| 235 | return struct { | |
| 236 | x: T, | |
| 237 | }; | |
| 238 | } | |
| 239 | f0: H(*@This()), | |
| 240 | f1: H(**@This()), | |
| 241 | f2: H(***@This()), | |
| 242 | }; | |
| 243 | const S = struct { | |
| 244 | fn doTheTest(comptime T: type, comptime result: bool) !void { | |
| 245 | try expectEqual(result, @sizeOf(T) > 0); | |
| 246 | } | |
| 247 | }; | |
| 248 | // Zero-sized type | |
| 249 | try S.doTheTest(u0, false); | |
| 250 | try S.doTheTest(*u0, false); | |
| 251 | // Non byte-sized type | |
| 252 | try S.doTheTest(u1, true); | |
| 253 | try S.doTheTest(*u1, true); | |
| 254 | // Regular type | |
| 255 | try S.doTheTest(u8, true); | |
| 256 | try S.doTheTest(*u8, true); | |
| 257 | try S.doTheTest(f32, true); | |
| 258 | try S.doTheTest(*f32, true); | |
| 259 | // Container with ptr pointing to themselves | |
| 260 | try S.doTheTest(S0, true); | |
| 261 | try S.doTheTest(U0, true); | |
| 262 | try S.doTheTest(S1, true); | |
| 263 | try S.doTheTest(U1, true); | |
| 264 | } |
test/behavior/sizeof_and_typeof_stage1.zig created+218| ... | ... | @@ -0,0 +1,218 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = std.builtin; | |
| 3 | const expect = std.testing.expect; | |
| 4 | const expectEqual = std.testing.expectEqual; | |
| 5 | ||
| 6 | const A = struct { | |
| 7 | a: u8, | |
| 8 | b: u32, | |
| 9 | c: u8, | |
| 10 | d: u3, | |
| 11 | e: u5, | |
| 12 | f: u16, | |
| 13 | g: u16, | |
| 14 | h: u9, | |
| 15 | i: u7, | |
| 16 | }; | |
| 17 | ||
| 18 | const P = packed struct { | |
| 19 | a: u8, | |
| 20 | b: u32, | |
| 21 | c: u8, | |
| 22 | d: u3, | |
| 23 | e: u5, | |
| 24 | f: u16, | |
| 25 | g: u16, | |
| 26 | h: u9, | |
| 27 | i: u7, | |
| 28 | }; | |
| 29 | ||
| 30 | test "@offsetOf" { | |
| 31 | // Packed structs have fixed memory layout | |
| 32 | try expect(@offsetOf(P, "a") == 0); | |
| 33 | try expect(@offsetOf(P, "b") == 1); | |
| 34 | try expect(@offsetOf(P, "c") == 5); | |
| 35 | try expect(@offsetOf(P, "d") == 6); | |
| 36 | try expect(@offsetOf(P, "e") == 6); | |
| 37 | try expect(@offsetOf(P, "f") == 7); | |
| 38 | try expect(@offsetOf(P, "g") == 9); | |
| 39 | try expect(@offsetOf(P, "h") == 11); | |
| 40 | try expect(@offsetOf(P, "i") == 12); | |
| 41 | ||
| 42 | // Normal struct fields can be moved/padded | |
| 43 | var a: A = undefined; | |
| 44 | try expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a")); | |
| 45 | try expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(A, "b")); | |
| 46 | try expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(A, "c")); | |
| 47 | try expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @offsetOf(A, "d")); | |
| 48 | try expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @offsetOf(A, "e")); | |
| 49 | try expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @offsetOf(A, "f")); | |
| 50 | try expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @offsetOf(A, "g")); | |
| 51 | try expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @offsetOf(A, "h")); | |
| 52 | try expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @offsetOf(A, "i")); | |
| 53 | } | |
| 54 | ||
| 55 | test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" { | |
| 56 | const p3a_len = 3; | |
| 57 | const P3 = packed struct { | |
| 58 | a: [p3a_len]u8, | |
| 59 | b: usize, | |
| 60 | }; | |
| 61 | try std.testing.expectEqual(0, @offsetOf(P3, "a")); | |
| 62 | try std.testing.expectEqual(p3a_len, @offsetOf(P3, "b")); | |
| 63 | ||
| 64 | const p5a_len = 5; | |
| 65 | const P5 = packed struct { | |
| 66 | a: [p5a_len]u8, | |
| 67 | b: usize, | |
| 68 | }; | |
| 69 | try std.testing.expectEqual(0, @offsetOf(P5, "a")); | |
| 70 | try std.testing.expectEqual(p5a_len, @offsetOf(P5, "b")); | |
| 71 | ||
| 72 | const p6a_len = 6; | |
| 73 | const P6 = packed struct { | |
| 74 | a: [p6a_len]u8, | |
| 75 | b: usize, | |
| 76 | }; | |
| 77 | try std.testing.expectEqual(0, @offsetOf(P6, "a")); | |
| 78 | try std.testing.expectEqual(p6a_len, @offsetOf(P6, "b")); | |
| 79 | ||
| 80 | const p7a_len = 7; | |
| 81 | const P7 = packed struct { | |
| 82 | a: [p7a_len]u8, | |
| 83 | b: usize, | |
| 84 | }; | |
| 85 | try std.testing.expectEqual(0, @offsetOf(P7, "a")); | |
| 86 | try std.testing.expectEqual(p7a_len, @offsetOf(P7, "b")); | |
| 87 | ||
| 88 | const p9a_len = 9; | |
| 89 | const P9 = packed struct { | |
| 90 | a: [p9a_len]u8, | |
| 91 | b: usize, | |
| 92 | }; | |
| 93 | try std.testing.expectEqual(0, @offsetOf(P9, "a")); | |
| 94 | try std.testing.expectEqual(p9a_len, @offsetOf(P9, "b")); | |
| 95 | ||
| 96 | // 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases | |
| 97 | } | |
| 98 | ||
| 99 | test "@bitOffsetOf" { | |
| 100 | // Packed structs have fixed memory layout | |
| 101 | try expect(@bitOffsetOf(P, "a") == 0); | |
| 102 | try expect(@bitOffsetOf(P, "b") == 8); | |
| 103 | try expect(@bitOffsetOf(P, "c") == 40); | |
| 104 | try expect(@bitOffsetOf(P, "d") == 48); | |
| 105 | try expect(@bitOffsetOf(P, "e") == 51); | |
| 106 | try expect(@bitOffsetOf(P, "f") == 56); | |
| 107 | try expect(@bitOffsetOf(P, "g") == 72); | |
| 108 | ||
| 109 | try expect(@offsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); | |
| 110 | try expect(@offsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); | |
| 111 | try expect(@offsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); | |
| 112 | try expect(@offsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); | |
| 113 | try expect(@offsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); | |
| 114 | try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); | |
| 115 | try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); | |
| 116 | } | |
| 117 | ||
| 118 | test "@sizeOf(T) == 0 doesn't force resolving struct size" { | |
| 119 | const S = struct { | |
| 120 | const Foo = struct { | |
| 121 | y: if (@sizeOf(Foo) == 0) u64 else u32, | |
| 122 | }; | |
| 123 | const Bar = struct { | |
| 124 | x: i32, | |
| 125 | y: if (0 == @sizeOf(Bar)) u64 else u32, | |
| 126 | }; | |
| 127 | }; | |
| 128 | ||
| 129 | try expect(@sizeOf(S.Foo) == 4); | |
| 130 | try expect(@sizeOf(S.Bar) == 8); | |
| 131 | } | |
| 132 | ||
| 133 | test "@TypeOf() has no runtime side effects" { | |
| 134 | const S = struct { | |
| 135 | fn foo(comptime T: type, ptr: *T) T { | |
| 136 | ptr.* += 1; | |
| 137 | return ptr.*; | |
| 138 | } | |
| 139 | }; | |
| 140 | var data: i32 = 0; | |
| 141 | const T = @TypeOf(S.foo(i32, &data)); | |
| 142 | comptime try expect(T == i32); | |
| 143 | try expect(data == 0); | |
| 144 | } | |
| 145 | ||
| 146 | test "branching logic inside @TypeOf" { | |
| 147 | const S = struct { | |
| 148 | var data: i32 = 0; | |
| 149 | fn foo() anyerror!i32 { | |
| 150 | data += 1; | |
| 151 | return undefined; | |
| 152 | } | |
| 153 | }; | |
| 154 | const T = @TypeOf(S.foo() catch undefined); | |
| 155 | comptime try expect(T == i32); | |
| 156 | try expect(S.data == 0); | |
| 157 | } | |
| 158 | ||
| 159 | test "@bitSizeOf" { | |
| 160 | try expect(@bitSizeOf(u2) == 2); | |
| 161 | try expect(@bitSizeOf(u8) == @sizeOf(u8) * 8); | |
| 162 | try expect(@bitSizeOf(struct { | |
| 163 | a: u2, | |
| 164 | }) == 8); | |
| 165 | try expect(@bitSizeOf(packed struct { | |
| 166 | a: u2, | |
| 167 | }) == 2); | |
| 168 | } | |
| 169 | ||
| 170 | test "@sizeOf comparison against zero" { | |
| 171 | const S0 = struct { | |
| 172 | f: *@This(), | |
| 173 | }; | |
| 174 | const U0 = union { | |
| 175 | f: *@This(), | |
| 176 | }; | |
| 177 | const S1 = struct { | |
| 178 | fn H(comptime T: type) type { | |
| 179 | return struct { | |
| 180 | x: T, | |
| 181 | }; | |
| 182 | } | |
| 183 | f0: H(*@This()), | |
| 184 | f1: H(**@This()), | |
| 185 | f2: H(***@This()), | |
| 186 | }; | |
| 187 | const U1 = union { | |
| 188 | fn H(comptime T: type) type { | |
| 189 | return struct { | |
| 190 | x: T, | |
| 191 | }; | |
| 192 | } | |
| 193 | f0: H(*@This()), | |
| 194 | f1: H(**@This()), | |
| 195 | f2: H(***@This()), | |
| 196 | }; | |
| 197 | const S = struct { | |
| 198 | fn doTheTest(comptime T: type, comptime result: bool) !void { | |
| 199 | try expectEqual(result, @sizeOf(T) > 0); | |
| 200 | } | |
| 201 | }; | |
| 202 | // Zero-sized type | |
| 203 | try S.doTheTest(u0, false); | |
| 204 | try S.doTheTest(*u0, false); | |
| 205 | // Non byte-sized type | |
| 206 | try S.doTheTest(u1, true); | |
| 207 | try S.doTheTest(*u1, true); | |
| 208 | // Regular type | |
| 209 | try S.doTheTest(u8, true); | |
| 210 | try S.doTheTest(*u8, true); | |
| 211 | try S.doTheTest(f32, true); | |
| 212 | try S.doTheTest(*f32, true); | |
| 213 | // Container with ptr pointing to themselves | |
| 214 | try S.doTheTest(S0, true); | |
| 215 | try S.doTheTest(U0, true); | |
| 216 | try S.doTheTest(S1, true); | |
| 217 | try S.doTheTest(U1, true); | |
| 218 | } |
test/behavior/struct.zig+49-871| ... | ... | @@ -5,56 +5,38 @@ const expect = std.testing.expect; |
| 5 | 5 | const expectEqual = std.testing.expectEqual; |
| 6 | 6 | const expectEqualSlices = std.testing.expectEqualSlices; |
| 7 | 7 | const maxInt = std.math.maxInt; |
| 8 | ||
| 8 | 9 | const StructWithNoFields = struct { |
| 9 | 10 | fn add(a: i32, b: i32) i32 { |
| 10 | 11 | return a + b; |
| 11 | 12 | } |
| 12 | 13 | }; |
| 13 | const empty_global_instance = StructWithNoFields{}; | |
| 14 | ||
| 15 | top_level_field: i32, | |
| 16 | ||
| 17 | test "top level fields" { | |
| 18 | var instance = @This(){ | |
| 19 | .top_level_field = 1234, | |
| 20 | }; | |
| 21 | instance.top_level_field += 1; | |
| 22 | try expectEqual(@as(i32, 1235), instance.top_level_field); | |
| 23 | } | |
| 24 | 14 | |
| 25 | 15 | test "call struct static method" { |
| 26 | 16 | const result = StructWithNoFields.add(3, 4); |
| 27 | 17 | try expect(result == 7); |
| 28 | 18 | } |
| 29 | 19 | |
| 30 | test "return empty struct instance" { | |
| 31 | _ = returnEmptyStructInstance(); | |
| 32 | } | |
| 33 | fn returnEmptyStructInstance() StructWithNoFields { | |
| 34 | return empty_global_instance; | |
| 35 | } | |
| 36 | ||
| 37 | 20 | const should_be_11 = StructWithNoFields.add(5, 6); |
| 38 | 21 | |
| 39 | 22 | test "invoke static method in global scope" { |
| 40 | 23 | try expect(should_be_11 == 11); |
| 41 | 24 | } |
| 42 | 25 | |
| 43 | test "void struct fields" { | |
| 44 | const foo = VoidStructFieldsFoo{ | |
| 45 | .a = void{}, | |
| 46 | .b = 1, | |
| 47 | .c = void{}, | |
| 48 | }; | |
| 49 | try expect(foo.b == 1); | |
| 50 | try expect(@sizeOf(VoidStructFieldsFoo) == 4); | |
| 26 | const empty_global_instance = StructWithNoFields{}; | |
| 27 | ||
| 28 | test "return empty struct instance" { | |
| 29 | _ = returnEmptyStructInstance(); | |
| 30 | } | |
| 31 | fn returnEmptyStructInstance() StructWithNoFields { | |
| 32 | return empty_global_instance; | |
| 51 | 33 | } |
| 52 | const VoidStructFieldsFoo = struct { | |
| 53 | a: void, | |
| 54 | b: i32, | |
| 55 | c: void, | |
| 56 | }; | |
| 57 | 34 | |
| 35 | const StructFoo = struct { | |
| 36 | a: i32, | |
| 37 | b: bool, | |
| 38 | c: f32, | |
| 39 | }; | |
| 58 | 40 | test "structs" { |
| 59 | 41 | var foo: StructFoo = undefined; |
| 60 | 42 | @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); |
| ... | ... | @@ -64,11 +46,6 @@ test "structs" { |
| 64 | 46 | testMutation(&foo); |
| 65 | 47 | try expect(foo.c == 100); |
| 66 | 48 | } |
| 67 | const StructFoo = struct { | |
| 68 | a: i32, | |
| 69 | b: bool, | |
| 70 | c: f32, | |
| 71 | }; | |
| 72 | 49 | fn testFoo(foo: StructFoo) !void { |
| 73 | 50 | try expect(foo.b); |
| 74 | 51 | } |
| ... | ... | @@ -76,28 +53,6 @@ fn testMutation(foo: *StructFoo) void { |
| 76 | 53 | foo.c = 100; |
| 77 | 54 | } |
| 78 | 55 | |
| 79 | const Node = struct { | |
| 80 | val: Val, | |
| 81 | next: *Node, | |
| 82 | }; | |
| 83 | ||
| 84 | const Val = struct { | |
| 85 | x: i32, | |
| 86 | }; | |
| 87 | ||
| 88 | test "struct point to self" { | |
| 89 | var root: Node = undefined; | |
| 90 | root.val.x = 1; | |
| 91 | ||
| 92 | var node: Node = undefined; | |
| 93 | node.next = &root; | |
| 94 | node.val.x = 2; | |
| 95 | ||
| 96 | root.next = &node; | |
| 97 | ||
| 98 | try expect(node.next.next.next.val.x == 1); | |
| 99 | } | |
| 100 | ||
| 101 | 56 | test "struct byval assign" { |
| 102 | 57 | var foo1: StructFoo = undefined; |
| 103 | 58 | var foo2: StructFoo = undefined; |
| ... | ... | @@ -109,34 +64,20 @@ test "struct byval assign" { |
| 109 | 64 | try expect(foo2.a == 1234); |
| 110 | 65 | } |
| 111 | 66 | |
| 112 | fn structInitializer() void { | |
| 113 | const val = Val{ .x = 42 }; | |
| 114 | try expect(val.x == 42); | |
| 115 | } | |
| 116 | ||
| 117 | test "fn call of struct field" { | |
| 118 | const Foo = struct { | |
| 119 | ptr: fn () i32, | |
| 120 | }; | |
| 121 | const S = struct { | |
| 122 | fn aFunc() i32 { | |
| 123 | return 13; | |
| 124 | } | |
| 67 | const Node = struct { | |
| 68 | val: Val, | |
| 69 | next: *Node, | |
| 70 | }; | |
| 125 | 71 | |
| 126 | fn callStructField(foo: Foo) i32 { | |
| 127 | return foo.ptr(); | |
| 128 | } | |
| 129 | }; | |
| 72 | const Val = struct { | |
| 73 | x: i32, | |
| 74 | }; | |
| 130 | 75 | |
| 131 | try expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13); | |
| 76 | test "struct initializer" { | |
| 77 | const val = Val{ .x = 42 }; | |
| 78 | try expect(val.x == 42); | |
| 132 | 79 | } |
| 133 | 80 | |
| 134 | test "store member function in variable" { | |
| 135 | const instance = MemberFnTestFoo{ .x = 1234 }; | |
| 136 | const memberFn = MemberFnTestFoo.member; | |
| 137 | const result = memberFn(instance); | |
| 138 | try expect(result == 1234); | |
| 139 | } | |
| 140 | 81 | const MemberFnTestFoo = struct { |
| 141 | 82 | x: i32, |
| 142 | 83 | fn member(foo: MemberFnTestFoo) i32 { |
| ... | ... | @@ -150,804 +91,41 @@ test "call member function directly" { |
| 150 | 91 | try expect(result == 1234); |
| 151 | 92 | } |
| 152 | 93 | |
| 153 | test "member functions" { | |
| 154 | const r = MemberFnRand{ .seed = 1234 }; | |
| 155 | try expect(r.getSeed() == 1234); | |
| 156 | } | |
| 157 | const MemberFnRand = struct { | |
| 158 | seed: u32, | |
| 159 | pub fn getSeed(r: *const MemberFnRand) u32 { | |
| 160 | return r.seed; | |
| 161 | } | |
| 162 | }; | |
| 163 | ||
| 164 | test "return struct byval from function" { | |
| 165 | const bar = makeBar2(1234, 5678); | |
| 166 | try expect(bar.y == 5678); | |
| 167 | } | |
| 168 | const Bar = struct { | |
| 169 | x: i32, | |
| 170 | y: i32, | |
| 171 | }; | |
| 172 | fn makeBar2(x: i32, y: i32) Bar { | |
| 173 | return Bar{ | |
| 174 | .x = x, | |
| 175 | .y = y, | |
| 176 | }; | |
| 177 | } | |
| 178 | ||
| 179 | test "empty struct method call" { | |
| 180 | const es = EmptyStruct{}; | |
| 181 | try expect(es.method() == 1234); | |
| 182 | } | |
| 183 | const EmptyStruct = struct { | |
| 184 | fn method(es: *const EmptyStruct) i32 { | |
| 185 | _ = es; | |
| 186 | return 1234; | |
| 187 | } | |
| 188 | }; | |
| 189 | ||
| 190 | test "return empty struct from fn" { | |
| 191 | _ = testReturnEmptyStructFromFn(); | |
| 192 | } | |
| 193 | const EmptyStruct2 = struct {}; | |
| 194 | fn testReturnEmptyStructFromFn() EmptyStruct2 { | |
| 195 | return EmptyStruct2{}; | |
| 196 | } | |
| 197 | ||
| 198 | test "pass slice of empty struct to fn" { | |
| 199 | try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); | |
| 200 | } | |
| 201 | fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { | |
| 202 | return slice.len; | |
| 203 | } | |
| 204 | ||
| 205 | const APackedStruct = packed struct { | |
| 206 | x: u8, | |
| 207 | y: u8, | |
| 208 | }; | |
| 209 | ||
| 210 | test "packed struct" { | |
| 211 | var foo = APackedStruct{ | |
| 212 | .x = 1, | |
| 213 | .y = 2, | |
| 214 | }; | |
| 215 | foo.y += 1; | |
| 216 | const four = foo.x + foo.y; | |
| 217 | try expect(four == 4); | |
| 218 | } | |
| 219 | ||
| 220 | const BitField1 = packed struct { | |
| 221 | a: u3, | |
| 222 | b: u3, | |
| 223 | c: u2, | |
| 224 | }; | |
| 225 | ||
| 226 | const bit_field_1 = BitField1{ | |
| 227 | .a = 1, | |
| 228 | .b = 2, | |
| 229 | .c = 3, | |
| 230 | }; | |
| 231 | ||
| 232 | test "bit field access" { | |
| 233 | var data = bit_field_1; | |
| 234 | try expect(getA(&data) == 1); | |
| 235 | try expect(getB(&data) == 2); | |
| 236 | try expect(getC(&data) == 3); | |
| 237 | comptime try expect(@sizeOf(BitField1) == 1); | |
| 238 | ||
| 239 | data.b += 1; | |
| 240 | try expect(data.b == 3); | |
| 241 | ||
| 242 | data.a += 1; | |
| 243 | try expect(data.a == 2); | |
| 244 | try expect(data.b == 3); | |
| 245 | } | |
| 246 | ||
| 247 | fn getA(data: *const BitField1) u3 { | |
| 248 | return data.a; | |
| 249 | } | |
| 250 | ||
| 251 | fn getB(data: *const BitField1) u3 { | |
| 252 | return data.b; | |
| 253 | } | |
| 254 | ||
| 255 | fn getC(data: *const BitField1) u2 { | |
| 256 | return data.c; | |
| 257 | } | |
| 258 | ||
| 259 | const Foo24Bits = packed struct { | |
| 260 | field: u24, | |
| 261 | }; | |
| 262 | const Foo96Bits = packed struct { | |
| 263 | a: u24, | |
| 264 | b: u24, | |
| 265 | c: u24, | |
| 266 | d: u24, | |
| 267 | }; | |
| 268 | ||
| 269 | test "packed struct 24bits" { | |
| 270 | comptime { | |
| 271 | try expect(@sizeOf(Foo24Bits) == 4); | |
| 272 | if (@sizeOf(usize) == 4) { | |
| 273 | try expect(@sizeOf(Foo96Bits) == 12); | |
| 274 | } else { | |
| 275 | try expect(@sizeOf(Foo96Bits) == 16); | |
| 276 | } | |
| 277 | } | |
| 278 | ||
| 279 | var value = Foo96Bits{ | |
| 280 | .a = 0, | |
| 281 | .b = 0, | |
| 282 | .c = 0, | |
| 283 | .d = 0, | |
| 284 | }; | |
| 285 | value.a += 1; | |
| 286 | try expect(value.a == 1); | |
| 287 | try expect(value.b == 0); | |
| 288 | try expect(value.c == 0); | |
| 289 | try expect(value.d == 0); | |
| 290 | ||
| 291 | value.b += 1; | |
| 292 | try expect(value.a == 1); | |
| 293 | try expect(value.b == 1); | |
| 294 | try expect(value.c == 0); | |
| 295 | try expect(value.d == 0); | |
| 296 | ||
| 297 | value.c += 1; | |
| 298 | try expect(value.a == 1); | |
| 299 | try expect(value.b == 1); | |
| 300 | try expect(value.c == 1); | |
| 301 | try expect(value.d == 0); | |
| 302 | ||
| 303 | value.d += 1; | |
| 304 | try expect(value.a == 1); | |
| 305 | try expect(value.b == 1); | |
| 306 | try expect(value.c == 1); | |
| 307 | try expect(value.d == 1); | |
| 308 | } | |
| 309 | ||
| 310 | const Foo32Bits = packed struct { | |
| 311 | field: u24, | |
| 312 | pad: u8, | |
| 313 | }; | |
| 314 | ||
| 315 | const FooArray24Bits = packed struct { | |
| 316 | a: u16, | |
| 317 | b: [2]Foo32Bits, | |
| 318 | c: u16, | |
| 319 | }; | |
| 320 | ||
| 321 | // TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512 | |
| 322 | test "packed array 24bits" { | |
| 323 | comptime { | |
| 324 | try expect(@sizeOf([9]Foo32Bits) == 9 * 4); | |
| 325 | try expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2); | |
| 326 | } | |
| 327 | ||
| 328 | var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1); | |
| 329 | bytes[bytes.len - 1] = 0xaa; | |
| 330 | const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; | |
| 331 | try expect(ptr.a == 0); | |
| 332 | try expect(ptr.b[0].field == 0); | |
| 333 | try expect(ptr.b[1].field == 0); | |
| 334 | try expect(ptr.c == 0); | |
| 335 | ||
| 336 | ptr.a = maxInt(u16); | |
| 337 | try expect(ptr.a == maxInt(u16)); | |
| 338 | try expect(ptr.b[0].field == 0); | |
| 339 | try expect(ptr.b[1].field == 0); | |
| 340 | try expect(ptr.c == 0); | |
| 341 | ||
| 342 | ptr.b[0].field = maxInt(u24); | |
| 343 | try expect(ptr.a == maxInt(u16)); | |
| 344 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 345 | try expect(ptr.b[1].field == 0); | |
| 346 | try expect(ptr.c == 0); | |
| 347 | ||
| 348 | ptr.b[1].field = maxInt(u24); | |
| 349 | try expect(ptr.a == maxInt(u16)); | |
| 350 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 351 | try expect(ptr.b[1].field == maxInt(u24)); | |
| 352 | try expect(ptr.c == 0); | |
| 353 | ||
| 354 | ptr.c = maxInt(u16); | |
| 355 | try expect(ptr.a == maxInt(u16)); | |
| 356 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 357 | try expect(ptr.b[1].field == maxInt(u24)); | |
| 358 | try expect(ptr.c == maxInt(u16)); | |
| 359 | ||
| 360 | try expect(bytes[bytes.len - 1] == 0xaa); | |
| 361 | } | |
| 362 | ||
| 363 | const FooStructAligned = packed struct { | |
| 364 | a: u8, | |
| 365 | b: u8, | |
| 366 | }; | |
| 367 | ||
| 368 | const FooArrayOfAligned = packed struct { | |
| 369 | a: [2]FooStructAligned, | |
| 370 | }; | |
| 94 | test "struct point to self" { | |
| 95 | var root: Node = undefined; | |
| 96 | root.val.x = 1; | |
| 371 | 97 | |
| 372 | test "aligned array of packed struct" { | |
| 373 | comptime { | |
| 374 | try expect(@sizeOf(FooStructAligned) == 2); | |
| 375 | try expect(@sizeOf(FooArrayOfAligned) == 2 * 2); | |
| 376 | } | |
| 98 | var node: Node = undefined; | |
| 99 | node.next = &root; | |
| 100 | node.val.x = 2; | |
| 377 | 101 | |
| 378 | var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned); | |
| 379 | const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0]; | |
| 102 | root.next = &node; | |
| 380 | 103 | |
| 381 | try expect(ptr.a[0].a == 0xbb); | |
| 382 | try expect(ptr.a[0].b == 0xbb); | |
| 383 | try expect(ptr.a[1].a == 0xbb); | |
| 384 | try expect(ptr.a[1].b == 0xbb); | |
| 104 | try expect(node.next.next.next.val.x == 1); | |
| 385 | 105 | } |
| 386 | 106 | |
| 387 | test "runtime struct initialization of bitfield" { | |
| 388 | const s1 = Nibbles{ | |
| 389 | .x = x1, | |
| 390 | .y = x1, | |
| 391 | }; | |
| 392 | const s2 = Nibbles{ | |
| 393 | .x = @intCast(u4, x2), | |
| 394 | .y = @intCast(u4, x2), | |
| 107 | test "void struct fields" { | |
| 108 | const foo = VoidStructFieldsFoo{ | |
| 109 | .a = void{}, | |
| 110 | .b = 1, | |
| 111 | .c = void{}, | |
| 395 | 112 | }; |
| 396 | ||
| 397 | try expect(s1.x == x1); | |
| 398 | try expect(s1.y == x1); | |
| 399 | try expect(s2.x == @intCast(u4, x2)); | |
| 400 | try expect(s2.y == @intCast(u4, x2)); | |
| 401 | } | |
| 402 | ||
| 403 | var x1 = @as(u4, 1); | |
| 404 | var x2 = @as(u8, 2); | |
| 405 | ||
| 406 | const Nibbles = packed struct { | |
| 407 | x: u4, | |
| 408 | y: u4, | |
| 409 | }; | |
| 410 | ||
| 411 | const Bitfields = packed struct { | |
| 412 | f1: u16, | |
| 413 | f2: u16, | |
| 414 | f3: u8, | |
| 415 | f4: u8, | |
| 416 | f5: u4, | |
| 417 | f6: u4, | |
| 418 | f7: u8, | |
| 419 | }; | |
| 420 | ||
| 421 | test "native bit field understands endianness" { | |
| 422 | var all: u64 = if (native_endian != .Little) | |
| 423 | 0x1111222233445677 | |
| 424 | else | |
| 425 | 0x7765443322221111; | |
| 426 | var bytes: [8]u8 = undefined; | |
| 427 | @memcpy(&bytes, @ptrCast([*]u8, &all), 8); | |
| 428 | var bitfields = @ptrCast(*Bitfields, &bytes).*; | |
| 429 | ||
| 430 | try expect(bitfields.f1 == 0x1111); | |
| 431 | try expect(bitfields.f2 == 0x2222); | |
| 432 | try expect(bitfields.f3 == 0x33); | |
| 433 | try expect(bitfields.f4 == 0x44); | |
| 434 | try expect(bitfields.f5 == 0x5); | |
| 435 | try expect(bitfields.f6 == 0x6); | |
| 436 | try expect(bitfields.f7 == 0x77); | |
| 437 | } | |
| 438 | ||
| 439 | test "align 1 field before self referential align 8 field as slice return type" { | |
| 440 | const result = alloc(Expr); | |
| 441 | try expect(result.len == 0); | |
| 113 | try expect(foo.b == 1); | |
| 114 | try expect(@sizeOf(VoidStructFieldsFoo) == 4); | |
| 442 | 115 | } |
| 443 | ||
| 444 | const Expr = union(enum) { | |
| 445 | Literal: u8, | |
| 446 | Question: *Expr, | |
| 116 | const VoidStructFieldsFoo = struct { | |
| 117 | a: void, | |
| 118 | b: i32, | |
| 119 | c: void, | |
| 447 | 120 | }; |
| 448 | 121 | |
| 449 | fn alloc(comptime T: type) []T { | |
| 450 | return &[_]T{}; | |
| 451 | } | |
| 452 | ||
| 453 | test "call method with mutable reference to struct with no fields" { | |
| 454 | const S = struct { | |
| 455 | fn doC(s: *const @This()) bool { | |
| 456 | _ = s; | |
| 457 | return true; | |
| 458 | } | |
| 459 | fn do(s: *@This()) bool { | |
| 460 | _ = s; | |
| 461 | return true; | |
| 462 | } | |
| 463 | }; | |
| 464 | ||
| 465 | var s = S{}; | |
| 466 | try expect(S.doC(&s)); | |
| 467 | try expect(s.doC()); | |
| 468 | try expect(S.do(&s)); | |
| 469 | try expect(s.do()); | |
| 470 | } | |
| 471 | ||
| 472 | test "implicit cast packed struct field to const ptr" { | |
| 473 | const LevelUpMove = packed struct { | |
| 474 | move_id: u9, | |
| 475 | level: u7, | |
| 476 | ||
| 477 | fn toInt(value: u7) u7 { | |
| 478 | return value; | |
| 479 | } | |
| 480 | }; | |
| 481 | ||
| 482 | var lup: LevelUpMove = undefined; | |
| 483 | lup.level = 12; | |
| 484 | const res = LevelUpMove.toInt(lup.level); | |
| 485 | try expect(res == 12); | |
| 486 | } | |
| 487 | ||
| 488 | test "pointer to packed struct member in a stack variable" { | |
| 489 | const S = packed struct { | |
| 490 | a: u2, | |
| 491 | b: u2, | |
| 492 | }; | |
| 493 | ||
| 494 | var s = S{ .a = 2, .b = 0 }; | |
| 495 | var b_ptr = &s.b; | |
| 496 | try expect(s.b == 0); | |
| 497 | b_ptr.* = 2; | |
| 498 | try expect(s.b == 2); | |
| 499 | } | |
| 500 | ||
| 501 | test "non-byte-aligned array inside packed struct" { | |
| 502 | const Foo = packed struct { | |
| 503 | a: bool, | |
| 504 | b: [0x16]u8, | |
| 505 | }; | |
| 506 | const S = struct { | |
| 507 | fn bar(slice: []const u8) !void { | |
| 508 | try expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu"); | |
| 509 | } | |
| 510 | fn doTheTest() !void { | |
| 511 | var foo = Foo{ | |
| 512 | .a = true, | |
| 513 | .b = "abcdefghijklmnopqurstu".*, | |
| 514 | }; | |
| 515 | const value = foo.b; | |
| 516 | try bar(&value); | |
| 517 | } | |
| 518 | }; | |
| 519 | try S.doTheTest(); | |
| 520 | comptime try S.doTheTest(); | |
| 521 | } | |
| 522 | ||
| 523 | test "packed struct with u0 field access" { | |
| 524 | const S = packed struct { | |
| 525 | f0: u0, | |
| 526 | }; | |
| 527 | var s = S{ .f0 = 0 }; | |
| 528 | comptime try expect(s.f0 == 0); | |
| 122 | test "member functions" { | |
| 123 | const r = MemberFnRand{ .seed = 1234 }; | |
| 124 | try expect(r.getSeed() == 1234); | |
| 529 | 125 | } |
| 530 | ||
| 531 | const S0 = struct { | |
| 532 | bar: S1, | |
| 533 | ||
| 534 | pub const S1 = struct { | |
| 535 | value: u8, | |
| 536 | }; | |
| 537 | ||
| 538 | fn init() @This() { | |
| 539 | return S0{ .bar = S1{ .value = 123 } }; | |
| 126 | const MemberFnRand = struct { | |
| 127 | seed: u32, | |
| 128 | pub fn getSeed(r: *const MemberFnRand) u32 { | |
| 129 | return r.seed; | |
| 540 | 130 | } |
| 541 | 131 | }; |
| 542 | ||
| 543 | var g_foo: S0 = S0.init(); | |
| 544 | ||
| 545 | test "access to global struct fields" { | |
| 546 | g_foo.bar.value = 42; | |
| 547 | try expect(g_foo.bar.value == 42); | |
| 548 | } | |
| 549 | ||
| 550 | test "packed struct with fp fields" { | |
| 551 | const S = packed struct { | |
| 552 | data: [3]f32, | |
| 553 | ||
| 554 | pub fn frob(self: *@This()) void { | |
| 555 | self.data[0] += self.data[1] + self.data[2]; | |
| 556 | self.data[1] += self.data[0] + self.data[2]; | |
| 557 | self.data[2] += self.data[0] + self.data[1]; | |
| 558 | } | |
| 559 | }; | |
| 560 | ||
| 561 | var s: S = undefined; | |
| 562 | s.data[0] = 1.0; | |
| 563 | s.data[1] = 2.0; | |
| 564 | s.data[2] = 3.0; | |
| 565 | s.frob(); | |
| 566 | try expectEqual(@as(f32, 6.0), s.data[0]); | |
| 567 | try expectEqual(@as(f32, 11.0), s.data[1]); | |
| 568 | try expectEqual(@as(f32, 20.0), s.data[2]); | |
| 569 | } | |
| 570 | ||
| 571 | test "use within struct scope" { | |
| 572 | const S = struct { | |
| 573 | usingnamespace struct { | |
| 574 | pub fn inner() i32 { | |
| 575 | return 42; | |
| 576 | } | |
| 577 | }; | |
| 578 | }; | |
| 579 | try expectEqual(@as(i32, 42), S.inner()); | |
| 580 | } | |
| 581 | ||
| 582 | test "default struct initialization fields" { | |
| 583 | const S = struct { | |
| 584 | a: i32 = 1234, | |
| 585 | b: i32, | |
| 586 | }; | |
| 587 | const x = S{ | |
| 588 | .b = 5, | |
| 589 | }; | |
| 590 | var five: i32 = 5; | |
| 591 | const y = S{ | |
| 592 | .b = five, | |
| 593 | }; | |
| 594 | if (x.a + x.b != 1239) { | |
| 595 | @compileError("it should be comptime known"); | |
| 596 | } | |
| 597 | try expectEqual(y, x); | |
| 598 | try expectEqual(1239, x.a + x.b); | |
| 599 | } | |
| 600 | ||
| 601 | test "fn with C calling convention returns struct by value" { | |
| 602 | const S = struct { | |
| 603 | fn entry() !void { | |
| 604 | var x = makeBar(10); | |
| 605 | try expectEqual(@as(i32, 10), x.handle); | |
| 606 | } | |
| 607 | ||
| 608 | const ExternBar = extern struct { | |
| 609 | handle: i32, | |
| 610 | }; | |
| 611 | ||
| 612 | fn makeBar(t: i32) callconv(.C) ExternBar { | |
| 613 | return ExternBar{ | |
| 614 | .handle = t, | |
| 615 | }; | |
| 616 | } | |
| 617 | }; | |
| 618 | try S.entry(); | |
| 619 | comptime try S.entry(); | |
| 620 | } | |
| 621 | ||
| 622 | test "for loop over pointers to struct, getting field from struct pointer" { | |
| 623 | const S = struct { | |
| 624 | const Foo = struct { | |
| 625 | name: []const u8, | |
| 626 | }; | |
| 627 | ||
| 628 | var ok = true; | |
| 629 | ||
| 630 | fn eql(a: []const u8) bool { | |
| 631 | _ = a; | |
| 632 | return true; | |
| 633 | } | |
| 634 | ||
| 635 | const ArrayList = struct { | |
| 636 | fn toSlice(self: *ArrayList) []*Foo { | |
| 637 | _ = self; | |
| 638 | return @as([*]*Foo, undefined)[0..0]; | |
| 639 | } | |
| 640 | }; | |
| 641 | ||
| 642 | fn doTheTest() !void { | |
| 643 | var objects: ArrayList = undefined; | |
| 644 | ||
| 645 | for (objects.toSlice()) |obj| { | |
| 646 | if (eql(obj.name)) { | |
| 647 | ok = false; | |
| 648 | } | |
| 649 | } | |
| 650 | ||
| 651 | try expect(ok); | |
| 652 | } | |
| 653 | }; | |
| 654 | try S.doTheTest(); | |
| 655 | } | |
| 656 | ||
| 657 | test "zero-bit field in packed struct" { | |
| 658 | const S = packed struct { | |
| 659 | x: u10, | |
| 660 | y: void, | |
| 661 | }; | |
| 662 | var x: S = undefined; | |
| 663 | _ = x; | |
| 664 | } | |
| 665 | ||
| 666 | test "struct field init with catch" { | |
| 667 | const S = struct { | |
| 668 | fn doTheTest() !void { | |
| 669 | var x: anyerror!isize = 1; | |
| 670 | var req = Foo{ | |
| 671 | .field = x catch undefined, | |
| 672 | }; | |
| 673 | try expect(req.field == 1); | |
| 674 | } | |
| 675 | ||
| 676 | pub const Foo = extern struct { | |
| 677 | field: isize, | |
| 678 | }; | |
| 679 | }; | |
| 680 | try S.doTheTest(); | |
| 681 | comptime try S.doTheTest(); | |
| 682 | } | |
| 683 | ||
| 684 | test "packed struct with non-ABI-aligned field" { | |
| 685 | const S = packed struct { | |
| 686 | x: u9, | |
| 687 | y: u183, | |
| 688 | }; | |
| 689 | var s: S = undefined; | |
| 690 | s.x = 1; | |
| 691 | s.y = 42; | |
| 692 | try expect(s.x == 1); | |
| 693 | try expect(s.y == 42); | |
| 694 | } | |
| 695 | ||
| 696 | test "non-packed struct with u128 entry in union" { | |
| 697 | const U = union(enum) { | |
| 698 | Num: u128, | |
| 699 | Void, | |
| 700 | }; | |
| 701 | ||
| 702 | const S = struct { | |
| 703 | f1: U, | |
| 704 | f2: U, | |
| 705 | }; | |
| 706 | ||
| 707 | var sx: S = undefined; | |
| 708 | var s = &sx; | |
| 709 | try std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @offsetOf(S, "f2")); | |
| 710 | var v2 = U{ .Num = 123 }; | |
| 711 | s.f2 = v2; | |
| 712 | try std.testing.expect(s.f2.Num == 123); | |
| 713 | } | |
| 714 | ||
| 715 | test "packed struct field passed to generic function" { | |
| 716 | const S = struct { | |
| 717 | const P = packed struct { | |
| 718 | b: u5, | |
| 719 | g: u5, | |
| 720 | r: u5, | |
| 721 | a: u1, | |
| 722 | }; | |
| 723 | ||
| 724 | fn genericReadPackedField(ptr: anytype) u5 { | |
| 725 | return ptr.*; | |
| 726 | } | |
| 727 | }; | |
| 728 | ||
| 729 | var p: S.P = undefined; | |
| 730 | p.b = 29; | |
| 731 | var loaded = S.genericReadPackedField(&p.b); | |
| 732 | try expect(loaded == 29); | |
| 733 | } | |
| 734 | ||
| 735 | test "anonymous struct literal syntax" { | |
| 736 | const S = struct { | |
| 737 | const Point = struct { | |
| 738 | x: i32, | |
| 739 | y: i32, | |
| 740 | }; | |
| 741 | ||
| 742 | fn doTheTest() !void { | |
| 743 | var p: Point = .{ | |
| 744 | .x = 1, | |
| 745 | .y = 2, | |
| 746 | }; | |
| 747 | try expect(p.x == 1); | |
| 748 | try expect(p.y == 2); | |
| 749 | } | |
| 750 | }; | |
| 751 | try S.doTheTest(); | |
| 752 | comptime try S.doTheTest(); | |
| 753 | } | |
| 754 | ||
| 755 | test "fully anonymous struct" { | |
| 756 | const S = struct { | |
| 757 | fn doTheTest() !void { | |
| 758 | try dump(.{ | |
| 759 | .int = @as(u32, 1234), | |
| 760 | .float = @as(f64, 12.34), | |
| 761 | .b = true, | |
| 762 | .s = "hi", | |
| 763 | }); | |
| 764 | } | |
| 765 | fn dump(args: anytype) !void { | |
| 766 | try expect(args.int == 1234); | |
| 767 | try expect(args.float == 12.34); | |
| 768 | try expect(args.b); | |
| 769 | try expect(args.s[0] == 'h'); | |
| 770 | try expect(args.s[1] == 'i'); | |
| 771 | } | |
| 772 | }; | |
| 773 | try S.doTheTest(); | |
| 774 | comptime try S.doTheTest(); | |
| 775 | } | |
| 776 | ||
| 777 | test "fully anonymous list literal" { | |
| 778 | const S = struct { | |
| 779 | fn doTheTest() !void { | |
| 780 | try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" }); | |
| 781 | } | |
| 782 | fn dump(args: anytype) !void { | |
| 783 | try expect(args.@"0" == 1234); | |
| 784 | try expect(args.@"1" == 12.34); | |
| 785 | try expect(args.@"2"); | |
| 786 | try expect(args.@"3"[0] == 'h'); | |
| 787 | try expect(args.@"3"[1] == 'i'); | |
| 788 | } | |
| 789 | }; | |
| 790 | try S.doTheTest(); | |
| 791 | comptime try S.doTheTest(); | |
| 792 | } | |
| 793 | ||
| 794 | test "anonymous struct literal assigned to variable" { | |
| 795 | var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) }; | |
| 796 | try expect(vec.@"0" == 22); | |
| 797 | try expect(vec.@"1" == 55); | |
| 798 | try expect(vec.@"2" == 99); | |
| 799 | } | |
| 800 | ||
| 801 | test "struct with var field" { | |
| 802 | const Point = struct { | |
| 803 | x: anytype, | |
| 804 | y: anytype, | |
| 805 | }; | |
| 806 | const pt = Point{ | |
| 807 | .x = 1, | |
| 808 | .y = 2, | |
| 809 | }; | |
| 810 | try expect(pt.x == 1); | |
| 811 | try expect(pt.y == 2); | |
| 812 | } | |
| 813 | ||
| 814 | test "comptime struct field" { | |
| 815 | const T = struct { | |
| 816 | a: i32, | |
| 817 | comptime b: i32 = 1234, | |
| 818 | }; | |
| 819 | ||
| 820 | var foo: T = undefined; | |
| 821 | comptime try expect(foo.b == 1234); | |
| 822 | } | |
| 823 | ||
| 824 | test "anon struct literal field value initialized with fn call" { | |
| 825 | const S = struct { | |
| 826 | fn doTheTest() !void { | |
| 827 | var x = .{foo()}; | |
| 828 | try expectEqualSlices(u8, x[0], "hi"); | |
| 829 | } | |
| 830 | fn foo() []const u8 { | |
| 831 | return "hi"; | |
| 832 | } | |
| 833 | }; | |
| 834 | try S.doTheTest(); | |
| 835 | comptime try S.doTheTest(); | |
| 836 | } | |
| 837 | ||
| 838 | test "self-referencing struct via array member" { | |
| 839 | const T = struct { | |
| 840 | children: [1]*@This(), | |
| 841 | }; | |
| 842 | var x: T = undefined; | |
| 843 | x = T{ .children = .{&x} }; | |
| 844 | try expect(x.children[0] == &x); | |
| 845 | } | |
| 846 | ||
| 847 | test "struct with union field" { | |
| 848 | const Value = struct { | |
| 849 | ref: u32 = 2, | |
| 850 | kind: union(enum) { | |
| 851 | None: usize, | |
| 852 | Bool: bool, | |
| 853 | }, | |
| 854 | }; | |
| 855 | ||
| 856 | var True = Value{ | |
| 857 | .kind = .{ .Bool = true }, | |
| 858 | }; | |
| 859 | try expectEqual(@as(u32, 2), True.ref); | |
| 860 | try expectEqual(true, True.kind.Bool); | |
| 861 | } | |
| 862 | ||
| 863 | test "type coercion of anon struct literal to struct" { | |
| 864 | const S = struct { | |
| 865 | const S2 = struct { | |
| 866 | A: u32, | |
| 867 | B: []const u8, | |
| 868 | C: void, | |
| 869 | D: Foo = .{}, | |
| 870 | }; | |
| 871 | ||
| 872 | const Foo = struct { | |
| 873 | field: i32 = 1234, | |
| 874 | }; | |
| 875 | ||
| 876 | fn doTheTest() !void { | |
| 877 | var y: u32 = 42; | |
| 878 | const t0 = .{ .A = 123, .B = "foo", .C = {} }; | |
| 879 | const t1 = .{ .A = y, .B = "foo", .C = {} }; | |
| 880 | const y0: S2 = t0; | |
| 881 | var y1: S2 = t1; | |
| 882 | try expect(y0.A == 123); | |
| 883 | try expect(std.mem.eql(u8, y0.B, "foo")); | |
| 884 | try expect(y0.C == {}); | |
| 885 | try expect(y0.D.field == 1234); | |
| 886 | try expect(y1.A == y); | |
| 887 | try expect(std.mem.eql(u8, y1.B, "foo")); | |
| 888 | try expect(y1.C == {}); | |
| 889 | try expect(y1.D.field == 1234); | |
| 890 | } | |
| 891 | }; | |
| 892 | try S.doTheTest(); | |
| 893 | comptime try S.doTheTest(); | |
| 894 | } | |
| 895 | ||
| 896 | test "type coercion of pointer to anon struct literal to pointer to struct" { | |
| 897 | const S = struct { | |
| 898 | const S2 = struct { | |
| 899 | A: u32, | |
| 900 | B: []const u8, | |
| 901 | C: void, | |
| 902 | D: Foo = .{}, | |
| 903 | }; | |
| 904 | ||
| 905 | const Foo = struct { | |
| 906 | field: i32 = 1234, | |
| 907 | }; | |
| 908 | ||
| 909 | fn doTheTest() !void { | |
| 910 | var y: u32 = 42; | |
| 911 | const t0 = &.{ .A = 123, .B = "foo", .C = {} }; | |
| 912 | const t1 = &.{ .A = y, .B = "foo", .C = {} }; | |
| 913 | const y0: *const S2 = t0; | |
| 914 | var y1: *const S2 = t1; | |
| 915 | try expect(y0.A == 123); | |
| 916 | try expect(std.mem.eql(u8, y0.B, "foo")); | |
| 917 | try expect(y0.C == {}); | |
| 918 | try expect(y0.D.field == 1234); | |
| 919 | try expect(y1.A == y); | |
| 920 | try expect(std.mem.eql(u8, y1.B, "foo")); | |
| 921 | try expect(y1.C == {}); | |
| 922 | try expect(y1.D.field == 1234); | |
| 923 | } | |
| 924 | }; | |
| 925 | try S.doTheTest(); | |
| 926 | comptime try S.doTheTest(); | |
| 927 | } | |
| 928 | ||
| 929 | test "packed struct with undefined initializers" { | |
| 930 | const S = struct { | |
| 931 | const P = packed struct { | |
| 932 | a: u3, | |
| 933 | _a: u3 = undefined, | |
| 934 | b: u3, | |
| 935 | _b: u3 = undefined, | |
| 936 | c: u3, | |
| 937 | _c: u3 = undefined, | |
| 938 | }; | |
| 939 | ||
| 940 | fn doTheTest() !void { | |
| 941 | var p: P = undefined; | |
| 942 | p = P{ .a = 2, .b = 4, .c = 6 }; | |
| 943 | // Make sure the compiler doesn't touch the unprefixed fields. | |
| 944 | // Use expect since i386-linux doesn't like expectEqual | |
| 945 | try expect(p.a == 2); | |
| 946 | try expect(p.b == 4); | |
| 947 | try expect(p.c == 6); | |
| 948 | } | |
| 949 | }; | |
| 950 | ||
| 951 | try S.doTheTest(); | |
| 952 | comptime try S.doTheTest(); | |
| 953 | } |
test/behavior/struct_stage1.zig created+853| ... | ... | @@ -0,0 +1,853 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const native_endian = builtin.target.cpu.arch.endian(); | |
| 4 | const expect = std.testing.expect; | |
| 5 | const expectEqual = std.testing.expectEqual; | |
| 6 | const expectEqualSlices = std.testing.expectEqualSlices; | |
| 7 | const maxInt = std.math.maxInt; | |
| 8 | ||
| 9 | top_level_field: i32, | |
| 10 | ||
| 11 | test "top level fields" { | |
| 12 | var instance = @This(){ | |
| 13 | .top_level_field = 1234, | |
| 14 | }; | |
| 15 | instance.top_level_field += 1; | |
| 16 | try expectEqual(@as(i32, 1235), instance.top_level_field); | |
| 17 | } | |
| 18 | ||
| 19 | const StructFoo = struct { | |
| 20 | a: i32, | |
| 21 | b: bool, | |
| 22 | c: f32, | |
| 23 | }; | |
| 24 | ||
| 25 | const Node = struct { | |
| 26 | val: Val, | |
| 27 | next: *Node, | |
| 28 | }; | |
| 29 | ||
| 30 | const Val = struct { | |
| 31 | x: i32, | |
| 32 | }; | |
| 33 | ||
| 34 | test "fn call of struct field" { | |
| 35 | const Foo = struct { | |
| 36 | ptr: fn () i32, | |
| 37 | }; | |
| 38 | const S = struct { | |
| 39 | fn aFunc() i32 { | |
| 40 | return 13; | |
| 41 | } | |
| 42 | ||
| 43 | fn callStructField(foo: Foo) i32 { | |
| 44 | return foo.ptr(); | |
| 45 | } | |
| 46 | }; | |
| 47 | ||
| 48 | try expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13); | |
| 49 | } | |
| 50 | ||
| 51 | const MemberFnTestFoo = struct { | |
| 52 | x: i32, | |
| 53 | fn member(foo: MemberFnTestFoo) i32 { | |
| 54 | return foo.x; | |
| 55 | } | |
| 56 | }; | |
| 57 | test "store member function in variable" { | |
| 58 | const instance = MemberFnTestFoo{ .x = 1234 }; | |
| 59 | const memberFn = MemberFnTestFoo.member; | |
| 60 | const result = memberFn(instance); | |
| 61 | try expect(result == 1234); | |
| 62 | } | |
| 63 | ||
| 64 | test "return struct byval from function" { | |
| 65 | const bar = makeBar2(1234, 5678); | |
| 66 | try expect(bar.y == 5678); | |
| 67 | } | |
| 68 | const Bar = struct { | |
| 69 | x: i32, | |
| 70 | y: i32, | |
| 71 | }; | |
| 72 | fn makeBar2(x: i32, y: i32) Bar { | |
| 73 | return Bar{ | |
| 74 | .x = x, | |
| 75 | .y = y, | |
| 76 | }; | |
| 77 | } | |
| 78 | ||
| 79 | test "empty struct method call" { | |
| 80 | const es = EmptyStruct{}; | |
| 81 | try expect(es.method() == 1234); | |
| 82 | } | |
| 83 | const EmptyStruct = struct { | |
| 84 | fn method(es: *const EmptyStruct) i32 { | |
| 85 | _ = es; | |
| 86 | return 1234; | |
| 87 | } | |
| 88 | }; | |
| 89 | ||
| 90 | test "return empty struct from fn" { | |
| 91 | _ = testReturnEmptyStructFromFn(); | |
| 92 | } | |
| 93 | const EmptyStruct2 = struct {}; | |
| 94 | fn testReturnEmptyStructFromFn() EmptyStruct2 { | |
| 95 | return EmptyStruct2{}; | |
| 96 | } | |
| 97 | ||
| 98 | test "pass slice of empty struct to fn" { | |
| 99 | try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); | |
| 100 | } | |
| 101 | fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { | |
| 102 | return slice.len; | |
| 103 | } | |
| 104 | ||
| 105 | const APackedStruct = packed struct { | |
| 106 | x: u8, | |
| 107 | y: u8, | |
| 108 | }; | |
| 109 | ||
| 110 | test "packed struct" { | |
| 111 | var foo = APackedStruct{ | |
| 112 | .x = 1, | |
| 113 | .y = 2, | |
| 114 | }; | |
| 115 | foo.y += 1; | |
| 116 | const four = foo.x + foo.y; | |
| 117 | try expect(four == 4); | |
| 118 | } | |
| 119 | ||
| 120 | const BitField1 = packed struct { | |
| 121 | a: u3, | |
| 122 | b: u3, | |
| 123 | c: u2, | |
| 124 | }; | |
| 125 | ||
| 126 | const bit_field_1 = BitField1{ | |
| 127 | .a = 1, | |
| 128 | .b = 2, | |
| 129 | .c = 3, | |
| 130 | }; | |
| 131 | ||
| 132 | test "bit field access" { | |
| 133 | var data = bit_field_1; | |
| 134 | try expect(getA(&data) == 1); | |
| 135 | try expect(getB(&data) == 2); | |
| 136 | try expect(getC(&data) == 3); | |
| 137 | comptime try expect(@sizeOf(BitField1) == 1); | |
| 138 | ||
| 139 | data.b += 1; | |
| 140 | try expect(data.b == 3); | |
| 141 | ||
| 142 | data.a += 1; | |
| 143 | try expect(data.a == 2); | |
| 144 | try expect(data.b == 3); | |
| 145 | } | |
| 146 | ||
| 147 | fn getA(data: *const BitField1) u3 { | |
| 148 | return data.a; | |
| 149 | } | |
| 150 | ||
| 151 | fn getB(data: *const BitField1) u3 { | |
| 152 | return data.b; | |
| 153 | } | |
| 154 | ||
| 155 | fn getC(data: *const BitField1) u2 { | |
| 156 | return data.c; | |
| 157 | } | |
| 158 | ||
| 159 | const Foo24Bits = packed struct { | |
| 160 | field: u24, | |
| 161 | }; | |
| 162 | const Foo96Bits = packed struct { | |
| 163 | a: u24, | |
| 164 | b: u24, | |
| 165 | c: u24, | |
| 166 | d: u24, | |
| 167 | }; | |
| 168 | ||
| 169 | test "packed struct 24bits" { | |
| 170 | comptime { | |
| 171 | try expect(@sizeOf(Foo24Bits) == 4); | |
| 172 | if (@sizeOf(usize) == 4) { | |
| 173 | try expect(@sizeOf(Foo96Bits) == 12); | |
| 174 | } else { | |
| 175 | try expect(@sizeOf(Foo96Bits) == 16); | |
| 176 | } | |
| 177 | } | |
| 178 | ||
| 179 | var value = Foo96Bits{ | |
| 180 | .a = 0, | |
| 181 | .b = 0, | |
| 182 | .c = 0, | |
| 183 | .d = 0, | |
| 184 | }; | |
| 185 | value.a += 1; | |
| 186 | try expect(value.a == 1); | |
| 187 | try expect(value.b == 0); | |
| 188 | try expect(value.c == 0); | |
| 189 | try expect(value.d == 0); | |
| 190 | ||
| 191 | value.b += 1; | |
| 192 | try expect(value.a == 1); | |
| 193 | try expect(value.b == 1); | |
| 194 | try expect(value.c == 0); | |
| 195 | try expect(value.d == 0); | |
| 196 | ||
| 197 | value.c += 1; | |
| 198 | try expect(value.a == 1); | |
| 199 | try expect(value.b == 1); | |
| 200 | try expect(value.c == 1); | |
| 201 | try expect(value.d == 0); | |
| 202 | ||
| 203 | value.d += 1; | |
| 204 | try expect(value.a == 1); | |
| 205 | try expect(value.b == 1); | |
| 206 | try expect(value.c == 1); | |
| 207 | try expect(value.d == 1); | |
| 208 | } | |
| 209 | ||
| 210 | const Foo32Bits = packed struct { | |
| 211 | field: u24, | |
| 212 | pad: u8, | |
| 213 | }; | |
| 214 | ||
| 215 | const FooArray24Bits = packed struct { | |
| 216 | a: u16, | |
| 217 | b: [2]Foo32Bits, | |
| 218 | c: u16, | |
| 219 | }; | |
| 220 | ||
| 221 | // TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512 | |
| 222 | test "packed array 24bits" { | |
| 223 | comptime { | |
| 224 | try expect(@sizeOf([9]Foo32Bits) == 9 * 4); | |
| 225 | try expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2); | |
| 226 | } | |
| 227 | ||
| 228 | var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1); | |
| 229 | bytes[bytes.len - 1] = 0xaa; | |
| 230 | const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; | |
| 231 | try expect(ptr.a == 0); | |
| 232 | try expect(ptr.b[0].field == 0); | |
| 233 | try expect(ptr.b[1].field == 0); | |
| 234 | try expect(ptr.c == 0); | |
| 235 | ||
| 236 | ptr.a = maxInt(u16); | |
| 237 | try expect(ptr.a == maxInt(u16)); | |
| 238 | try expect(ptr.b[0].field == 0); | |
| 239 | try expect(ptr.b[1].field == 0); | |
| 240 | try expect(ptr.c == 0); | |
| 241 | ||
| 242 | ptr.b[0].field = maxInt(u24); | |
| 243 | try expect(ptr.a == maxInt(u16)); | |
| 244 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 245 | try expect(ptr.b[1].field == 0); | |
| 246 | try expect(ptr.c == 0); | |
| 247 | ||
| 248 | ptr.b[1].field = maxInt(u24); | |
| 249 | try expect(ptr.a == maxInt(u16)); | |
| 250 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 251 | try expect(ptr.b[1].field == maxInt(u24)); | |
| 252 | try expect(ptr.c == 0); | |
| 253 | ||
| 254 | ptr.c = maxInt(u16); | |
| 255 | try expect(ptr.a == maxInt(u16)); | |
| 256 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 257 | try expect(ptr.b[1].field == maxInt(u24)); | |
| 258 | try expect(ptr.c == maxInt(u16)); | |
| 259 | ||
| 260 | try expect(bytes[bytes.len - 1] == 0xaa); | |
| 261 | } | |
| 262 | ||
| 263 | const FooStructAligned = packed struct { | |
| 264 | a: u8, | |
| 265 | b: u8, | |
| 266 | }; | |
| 267 | ||
| 268 | const FooArrayOfAligned = packed struct { | |
| 269 | a: [2]FooStructAligned, | |
| 270 | }; | |
| 271 | ||
| 272 | test "aligned array of packed struct" { | |
| 273 | comptime { | |
| 274 | try expect(@sizeOf(FooStructAligned) == 2); | |
| 275 | try expect(@sizeOf(FooArrayOfAligned) == 2 * 2); | |
| 276 | } | |
| 277 | ||
| 278 | var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned); | |
| 279 | const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0]; | |
| 280 | ||
| 281 | try expect(ptr.a[0].a == 0xbb); | |
| 282 | try expect(ptr.a[0].b == 0xbb); | |
| 283 | try expect(ptr.a[1].a == 0xbb); | |
| 284 | try expect(ptr.a[1].b == 0xbb); | |
| 285 | } | |
| 286 | ||
| 287 | test "runtime struct initialization of bitfield" { | |
| 288 | const s1 = Nibbles{ | |
| 289 | .x = x1, | |
| 290 | .y = x1, | |
| 291 | }; | |
| 292 | const s2 = Nibbles{ | |
| 293 | .x = @intCast(u4, x2), | |
| 294 | .y = @intCast(u4, x2), | |
| 295 | }; | |
| 296 | ||
| 297 | try expect(s1.x == x1); | |
| 298 | try expect(s1.y == x1); | |
| 299 | try expect(s2.x == @intCast(u4, x2)); | |
| 300 | try expect(s2.y == @intCast(u4, x2)); | |
| 301 | } | |
| 302 | ||
| 303 | var x1 = @as(u4, 1); | |
| 304 | var x2 = @as(u8, 2); | |
| 305 | ||
| 306 | const Nibbles = packed struct { | |
| 307 | x: u4, | |
| 308 | y: u4, | |
| 309 | }; | |
| 310 | ||
| 311 | const Bitfields = packed struct { | |
| 312 | f1: u16, | |
| 313 | f2: u16, | |
| 314 | f3: u8, | |
| 315 | f4: u8, | |
| 316 | f5: u4, | |
| 317 | f6: u4, | |
| 318 | f7: u8, | |
| 319 | }; | |
| 320 | ||
| 321 | test "native bit field understands endianness" { | |
| 322 | var all: u64 = if (native_endian != .Little) | |
| 323 | 0x1111222233445677 | |
| 324 | else | |
| 325 | 0x7765443322221111; | |
| 326 | var bytes: [8]u8 = undefined; | |
| 327 | @memcpy(&bytes, @ptrCast([*]u8, &all), 8); | |
| 328 | var bitfields = @ptrCast(*Bitfields, &bytes).*; | |
| 329 | ||
| 330 | try expect(bitfields.f1 == 0x1111); | |
| 331 | try expect(bitfields.f2 == 0x2222); | |
| 332 | try expect(bitfields.f3 == 0x33); | |
| 333 | try expect(bitfields.f4 == 0x44); | |
| 334 | try expect(bitfields.f5 == 0x5); | |
| 335 | try expect(bitfields.f6 == 0x6); | |
| 336 | try expect(bitfields.f7 == 0x77); | |
| 337 | } | |
| 338 | ||
| 339 | test "align 1 field before self referential align 8 field as slice return type" { | |
| 340 | const result = alloc(Expr); | |
| 341 | try expect(result.len == 0); | |
| 342 | } | |
| 343 | ||
| 344 | const Expr = union(enum) { | |
| 345 | Literal: u8, | |
| 346 | Question: *Expr, | |
| 347 | }; | |
| 348 | ||
| 349 | fn alloc(comptime T: type) []T { | |
| 350 | return &[_]T{}; | |
| 351 | } | |
| 352 | ||
| 353 | test "call method with mutable reference to struct with no fields" { | |
| 354 | const S = struct { | |
| 355 | fn doC(s: *const @This()) bool { | |
| 356 | _ = s; | |
| 357 | return true; | |
| 358 | } | |
| 359 | fn do(s: *@This()) bool { | |
| 360 | _ = s; | |
| 361 | return true; | |
| 362 | } | |
| 363 | }; | |
| 364 | ||
| 365 | var s = S{}; | |
| 366 | try expect(S.doC(&s)); | |
| 367 | try expect(s.doC()); | |
| 368 | try expect(S.do(&s)); | |
| 369 | try expect(s.do()); | |
| 370 | } | |
| 371 | ||
| 372 | test "implicit cast packed struct field to const ptr" { | |
| 373 | const LevelUpMove = packed struct { | |
| 374 | move_id: u9, | |
| 375 | level: u7, | |
| 376 | ||
| 377 | fn toInt(value: u7) u7 { | |
| 378 | return value; | |
| 379 | } | |
| 380 | }; | |
| 381 | ||
| 382 | var lup: LevelUpMove = undefined; | |
| 383 | lup.level = 12; | |
| 384 | const res = LevelUpMove.toInt(lup.level); | |
| 385 | try expect(res == 12); | |
| 386 | } | |
| 387 | ||
| 388 | test "pointer to packed struct member in a stack variable" { | |
| 389 | const S = packed struct { | |
| 390 | a: u2, | |
| 391 | b: u2, | |
| 392 | }; | |
| 393 | ||
| 394 | var s = S{ .a = 2, .b = 0 }; | |
| 395 | var b_ptr = &s.b; | |
| 396 | try expect(s.b == 0); | |
| 397 | b_ptr.* = 2; | |
| 398 | try expect(s.b == 2); | |
| 399 | } | |
| 400 | ||
| 401 | test "non-byte-aligned array inside packed struct" { | |
| 402 | const Foo = packed struct { | |
| 403 | a: bool, | |
| 404 | b: [0x16]u8, | |
| 405 | }; | |
| 406 | const S = struct { | |
| 407 | fn bar(slice: []const u8) !void { | |
| 408 | try expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu"); | |
| 409 | } | |
| 410 | fn doTheTest() !void { | |
| 411 | var foo = Foo{ | |
| 412 | .a = true, | |
| 413 | .b = "abcdefghijklmnopqurstu".*, | |
| 414 | }; | |
| 415 | const value = foo.b; | |
| 416 | try bar(&value); | |
| 417 | } | |
| 418 | }; | |
| 419 | try S.doTheTest(); | |
| 420 | comptime try S.doTheTest(); | |
| 421 | } | |
| 422 | ||
| 423 | test "packed struct with u0 field access" { | |
| 424 | const S = packed struct { | |
| 425 | f0: u0, | |
| 426 | }; | |
| 427 | var s = S{ .f0 = 0 }; | |
| 428 | comptime try expect(s.f0 == 0); | |
| 429 | } | |
| 430 | ||
| 431 | const S0 = struct { | |
| 432 | bar: S1, | |
| 433 | ||
| 434 | pub const S1 = struct { | |
| 435 | value: u8, | |
| 436 | }; | |
| 437 | ||
| 438 | fn init() @This() { | |
| 439 | return S0{ .bar = S1{ .value = 123 } }; | |
| 440 | } | |
| 441 | }; | |
| 442 | ||
| 443 | var g_foo: S0 = S0.init(); | |
| 444 | ||
| 445 | test "access to global struct fields" { | |
| 446 | g_foo.bar.value = 42; | |
| 447 | try expect(g_foo.bar.value == 42); | |
| 448 | } | |
| 449 | ||
| 450 | test "packed struct with fp fields" { | |
| 451 | const S = packed struct { | |
| 452 | data: [3]f32, | |
| 453 | ||
| 454 | pub fn frob(self: *@This()) void { | |
| 455 | self.data[0] += self.data[1] + self.data[2]; | |
| 456 | self.data[1] += self.data[0] + self.data[2]; | |
| 457 | self.data[2] += self.data[0] + self.data[1]; | |
| 458 | } | |
| 459 | }; | |
| 460 | ||
| 461 | var s: S = undefined; | |
| 462 | s.data[0] = 1.0; | |
| 463 | s.data[1] = 2.0; | |
| 464 | s.data[2] = 3.0; | |
| 465 | s.frob(); | |
| 466 | try expectEqual(@as(f32, 6.0), s.data[0]); | |
| 467 | try expectEqual(@as(f32, 11.0), s.data[1]); | |
| 468 | try expectEqual(@as(f32, 20.0), s.data[2]); | |
| 469 | } | |
| 470 | ||
| 471 | test "use within struct scope" { | |
| 472 | const S = struct { | |
| 473 | usingnamespace struct { | |
| 474 | pub fn inner() i32 { | |
| 475 | return 42; | |
| 476 | } | |
| 477 | }; | |
| 478 | }; | |
| 479 | try expectEqual(@as(i32, 42), S.inner()); | |
| 480 | } | |
| 481 | ||
| 482 | test "default struct initialization fields" { | |
| 483 | const S = struct { | |
| 484 | a: i32 = 1234, | |
| 485 | b: i32, | |
| 486 | }; | |
| 487 | const x = S{ | |
| 488 | .b = 5, | |
| 489 | }; | |
| 490 | var five: i32 = 5; | |
| 491 | const y = S{ | |
| 492 | .b = five, | |
| 493 | }; | |
| 494 | if (x.a + x.b != 1239) { | |
| 495 | @compileError("it should be comptime known"); | |
| 496 | } | |
| 497 | try expectEqual(y, x); | |
| 498 | try expectEqual(1239, x.a + x.b); | |
| 499 | } | |
| 500 | ||
| 501 | test "fn with C calling convention returns struct by value" { | |
| 502 | const S = struct { | |
| 503 | fn entry() !void { | |
| 504 | var x = makeBar(10); | |
| 505 | try expectEqual(@as(i32, 10), x.handle); | |
| 506 | } | |
| 507 | ||
| 508 | const ExternBar = extern struct { | |
| 509 | handle: i32, | |
| 510 | }; | |
| 511 | ||
| 512 | fn makeBar(t: i32) callconv(.C) ExternBar { | |
| 513 | return ExternBar{ | |
| 514 | .handle = t, | |
| 515 | }; | |
| 516 | } | |
| 517 | }; | |
| 518 | try S.entry(); | |
| 519 | comptime try S.entry(); | |
| 520 | } | |
| 521 | ||
| 522 | test "for loop over pointers to struct, getting field from struct pointer" { | |
| 523 | const S = struct { | |
| 524 | const Foo = struct { | |
| 525 | name: []const u8, | |
| 526 | }; | |
| 527 | ||
| 528 | var ok = true; | |
| 529 | ||
| 530 | fn eql(a: []const u8) bool { | |
| 531 | _ = a; | |
| 532 | return true; | |
| 533 | } | |
| 534 | ||
| 535 | const ArrayList = struct { | |
| 536 | fn toSlice(self: *ArrayList) []*Foo { | |
| 537 | _ = self; | |
| 538 | return @as([*]*Foo, undefined)[0..0]; | |
| 539 | } | |
| 540 | }; | |
| 541 | ||
| 542 | fn doTheTest() !void { | |
| 543 | var objects: ArrayList = undefined; | |
| 544 | ||
| 545 | for (objects.toSlice()) |obj| { | |
| 546 | if (eql(obj.name)) { | |
| 547 | ok = false; | |
| 548 | } | |
| 549 | } | |
| 550 | ||
| 551 | try expect(ok); | |
| 552 | } | |
| 553 | }; | |
| 554 | try S.doTheTest(); | |
| 555 | } | |
| 556 | ||
| 557 | test "zero-bit field in packed struct" { | |
| 558 | const S = packed struct { | |
| 559 | x: u10, | |
| 560 | y: void, | |
| 561 | }; | |
| 562 | var x: S = undefined; | |
| 563 | _ = x; | |
| 564 | } | |
| 565 | ||
| 566 | test "struct field init with catch" { | |
| 567 | const S = struct { | |
| 568 | fn doTheTest() !void { | |
| 569 | var x: anyerror!isize = 1; | |
| 570 | var req = Foo{ | |
| 571 | .field = x catch undefined, | |
| 572 | }; | |
| 573 | try expect(req.field == 1); | |
| 574 | } | |
| 575 | ||
| 576 | pub const Foo = extern struct { | |
| 577 | field: isize, | |
| 578 | }; | |
| 579 | }; | |
| 580 | try S.doTheTest(); | |
| 581 | comptime try S.doTheTest(); | |
| 582 | } | |
| 583 | ||
| 584 | test "packed struct with non-ABI-aligned field" { | |
| 585 | const S = packed struct { | |
| 586 | x: u9, | |
| 587 | y: u183, | |
| 588 | }; | |
| 589 | var s: S = undefined; | |
| 590 | s.x = 1; | |
| 591 | s.y = 42; | |
| 592 | try expect(s.x == 1); | |
| 593 | try expect(s.y == 42); | |
| 594 | } | |
| 595 | ||
| 596 | test "non-packed struct with u128 entry in union" { | |
| 597 | const U = union(enum) { | |
| 598 | Num: u128, | |
| 599 | Void, | |
| 600 | }; | |
| 601 | ||
| 602 | const S = struct { | |
| 603 | f1: U, | |
| 604 | f2: U, | |
| 605 | }; | |
| 606 | ||
| 607 | var sx: S = undefined; | |
| 608 | var s = &sx; | |
| 609 | try std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @offsetOf(S, "f2")); | |
| 610 | var v2 = U{ .Num = 123 }; | |
| 611 | s.f2 = v2; | |
| 612 | try std.testing.expect(s.f2.Num == 123); | |
| 613 | } | |
| 614 | ||
| 615 | test "packed struct field passed to generic function" { | |
| 616 | const S = struct { | |
| 617 | const P = packed struct { | |
| 618 | b: u5, | |
| 619 | g: u5, | |
| 620 | r: u5, | |
| 621 | a: u1, | |
| 622 | }; | |
| 623 | ||
| 624 | fn genericReadPackedField(ptr: anytype) u5 { | |
| 625 | return ptr.*; | |
| 626 | } | |
| 627 | }; | |
| 628 | ||
| 629 | var p: S.P = undefined; | |
| 630 | p.b = 29; | |
| 631 | var loaded = S.genericReadPackedField(&p.b); | |
| 632 | try expect(loaded == 29); | |
| 633 | } | |
| 634 | ||
| 635 | test "anonymous struct literal syntax" { | |
| 636 | const S = struct { | |
| 637 | const Point = struct { | |
| 638 | x: i32, | |
| 639 | y: i32, | |
| 640 | }; | |
| 641 | ||
| 642 | fn doTheTest() !void { | |
| 643 | var p: Point = .{ | |
| 644 | .x = 1, | |
| 645 | .y = 2, | |
| 646 | }; | |
| 647 | try expect(p.x == 1); | |
| 648 | try expect(p.y == 2); | |
| 649 | } | |
| 650 | }; | |
| 651 | try S.doTheTest(); | |
| 652 | comptime try S.doTheTest(); | |
| 653 | } | |
| 654 | ||
| 655 | test "fully anonymous struct" { | |
| 656 | const S = struct { | |
| 657 | fn doTheTest() !void { | |
| 658 | try dump(.{ | |
| 659 | .int = @as(u32, 1234), | |
| 660 | .float = @as(f64, 12.34), | |
| 661 | .b = true, | |
| 662 | .s = "hi", | |
| 663 | }); | |
| 664 | } | |
| 665 | fn dump(args: anytype) !void { | |
| 666 | try expect(args.int == 1234); | |
| 667 | try expect(args.float == 12.34); | |
| 668 | try expect(args.b); | |
| 669 | try expect(args.s[0] == 'h'); | |
| 670 | try expect(args.s[1] == 'i'); | |
| 671 | } | |
| 672 | }; | |
| 673 | try S.doTheTest(); | |
| 674 | comptime try S.doTheTest(); | |
| 675 | } | |
| 676 | ||
| 677 | test "fully anonymous list literal" { | |
| 678 | const S = struct { | |
| 679 | fn doTheTest() !void { | |
| 680 | try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" }); | |
| 681 | } | |
| 682 | fn dump(args: anytype) !void { | |
| 683 | try expect(args.@"0" == 1234); | |
| 684 | try expect(args.@"1" == 12.34); | |
| 685 | try expect(args.@"2"); | |
| 686 | try expect(args.@"3"[0] == 'h'); | |
| 687 | try expect(args.@"3"[1] == 'i'); | |
| 688 | } | |
| 689 | }; | |
| 690 | try S.doTheTest(); | |
| 691 | comptime try S.doTheTest(); | |
| 692 | } | |
| 693 | ||
| 694 | test "anonymous struct literal assigned to variable" { | |
| 695 | var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) }; | |
| 696 | try expect(vec.@"0" == 22); | |
| 697 | try expect(vec.@"1" == 55); | |
| 698 | try expect(vec.@"2" == 99); | |
| 699 | } | |
| 700 | ||
| 701 | test "struct with var field" { | |
| 702 | const Point = struct { | |
| 703 | x: anytype, | |
| 704 | y: anytype, | |
| 705 | }; | |
| 706 | const pt = Point{ | |
| 707 | .x = 1, | |
| 708 | .y = 2, | |
| 709 | }; | |
| 710 | try expect(pt.x == 1); | |
| 711 | try expect(pt.y == 2); | |
| 712 | } | |
| 713 | ||
| 714 | test "comptime struct field" { | |
| 715 | const T = struct { | |
| 716 | a: i32, | |
| 717 | comptime b: i32 = 1234, | |
| 718 | }; | |
| 719 | ||
| 720 | var foo: T = undefined; | |
| 721 | comptime try expect(foo.b == 1234); | |
| 722 | } | |
| 723 | ||
| 724 | test "anon struct literal field value initialized with fn call" { | |
| 725 | const S = struct { | |
| 726 | fn doTheTest() !void { | |
| 727 | var x = .{foo()}; | |
| 728 | try expectEqualSlices(u8, x[0], "hi"); | |
| 729 | } | |
| 730 | fn foo() []const u8 { | |
| 731 | return "hi"; | |
| 732 | } | |
| 733 | }; | |
| 734 | try S.doTheTest(); | |
| 735 | comptime try S.doTheTest(); | |
| 736 | } | |
| 737 | ||
| 738 | test "self-referencing struct via array member" { | |
| 739 | const T = struct { | |
| 740 | children: [1]*@This(), | |
| 741 | }; | |
| 742 | var x: T = undefined; | |
| 743 | x = T{ .children = .{&x} }; | |
| 744 | try expect(x.children[0] == &x); | |
| 745 | } | |
| 746 | ||
| 747 | test "struct with union field" { | |
| 748 | const Value = struct { | |
| 749 | ref: u32 = 2, | |
| 750 | kind: union(enum) { | |
| 751 | None: usize, | |
| 752 | Bool: bool, | |
| 753 | }, | |
| 754 | }; | |
| 755 | ||
| 756 | var True = Value{ | |
| 757 | .kind = .{ .Bool = true }, | |
| 758 | }; | |
| 759 | try expectEqual(@as(u32, 2), True.ref); | |
| 760 | try expectEqual(true, True.kind.Bool); | |
| 761 | } | |
| 762 | ||
| 763 | test "type coercion of anon struct literal to struct" { | |
| 764 | const S = struct { | |
| 765 | const S2 = struct { | |
| 766 | A: u32, | |
| 767 | B: []const u8, | |
| 768 | C: void, | |
| 769 | D: Foo = .{}, | |
| 770 | }; | |
| 771 | ||
| 772 | const Foo = struct { | |
| 773 | field: i32 = 1234, | |
| 774 | }; | |
| 775 | ||
| 776 | fn doTheTest() !void { | |
| 777 | var y: u32 = 42; | |
| 778 | const t0 = .{ .A = 123, .B = "foo", .C = {} }; | |
| 779 | const t1 = .{ .A = y, .B = "foo", .C = {} }; | |
| 780 | const y0: S2 = t0; | |
| 781 | var y1: S2 = t1; | |
| 782 | try expect(y0.A == 123); | |
| 783 | try expect(std.mem.eql(u8, y0.B, "foo")); | |
| 784 | try expect(y0.C == {}); | |
| 785 | try expect(y0.D.field == 1234); | |
| 786 | try expect(y1.A == y); | |
| 787 | try expect(std.mem.eql(u8, y1.B, "foo")); | |
| 788 | try expect(y1.C == {}); | |
| 789 | try expect(y1.D.field == 1234); | |
| 790 | } | |
| 791 | }; | |
| 792 | try S.doTheTest(); | |
| 793 | comptime try S.doTheTest(); | |
| 794 | } | |
| 795 | ||
| 796 | test "type coercion of pointer to anon struct literal to pointer to struct" { | |
| 797 | const S = struct { | |
| 798 | const S2 = struct { | |
| 799 | A: u32, | |
| 800 | B: []const u8, | |
| 801 | C: void, | |
| 802 | D: Foo = .{}, | |
| 803 | }; | |
| 804 | ||
| 805 | const Foo = struct { | |
| 806 | field: i32 = 1234, | |
| 807 | }; | |
| 808 | ||
| 809 | fn doTheTest() !void { | |
| 810 | var y: u32 = 42; | |
| 811 | const t0 = &.{ .A = 123, .B = "foo", .C = {} }; | |
| 812 | const t1 = &.{ .A = y, .B = "foo", .C = {} }; | |
| 813 | const y0: *const S2 = t0; | |
| 814 | var y1: *const S2 = t1; | |
| 815 | try expect(y0.A == 123); | |
| 816 | try expect(std.mem.eql(u8, y0.B, "foo")); | |
| 817 | try expect(y0.C == {}); | |
| 818 | try expect(y0.D.field == 1234); | |
| 819 | try expect(y1.A == y); | |
| 820 | try expect(std.mem.eql(u8, y1.B, "foo")); | |
| 821 | try expect(y1.C == {}); | |
| 822 | try expect(y1.D.field == 1234); | |
| 823 | } | |
| 824 | }; | |
| 825 | try S.doTheTest(); | |
| 826 | comptime try S.doTheTest(); | |
| 827 | } | |
| 828 | ||
| 829 | test "packed struct with undefined initializers" { | |
| 830 | const S = struct { | |
| 831 | const P = packed struct { | |
| 832 | a: u3, | |
| 833 | _a: u3 = undefined, | |
| 834 | b: u3, | |
| 835 | _b: u3 = undefined, | |
| 836 | c: u3, | |
| 837 | _c: u3 = undefined, | |
| 838 | }; | |
| 839 | ||
| 840 | fn doTheTest() !void { | |
| 841 | var p: P = undefined; | |
| 842 | p = P{ .a = 2, .b = 4, .c = 6 }; | |
| 843 | // Make sure the compiler doesn't touch the unprefixed fields. | |
| 844 | // Use expect since i386-linux doesn't like expectEqual | |
| 845 | try expect(p.a == 2); | |
| 846 | try expect(p.b == 4); | |
| 847 | try expect(p.c == 6); | |
| 848 | } | |
| 849 | }; | |
| 850 | ||
| 851 | try S.doTheTest(); | |
| 852 | comptime try S.doTheTest(); | |
| 853 | } |
test/behavior/switch.zig+7-7| ... | ... | @@ -65,18 +65,18 @@ fn nonConstSwitchOnEnum(fruit: Fruit) void { |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | 67 | test "switch statement" { |
| 68 | try nonConstSwitch(SwitchStatmentFoo.C); | |
| 68 | try nonConstSwitch(SwitchStatementFoo.C); | |
| 69 | 69 | } |
| 70 | fn nonConstSwitch(foo: SwitchStatmentFoo) !void { | |
| 70 | fn nonConstSwitch(foo: SwitchStatementFoo) !void { | |
| 71 | 71 | const val = switch (foo) { |
| 72 | SwitchStatmentFoo.A => @as(i32, 1), | |
| 73 | SwitchStatmentFoo.B => 2, | |
| 74 | SwitchStatmentFoo.C => 3, | |
| 75 | SwitchStatmentFoo.D => 4, | |
| 72 | SwitchStatementFoo.A => @as(i32, 1), | |
| 73 | SwitchStatementFoo.B => 2, | |
| 74 | SwitchStatementFoo.C => 3, | |
| 75 | SwitchStatementFoo.D => 4, | |
| 76 | 76 | }; |
| 77 | 77 | try expect(val == 3); |
| 78 | 78 | } |
| 79 | const SwitchStatmentFoo = enum { | |
| 79 | const SwitchStatementFoo = enum { | |
| 80 | 80 | A, |
| 81 | 81 | B, |
| 82 | 82 | C, |
test/behavior/this.zig+4-5| ... | ... | @@ -24,11 +24,10 @@ test "this refer to module call private fn" { |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | test "this refer to container" { |
| 27 | var pt = Point(i32){ | |
| 28 | .x = 12, | |
| 29 | .y = 34, | |
| 30 | }; | |
| 31 | pt.addOne(); | |
| 27 | var pt: Point(i32) = undefined; | |
| 28 | pt.x = 12; | |
| 29 | pt.y = 34; | |
| 30 | Point(i32).addOne(&pt); | |
| 32 | 31 | try expect(pt.x == 13); |
| 33 | 32 | try expect(pt.y == 35); |
| 34 | 33 | } |
test/behavior/translate_c_macros.zig-22| ... | ... | @@ -3,28 +3,6 @@ const expectEqual = @import("std").testing.expectEqual; |
| 3 | 3 | |
| 4 | 4 | const h = @cImport(@cInclude("behavior/translate_c_macros.h")); |
| 5 | 5 | |
| 6 | test "initializer list expression" { | |
| 7 | try expectEqual(h.Color{ | |
| 8 | .r = 200, | |
| 9 | .g = 200, | |
| 10 | .b = 200, | |
| 11 | .a = 255, | |
| 12 | }, h.LIGHTGRAY); | |
| 13 | } | |
| 14 | ||
| 15 | test "sizeof in macros" { | |
| 16 | try expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF(u32)); | |
| 17 | try expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF2(u32)); | |
| 18 | } | |
| 19 | ||
| 20 | test "reference to a struct type" { | |
| 21 | try expectEqual(@sizeOf(h.struct_Foo), h.SIZE_OF_FOO); | |
| 22 | } | |
| 23 | ||
| 24 | test "cast negative integer to pointer" { | |
| 25 | try expectEqual(@intToPtr(?*c_void, @bitCast(usize, @as(isize, -1))), h.MAP_FAILED); | |
| 26 | } | |
| 27 | ||
| 28 | 6 | test "casting to void with a macro" { |
| 29 | 7 | h.IGNORE_ME_1(42); |
| 30 | 8 | h.IGNORE_ME_2(42); |
test/behavior/translate_c_macros_stage1.zig created+26| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | const expect = @import("std").testing.expect; | |
| 2 | const expectEqual = @import("std").testing.expectEqual; | |
| 3 | ||
| 4 | const h = @cImport(@cInclude("behavior/translate_c_macros.h")); | |
| 5 | ||
| 6 | test "initializer list expression" { | |
| 7 | try expectEqual(h.Color{ | |
| 8 | .r = 200, | |
| 9 | .g = 200, | |
| 10 | .b = 200, | |
| 11 | .a = 255, | |
| 12 | }, h.LIGHTGRAY); | |
| 13 | } | |
| 14 | ||
| 15 | test "sizeof in macros" { | |
| 16 | try expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF(u32)); | |
| 17 | try expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF2(u32)); | |
| 18 | } | |
| 19 | ||
| 20 | test "reference to a struct type" { | |
| 21 | try expectEqual(@sizeOf(h.struct_Foo), h.SIZE_OF_FOO); | |
| 22 | } | |
| 23 | ||
| 24 | test "cast negative integer to pointer" { | |
| 25 | try expectEqual(@intToPtr(?*c_void, @bitCast(usize, @as(isize, -1))), h.MAP_FAILED); | |
| 26 | } |
test/behavior/type.zig+1| ... | ... | @@ -137,6 +137,7 @@ test "@Type create slice with null sentinel" { |
| 137 | 137 | .is_volatile = false, |
| 138 | 138 | .is_allowzero = false, |
| 139 | 139 | .alignment = 8, |
| 140 | .address_space = .generic, | |
| 140 | 141 | .child = *i32, |
| 141 | 142 | .sentinel = null, |
| 142 | 143 | }, |
test/behavior/union.zig-783| ... | ... | @@ -3,42 +3,6 @@ const expect = std.testing.expect; |
| 3 | 3 | const expectEqual = std.testing.expectEqual; |
| 4 | 4 | const Tag = std.meta.Tag; |
| 5 | 5 | |
| 6 | const Value = union(enum) { | |
| 7 | Int: u64, | |
| 8 | Array: [9]u8, | |
| 9 | }; | |
| 10 | ||
| 11 | const Agg = struct { | |
| 12 | val1: Value, | |
| 13 | val2: Value, | |
| 14 | }; | |
| 15 | ||
| 16 | const v1 = Value{ .Int = 1234 }; | |
| 17 | const v2 = Value{ .Array = [_]u8{3} ** 9 }; | |
| 18 | ||
| 19 | const err = @as(anyerror!Agg, Agg{ | |
| 20 | .val1 = v1, | |
| 21 | .val2 = v2, | |
| 22 | }); | |
| 23 | ||
| 24 | const array = [_]Value{ | |
| 25 | v1, | |
| 26 | v2, | |
| 27 | v1, | |
| 28 | v2, | |
| 29 | }; | |
| 30 | ||
| 31 | test "unions embedded in aggregate types" { | |
| 32 | switch (array[1]) { | |
| 33 | Value.Array => |arr| try expect(arr[4] == 3), | |
| 34 | else => unreachable, | |
| 35 | } | |
| 36 | switch ((err catch unreachable).val1) { | |
| 37 | Value.Int => |x| try expect(x == 1234), | |
| 38 | else => unreachable, | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | 6 | const Foo = union { |
| 43 | 7 | float: f64, |
| 44 | 8 | int: i32, |
| ... | ... | @@ -51,16 +15,6 @@ test "basic unions" { |
| 51 | 15 | try expect(foo.float == 12.34); |
| 52 | 16 | } |
| 53 | 17 | |
| 54 | test "comptime union field access" { | |
| 55 | comptime { | |
| 56 | var foo = Foo{ .int = 0 }; | |
| 57 | try expect(foo.int == 0); | |
| 58 | ||
| 59 | foo = Foo{ .float = 42.42 }; | |
| 60 | try expect(foo.float == 42.42); | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | 18 | test "init union with runtime value" { |
| 65 | 19 | var foo: Foo = undefined; |
| 66 | 20 | |
| ... | ... | @@ -78,740 +32,3 @@ fn setFloat(foo: *Foo, x: f64) void { |
| 78 | 32 | fn setInt(foo: *Foo, x: i32) void { |
| 79 | 33 | foo.* = Foo{ .int = x }; |
| 80 | 34 | } |
| 81 | ||
| 82 | const FooExtern = extern union { | |
| 83 | float: f64, | |
| 84 | int: i32, | |
| 85 | }; | |
| 86 | ||
| 87 | test "basic extern unions" { | |
| 88 | var foo = FooExtern{ .int = 1 }; | |
| 89 | try expect(foo.int == 1); | |
| 90 | foo.float = 12.34; | |
| 91 | try expect(foo.float == 12.34); | |
| 92 | } | |
| 93 | ||
| 94 | const Letter = enum { | |
| 95 | A, | |
| 96 | B, | |
| 97 | C, | |
| 98 | }; | |
| 99 | const Payload = union(Letter) { | |
| 100 | A: i32, | |
| 101 | B: f64, | |
| 102 | C: bool, | |
| 103 | }; | |
| 104 | ||
| 105 | test "union with specified enum tag" { | |
| 106 | try doTest(); | |
| 107 | comptime try doTest(); | |
| 108 | } | |
| 109 | ||
| 110 | fn doTest() error{TestUnexpectedResult}!void { | |
| 111 | try expect((try bar(Payload{ .A = 1234 })) == -10); | |
| 112 | } | |
| 113 | ||
| 114 | fn bar(value: Payload) error{TestUnexpectedResult}!i32 { | |
| 115 | try expect(@as(Letter, value) == Letter.A); | |
| 116 | return switch (value) { | |
| 117 | Payload.A => |x| return x - 1244, | |
| 118 | Payload.B => |x| if (x == 12.34) @as(i32, 20) else 21, | |
| 119 | Payload.C => |x| if (x) @as(i32, 30) else 31, | |
| 120 | }; | |
| 121 | } | |
| 122 | ||
| 123 | const MultipleChoice = union(enum(u32)) { | |
| 124 | A = 20, | |
| 125 | B = 40, | |
| 126 | C = 60, | |
| 127 | D = 1000, | |
| 128 | }; | |
| 129 | test "simple union(enum(u32))" { | |
| 130 | var x = MultipleChoice.C; | |
| 131 | try expect(x == MultipleChoice.C); | |
| 132 | try expect(@enumToInt(@as(Tag(MultipleChoice), x)) == 60); | |
| 133 | } | |
| 134 | ||
| 135 | const MultipleChoice2 = union(enum(u32)) { | |
| 136 | Unspecified1: i32, | |
| 137 | A: f32 = 20, | |
| 138 | Unspecified2: void, | |
| 139 | B: bool = 40, | |
| 140 | Unspecified3: i32, | |
| 141 | C: i8 = 60, | |
| 142 | Unspecified4: void, | |
| 143 | D: void = 1000, | |
| 144 | Unspecified5: i32, | |
| 145 | }; | |
| 146 | ||
| 147 | test "union(enum(u32)) with specified and unspecified tag values" { | |
| 148 | comptime try expect(Tag(Tag(MultipleChoice2)) == u32); | |
| 149 | try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); | |
| 150 | comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); | |
| 151 | } | |
| 152 | ||
| 153 | fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void { | |
| 154 | try expect(@enumToInt(@as(Tag(MultipleChoice2), x)) == 60); | |
| 155 | try expect(1123 == switch (x) { | |
| 156 | MultipleChoice2.A => 1, | |
| 157 | MultipleChoice2.B => 2, | |
| 158 | MultipleChoice2.C => |v| @as(i32, 1000) + v, | |
| 159 | MultipleChoice2.D => 4, | |
| 160 | MultipleChoice2.Unspecified1 => 5, | |
| 161 | MultipleChoice2.Unspecified2 => 6, | |
| 162 | MultipleChoice2.Unspecified3 => 7, | |
| 163 | MultipleChoice2.Unspecified4 => 8, | |
| 164 | MultipleChoice2.Unspecified5 => 9, | |
| 165 | }); | |
| 166 | } | |
| 167 | ||
| 168 | const ExternPtrOrInt = extern union { | |
| 169 | ptr: *u8, | |
| 170 | int: u64, | |
| 171 | }; | |
| 172 | test "extern union size" { | |
| 173 | comptime try expect(@sizeOf(ExternPtrOrInt) == 8); | |
| 174 | } | |
| 175 | ||
| 176 | const PackedPtrOrInt = packed union { | |
| 177 | ptr: *u8, | |
| 178 | int: u64, | |
| 179 | }; | |
| 180 | test "extern union size" { | |
| 181 | comptime try expect(@sizeOf(PackedPtrOrInt) == 8); | |
| 182 | } | |
| 183 | ||
| 184 | const ZeroBits = union { | |
| 185 | OnlyField: void, | |
| 186 | }; | |
| 187 | test "union with only 1 field which is void should be zero bits" { | |
| 188 | comptime try expect(@sizeOf(ZeroBits) == 0); | |
| 189 | } | |
| 190 | ||
| 191 | const TheTag = enum { | |
| 192 | A, | |
| 193 | B, | |
| 194 | C, | |
| 195 | }; | |
| 196 | const TheUnion = union(TheTag) { | |
| 197 | A: i32, | |
| 198 | B: i32, | |
| 199 | C: i32, | |
| 200 | }; | |
| 201 | test "union field access gives the enum values" { | |
| 202 | try expect(TheUnion.A == TheTag.A); | |
| 203 | try expect(TheUnion.B == TheTag.B); | |
| 204 | try expect(TheUnion.C == TheTag.C); | |
| 205 | } | |
| 206 | ||
| 207 | test "cast union to tag type of union" { | |
| 208 | try testCastUnionToTag(TheUnion{ .B = 1234 }); | |
| 209 | comptime try testCastUnionToTag(TheUnion{ .B = 1234 }); | |
| 210 | } | |
| 211 | ||
| 212 | fn testCastUnionToTag(x: TheUnion) !void { | |
| 213 | try expect(@as(TheTag, x) == TheTag.B); | |
| 214 | } | |
| 215 | ||
| 216 | test "cast tag type of union to union" { | |
| 217 | var x: Value2 = Letter2.B; | |
| 218 | try expect(@as(Letter2, x) == Letter2.B); | |
| 219 | } | |
| 220 | const Letter2 = enum { | |
| 221 | A, | |
| 222 | B, | |
| 223 | C, | |
| 224 | }; | |
| 225 | const Value2 = union(Letter2) { | |
| 226 | A: i32, | |
| 227 | B, | |
| 228 | C, | |
| 229 | }; | |
| 230 | ||
| 231 | test "implicit cast union to its tag type" { | |
| 232 | var x: Value2 = Letter2.B; | |
| 233 | try expect(x == Letter2.B); | |
| 234 | try giveMeLetterB(x); | |
| 235 | } | |
| 236 | fn giveMeLetterB(x: Letter2) !void { | |
| 237 | try expect(x == Value2.B); | |
| 238 | } | |
| 239 | ||
| 240 | pub const PackThis = union(enum) { | |
| 241 | Invalid: bool, | |
| 242 | StringLiteral: u2, | |
| 243 | }; | |
| 244 | ||
| 245 | test "constant packed union" { | |
| 246 | try testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }}); | |
| 247 | } | |
| 248 | ||
| 249 | fn testConstPackedUnion(expected_tokens: []const PackThis) !void { | |
| 250 | try expect(expected_tokens[0].StringLiteral == 1); | |
| 251 | } | |
| 252 | ||
| 253 | test "switch on union with only 1 field" { | |
| 254 | var r: PartialInst = undefined; | |
| 255 | r = PartialInst.Compiled; | |
| 256 | switch (r) { | |
| 257 | PartialInst.Compiled => { | |
| 258 | var z: PartialInstWithPayload = undefined; | |
| 259 | z = PartialInstWithPayload{ .Compiled = 1234 }; | |
| 260 | switch (z) { | |
| 261 | PartialInstWithPayload.Compiled => |x| { | |
| 262 | try expect(x == 1234); | |
| 263 | return; | |
| 264 | }, | |
| 265 | } | |
| 266 | }, | |
| 267 | } | |
| 268 | unreachable; | |
| 269 | } | |
| 270 | ||
| 271 | const PartialInst = union(enum) { | |
| 272 | Compiled, | |
| 273 | }; | |
| 274 | ||
| 275 | const PartialInstWithPayload = union(enum) { | |
| 276 | Compiled: i32, | |
| 277 | }; | |
| 278 | ||
| 279 | test "access a member of tagged union with conflicting enum tag name" { | |
| 280 | const Bar = union(enum) { | |
| 281 | A: A, | |
| 282 | B: B, | |
| 283 | ||
| 284 | const A = u8; | |
| 285 | const B = void; | |
| 286 | }; | |
| 287 | ||
| 288 | comptime try expect(Bar.A == u8); | |
| 289 | } | |
| 290 | ||
| 291 | test "tagged union initialization with runtime void" { | |
| 292 | try expect(testTaggedUnionInit({})); | |
| 293 | } | |
| 294 | ||
| 295 | const TaggedUnionWithAVoid = union(enum) { | |
| 296 | A, | |
| 297 | B: i32, | |
| 298 | }; | |
| 299 | ||
| 300 | fn testTaggedUnionInit(x: anytype) bool { | |
| 301 | const y = TaggedUnionWithAVoid{ .A = x }; | |
| 302 | return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A; | |
| 303 | } | |
| 304 | ||
| 305 | pub const UnionEnumNoPayloads = union(enum) { | |
| 306 | A, | |
| 307 | B, | |
| 308 | }; | |
| 309 | ||
| 310 | test "tagged union with no payloads" { | |
| 311 | const a = UnionEnumNoPayloads{ .B = {} }; | |
| 312 | switch (a) { | |
| 313 | Tag(UnionEnumNoPayloads).A => @panic("wrong"), | |
| 314 | Tag(UnionEnumNoPayloads).B => {}, | |
| 315 | } | |
| 316 | } | |
| 317 | ||
| 318 | test "union with only 1 field casted to its enum type" { | |
| 319 | const Literal = union(enum) { | |
| 320 | Number: f64, | |
| 321 | Bool: bool, | |
| 322 | }; | |
| 323 | ||
| 324 | const Expr = union(enum) { | |
| 325 | Literal: Literal, | |
| 326 | }; | |
| 327 | ||
| 328 | var e = Expr{ .Literal = Literal{ .Bool = true } }; | |
| 329 | const ExprTag = Tag(Expr); | |
| 330 | comptime try expect(Tag(ExprTag) == u0); | |
| 331 | var t = @as(ExprTag, e); | |
| 332 | try expect(t == Expr.Literal); | |
| 333 | } | |
| 334 | ||
| 335 | test "union with only 1 field casted to its enum type which has enum value specified" { | |
| 336 | const Literal = union(enum) { | |
| 337 | Number: f64, | |
| 338 | Bool: bool, | |
| 339 | }; | |
| 340 | ||
| 341 | const ExprTag = enum(comptime_int) { | |
| 342 | Literal = 33, | |
| 343 | }; | |
| 344 | ||
| 345 | const Expr = union(ExprTag) { | |
| 346 | Literal: Literal, | |
| 347 | }; | |
| 348 | ||
| 349 | var e = Expr{ .Literal = Literal{ .Bool = true } }; | |
| 350 | comptime try expect(Tag(ExprTag) == comptime_int); | |
| 351 | var t = @as(ExprTag, e); | |
| 352 | try expect(t == Expr.Literal); | |
| 353 | try expect(@enumToInt(t) == 33); | |
| 354 | comptime try expect(@enumToInt(t) == 33); | |
| 355 | } | |
| 356 | ||
| 357 | test "@enumToInt works on unions" { | |
| 358 | const Bar = union(enum) { | |
| 359 | A: bool, | |
| 360 | B: u8, | |
| 361 | C, | |
| 362 | }; | |
| 363 | ||
| 364 | const a = Bar{ .A = true }; | |
| 365 | var b = Bar{ .B = undefined }; | |
| 366 | var c = Bar.C; | |
| 367 | try expect(@enumToInt(a) == 0); | |
| 368 | try expect(@enumToInt(b) == 1); | |
| 369 | try expect(@enumToInt(c) == 2); | |
| 370 | } | |
| 371 | ||
| 372 | const Attribute = union(enum) { | |
| 373 | A: bool, | |
| 374 | B: u8, | |
| 375 | }; | |
| 376 | ||
| 377 | fn setAttribute(attr: Attribute) void { | |
| 378 | _ = attr; | |
| 379 | } | |
| 380 | ||
| 381 | fn Setter(attr: Attribute) type { | |
| 382 | return struct { | |
| 383 | fn set() void { | |
| 384 | setAttribute(attr); | |
| 385 | } | |
| 386 | }; | |
| 387 | } | |
| 388 | ||
| 389 | test "comptime union field value equality" { | |
| 390 | const a0 = Setter(Attribute{ .A = false }); | |
| 391 | const a1 = Setter(Attribute{ .A = true }); | |
| 392 | const a2 = Setter(Attribute{ .A = false }); | |
| 393 | ||
| 394 | const b0 = Setter(Attribute{ .B = 5 }); | |
| 395 | const b1 = Setter(Attribute{ .B = 9 }); | |
| 396 | const b2 = Setter(Attribute{ .B = 5 }); | |
| 397 | ||
| 398 | try expect(a0 == a0); | |
| 399 | try expect(a1 == a1); | |
| 400 | try expect(a0 == a2); | |
| 401 | ||
| 402 | try expect(b0 == b0); | |
| 403 | try expect(b1 == b1); | |
| 404 | try expect(b0 == b2); | |
| 405 | ||
| 406 | try expect(a0 != b0); | |
| 407 | try expect(a0 != a1); | |
| 408 | try expect(b0 != b1); | |
| 409 | } | |
| 410 | ||
| 411 | test "return union init with void payload" { | |
| 412 | const S = struct { | |
| 413 | fn entry() !void { | |
| 414 | try expect(func().state == State.one); | |
| 415 | } | |
| 416 | const Outer = union(enum) { | |
| 417 | state: State, | |
| 418 | }; | |
| 419 | const State = union(enum) { | |
| 420 | one: void, | |
| 421 | two: u32, | |
| 422 | }; | |
| 423 | fn func() Outer { | |
| 424 | return Outer{ .state = State{ .one = {} } }; | |
| 425 | } | |
| 426 | }; | |
| 427 | try S.entry(); | |
| 428 | comptime try S.entry(); | |
| 429 | } | |
| 430 | ||
| 431 | test "@unionInit can modify a union type" { | |
| 432 | const UnionInitEnum = union(enum) { | |
| 433 | Boolean: bool, | |
| 434 | Byte: u8, | |
| 435 | }; | |
| 436 | ||
| 437 | var value: UnionInitEnum = undefined; | |
| 438 | ||
| 439 | value = @unionInit(UnionInitEnum, "Boolean", true); | |
| 440 | try expect(value.Boolean == true); | |
| 441 | value.Boolean = false; | |
| 442 | try expect(value.Boolean == false); | |
| 443 | ||
| 444 | value = @unionInit(UnionInitEnum, "Byte", 2); | |
| 445 | try expect(value.Byte == 2); | |
| 446 | value.Byte = 3; | |
| 447 | try expect(value.Byte == 3); | |
| 448 | } | |
| 449 | ||
| 450 | test "@unionInit can modify a pointer value" { | |
| 451 | const UnionInitEnum = union(enum) { | |
| 452 | Boolean: bool, | |
| 453 | Byte: u8, | |
| 454 | }; | |
| 455 | ||
| 456 | var value: UnionInitEnum = undefined; | |
| 457 | var value_ptr = &value; | |
| 458 | ||
| 459 | value_ptr.* = @unionInit(UnionInitEnum, "Boolean", true); | |
| 460 | try expect(value.Boolean == true); | |
| 461 | ||
| 462 | value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2); | |
| 463 | try expect(value.Byte == 2); | |
| 464 | } | |
| 465 | ||
| 466 | test "union no tag with struct member" { | |
| 467 | const Struct = struct {}; | |
| 468 | const Union = union { | |
| 469 | s: Struct, | |
| 470 | pub fn foo(self: *@This()) void { | |
| 471 | _ = self; | |
| 472 | } | |
| 473 | }; | |
| 474 | var u = Union{ .s = Struct{} }; | |
| 475 | u.foo(); | |
| 476 | } | |
| 477 | ||
| 478 | fn testComparison() !void { | |
| 479 | var x = Payload{ .A = 42 }; | |
| 480 | try expect(x == .A); | |
| 481 | try expect(x != .B); | |
| 482 | try expect(x != .C); | |
| 483 | try expect((x == .B) == false); | |
| 484 | try expect((x == .C) == false); | |
| 485 | try expect((x != .A) == false); | |
| 486 | } | |
| 487 | ||
| 488 | test "comparison between union and enum literal" { | |
| 489 | try testComparison(); | |
| 490 | comptime try testComparison(); | |
| 491 | } | |
| 492 | ||
| 493 | test "packed union generates correctly aligned LLVM type" { | |
| 494 | const U = packed union { | |
| 495 | f1: fn () error{TestUnexpectedResult}!void, | |
| 496 | f2: u32, | |
| 497 | }; | |
| 498 | var foo = [_]U{ | |
| 499 | U{ .f1 = doTest }, | |
| 500 | U{ .f2 = 0 }, | |
| 501 | }; | |
| 502 | try foo[0].f1(); | |
| 503 | } | |
| 504 | ||
| 505 | test "union with one member defaults to u0 tag type" { | |
| 506 | const U0 = union(enum) { | |
| 507 | X: u32, | |
| 508 | }; | |
| 509 | comptime try expect(Tag(Tag(U0)) == u0); | |
| 510 | } | |
| 511 | ||
| 512 | test "union with comptime_int tag" { | |
| 513 | const Union = union(enum(comptime_int)) { | |
| 514 | X: u32, | |
| 515 | Y: u16, | |
| 516 | Z: u8, | |
| 517 | }; | |
| 518 | comptime try expect(Tag(Tag(Union)) == comptime_int); | |
| 519 | } | |
| 520 | ||
| 521 | test "extern union doesn't trigger field check at comptime" { | |
| 522 | const U = extern union { | |
| 523 | x: u32, | |
| 524 | y: u8, | |
| 525 | }; | |
| 526 | ||
| 527 | const x = U{ .x = 0x55AAAA55 }; | |
| 528 | comptime try expect(x.y == 0x55); | |
| 529 | } | |
| 530 | ||
| 531 | const Foo1 = union(enum) { | |
| 532 | f: struct { | |
| 533 | x: usize, | |
| 534 | }, | |
| 535 | }; | |
| 536 | var glbl: Foo1 = undefined; | |
| 537 | ||
| 538 | test "global union with single field is correctly initialized" { | |
| 539 | glbl = Foo1{ | |
| 540 | .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 }, | |
| 541 | }; | |
| 542 | try expect(glbl.f.x == 123); | |
| 543 | } | |
| 544 | ||
| 545 | pub const FooUnion = union(enum) { | |
| 546 | U0: usize, | |
| 547 | U1: u8, | |
| 548 | }; | |
| 549 | ||
| 550 | var glbl_array: [2]FooUnion = undefined; | |
| 551 | ||
| 552 | test "initialize global array of union" { | |
| 553 | glbl_array[1] = FooUnion{ .U1 = 2 }; | |
| 554 | glbl_array[0] = FooUnion{ .U0 = 1 }; | |
| 555 | try expect(glbl_array[0].U0 == 1); | |
| 556 | try expect(glbl_array[1].U1 == 2); | |
| 557 | } | |
| 558 | ||
| 559 | test "anonymous union literal syntax" { | |
| 560 | const S = struct { | |
| 561 | const Number = union { | |
| 562 | int: i32, | |
| 563 | float: f64, | |
| 564 | }; | |
| 565 | ||
| 566 | fn doTheTest() !void { | |
| 567 | var i: Number = .{ .int = 42 }; | |
| 568 | var f = makeNumber(); | |
| 569 | try expect(i.int == 42); | |
| 570 | try expect(f.float == 12.34); | |
| 571 | } | |
| 572 | ||
| 573 | fn makeNumber() Number { | |
| 574 | return .{ .float = 12.34 }; | |
| 575 | } | |
| 576 | }; | |
| 577 | try S.doTheTest(); | |
| 578 | comptime try S.doTheTest(); | |
| 579 | } | |
| 580 | ||
| 581 | test "update the tag value for zero-sized unions" { | |
| 582 | const S = union(enum) { | |
| 583 | U0: void, | |
| 584 | U1: void, | |
| 585 | }; | |
| 586 | var x = S{ .U0 = {} }; | |
| 587 | try expect(x == .U0); | |
| 588 | x = S{ .U1 = {} }; | |
| 589 | try expect(x == .U1); | |
| 590 | } | |
| 591 | ||
| 592 | test "function call result coerces from tagged union to the tag" { | |
| 593 | const S = struct { | |
| 594 | const Arch = union(enum) { | |
| 595 | One, | |
| 596 | Two: usize, | |
| 597 | }; | |
| 598 | ||
| 599 | const ArchTag = Tag(Arch); | |
| 600 | ||
| 601 | fn doTheTest() !void { | |
| 602 | var x: ArchTag = getArch1(); | |
| 603 | try expect(x == .One); | |
| 604 | ||
| 605 | var y: ArchTag = getArch2(); | |
| 606 | try expect(y == .Two); | |
| 607 | } | |
| 608 | ||
| 609 | pub fn getArch1() Arch { | |
| 610 | return .One; | |
| 611 | } | |
| 612 | ||
| 613 | pub fn getArch2() Arch { | |
| 614 | return .{ .Two = 99 }; | |
| 615 | } | |
| 616 | }; | |
| 617 | try S.doTheTest(); | |
| 618 | comptime try S.doTheTest(); | |
| 619 | } | |
| 620 | ||
| 621 | test "0-sized extern union definition" { | |
| 622 | const U = extern union { | |
| 623 | a: void, | |
| 624 | const f = 1; | |
| 625 | }; | |
| 626 | ||
| 627 | try expect(U.f == 1); | |
| 628 | } | |
| 629 | ||
| 630 | test "union initializer generates padding only if needed" { | |
| 631 | const U = union(enum) { | |
| 632 | A: u24, | |
| 633 | }; | |
| 634 | ||
| 635 | var v = U{ .A = 532 }; | |
| 636 | try expect(v.A == 532); | |
| 637 | } | |
| 638 | ||
| 639 | test "runtime tag name with single field" { | |
| 640 | const U = union(enum) { | |
| 641 | A: i32, | |
| 642 | }; | |
| 643 | ||
| 644 | var v = U{ .A = 42 }; | |
| 645 | try expect(std.mem.eql(u8, @tagName(v), "A")); | |
| 646 | } | |
| 647 | ||
| 648 | test "cast from anonymous struct to union" { | |
| 649 | const S = struct { | |
| 650 | const U = union(enum) { | |
| 651 | A: u32, | |
| 652 | B: []const u8, | |
| 653 | C: void, | |
| 654 | }; | |
| 655 | fn doTheTest() !void { | |
| 656 | var y: u32 = 42; | |
| 657 | const t0 = .{ .A = 123 }; | |
| 658 | const t1 = .{ .B = "foo" }; | |
| 659 | const t2 = .{ .C = {} }; | |
| 660 | const t3 = .{ .A = y }; | |
| 661 | const x0: U = t0; | |
| 662 | var x1: U = t1; | |
| 663 | const x2: U = t2; | |
| 664 | var x3: U = t3; | |
| 665 | try expect(x0.A == 123); | |
| 666 | try expect(std.mem.eql(u8, x1.B, "foo")); | |
| 667 | try expect(x2 == .C); | |
| 668 | try expect(x3.A == y); | |
| 669 | } | |
| 670 | }; | |
| 671 | try S.doTheTest(); | |
| 672 | comptime try S.doTheTest(); | |
| 673 | } | |
| 674 | ||
| 675 | test "cast from pointer to anonymous struct to pointer to union" { | |
| 676 | const S = struct { | |
| 677 | const U = union(enum) { | |
| 678 | A: u32, | |
| 679 | B: []const u8, | |
| 680 | C: void, | |
| 681 | }; | |
| 682 | fn doTheTest() !void { | |
| 683 | var y: u32 = 42; | |
| 684 | const t0 = &.{ .A = 123 }; | |
| 685 | const t1 = &.{ .B = "foo" }; | |
| 686 | const t2 = &.{ .C = {} }; | |
| 687 | const t3 = &.{ .A = y }; | |
| 688 | const x0: *const U = t0; | |
| 689 | var x1: *const U = t1; | |
| 690 | const x2: *const U = t2; | |
| 691 | var x3: *const U = t3; | |
| 692 | try expect(x0.A == 123); | |
| 693 | try expect(std.mem.eql(u8, x1.B, "foo")); | |
| 694 | try expect(x2.* == .C); | |
| 695 | try expect(x3.A == y); | |
| 696 | } | |
| 697 | }; | |
| 698 | try S.doTheTest(); | |
| 699 | comptime try S.doTheTest(); | |
| 700 | } | |
| 701 | ||
| 702 | test "method call on an empty union" { | |
| 703 | const S = struct { | |
| 704 | const MyUnion = union(MyUnionTag) { | |
| 705 | pub const MyUnionTag = enum { X1, X2 }; | |
| 706 | X1: [0]u8, | |
| 707 | X2: [0]u8, | |
| 708 | ||
| 709 | pub fn useIt(self: *@This()) bool { | |
| 710 | _ = self; | |
| 711 | return true; | |
| 712 | } | |
| 713 | }; | |
| 714 | ||
| 715 | fn doTheTest() !void { | |
| 716 | var u = MyUnion{ .X1 = [0]u8{} }; | |
| 717 | try expect(u.useIt()); | |
| 718 | } | |
| 719 | }; | |
| 720 | try S.doTheTest(); | |
| 721 | comptime try S.doTheTest(); | |
| 722 | } | |
| 723 | ||
| 724 | test "switching on non exhaustive union" { | |
| 725 | const S = struct { | |
| 726 | const E = enum(u8) { | |
| 727 | a, | |
| 728 | b, | |
| 729 | _, | |
| 730 | }; | |
| 731 | const U = union(E) { | |
| 732 | a: i32, | |
| 733 | b: u32, | |
| 734 | }; | |
| 735 | fn doTheTest() !void { | |
| 736 | var a = U{ .a = 2 }; | |
| 737 | switch (a) { | |
| 738 | .a => |val| try expect(val == 2), | |
| 739 | .b => unreachable, | |
| 740 | } | |
| 741 | } | |
| 742 | }; | |
| 743 | try S.doTheTest(); | |
| 744 | comptime try S.doTheTest(); | |
| 745 | } | |
| 746 | ||
| 747 | test "containers with single-field enums" { | |
| 748 | const S = struct { | |
| 749 | const A = union(enum) { f1 }; | |
| 750 | const B = union(enum) { f1: void }; | |
| 751 | const C = struct { a: A }; | |
| 752 | const D = struct { a: B }; | |
| 753 | ||
| 754 | fn doTheTest() !void { | |
| 755 | var array1 = [1]A{A{ .f1 = {} }}; | |
| 756 | var array2 = [1]B{B{ .f1 = {} }}; | |
| 757 | try expect(array1[0] == .f1); | |
| 758 | try expect(array2[0] == .f1); | |
| 759 | ||
| 760 | var struct1 = C{ .a = A{ .f1 = {} } }; | |
| 761 | var struct2 = D{ .a = B{ .f1 = {} } }; | |
| 762 | try expect(struct1.a == .f1); | |
| 763 | try expect(struct2.a == .f1); | |
| 764 | } | |
| 765 | }; | |
| 766 | ||
| 767 | try S.doTheTest(); | |
| 768 | comptime try S.doTheTest(); | |
| 769 | } | |
| 770 | ||
| 771 | test "@unionInit on union w/ tag but no fields" { | |
| 772 | const S = struct { | |
| 773 | const Type = enum(u8) { no_op = 105 }; | |
| 774 | ||
| 775 | const Data = union(Type) { | |
| 776 | no_op: void, | |
| 777 | ||
| 778 | pub fn decode(buf: []const u8) Data { | |
| 779 | _ = buf; | |
| 780 | return @unionInit(Data, "no_op", {}); | |
| 781 | } | |
| 782 | }; | |
| 783 | ||
| 784 | comptime { | |
| 785 | std.debug.assert(@sizeOf(Data) != 0); | |
| 786 | } | |
| 787 | ||
| 788 | fn doTheTest() !void { | |
| 789 | var data: Data = .{ .no_op = .{} }; | |
| 790 | _ = data; | |
| 791 | var o = Data.decode(&[_]u8{}); | |
| 792 | try expectEqual(Type.no_op, o); | |
| 793 | } | |
| 794 | }; | |
| 795 | ||
| 796 | try S.doTheTest(); | |
| 797 | comptime try S.doTheTest(); | |
| 798 | } | |
| 799 | ||
| 800 | test "union enum type gets a separate scope" { | |
| 801 | const S = struct { | |
| 802 | const U = union(enum) { | |
| 803 | a: u8, | |
| 804 | const foo = 1; | |
| 805 | }; | |
| 806 | ||
| 807 | fn doTheTest() !void { | |
| 808 | try expect(!@hasDecl(Tag(U), "foo")); | |
| 809 | } | |
| 810 | }; | |
| 811 | ||
| 812 | try S.doTheTest(); | |
| 813 | } | |
| 814 | test "anytype union field: issue #9233" { | |
| 815 | const Baz = union(enum) { bar: anytype }; | |
| 816 | _ = Baz; | |
| 817 | } |
test/behavior/union_stage1.zig created+775| ... | ... | @@ -0,0 +1,775 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const expectEqual = std.testing.expectEqual; | |
| 4 | const Tag = std.meta.Tag; | |
| 5 | ||
| 6 | const Value = union(enum) { | |
| 7 | Int: u64, | |
| 8 | Array: [9]u8, | |
| 9 | }; | |
| 10 | ||
| 11 | const Agg = struct { | |
| 12 | val1: Value, | |
| 13 | val2: Value, | |
| 14 | }; | |
| 15 | ||
| 16 | const v1 = Value{ .Int = 1234 }; | |
| 17 | const v2 = Value{ .Array = [_]u8{3} ** 9 }; | |
| 18 | ||
| 19 | const err = @as(anyerror!Agg, Agg{ | |
| 20 | .val1 = v1, | |
| 21 | .val2 = v2, | |
| 22 | }); | |
| 23 | ||
| 24 | const array = [_]Value{ v1, v2, v1, v2 }; | |
| 25 | ||
| 26 | test "unions embedded in aggregate types" { | |
| 27 | switch (array[1]) { | |
| 28 | Value.Array => |arr| try expect(arr[4] == 3), | |
| 29 | else => unreachable, | |
| 30 | } | |
| 31 | switch ((err catch unreachable).val1) { | |
| 32 | Value.Int => |x| try expect(x == 1234), | |
| 33 | else => unreachable, | |
| 34 | } | |
| 35 | } | |
| 36 | ||
| 37 | const Foo = union { | |
| 38 | float: f64, | |
| 39 | int: i32, | |
| 40 | }; | |
| 41 | ||
| 42 | test "comptime union field access" { | |
| 43 | comptime { | |
| 44 | var foo = Foo{ .int = 0 }; | |
| 45 | try expect(foo.int == 0); | |
| 46 | ||
| 47 | foo = Foo{ .float = 42.42 }; | |
| 48 | try expect(foo.float == 42.42); | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | const FooExtern = extern union { | |
| 53 | float: f64, | |
| 54 | int: i32, | |
| 55 | }; | |
| 56 | ||
| 57 | test "basic extern unions" { | |
| 58 | var foo = FooExtern{ .int = 1 }; | |
| 59 | try expect(foo.int == 1); | |
| 60 | foo.float = 12.34; | |
| 61 | try expect(foo.float == 12.34); | |
| 62 | } | |
| 63 | ||
| 64 | const Letter = enum { A, B, C }; | |
| 65 | const Payload = union(Letter) { | |
| 66 | A: i32, | |
| 67 | B: f64, | |
| 68 | C: bool, | |
| 69 | }; | |
| 70 | ||
| 71 | test "union with specified enum tag" { | |
| 72 | try doTest(); | |
| 73 | comptime try doTest(); | |
| 74 | } | |
| 75 | ||
| 76 | fn doTest() error{TestUnexpectedResult}!void { | |
| 77 | try expect((try bar(Payload{ .A = 1234 })) == -10); | |
| 78 | } | |
| 79 | ||
| 80 | fn bar(value: Payload) error{TestUnexpectedResult}!i32 { | |
| 81 | try expect(@as(Letter, value) == Letter.A); | |
| 82 | return switch (value) { | |
| 83 | Payload.A => |x| return x - 1244, | |
| 84 | Payload.B => |x| if (x == 12.34) @as(i32, 20) else 21, | |
| 85 | Payload.C => |x| if (x) @as(i32, 30) else 31, | |
| 86 | }; | |
| 87 | } | |
| 88 | ||
| 89 | const MultipleChoice = union(enum(u32)) { | |
| 90 | A = 20, | |
| 91 | B = 40, | |
| 92 | C = 60, | |
| 93 | D = 1000, | |
| 94 | }; | |
| 95 | test "simple union(enum(u32))" { | |
| 96 | var x = MultipleChoice.C; | |
| 97 | try expect(x == MultipleChoice.C); | |
| 98 | try expect(@enumToInt(@as(Tag(MultipleChoice), x)) == 60); | |
| 99 | } | |
| 100 | ||
| 101 | const MultipleChoice2 = union(enum(u32)) { | |
| 102 | Unspecified1: i32, | |
| 103 | A: f32 = 20, | |
| 104 | Unspecified2: void, | |
| 105 | B: bool = 40, | |
| 106 | Unspecified3: i32, | |
| 107 | C: i8 = 60, | |
| 108 | Unspecified4: void, | |
| 109 | D: void = 1000, | |
| 110 | Unspecified5: i32, | |
| 111 | }; | |
| 112 | ||
| 113 | test "union(enum(u32)) with specified and unspecified tag values" { | |
| 114 | comptime try expect(Tag(Tag(MultipleChoice2)) == u32); | |
| 115 | try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); | |
| 116 | comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); | |
| 117 | } | |
| 118 | ||
| 119 | fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void { | |
| 120 | try expect(@enumToInt(@as(Tag(MultipleChoice2), x)) == 60); | |
| 121 | try expect(1123 == switch (x) { | |
| 122 | MultipleChoice2.A => 1, | |
| 123 | MultipleChoice2.B => 2, | |
| 124 | MultipleChoice2.C => |v| @as(i32, 1000) + v, | |
| 125 | MultipleChoice2.D => 4, | |
| 126 | MultipleChoice2.Unspecified1 => 5, | |
| 127 | MultipleChoice2.Unspecified2 => 6, | |
| 128 | MultipleChoice2.Unspecified3 => 7, | |
| 129 | MultipleChoice2.Unspecified4 => 8, | |
| 130 | MultipleChoice2.Unspecified5 => 9, | |
| 131 | }); | |
| 132 | } | |
| 133 | ||
| 134 | const ExternPtrOrInt = extern union { | |
| 135 | ptr: *u8, | |
| 136 | int: u64, | |
| 137 | }; | |
| 138 | test "extern union size" { | |
| 139 | comptime try expect(@sizeOf(ExternPtrOrInt) == 8); | |
| 140 | } | |
| 141 | ||
| 142 | const PackedPtrOrInt = packed union { | |
| 143 | ptr: *u8, | |
| 144 | int: u64, | |
| 145 | }; | |
| 146 | test "extern union size" { | |
| 147 | comptime try expect(@sizeOf(PackedPtrOrInt) == 8); | |
| 148 | } | |
| 149 | ||
| 150 | const ZeroBits = union { | |
| 151 | OnlyField: void, | |
| 152 | }; | |
| 153 | test "union with only 1 field which is void should be zero bits" { | |
| 154 | comptime try expect(@sizeOf(ZeroBits) == 0); | |
| 155 | } | |
| 156 | ||
| 157 | const TheTag = enum { A, B, C }; | |
| 158 | const TheUnion = union(TheTag) { | |
| 159 | A: i32, | |
| 160 | B: i32, | |
| 161 | C: i32, | |
| 162 | }; | |
| 163 | test "union field access gives the enum values" { | |
| 164 | try expect(TheUnion.A == TheTag.A); | |
| 165 | try expect(TheUnion.B == TheTag.B); | |
| 166 | try expect(TheUnion.C == TheTag.C); | |
| 167 | } | |
| 168 | ||
| 169 | test "cast union to tag type of union" { | |
| 170 | try testCastUnionToTag(); | |
| 171 | comptime try testCastUnionToTag(); | |
| 172 | } | |
| 173 | ||
| 174 | fn testCastUnionToTag() !void { | |
| 175 | var u = TheUnion{ .B = 1234 }; | |
| 176 | try expect(@as(TheTag, u) == TheTag.B); | |
| 177 | } | |
| 178 | ||
| 179 | test "cast tag type of union to union" { | |
| 180 | var x: Value2 = Letter2.B; | |
| 181 | try expect(@as(Letter2, x) == Letter2.B); | |
| 182 | } | |
| 183 | const Letter2 = enum { A, B, C }; | |
| 184 | const Value2 = union(Letter2) { | |
| 185 | A: i32, | |
| 186 | B, | |
| 187 | C, | |
| 188 | }; | |
| 189 | ||
| 190 | test "implicit cast union to its tag type" { | |
| 191 | var x: Value2 = Letter2.B; | |
| 192 | try expect(x == Letter2.B); | |
| 193 | try giveMeLetterB(x); | |
| 194 | } | |
| 195 | fn giveMeLetterB(x: Letter2) !void { | |
| 196 | try expect(x == Value2.B); | |
| 197 | } | |
| 198 | ||
| 199 | // TODO it looks like this test intended to test packed unions, but this is not a packed | |
| 200 | // union. go through git history and find out what happened. | |
| 201 | pub const PackThis = union(enum) { | |
| 202 | Invalid: bool, | |
| 203 | StringLiteral: u2, | |
| 204 | }; | |
| 205 | ||
| 206 | test "constant packed union" { | |
| 207 | try testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }}); | |
| 208 | } | |
| 209 | ||
| 210 | fn testConstPackedUnion(expected_tokens: []const PackThis) !void { | |
| 211 | try expect(expected_tokens[0].StringLiteral == 1); | |
| 212 | } | |
| 213 | ||
| 214 | test "switch on union with only 1 field" { | |
| 215 | var r: PartialInst = undefined; | |
| 216 | r = PartialInst.Compiled; | |
| 217 | switch (r) { | |
| 218 | PartialInst.Compiled => { | |
| 219 | var z: PartialInstWithPayload = undefined; | |
| 220 | z = PartialInstWithPayload{ .Compiled = 1234 }; | |
| 221 | switch (z) { | |
| 222 | PartialInstWithPayload.Compiled => |x| { | |
| 223 | try expect(x == 1234); | |
| 224 | return; | |
| 225 | }, | |
| 226 | } | |
| 227 | }, | |
| 228 | } | |
| 229 | unreachable; | |
| 230 | } | |
| 231 | ||
| 232 | const PartialInst = union(enum) { | |
| 233 | Compiled, | |
| 234 | }; | |
| 235 | ||
| 236 | const PartialInstWithPayload = union(enum) { | |
| 237 | Compiled: i32, | |
| 238 | }; | |
| 239 | ||
| 240 | test "access a member of tagged union with conflicting enum tag name" { | |
| 241 | const Bar = union(enum) { | |
| 242 | A: A, | |
| 243 | B: B, | |
| 244 | ||
| 245 | const A = u8; | |
| 246 | const B = void; | |
| 247 | }; | |
| 248 | ||
| 249 | comptime try expect(Bar.A == u8); | |
| 250 | } | |
| 251 | ||
| 252 | test "tagged union initialization with runtime void" { | |
| 253 | try expect(testTaggedUnionInit({})); | |
| 254 | } | |
| 255 | ||
| 256 | const TaggedUnionWithAVoid = union(enum) { | |
| 257 | A, | |
| 258 | B: i32, | |
| 259 | }; | |
| 260 | ||
| 261 | fn testTaggedUnionInit(x: anytype) bool { | |
| 262 | const y = TaggedUnionWithAVoid{ .A = x }; | |
| 263 | return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A; | |
| 264 | } | |
| 265 | ||
| 266 | pub const UnionEnumNoPayloads = union(enum) { A, B }; | |
| 267 | ||
| 268 | test "tagged union with no payloads" { | |
| 269 | const a = UnionEnumNoPayloads{ .B = {} }; | |
| 270 | switch (a) { | |
| 271 | Tag(UnionEnumNoPayloads).A => @panic("wrong"), | |
| 272 | Tag(UnionEnumNoPayloads).B => {}, | |
| 273 | } | |
| 274 | } | |
| 275 | ||
| 276 | test "union with only 1 field casted to its enum type" { | |
| 277 | const Literal = union(enum) { | |
| 278 | Number: f64, | |
| 279 | Bool: bool, | |
| 280 | }; | |
| 281 | ||
| 282 | const Expr = union(enum) { | |
| 283 | Literal: Literal, | |
| 284 | }; | |
| 285 | ||
| 286 | var e = Expr{ .Literal = Literal{ .Bool = true } }; | |
| 287 | const ExprTag = Tag(Expr); | |
| 288 | comptime try expect(Tag(ExprTag) == u0); | |
| 289 | var t = @as(ExprTag, e); | |
| 290 | try expect(t == Expr.Literal); | |
| 291 | } | |
| 292 | ||
| 293 | test "union with only 1 field casted to its enum type which has enum value specified" { | |
| 294 | const Literal = union(enum) { | |
| 295 | Number: f64, | |
| 296 | Bool: bool, | |
| 297 | }; | |
| 298 | ||
| 299 | const ExprTag = enum(comptime_int) { | |
| 300 | Literal = 33, | |
| 301 | }; | |
| 302 | ||
| 303 | const Expr = union(ExprTag) { | |
| 304 | Literal: Literal, | |
| 305 | }; | |
| 306 | ||
| 307 | var e = Expr{ .Literal = Literal{ .Bool = true } }; | |
| 308 | comptime try expect(Tag(ExprTag) == comptime_int); | |
| 309 | var t = @as(ExprTag, e); | |
| 310 | try expect(t == Expr.Literal); | |
| 311 | try expect(@enumToInt(t) == 33); | |
| 312 | comptime try expect(@enumToInt(t) == 33); | |
| 313 | } | |
| 314 | ||
| 315 | test "@enumToInt works on unions" { | |
| 316 | const Bar = union(enum) { | |
| 317 | A: bool, | |
| 318 | B: u8, | |
| 319 | C, | |
| 320 | }; | |
| 321 | ||
| 322 | const a = Bar{ .A = true }; | |
| 323 | var b = Bar{ .B = undefined }; | |
| 324 | var c = Bar.C; | |
| 325 | try expect(@enumToInt(a) == 0); | |
| 326 | try expect(@enumToInt(b) == 1); | |
| 327 | try expect(@enumToInt(c) == 2); | |
| 328 | } | |
| 329 | ||
| 330 | const Attribute = union(enum) { | |
| 331 | A: bool, | |
| 332 | B: u8, | |
| 333 | }; | |
| 334 | ||
| 335 | fn setAttribute(attr: Attribute) void { | |
| 336 | _ = attr; | |
| 337 | } | |
| 338 | ||
| 339 | fn Setter(attr: Attribute) type { | |
| 340 | return struct { | |
| 341 | fn set() void { | |
| 342 | setAttribute(attr); | |
| 343 | } | |
| 344 | }; | |
| 345 | } | |
| 346 | ||
| 347 | test "comptime union field value equality" { | |
| 348 | const a0 = Setter(Attribute{ .A = false }); | |
| 349 | const a1 = Setter(Attribute{ .A = true }); | |
| 350 | const a2 = Setter(Attribute{ .A = false }); | |
| 351 | ||
| 352 | const b0 = Setter(Attribute{ .B = 5 }); | |
| 353 | const b1 = Setter(Attribute{ .B = 9 }); | |
| 354 | const b2 = Setter(Attribute{ .B = 5 }); | |
| 355 | ||
| 356 | try expect(a0 == a0); | |
| 357 | try expect(a1 == a1); | |
| 358 | try expect(a0 == a2); | |
| 359 | ||
| 360 | try expect(b0 == b0); | |
| 361 | try expect(b1 == b1); | |
| 362 | try expect(b0 == b2); | |
| 363 | ||
| 364 | try expect(a0 != b0); | |
| 365 | try expect(a0 != a1); | |
| 366 | try expect(b0 != b1); | |
| 367 | } | |
| 368 | ||
| 369 | test "return union init with void payload" { | |
| 370 | const S = struct { | |
| 371 | fn entry() !void { | |
| 372 | try expect(func().state == State.one); | |
| 373 | } | |
| 374 | const Outer = union(enum) { | |
| 375 | state: State, | |
| 376 | }; | |
| 377 | const State = union(enum) { | |
| 378 | one: void, | |
| 379 | two: u32, | |
| 380 | }; | |
| 381 | fn func() Outer { | |
| 382 | return Outer{ .state = State{ .one = {} } }; | |
| 383 | } | |
| 384 | }; | |
| 385 | try S.entry(); | |
| 386 | comptime try S.entry(); | |
| 387 | } | |
| 388 | ||
| 389 | test "@unionInit can modify a union type" { | |
| 390 | const UnionInitEnum = union(enum) { | |
| 391 | Boolean: bool, | |
| 392 | Byte: u8, | |
| 393 | }; | |
| 394 | ||
| 395 | var value: UnionInitEnum = undefined; | |
| 396 | ||
| 397 | value = @unionInit(UnionInitEnum, "Boolean", true); | |
| 398 | try expect(value.Boolean == true); | |
| 399 | value.Boolean = false; | |
| 400 | try expect(value.Boolean == false); | |
| 401 | ||
| 402 | value = @unionInit(UnionInitEnum, "Byte", 2); | |
| 403 | try expect(value.Byte == 2); | |
| 404 | value.Byte = 3; | |
| 405 | try expect(value.Byte == 3); | |
| 406 | } | |
| 407 | ||
| 408 | test "@unionInit can modify a pointer value" { | |
| 409 | const UnionInitEnum = union(enum) { | |
| 410 | Boolean: bool, | |
| 411 | Byte: u8, | |
| 412 | }; | |
| 413 | ||
| 414 | var value: UnionInitEnum = undefined; | |
| 415 | var value_ptr = &value; | |
| 416 | ||
| 417 | value_ptr.* = @unionInit(UnionInitEnum, "Boolean", true); | |
| 418 | try expect(value.Boolean == true); | |
| 419 | ||
| 420 | value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2); | |
| 421 | try expect(value.Byte == 2); | |
| 422 | } | |
| 423 | ||
| 424 | test "union no tag with struct member" { | |
| 425 | const Struct = struct {}; | |
| 426 | const Union = union { | |
| 427 | s: Struct, | |
| 428 | pub fn foo(self: *@This()) void { | |
| 429 | _ = self; | |
| 430 | } | |
| 431 | }; | |
| 432 | var u = Union{ .s = Struct{} }; | |
| 433 | u.foo(); | |
| 434 | } | |
| 435 | ||
| 436 | fn testComparison() !void { | |
| 437 | var x = Payload{ .A = 42 }; | |
| 438 | try expect(x == .A); | |
| 439 | try expect(x != .B); | |
| 440 | try expect(x != .C); | |
| 441 | try expect((x == .B) == false); | |
| 442 | try expect((x == .C) == false); | |
| 443 | try expect((x != .A) == false); | |
| 444 | } | |
| 445 | ||
| 446 | test "comparison between union and enum literal" { | |
| 447 | try testComparison(); | |
| 448 | comptime try testComparison(); | |
| 449 | } | |
| 450 | ||
| 451 | test "packed union generates correctly aligned LLVM type" { | |
| 452 | const U = packed union { | |
| 453 | f1: fn () error{TestUnexpectedResult}!void, | |
| 454 | f2: u32, | |
| 455 | }; | |
| 456 | var foo = [_]U{ | |
| 457 | U{ .f1 = doTest }, | |
| 458 | U{ .f2 = 0 }, | |
| 459 | }; | |
| 460 | try foo[0].f1(); | |
| 461 | } | |
| 462 | ||
| 463 | test "union with one member defaults to u0 tag type" { | |
| 464 | const U0 = union(enum) { | |
| 465 | X: u32, | |
| 466 | }; | |
| 467 | comptime try expect(Tag(Tag(U0)) == u0); | |
| 468 | } | |
| 469 | ||
| 470 | test "union with comptime_int tag" { | |
| 471 | const Union = union(enum(comptime_int)) { | |
| 472 | X: u32, | |
| 473 | Y: u16, | |
| 474 | Z: u8, | |
| 475 | }; | |
| 476 | comptime try expect(Tag(Tag(Union)) == comptime_int); | |
| 477 | } | |
| 478 | ||
| 479 | test "extern union doesn't trigger field check at comptime" { | |
| 480 | const U = extern union { | |
| 481 | x: u32, | |
| 482 | y: u8, | |
| 483 | }; | |
| 484 | ||
| 485 | const x = U{ .x = 0x55AAAA55 }; | |
| 486 | comptime try expect(x.y == 0x55); | |
| 487 | } | |
| 488 | ||
| 489 | const Foo1 = union(enum) { | |
| 490 | f: struct { | |
| 491 | x: usize, | |
| 492 | }, | |
| 493 | }; | |
| 494 | var glbl: Foo1 = undefined; | |
| 495 | ||
| 496 | test "global union with single field is correctly initialized" { | |
| 497 | glbl = Foo1{ | |
| 498 | .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 }, | |
| 499 | }; | |
| 500 | try expect(glbl.f.x == 123); | |
| 501 | } | |
| 502 | ||
| 503 | pub const FooUnion = union(enum) { | |
| 504 | U0: usize, | |
| 505 | U1: u8, | |
| 506 | }; | |
| 507 | ||
| 508 | var glbl_array: [2]FooUnion = undefined; | |
| 509 | ||
| 510 | test "initialize global array of union" { | |
| 511 | glbl_array[1] = FooUnion{ .U1 = 2 }; | |
| 512 | glbl_array[0] = FooUnion{ .U0 = 1 }; | |
| 513 | try expect(glbl_array[0].U0 == 1); | |
| 514 | try expect(glbl_array[1].U1 == 2); | |
| 515 | } | |
| 516 | ||
| 517 | test "anonymous union literal syntax" { | |
| 518 | const S = struct { | |
| 519 | const Number = union { | |
| 520 | int: i32, | |
| 521 | float: f64, | |
| 522 | }; | |
| 523 | ||
| 524 | fn doTheTest() !void { | |
| 525 | var i: Number = .{ .int = 42 }; | |
| 526 | var f = makeNumber(); | |
| 527 | try expect(i.int == 42); | |
| 528 | try expect(f.float == 12.34); | |
| 529 | } | |
| 530 | ||
| 531 | fn makeNumber() Number { | |
| 532 | return .{ .float = 12.34 }; | |
| 533 | } | |
| 534 | }; | |
| 535 | try S.doTheTest(); | |
| 536 | comptime try S.doTheTest(); | |
| 537 | } | |
| 538 | ||
| 539 | test "update the tag value for zero-sized unions" { | |
| 540 | const S = union(enum) { | |
| 541 | U0: void, | |
| 542 | U1: void, | |
| 543 | }; | |
| 544 | var x = S{ .U0 = {} }; | |
| 545 | try expect(x == .U0); | |
| 546 | x = S{ .U1 = {} }; | |
| 547 | try expect(x == .U1); | |
| 548 | } | |
| 549 | ||
| 550 | test "function call result coerces from tagged union to the tag" { | |
| 551 | const S = struct { | |
| 552 | const Arch = union(enum) { | |
| 553 | One, | |
| 554 | Two: usize, | |
| 555 | }; | |
| 556 | ||
| 557 | const ArchTag = Tag(Arch); | |
| 558 | ||
| 559 | fn doTheTest() !void { | |
| 560 | var x: ArchTag = getArch1(); | |
| 561 | try expect(x == .One); | |
| 562 | ||
| 563 | var y: ArchTag = getArch2(); | |
| 564 | try expect(y == .Two); | |
| 565 | } | |
| 566 | ||
| 567 | pub fn getArch1() Arch { | |
| 568 | return .One; | |
| 569 | } | |
| 570 | ||
| 571 | pub fn getArch2() Arch { | |
| 572 | return .{ .Two = 99 }; | |
| 573 | } | |
| 574 | }; | |
| 575 | try S.doTheTest(); | |
| 576 | comptime try S.doTheTest(); | |
| 577 | } | |
| 578 | ||
| 579 | test "0-sized extern union definition" { | |
| 580 | const U = extern union { | |
| 581 | a: void, | |
| 582 | const f = 1; | |
| 583 | }; | |
| 584 | ||
| 585 | try expect(U.f == 1); | |
| 586 | } | |
| 587 | ||
| 588 | test "union initializer generates padding only if needed" { | |
| 589 | const U = union(enum) { | |
| 590 | A: u24, | |
| 591 | }; | |
| 592 | ||
| 593 | var v = U{ .A = 532 }; | |
| 594 | try expect(v.A == 532); | |
| 595 | } | |
| 596 | ||
| 597 | test "runtime tag name with single field" { | |
| 598 | const U = union(enum) { | |
| 599 | A: i32, | |
| 600 | }; | |
| 601 | ||
| 602 | var v = U{ .A = 42 }; | |
| 603 | try expect(std.mem.eql(u8, @tagName(v), "A")); | |
| 604 | } | |
| 605 | ||
| 606 | test "cast from anonymous struct to union" { | |
| 607 | const S = struct { | |
| 608 | const U = union(enum) { | |
| 609 | A: u32, | |
| 610 | B: []const u8, | |
| 611 | C: void, | |
| 612 | }; | |
| 613 | fn doTheTest() !void { | |
| 614 | var y: u32 = 42; | |
| 615 | const t0 = .{ .A = 123 }; | |
| 616 | const t1 = .{ .B = "foo" }; | |
| 617 | const t2 = .{ .C = {} }; | |
| 618 | const t3 = .{ .A = y }; | |
| 619 | const x0: U = t0; | |
| 620 | var x1: U = t1; | |
| 621 | const x2: U = t2; | |
| 622 | var x3: U = t3; | |
| 623 | try expect(x0.A == 123); | |
| 624 | try expect(std.mem.eql(u8, x1.B, "foo")); | |
| 625 | try expect(x2 == .C); | |
| 626 | try expect(x3.A == y); | |
| 627 | } | |
| 628 | }; | |
| 629 | try S.doTheTest(); | |
| 630 | comptime try S.doTheTest(); | |
| 631 | } | |
| 632 | ||
| 633 | test "cast from pointer to anonymous struct to pointer to union" { | |
| 634 | const S = struct { | |
| 635 | const U = union(enum) { | |
| 636 | A: u32, | |
| 637 | B: []const u8, | |
| 638 | C: void, | |
| 639 | }; | |
| 640 | fn doTheTest() !void { | |
| 641 | var y: u32 = 42; | |
| 642 | const t0 = &.{ .A = 123 }; | |
| 643 | const t1 = &.{ .B = "foo" }; | |
| 644 | const t2 = &.{ .C = {} }; | |
| 645 | const t3 = &.{ .A = y }; | |
| 646 | const x0: *const U = t0; | |
| 647 | var x1: *const U = t1; | |
| 648 | const x2: *const U = t2; | |
| 649 | var x3: *const U = t3; | |
| 650 | try expect(x0.A == 123); | |
| 651 | try expect(std.mem.eql(u8, x1.B, "foo")); | |
| 652 | try expect(x2.* == .C); | |
| 653 | try expect(x3.A == y); | |
| 654 | } | |
| 655 | }; | |
| 656 | try S.doTheTest(); | |
| 657 | comptime try S.doTheTest(); | |
| 658 | } | |
| 659 | ||
| 660 | test "method call on an empty union" { | |
| 661 | const S = struct { | |
| 662 | const MyUnion = union(MyUnionTag) { | |
| 663 | pub const MyUnionTag = enum { X1, X2 }; | |
| 664 | X1: [0]u8, | |
| 665 | X2: [0]u8, | |
| 666 | ||
| 667 | pub fn useIt(self: *@This()) bool { | |
| 668 | _ = self; | |
| 669 | return true; | |
| 670 | } | |
| 671 | }; | |
| 672 | ||
| 673 | fn doTheTest() !void { | |
| 674 | var u = MyUnion{ .X1 = [0]u8{} }; | |
| 675 | try expect(u.useIt()); | |
| 676 | } | |
| 677 | }; | |
| 678 | try S.doTheTest(); | |
| 679 | comptime try S.doTheTest(); | |
| 680 | } | |
| 681 | ||
| 682 | test "switching on non exhaustive union" { | |
| 683 | const S = struct { | |
| 684 | const E = enum(u8) { | |
| 685 | a, | |
| 686 | b, | |
| 687 | _, | |
| 688 | }; | |
| 689 | const U = union(E) { | |
| 690 | a: i32, | |
| 691 | b: u32, | |
| 692 | }; | |
| 693 | fn doTheTest() !void { | |
| 694 | var a = U{ .a = 2 }; | |
| 695 | switch (a) { | |
| 696 | .a => |val| try expect(val == 2), | |
| 697 | .b => unreachable, | |
| 698 | } | |
| 699 | } | |
| 700 | }; | |
| 701 | try S.doTheTest(); | |
| 702 | comptime try S.doTheTest(); | |
| 703 | } | |
| 704 | ||
| 705 | test "containers with single-field enums" { | |
| 706 | const S = struct { | |
| 707 | const A = union(enum) { f1 }; | |
| 708 | const B = union(enum) { f1: void }; | |
| 709 | const C = struct { a: A }; | |
| 710 | const D = struct { a: B }; | |
| 711 | ||
| 712 | fn doTheTest() !void { | |
| 713 | var array1 = [1]A{A{ .f1 = {} }}; | |
| 714 | var array2 = [1]B{B{ .f1 = {} }}; | |
| 715 | try expect(array1[0] == .f1); | |
| 716 | try expect(array2[0] == .f1); | |
| 717 | ||
| 718 | var struct1 = C{ .a = A{ .f1 = {} } }; | |
| 719 | var struct2 = D{ .a = B{ .f1 = {} } }; | |
| 720 | try expect(struct1.a == .f1); | |
| 721 | try expect(struct2.a == .f1); | |
| 722 | } | |
| 723 | }; | |
| 724 | ||
| 725 | try S.doTheTest(); | |
| 726 | comptime try S.doTheTest(); | |
| 727 | } | |
| 728 | ||
| 729 | test "@unionInit on union w/ tag but no fields" { | |
| 730 | const S = struct { | |
| 731 | const Type = enum(u8) { no_op = 105 }; | |
| 732 | ||
| 733 | const Data = union(Type) { | |
| 734 | no_op: void, | |
| 735 | ||
| 736 | pub fn decode(buf: []const u8) Data { | |
| 737 | _ = buf; | |
| 738 | return @unionInit(Data, "no_op", {}); | |
| 739 | } | |
| 740 | }; | |
| 741 | ||
| 742 | comptime { | |
| 743 | std.debug.assert(@sizeOf(Data) != 0); | |
| 744 | } | |
| 745 | ||
| 746 | fn doTheTest() !void { | |
| 747 | var data: Data = .{ .no_op = .{} }; | |
| 748 | _ = data; | |
| 749 | var o = Data.decode(&[_]u8{}); | |
| 750 | try expectEqual(Type.no_op, o); | |
| 751 | } | |
| 752 | }; | |
| 753 | ||
| 754 | try S.doTheTest(); | |
| 755 | comptime try S.doTheTest(); | |
| 756 | } | |
| 757 | ||
| 758 | test "union enum type gets a separate scope" { | |
| 759 | const S = struct { | |
| 760 | const U = union(enum) { | |
| 761 | a: u8, | |
| 762 | const foo = 1; | |
| 763 | }; | |
| 764 | ||
| 765 | fn doTheTest() !void { | |
| 766 | try expect(!@hasDecl(Tag(U), "foo")); | |
| 767 | } | |
| 768 | }; | |
| 769 | ||
| 770 | try S.doTheTest(); | |
| 771 | } | |
| 772 | test "anytype union field: issue #9233" { | |
| 773 | const Baz = union(enum) { bar: anytype }; | |
| 774 | _ = Baz; | |
| 775 | } |
test/behavior/vector.zig+1-1| ... | ... | @@ -116,7 +116,7 @@ test "array to vector" { |
| 116 | 116 | _ = vec; |
| 117 | 117 | } |
| 118 | 118 | |
| 119 | test "vector casts of sizes not divisable by 8" { | |
| 119 | test "vector casts of sizes not divisible by 8" { | |
| 120 | 120 | const S = struct { |
| 121 | 121 | fn doTheTest() !void { |
| 122 | 122 | { |
test/behavior/widening.zig+14-2| ... | ... | @@ -19,6 +19,12 @@ test "implicit unsigned integer to signed integer" { |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | test "float widening" { |
| 22 | if (@import("builtin").zig_is_stage2) { | |
| 23 | // This test is passing but it depends on compiler-rt symbols, which | |
| 24 | // cannot yet be built with stage2 due to | |
| 25 | // "TODO implement equality comparison between a union's tag value and an enum literal" | |
| 26 | return error.SkipZigTest; | |
| 27 | } | |
| 22 | 28 | var a: f16 = 12.34; |
| 23 | 29 | var b: f32 = a; |
| 24 | 30 | var c: f64 = b; |
| ... | ... | @@ -29,9 +35,15 @@ test "float widening" { |
| 29 | 35 | } |
| 30 | 36 | |
| 31 | 37 | test "float widening f16 to f128" { |
| 38 | if (@import("builtin").zig_is_stage2) { | |
| 39 | // This test is passing but it depends on compiler-rt symbols, which | |
| 40 | // cannot yet be built with stage2 due to | |
| 41 | // "TODO implement equality comparison between a union's tag value and an enum literal" | |
| 42 | return error.SkipZigTest; | |
| 43 | } | |
| 32 | 44 | // TODO https://github.com/ziglang/zig/issues/3282 |
| 33 | if (@import("builtin").target.cpu.arch == .aarch64) return error.SkipZigTest; | |
| 34 | if (@import("builtin").target.cpu.arch == .powerpc64le) return error.SkipZigTest; | |
| 45 | if (@import("builtin").stage2_arch == .aarch64) return error.SkipZigTest; | |
| 46 | if (@import("builtin").stage2_arch == .powerpc64le) return error.SkipZigTest; | |
| 35 | 47 | |
| 36 | 48 | var x: f16 = 12.34; |
| 37 | 49 | var y: f128 = x; |
test/cases.zig+13-1| ... | ... | @@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 26 | 26 | var case = ctx.exe("hello world with updates", linux_x64); |
| 27 | 27 | |
| 28 | 28 | case.addError("", &[_][]const u8{ |
| 29 | ":90:9: error: struct 'tmp.tmp' has no member named 'main'", | |
| 29 | ":97:9: error: struct 'tmp.tmp' has no member named 'main'", | |
| 30 | 30 | }); |
| 31 | 31 | |
| 32 | 32 | // Incorrect return type |
| ... | ... | @@ -1807,4 +1807,16 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1807 | 1807 | \\} |
| 1808 | 1808 | , ""); |
| 1809 | 1809 | } |
| 1810 | ||
| 1811 | { | |
| 1812 | var case = ctx.exe("setting an address space on a local variable", linux_x64); | |
| 1813 | case.addError( | |
| 1814 | \\export fn entry() i32 { | |
| 1815 | \\ var foo: i32 addrspace(".general") = 1234; | |
| 1816 | \\ return foo; | |
| 1817 | \\} | |
| 1818 | , &[_][]const u8{ | |
| 1819 | ":2:28: error: cannot set address space of local variable 'foo'", | |
| 1820 | }); | |
| 1821 | } | |
| 1810 | 1822 | } |
test/compile_errors.zig+26-8| ... | ... | @@ -683,7 +683,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 683 | 683 | \\ _ = u; |
| 684 | 684 | \\} |
| 685 | 685 | , &[_][]const u8{ |
| 686 | "tmp.zig:12:16: error: runtime cast to union 'U' from non-exhustive enum", | |
| 686 | "tmp.zig:12:16: error: runtime cast to union 'U' from non-exhaustive enum", | |
| 687 | 687 | "tmp.zig:17:16: error: no tag by value 15", |
| 688 | 688 | }); |
| 689 | 689 | |
| ... | ... | @@ -711,6 +711,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 711 | 711 | \\ .is_const = false, |
| 712 | 712 | \\ .is_volatile = false, |
| 713 | 713 | \\ .alignment = 1, |
| 714 | \\ .address_space = .generic, | |
| 714 | 715 | \\ .child = u8, |
| 715 | 716 | \\ .is_allowzero = false, |
| 716 | 717 | \\ .sentinel = 0, |
| ... | ... | @@ -720,6 +721,23 @@ pub fn addCases(ctx: *TestContext) !void { |
| 720 | 721 | "tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers", |
| 721 | 722 | }); |
| 722 | 723 | |
| 724 | ctx.objErrStage1("@Type(.Pointer) with invalid address space ", | |
| 725 | \\export fn entry() void { | |
| 726 | \\ _ = @Type(.{ .Pointer = .{ | |
| 727 | \\ .size = .One, | |
| 728 | \\ .is_const = false, | |
| 729 | \\ .is_volatile = false, | |
| 730 | \\ .alignment = 1, | |
| 731 | \\ .address_space = .gs, | |
| 732 | \\ .child = u8, | |
| 733 | \\ .is_allowzero = false, | |
| 734 | \\ .sentinel = null, | |
| 735 | \\ }}); | |
| 736 | \\} | |
| 737 | , &[_][]const u8{ | |
| 738 | "tmp.zig:2:16: error: address space 'gs' not available in stage 1 compiler, must be .generic", | |
| 739 | }); | |
| 740 | ||
| 723 | 741 | ctx.testErrStage1("helpful return type error message", |
| 724 | 742 | \\export fn foo() u32 { |
| 725 | 743 | \\ return error.Ohno; |
| ... | ... | @@ -6127,9 +6145,9 @@ pub fn addCases(ctx: *TestContext) !void { |
| 6127 | 6145 | }); |
| 6128 | 6146 | |
| 6129 | 6147 | ctx.objErrStage1("endless loop in function evaluation", |
| 6130 | \\const seventh_fib_number = fibbonaci(7); | |
| 6131 | \\fn fibbonaci(x: i32) i32 { | |
| 6132 | \\ return fibbonaci(x - 1) + fibbonaci(x - 2); | |
| 6148 | \\const seventh_fib_number = fibonacci(7); | |
| 6149 | \\fn fibonacci(x: i32) i32 { | |
| 6150 | \\ return fibonacci(x - 1) + fibonacci(x - 2); | |
| 6133 | 6151 | \\} |
| 6134 | 6152 | \\ |
| 6135 | 6153 | \\export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); } |
| ... | ... | @@ -6757,7 +6775,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 6757 | 6775 | "tmp.zig:2:5: error: expression value is ignored", |
| 6758 | 6776 | }); |
| 6759 | 6777 | |
| 6760 | ctx.objErrStage1("ignored defered statement value", | |
| 6778 | ctx.objErrStage1("ignored deferred statement value", | |
| 6761 | 6779 | \\export fn foo() void { |
| 6762 | 6780 | \\ defer {1;} |
| 6763 | 6781 | \\} |
| ... | ... | @@ -6765,7 +6783,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 6765 | 6783 | "tmp.zig:2:12: error: expression value is ignored", |
| 6766 | 6784 | }); |
| 6767 | 6785 | |
| 6768 | ctx.objErrStage1("ignored defered function call", | |
| 6786 | ctx.objErrStage1("ignored deferred function call", | |
| 6769 | 6787 | \\export fn foo() void { |
| 6770 | 6788 | \\ defer bar(); |
| 6771 | 6789 | \\} |
| ... | ... | @@ -8841,9 +8859,9 @@ pub fn addCases(ctx: *TestContext) !void { |
| 8841 | 8859 | "tmp.zig:3:12: note: crosses namespace boundary here", |
| 8842 | 8860 | }); |
| 8843 | 8861 | |
| 8844 | ctx.objErrStage1("Issue #9619: saturating arithmetic builtins should fail to compile when given floats", | |
| 8862 | ctx.objErrStage1("saturating arithmetic does not allow floats", | |
| 8845 | 8863 | \\pub fn main() !void { |
| 8846 | \\ _ = @addWithSaturation(@as(f32, 1.0), @as(f32, 1.0)); | |
| 8864 | \\ _ = @as(f32, 1.0) +| @as(f32, 1.0); | |
| 8847 | 8865 | \\} |
| 8848 | 8866 | , &[_][]const u8{ |
| 8849 | 8867 | "error: invalid operands to binary expression: 'f32' and 'f32'", |
test/run_translated_c.zig+2-2| ... | ... | @@ -24,7 +24,7 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 24 | 24 | \\} |
| 25 | 25 | , "DEG2RAD is: 0.017453" ++ nl); |
| 26 | 26 | |
| 27 | cases.add("use global scope for record/enum/typedef type transalation if needed", | |
| 27 | cases.add("use global scope for record/enum/typedef type translation if needed", | |
| 28 | 28 | \\void bar(void); |
| 29 | 29 | \\void baz(void); |
| 30 | 30 | \\struct foo { int x; }; |
| ... | ... | @@ -394,7 +394,7 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 394 | 394 | \\} |
| 395 | 395 | , ""); |
| 396 | 396 | |
| 397 | cases.add("ensure array casts outisde +=", | |
| 397 | cases.add("ensure array casts outside +=", | |
| 398 | 398 | \\#include <stdlib.h> |
| 399 | 399 | \\static int hash_binary(int k) |
| 400 | 400 | \\{ |
test/stage2/darwin.zig+3-3| ... | ... | @@ -12,9 +12,9 @@ pub fn addCases(ctx: *TestContext) !void { |
| 12 | 12 | .os_tag = .macos, |
| 13 | 13 | }; |
| 14 | 14 | { |
| 15 | var case = ctx.exe("hello world with updates", target); | |
| 15 | var case = ctx.exe("darwin hello world with updates", target); | |
| 16 | 16 | case.addError("", &[_][]const u8{ |
| 17 | ":90:9: error: struct 'tmp.tmp' has no member named 'main'", | |
| 17 | ":97:9: error: struct 'tmp.tmp' has no member named 'main'", | |
| 18 | 18 | }); |
| 19 | 19 | |
| 20 | 20 | // Incorrect return type |
| ... | ... | @@ -118,7 +118,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 118 | 118 | { |
| 119 | 119 | var case = ctx.exe("corner case - update existing, singular TextBlock", target); |
| 120 | 120 | |
| 121 | // This test case also covers an infrequent scenarion where the string table *may* be relocated | |
| 121 | // This test case also covers an infrequent scenario where the string table *may* be relocated | |
| 122 | 122 | // into the position preceeding the symbol table which results in a dyld error. |
| 123 | 123 | case.addCompareOutput( |
| 124 | 124 | \\extern fn exit(usize) noreturn; |
test/stage2/llvm.zig+180| ... | ... | @@ -242,4 +242,184 @@ pub fn addCases(ctx: *TestContext) !void { |
| 242 | 242 | \\} |
| 243 | 243 | , ""); |
| 244 | 244 | } |
| 245 | ||
| 246 | { | |
| 247 | var case = ctx.exeUsingLlvmBackend("invalid address space coercion", linux_x64); | |
| 248 | case.addError( | |
| 249 | \\fn entry(a: *addrspace(.gs) i32) *i32 { | |
| 250 | \\ return a; | |
| 251 | \\} | |
| 252 | \\pub export fn main() void { _ = entry; } | |
| 253 | , &[_][]const u8{ | |
| 254 | ":2:12: error: expected *i32, found *addrspace(.gs) i32", | |
| 255 | }); | |
| 256 | } | |
| 257 | ||
| 258 | { | |
| 259 | var case = ctx.exeUsingLlvmBackend("pointer keeps address space", linux_x64); | |
| 260 | case.compiles( | |
| 261 | \\fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 { | |
| 262 | \\ return a; | |
| 263 | \\} | |
| 264 | \\pub export fn main() void { _ = entry; } | |
| 265 | ); | |
| 266 | } | |
| 267 | ||
| 268 | { | |
| 269 | var case = ctx.exeUsingLlvmBackend("pointer to explicit generic address space coerces to implicit pointer", linux_x64); | |
| 270 | case.compiles( | |
| 271 | \\fn entry(a: *addrspace(.generic) i32) *i32 { | |
| 272 | \\ return a; | |
| 273 | \\} | |
| 274 | \\pub export fn main() void { _ = entry; } | |
| 275 | ); | |
| 276 | } | |
| 277 | ||
| 278 | { | |
| 279 | var case = ctx.exeUsingLlvmBackend("pointers with different address spaces", linux_x64); | |
| 280 | case.addError( | |
| 281 | \\fn entry(a: *addrspace(.gs) i32) *addrspace(.fs) i32 { | |
| 282 | \\ return a; | |
| 283 | \\} | |
| 284 | \\pub export fn main() void { _ = entry; } | |
| 285 | , &[_][]const u8{ | |
| 286 | ":2:12: error: expected *addrspace(.fs) i32, found *addrspace(.gs) i32", | |
| 287 | }); | |
| 288 | } | |
| 289 | ||
| 290 | { | |
| 291 | var case = ctx.exeUsingLlvmBackend("pointers with different address spaces", linux_x64); | |
| 292 | case.addError( | |
| 293 | \\fn entry(a: ?*addrspace(.gs) i32) *i32 { | |
| 294 | \\ return a.?; | |
| 295 | \\} | |
| 296 | \\pub export fn main() void { _ = entry; } | |
| 297 | , &[_][]const u8{ | |
| 298 | ":2:13: error: expected *i32, found *addrspace(.gs) i32", | |
| 299 | }); | |
| 300 | } | |
| 301 | ||
| 302 | { | |
| 303 | var case = ctx.exeUsingLlvmBackend("invalid pointer keeps address space when taking address of dereference", linux_x64); | |
| 304 | case.addError( | |
| 305 | \\fn entry(a: *addrspace(.gs) i32) *i32 { | |
| 306 | \\ return &a.*; | |
| 307 | \\} | |
| 308 | \\pub export fn main() void { _ = entry; } | |
| 309 | , &[_][]const u8{ | |
| 310 | ":2:12: error: expected *i32, found *addrspace(.gs) i32", | |
| 311 | }); | |
| 312 | } | |
| 313 | ||
| 314 | { | |
| 315 | var case = ctx.exeUsingLlvmBackend("pointer keeps address space when taking address of dereference", linux_x64); | |
| 316 | case.compiles( | |
| 317 | \\fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 { | |
| 318 | \\ return &a.*; | |
| 319 | \\} | |
| 320 | \\pub export fn main() void { _ = entry; } | |
| 321 | ); | |
| 322 | } | |
| 323 | ||
| 324 | { | |
| 325 | var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: array pointer", linux_x64); | |
| 326 | case.compiles( | |
| 327 | \\fn entry(a: *addrspace(.gs) [1]i32) *addrspace(.gs) i32 { | |
| 328 | \\ return &a[0]; | |
| 329 | \\} | |
| 330 | \\pub export fn main() void { _ = entry; } | |
| 331 | ); | |
| 332 | } | |
| 333 | ||
| 334 | { | |
| 335 | var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: pointer to optional array", linux_x64); | |
| 336 | case.compiles( | |
| 337 | \\fn entry(a: *addrspace(.gs) ?[1]i32) *addrspace(.gs) i32 { | |
| 338 | \\ return &a.*.?[0]; | |
| 339 | \\} | |
| 340 | \\pub export fn main() void { _ = entry; } | |
| 341 | ); | |
| 342 | } | |
| 343 | ||
| 344 | { | |
| 345 | var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: struct pointer", linux_x64); | |
| 346 | case.compiles( | |
| 347 | \\const A = struct{ a: i32 }; | |
| 348 | \\fn entry(a: *addrspace(.gs) A) *addrspace(.gs) i32 { | |
| 349 | \\ return &a.a; | |
| 350 | \\} | |
| 351 | \\pub export fn main() void { _ = entry; } | |
| 352 | ); | |
| 353 | } | |
| 354 | ||
| 355 | { | |
| 356 | var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: complex", linux_x64); | |
| 357 | case.compiles( | |
| 358 | \\const A = struct{ a: ?[1]i32 }; | |
| 359 | \\fn entry(a: *addrspace(.gs) [1]A) *addrspace(.gs) i32 { | |
| 360 | \\ return &a[0].a.?[0]; | |
| 361 | \\} | |
| 362 | \\pub export fn main() void { _ = entry; } | |
| 363 | ); | |
| 364 | } | |
| 365 | ||
| 366 | { | |
| 367 | var case = ctx.exeUsingLlvmBackend("dereferencing through multiple pointers with address spaces", linux_x64); | |
| 368 | case.compiles( | |
| 369 | \\fn entry(a: *addrspace(.fs) *addrspace(.gs) *i32) *i32 { | |
| 370 | \\ return a.*.*; | |
| 371 | \\} | |
| 372 | \\pub export fn main() void { _ = entry; } | |
| 373 | ); | |
| 374 | } | |
| 375 | ||
| 376 | { | |
| 377 | var case = ctx.exeUsingLlvmBackend("f segment address space reading and writing", linux_x64); | |
| 378 | case.addCompareOutput( | |
| 379 | \\fn assert(ok: bool) void { | |
| 380 | \\ if (!ok) unreachable; | |
| 381 | \\} | |
| 382 | \\ | |
| 383 | \\fn setFs(value: c_ulong) void { | |
| 384 | \\ asm volatile ( | |
| 385 | \\ \\syscall | |
| 386 | \\ : | |
| 387 | \\ : [number] "{rax}" (158), | |
| 388 | \\ [code] "{rdi}" (0x1002), | |
| 389 | \\ [val] "{rsi}" (value), | |
| 390 | \\ : "rcx", "r11", "memory" | |
| 391 | \\ ); | |
| 392 | \\} | |
| 393 | \\ | |
| 394 | \\fn getFs() c_ulong { | |
| 395 | \\ var result: c_ulong = undefined; | |
| 396 | \\ asm volatile ( | |
| 397 | \\ \\syscall | |
| 398 | \\ : | |
| 399 | \\ : [number] "{rax}" (158), | |
| 400 | \\ [code] "{rdi}" (0x1003), | |
| 401 | \\ [ptr] "{rsi}" (@ptrToInt(&result)), | |
| 402 | \\ : "rcx", "r11", "memory" | |
| 403 | \\ ); | |
| 404 | \\ return result; | |
| 405 | \\} | |
| 406 | \\ | |
| 407 | \\var test_value: u64 = 12345; | |
| 408 | \\ | |
| 409 | \\pub export fn main() c_int { | |
| 410 | \\ const orig_fs = getFs(); | |
| 411 | \\ | |
| 412 | \\ setFs(@ptrToInt(&test_value)); | |
| 413 | \\ assert(getFs() == @ptrToInt(&test_value)); | |
| 414 | \\ | |
| 415 | \\ var test_ptr = @intToPtr(*allowzero addrspace(.fs) u64, 0); | |
| 416 | \\ assert(test_ptr.* == 12345); | |
| 417 | \\ test_ptr.* = 98765; | |
| 418 | \\ assert(test_value == 98765); | |
| 419 | \\ | |
| 420 | \\ setFs(orig_fs); | |
| 421 | \\ return 0; | |
| 422 | \\} | |
| 423 | , ""); | |
| 424 | } | |
| 245 | 425 | } |
test/standalone.zig+4| ... | ... | @@ -28,6 +28,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 28 | 28 | cases.addBuildFile("test/standalone/empty_env/build.zig", .{}); |
| 29 | 29 | cases.addBuildFile("test/standalone/issue_7030/build.zig", .{}); |
| 30 | 30 | cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{}); |
| 31 | cases.addBuildFile("test/standalone/issue_9812/build.zig", .{}); | |
| 31 | 32 | if (std.Target.current.os.tag != .wasi) { |
| 32 | 33 | cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{}); |
| 33 | 34 | } |
| ... | ... | @@ -36,6 +37,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 36 | 37 | } |
| 37 | 38 | cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ .build_modes = true, .cross_targets = true }); |
| 38 | 39 | |
| 40 | if (std.Target.current.os.tag == .windows) { | |
| 41 | cases.addC("test/standalone/issue_9402/main.zig"); | |
| 42 | } | |
| 39 | 43 | // Try to build and run a PIE executable. |
| 40 | 44 | if (std.Target.current.os.tag == .linux) { |
| 41 | 45 | cases.addBuildFile("test/standalone/pie/build.zig", .{}); |
test/standalone/issue_9402/main.zig created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | const FILE = extern struct { | |
| 2 | dummy_field: u8, | |
| 3 | }; | |
| 4 | ||
| 5 | extern fn _ftelli64([*c]FILE) i64; | |
| 6 | extern fn _fseeki64([*c]FILE, i64, c_int) c_int; | |
| 7 | ||
| 8 | pub export fn main(argc: c_int, argv: **u8) c_int { | |
| 9 | _ = argv; | |
| 10 | _ = argc; | |
| 11 | _ = _ftelli64(null); | |
| 12 | _ = _fseeki64(null, 123, 2); | |
| 13 | return 0; | |
| 14 | } |
test/standalone/issue_9812/build.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn build(b: *std.build.Builder) !void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | const zip_add = b.addTest("main.zig"); | |
| 6 | zip_add.setBuildMode(mode); | |
| 7 | zip_add.addCSourceFile("vendor/kuba-zip/zip.c", &[_][]const u8{ | |
| 8 | "-std=c99", | |
| 9 | "-fno-sanitize=undefined", | |
| 10 | }); | |
| 11 | zip_add.addIncludeDir("vendor/kuba-zip"); | |
| 12 | zip_add.linkLibC(); | |
| 13 | ||
| 14 | const test_step = b.step("test", "Test it"); | |
| 15 | test_step.dependOn(&zip_add.step); | |
| 16 | } |
test/standalone/issue_9812/main.zig created+45| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | const std = @import("std"); | |
| 2 | const testing = std.testing; | |
| 3 | ||
| 4 | const c = @cImport({ | |
| 5 | @cInclude("zip.h"); | |
| 6 | }); | |
| 7 | ||
| 8 | const Error = error{ | |
| 9 | FailedToWriteEntry, | |
| 10 | FileNotFound, | |
| 11 | FailedToCreateEntry, | |
| 12 | Overflow, | |
| 13 | OutOfMemory, | |
| 14 | InvalidCmdLine, | |
| 15 | }; | |
| 16 | ||
| 17 | test "" { | |
| 18 | const allocator = std.heap.c_allocator; | |
| 19 | ||
| 20 | const args = try std.process.argsAlloc(allocator); | |
| 21 | if (args.len != 4) { | |
| 22 | return; | |
| 23 | } | |
| 24 | ||
| 25 | const zip_file = args[1]; | |
| 26 | const src_file_name = args[2]; | |
| 27 | const dst_file_name = args[3]; | |
| 28 | ||
| 29 | errdefer |e| switch (@as(Error, e)) { | |
| 30 | error.FailedToWriteEntry => std.log.err("could not find {s}", .{src_file_name}), | |
| 31 | error.FileNotFound => std.log.err("could not open {s}", .{zip_file}), | |
| 32 | error.FailedToCreateEntry => std.log.err("could not create {s}", .{dst_file_name}), | |
| 33 | else => {}, | |
| 34 | }; | |
| 35 | ||
| 36 | const zip = c.zip_open(zip_file, c.ZIP_DEFAULT_COMPRESSION_LEVEL, 'a') orelse return error.FileNotFound; | |
| 37 | defer c.zip_close(zip); | |
| 38 | ||
| 39 | if (c.zip_entry_open(zip, dst_file_name) < 0) | |
| 40 | return error.FailedToCreateEntry; | |
| 41 | defer _ = c.zip_entry_close(zip); | |
| 42 | ||
| 43 | if (c.zip_entry_fwrite(zip, src_file_name) < 0) | |
| 44 | return error.FailedToWriteEntry; | |
| 45 | } |
test/standalone/issue_9812/vendor/kuba-zip/miniz.h created+6875| ... | ... | @@ -0,0 +1,6875 @@ |
| 1 | /* | |
| 2 | miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP | |
| 3 | reading/writing/appending, PNG writing See "unlicense" statement at the end | |
| 4 | of this file. Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, | |
| 5 | 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: | |
| 6 | http://www.ietf.org/rfc/rfc1951.txt | |
| 7 | ||
| 8 | Most API's defined in miniz.c are optional. For example, to disable the | |
| 9 | archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of | |
| 10 | all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). | |
| 11 | ||
| 12 | * Change History | |
| 13 | 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major | |
| 14 | release with Zip64 support (almost there!): | |
| 15 | - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug | |
| 16 | (thanks kahmyong.moon@hp.com) which could cause locate files to not find | |
| 17 | files. This bug would only have occured in earlier versions if you explicitly | |
| 18 | used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or | |
| 19 | mz_zip_add_mem_to_archive_file_in_place() (which used this flag). If you | |
| 20 | can't switch to v1.15 but want to fix this bug, just remove the uses of this | |
| 21 | flag from both helper funcs (and of course don't use the flag). | |
| 22 | - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when | |
| 23 | pUser_read_buf is not NULL and compressed size is > uncompressed size | |
| 24 | - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract | |
| 25 | compressed data from directory entries, to account for weird zipfiles which | |
| 26 | contain zero-size compressed data on dir entries. Hopefully this fix won't | |
| 27 | cause any issues on weird zip archives, because it assumes the low 16-bits of | |
| 28 | zip external attributes are DOS attributes (which I believe they always are | |
| 29 | in practice). | |
| 30 | - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the | |
| 31 | internal attributes, just the filename and external attributes | |
| 32 | - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed | |
| 33 | - Added cmake support for Linux builds which builds all the examples, | |
| 34 | tested with clang v3.3 and gcc v4.6. | |
| 35 | - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti | |
| 36 | - Merged MZ_FORCEINLINE fix from hdeanclark | |
| 37 | - Fix <time.h> include before config #ifdef, thanks emil.brink | |
| 38 | - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping | |
| 39 | (super useful for OpenGL apps), and explicit control over the compression | |
| 40 | level (so you can set it to 1 for real-time compression). | |
| 41 | - Merged in some compiler fixes from paulharris's github repro. | |
| 42 | - Retested this build under Windows (VS 2010, including static analysis), | |
| 43 | tcc 0.9.26, gcc v4.6 and clang v3.3. | |
| 44 | - Added example6.c, which dumps an image of the mandelbrot set to a PNG | |
| 45 | file. | |
| 46 | - Modified example2 to help test the | |
| 47 | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. | |
| 48 | - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix | |
| 49 | possible src file fclose() leak if alignment bytes+local header file write | |
| 50 | faiiled | |
| 51 | - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the | |
| 52 | wrong central dir header offset, appears harmless in this release, but it | |
| 53 | became a problem in the zip64 branch 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 | |
| 54 | compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect). | |
| 55 | 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix | |
| 56 | mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. | |
| 57 | - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and | |
| 58 | re-ran a randomized regression test on ~500k files. | |
| 59 | - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. | |
| 60 | - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze | |
| 61 | (static analysis) option and fixed all warnings (except for the silly "Use of | |
| 62 | the comma-operator in a tested expression.." analysis warning, which I | |
| 63 | purposely use to work around a MSVC compiler warning). | |
| 64 | - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and | |
| 65 | tested Linux executables. The codeblocks workspace is compatible with | |
| 66 | Linux+Win32/x64. | |
| 67 | - Added miniz_tester solution/project, which is a useful little app | |
| 68 | derived from LZHAM's tester app that I use as part of the regression test. | |
| 69 | - Ran miniz.c and tinfl.c through another series of regression testing on | |
| 70 | ~500,000 files and archives. | |
| 71 | - Modified example5.c so it purposely disables a bunch of high-level | |
| 72 | functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the | |
| 73 | MINIZ_NO_STDIO bug report.) | |
| 74 | - Fix ftell() usage in examples so they exit with an error on files which | |
| 75 | are too large (a limitation of the examples, not miniz itself). 4/12/12 v1.12 | |
| 76 | - More comments, added low-level example5.c, fixed a couple minor | |
| 77 | level_and_flags issues in the archive API's. level_and_flags can now be set | |
| 78 | to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced@valvesoftware.com> | |
| 79 | for the feedback/bug report. 5/28/11 v1.11 - Added statement from | |
| 80 | unlicense.org 5/27/11 v1.10 - Substantial compressor optimizations: | |
| 81 | - Level 1 is now ~4x faster than before. The L1 compressor's throughput | |
| 82 | now varies between 70-110MB/sec. on a | |
| 83 | - Core i7 (actual throughput varies depending on the type of data, and x64 | |
| 84 | vs. x86). | |
| 85 | - Improved baseline L2-L9 compression perf. Also, greatly improved | |
| 86 | compression perf. issues on some file types. | |
| 87 | - Refactored the compression code for better readability and | |
| 88 | maintainability. | |
| 89 | - Added level 10 compression level (L10 has slightly better ratio than | |
| 90 | level 9, but could have a potentially large drop in throughput on some | |
| 91 | files). 5/15/11 v1.09 - Initial stable release. | |
| 92 | ||
| 93 | * Low-level Deflate/Inflate implementation notes: | |
| 94 | ||
| 95 | Compression: Use the "tdefl" API's. The compressor supports raw, static, | |
| 96 | and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, | |
| 97 | and Huffman-only streams. It performs and compresses approximately as well as | |
| 98 | zlib. | |
| 99 | ||
| 100 | Decompression: Use the "tinfl" API's. The entire decompressor is | |
| 101 | implemented as a single function coroutine: see tinfl_decompress(). It | |
| 102 | supports decompression into a 32KB (or larger power of 2) wrapping buffer, or | |
| 103 | into a memory block large enough to hold the entire file. | |
| 104 | ||
| 105 | The low-level tdefl/tinfl API's do not make any use of dynamic memory | |
| 106 | allocation. | |
| 107 | ||
| 108 | * zlib-style API notes: | |
| 109 | ||
| 110 | miniz.c implements a fairly large subset of zlib. There's enough | |
| 111 | functionality present for it to be a drop-in zlib replacement in many apps: | |
| 112 | The z_stream struct, optional memory allocation callbacks | |
| 113 | deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound | |
| 114 | inflateInit/inflateInit2/inflate/inflateEnd | |
| 115 | compress, compress2, compressBound, uncompress | |
| 116 | CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly | |
| 117 | routines. Supports raw deflate streams or standard zlib streams with adler-32 | |
| 118 | checking. | |
| 119 | ||
| 120 | Limitations: | |
| 121 | The callback API's are not implemented yet. No support for gzip headers or | |
| 122 | zlib static dictionaries. I've tried to closely emulate zlib's various | |
| 123 | flavors of stream flushing and return status codes, but there are no | |
| 124 | guarantees that miniz.c pulls this off perfectly. | |
| 125 | ||
| 126 | * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, | |
| 127 | originally written by Alex Evans. Supports 1-4 bytes/pixel images. | |
| 128 | ||
| 129 | * ZIP archive API notes: | |
| 130 | ||
| 131 | The ZIP archive API's where designed with simplicity and efficiency in | |
| 132 | mind, with just enough abstraction to get the job done with minimal fuss. | |
| 133 | There are simple API's to retrieve file information, read files from existing | |
| 134 | archives, create new archives, append new files to existing archives, or | |
| 135 | clone archive data from one archive to another. It supports archives located | |
| 136 | in memory or the heap, on disk (using stdio.h), or you can specify custom | |
| 137 | file read/write callbacks. | |
| 138 | ||
| 139 | - Archive reading: Just call this function to read a single file from a | |
| 140 | disk archive: | |
| 141 | ||
| 142 | void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const | |
| 143 | char *pArchive_name, size_t *pSize, mz_uint zip_flags); | |
| 144 | ||
| 145 | For more complex cases, use the "mz_zip_reader" functions. Upon opening an | |
| 146 | archive, the entire central directory is located and read as-is into memory, | |
| 147 | and subsequent file access only occurs when reading individual files. | |
| 148 | ||
| 149 | - Archives file scanning: The simple way is to use this function to scan a | |
| 150 | loaded archive for a specific file: | |
| 151 | ||
| 152 | int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, | |
| 153 | const char *pComment, mz_uint flags); | |
| 154 | ||
| 155 | The locate operation can optionally check file comments too, which (as one | |
| 156 | example) can be used to identify multiple versions of the same file in an | |
| 157 | archive. This function uses a simple linear search through the central | |
| 158 | directory, so it's not very fast. | |
| 159 | ||
| 160 | Alternately, you can iterate through all the files in an archive (using | |
| 161 | mz_zip_reader_get_num_files()) and retrieve detailed info on each file by | |
| 162 | calling mz_zip_reader_file_stat(). | |
| 163 | ||
| 164 | - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer | |
| 165 | immediately writes compressed file data to disk and builds an exact image of | |
| 166 | the central directory in memory. The central directory image is written all | |
| 167 | at once at the end of the archive file when the archive is finalized. | |
| 168 | ||
| 169 | The archive writer can optionally align each file's local header and file | |
| 170 | data to any power of 2 alignment, which can be useful when the archive will | |
| 171 | be read from optical media. Also, the writer supports placing arbitrary data | |
| 172 | blobs at the very beginning of ZIP archives. Archives written using either | |
| 173 | feature are still readable by any ZIP tool. | |
| 174 | ||
| 175 | - Archive appending: The simple way to add a single file to an archive is | |
| 176 | to call this function: | |
| 177 | ||
| 178 | mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, | |
| 179 | const char *pArchive_name, const void *pBuf, size_t buf_size, const void | |
| 180 | *pComment, mz_uint16 comment_size, mz_uint level_and_flags); | |
| 181 | ||
| 182 | The archive will be created if it doesn't already exist, otherwise it'll be | |
| 183 | appended to. Note the appending is done in-place and is not an atomic | |
| 184 | operation, so if something goes wrong during the operation it's possible the | |
| 185 | archive could be left without a central directory (although the local file | |
| 186 | headers and file data will be fine, so the archive will be recoverable). | |
| 187 | ||
| 188 | For more complex archive modification scenarios: | |
| 189 | 1. The safest way is to use a mz_zip_reader to read the existing archive, | |
| 190 | cloning only those bits you want to preserve into a new archive using using | |
| 191 | the mz_zip_writer_add_from_zip_reader() function (which compiles the | |
| 192 | compressed file data as-is). When you're done, delete the old archive and | |
| 193 | rename the newly written archive, and you're done. This is safe but requires | |
| 194 | a bunch of temporary disk space or heap memory. | |
| 195 | ||
| 196 | 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using | |
| 197 | mz_zip_writer_init_from_reader(), append new files as needed, then finalize | |
| 198 | the archive which will write an updated central directory to the original | |
| 199 | archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() | |
| 200 | does.) There's a possibility that the archive's central directory could be | |
| 201 | lost with this method if anything goes wrong, though. | |
| 202 | ||
| 203 | - ZIP archive support limitations: | |
| 204 | No zip64 or spanning support. Extraction functions can only handle | |
| 205 | unencrypted, stored or deflated files. Requires streams capable of seeking. | |
| 206 | ||
| 207 | * This is a header file library, like stb_image.c. To get only a header file, | |
| 208 | either cut and paste the below header, or create miniz.h, #define | |
| 209 | MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. | |
| 210 | ||
| 211 | * Important: For best perf. be sure to customize the below macros for your | |
| 212 | target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define | |
| 213 | MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 | |
| 214 | ||
| 215 | * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before | |
| 216 | including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), | |
| 217 | stat64(), etc. Otherwise you won't be able to process large files (i.e. | |
| 218 | 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). | |
| 219 | */ | |
| 220 | ||
| 221 | #ifndef MINIZ_HEADER_INCLUDED | |
| 222 | #define MINIZ_HEADER_INCLUDED | |
| 223 | ||
| 224 | #include <stdint.h> | |
| 225 | #include <stdlib.h> | |
| 226 | ||
| 227 | // Defines to completely disable specific portions of miniz.c: | |
| 228 | // If all macros here are defined the only functionality remaining will be | |
| 229 | // CRC-32, adler-32, tinfl, and tdefl. | |
| 230 | ||
| 231 | // Define MINIZ_NO_STDIO to disable all usage and any functions which rely on | |
| 232 | // stdio for file I/O. | |
| 233 | //#define MINIZ_NO_STDIO | |
| 234 | ||
| 235 | // If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able | |
| 236 | // to get the current time, or get/set file times, and the C run-time funcs that | |
| 237 | // get/set times won't be called. The current downside is the times written to | |
| 238 | // your archives will be from 1979. | |
| 239 | //#define MINIZ_NO_TIME | |
| 240 | ||
| 241 | // Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. | |
| 242 | //#define MINIZ_NO_ARCHIVE_APIS | |
| 243 | ||
| 244 | // Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive | |
| 245 | // API's. | |
| 246 | //#define MINIZ_NO_ARCHIVE_WRITING_APIS | |
| 247 | ||
| 248 | // Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression | |
| 249 | // API's. | |
| 250 | //#define MINIZ_NO_ZLIB_APIS | |
| 251 | ||
| 252 | // Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent | |
| 253 | // conflicts against stock zlib. | |
| 254 | //#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES | |
| 255 | ||
| 256 | // Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. | |
| 257 | // Note if MINIZ_NO_MALLOC is defined then the user must always provide custom | |
| 258 | // user alloc/free/realloc callbacks to the zlib and archive API's, and a few | |
| 259 | // stand-alone helper API's which don't provide custom user functions (such as | |
| 260 | // tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. | |
| 261 | //#define MINIZ_NO_MALLOC | |
| 262 | ||
| 263 | #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) | |
| 264 | // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc | |
| 265 | // on Linux | |
| 266 | #define MINIZ_NO_TIME | |
| 267 | #endif | |
| 268 | ||
| 269 | #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) | |
| 270 | #include <time.h> | |
| 271 | #endif | |
| 272 | ||
| 273 | #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ | |
| 274 | defined(__i386) || defined(__i486__) || defined(__i486) || \ | |
| 275 | defined(i386) || defined(__ia64__) || defined(__x86_64__) | |
| 276 | // MINIZ_X86_OR_X64_CPU is only used to help set the below macros. | |
| 277 | #define MINIZ_X86_OR_X64_CPU 1 | |
| 278 | #endif | |
| 279 | ||
| 280 | #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU | |
| 281 | // Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. | |
| 282 | #define MINIZ_LITTLE_ENDIAN 1 | |
| 283 | #endif | |
| 284 | ||
| 285 | /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ | |
| 286 | #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) | |
| 287 | #if MINIZ_X86_OR_X64_CPU | |
| 288 | /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient | |
| 289 | * integer loads and stores from unaligned addresses. */ | |
| 290 | #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 | |
| 291 | #define MINIZ_UNALIGNED_USE_MEMCPY | |
| 292 | #else | |
| 293 | #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 | |
| 294 | #endif | |
| 295 | #endif | |
| 296 | ||
| 297 | #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || \ | |
| 298 | defined(_LP64) || defined(__LP64__) || defined(__ia64__) || \ | |
| 299 | defined(__x86_64__) | |
| 300 | // Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are | |
| 301 | // reasonably fast (and don't involve compiler generated calls to helper | |
| 302 | // functions). | |
| 303 | #define MINIZ_HAS_64BIT_REGISTERS 1 | |
| 304 | #endif | |
| 305 | ||
| 306 | #ifdef __APPLE__ | |
| 307 | #define ftello64 ftello | |
| 308 | #define fseeko64 fseeko | |
| 309 | #define fopen64 fopen | |
| 310 | #define freopen64 freopen | |
| 311 | ||
| 312 | // Darwin OSX | |
| 313 | #define MZ_PLATFORM 19 | |
| 314 | #endif | |
| 315 | ||
| 316 | #ifndef MZ_PLATFORM | |
| 317 | #if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__) | |
| 318 | #define MZ_PLATFORM 0 | |
| 319 | #else | |
| 320 | // UNIX | |
| 321 | #define MZ_PLATFORM 3 | |
| 322 | #endif | |
| 323 | #endif | |
| 324 | ||
| 325 | #ifdef __cplusplus | |
| 326 | extern "C" { | |
| 327 | #endif | |
| 328 | ||
| 329 | // ------------------- zlib-style API Definitions. | |
| 330 | ||
| 331 | // For more compatibility with zlib, miniz.c uses unsigned long for some | |
| 332 | // parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! | |
| 333 | typedef unsigned long mz_ulong; | |
| 334 | ||
| 335 | // mz_free() internally uses the MZ_FREE() macro (which by default calls free() | |
| 336 | // unless you've modified the MZ_MALLOC macro) to release a block allocated from | |
| 337 | // the heap. | |
| 338 | void mz_free(void *p); | |
| 339 | ||
| 340 | #define MZ_ADLER32_INIT (1) | |
| 341 | // mz_adler32() returns the initial adler-32 value to use when called with | |
| 342 | // ptr==NULL. | |
| 343 | mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); | |
| 344 | ||
| 345 | #define MZ_CRC32_INIT (0) | |
| 346 | // mz_crc32() returns the initial CRC-32 value to use when called with | |
| 347 | // ptr==NULL. | |
| 348 | mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); | |
| 349 | ||
| 350 | // Compression strategies. | |
| 351 | enum { | |
| 352 | MZ_DEFAULT_STRATEGY = 0, | |
| 353 | MZ_FILTERED = 1, | |
| 354 | MZ_HUFFMAN_ONLY = 2, | |
| 355 | MZ_RLE = 3, | |
| 356 | MZ_FIXED = 4 | |
| 357 | }; | |
| 358 | ||
| 359 | /* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or | |
| 360 | * modify this enum. */ | |
| 361 | typedef enum { | |
| 362 | MZ_ZIP_NO_ERROR = 0, | |
| 363 | MZ_ZIP_UNDEFINED_ERROR, | |
| 364 | MZ_ZIP_TOO_MANY_FILES, | |
| 365 | MZ_ZIP_FILE_TOO_LARGE, | |
| 366 | MZ_ZIP_UNSUPPORTED_METHOD, | |
| 367 | MZ_ZIP_UNSUPPORTED_ENCRYPTION, | |
| 368 | MZ_ZIP_UNSUPPORTED_FEATURE, | |
| 369 | MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, | |
| 370 | MZ_ZIP_NOT_AN_ARCHIVE, | |
| 371 | MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, | |
| 372 | MZ_ZIP_UNSUPPORTED_MULTIDISK, | |
| 373 | MZ_ZIP_DECOMPRESSION_FAILED, | |
| 374 | MZ_ZIP_COMPRESSION_FAILED, | |
| 375 | MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, | |
| 376 | MZ_ZIP_CRC_CHECK_FAILED, | |
| 377 | MZ_ZIP_UNSUPPORTED_CDIR_SIZE, | |
| 378 | MZ_ZIP_ALLOC_FAILED, | |
| 379 | MZ_ZIP_FILE_OPEN_FAILED, | |
| 380 | MZ_ZIP_FILE_CREATE_FAILED, | |
| 381 | MZ_ZIP_FILE_WRITE_FAILED, | |
| 382 | MZ_ZIP_FILE_READ_FAILED, | |
| 383 | MZ_ZIP_FILE_CLOSE_FAILED, | |
| 384 | MZ_ZIP_FILE_SEEK_FAILED, | |
| 385 | MZ_ZIP_FILE_STAT_FAILED, | |
| 386 | MZ_ZIP_INVALID_PARAMETER, | |
| 387 | MZ_ZIP_INVALID_FILENAME, | |
| 388 | MZ_ZIP_BUF_TOO_SMALL, | |
| 389 | MZ_ZIP_INTERNAL_ERROR, | |
| 390 | MZ_ZIP_FILE_NOT_FOUND, | |
| 391 | MZ_ZIP_ARCHIVE_TOO_LARGE, | |
| 392 | MZ_ZIP_VALIDATION_FAILED, | |
| 393 | MZ_ZIP_WRITE_CALLBACK_FAILED, | |
| 394 | MZ_ZIP_TOTAL_ERRORS | |
| 395 | } mz_zip_error; | |
| 396 | ||
| 397 | // Method | |
| 398 | #define MZ_DEFLATED 8 | |
| 399 | ||
| 400 | #ifndef MINIZ_NO_ZLIB_APIS | |
| 401 | ||
| 402 | // Heap allocation callbacks. | |
| 403 | // Note that mz_alloc_func parameter types purposely differ from zlib's: | |
| 404 | // items/size is size_t, not unsigned long. | |
| 405 | typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); | |
| 406 | typedef void (*mz_free_func)(void *opaque, void *address); | |
| 407 | typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, | |
| 408 | size_t size); | |
| 409 | ||
| 410 | #define MZ_VERSION "9.1.15" | |
| 411 | #define MZ_VERNUM 0x91F0 | |
| 412 | #define MZ_VER_MAJOR 9 | |
| 413 | #define MZ_VER_MINOR 1 | |
| 414 | #define MZ_VER_REVISION 15 | |
| 415 | #define MZ_VER_SUBREVISION 0 | |
| 416 | ||
| 417 | // Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The | |
| 418 | // other values are for advanced use (refer to the zlib docs). | |
| 419 | enum { | |
| 420 | MZ_NO_FLUSH = 0, | |
| 421 | MZ_PARTIAL_FLUSH = 1, | |
| 422 | MZ_SYNC_FLUSH = 2, | |
| 423 | MZ_FULL_FLUSH = 3, | |
| 424 | MZ_FINISH = 4, | |
| 425 | MZ_BLOCK = 5 | |
| 426 | }; | |
| 427 | ||
| 428 | // Return status codes. MZ_PARAM_ERROR is non-standard. | |
| 429 | enum { | |
| 430 | MZ_OK = 0, | |
| 431 | MZ_STREAM_END = 1, | |
| 432 | MZ_NEED_DICT = 2, | |
| 433 | MZ_ERRNO = -1, | |
| 434 | MZ_STREAM_ERROR = -2, | |
| 435 | MZ_DATA_ERROR = -3, | |
| 436 | MZ_MEM_ERROR = -4, | |
| 437 | MZ_BUF_ERROR = -5, | |
| 438 | MZ_VERSION_ERROR = -6, | |
| 439 | MZ_PARAM_ERROR = -10000 | |
| 440 | }; | |
| 441 | ||
| 442 | // Compression levels: 0-9 are the standard zlib-style levels, 10 is best | |
| 443 | // possible compression (not zlib compatible, and may be very slow), | |
| 444 | // MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. | |
| 445 | enum { | |
| 446 | MZ_NO_COMPRESSION = 0, | |
| 447 | MZ_BEST_SPEED = 1, | |
| 448 | MZ_BEST_COMPRESSION = 9, | |
| 449 | MZ_UBER_COMPRESSION = 10, | |
| 450 | MZ_DEFAULT_LEVEL = 6, | |
| 451 | MZ_DEFAULT_COMPRESSION = -1 | |
| 452 | }; | |
| 453 | ||
| 454 | // Window bits | |
| 455 | #define MZ_DEFAULT_WINDOW_BITS 15 | |
| 456 | ||
| 457 | struct mz_internal_state; | |
| 458 | ||
| 459 | // Compression/decompression stream struct. | |
| 460 | typedef struct mz_stream_s { | |
| 461 | const unsigned char *next_in; // pointer to next byte to read | |
| 462 | unsigned int avail_in; // number of bytes available at next_in | |
| 463 | mz_ulong total_in; // total number of bytes consumed so far | |
| 464 | ||
| 465 | unsigned char *next_out; // pointer to next byte to write | |
| 466 | unsigned int avail_out; // number of bytes that can be written to next_out | |
| 467 | mz_ulong total_out; // total number of bytes produced so far | |
| 468 | ||
| 469 | char *msg; // error msg (unused) | |
| 470 | struct mz_internal_state *state; // internal state, allocated by zalloc/zfree | |
| 471 | ||
| 472 | mz_alloc_func | |
| 473 | zalloc; // optional heap allocation function (defaults to malloc) | |
| 474 | mz_free_func zfree; // optional heap free function (defaults to free) | |
| 475 | void *opaque; // heap alloc function user pointer | |
| 476 | ||
| 477 | int data_type; // data_type (unused) | |
| 478 | mz_ulong adler; // adler32 of the source or uncompressed data | |
| 479 | mz_ulong reserved; // not used | |
| 480 | } mz_stream; | |
| 481 | ||
| 482 | typedef mz_stream *mz_streamp; | |
| 483 | ||
| 484 | // Returns the version string of miniz.c. | |
| 485 | const char *mz_version(void); | |
| 486 | ||
| 487 | // mz_deflateInit() initializes a compressor with default options: | |
| 488 | // Parameters: | |
| 489 | // pStream must point to an initialized mz_stream struct. | |
| 490 | // level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. | |
| 491 | // level 1 enables a specially optimized compression function that's been | |
| 492 | // optimized purely for performance, not ratio. (This special func. is | |
| 493 | // currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and | |
| 494 | // MINIZ_LITTLE_ENDIAN are defined.) | |
| 495 | // Return values: | |
| 496 | // MZ_OK on success. | |
| 497 | // MZ_STREAM_ERROR if the stream is bogus. | |
| 498 | // MZ_PARAM_ERROR if the input parameters are bogus. | |
| 499 | // MZ_MEM_ERROR on out of memory. | |
| 500 | int mz_deflateInit(mz_streamp pStream, int level); | |
| 501 | ||
| 502 | // mz_deflateInit2() is like mz_deflate(), except with more control: | |
| 503 | // Additional parameters: | |
| 504 | // method must be MZ_DEFLATED | |
| 505 | // window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with | |
| 506 | // zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no | |
| 507 | // header or footer) mem_level must be between [1, 9] (it's checked but | |
| 508 | // ignored by miniz.c) | |
| 509 | int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, | |
| 510 | int mem_level, int strategy); | |
| 511 | ||
| 512 | // Quickly resets a compressor without having to reallocate anything. Same as | |
| 513 | // calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). | |
| 514 | int mz_deflateReset(mz_streamp pStream); | |
| 515 | ||
| 516 | // mz_deflate() compresses the input to output, consuming as much of the input | |
| 517 | // and producing as much output as possible. Parameters: | |
| 518 | // pStream is the stream to read from and write to. You must initialize/update | |
| 519 | // the next_in, avail_in, next_out, and avail_out members. flush may be | |
| 520 | // MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. | |
| 521 | // Return values: | |
| 522 | // MZ_OK on success (when flushing, or if more input is needed but not | |
| 523 | // available, and/or there's more output to be written but the output buffer | |
| 524 | // is full). MZ_STREAM_END if all input has been consumed and all output bytes | |
| 525 | // have been written. Don't call mz_deflate() on the stream anymore. | |
| 526 | // MZ_STREAM_ERROR if the stream is bogus. | |
| 527 | // MZ_PARAM_ERROR if one of the parameters is invalid. | |
| 528 | // MZ_BUF_ERROR if no forward progress is possible because the input and/or | |
| 529 | // output buffers are empty. (Fill up the input buffer or free up some output | |
| 530 | // space and try again.) | |
| 531 | int mz_deflate(mz_streamp pStream, int flush); | |
| 532 | ||
| 533 | // mz_deflateEnd() deinitializes a compressor: | |
| 534 | // Return values: | |
| 535 | // MZ_OK on success. | |
| 536 | // MZ_STREAM_ERROR if the stream is bogus. | |
| 537 | int mz_deflateEnd(mz_streamp pStream); | |
| 538 | ||
| 539 | // mz_deflateBound() returns a (very) conservative upper bound on the amount of | |
| 540 | // data that could be generated by deflate(), assuming flush is set to only | |
| 541 | // MZ_NO_FLUSH or MZ_FINISH. | |
| 542 | mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); | |
| 543 | ||
| 544 | // Single-call compression functions mz_compress() and mz_compress2(): | |
| 545 | // Returns MZ_OK on success, or one of the error codes from mz_deflate() on | |
| 546 | // failure. | |
| 547 | int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, | |
| 548 | const unsigned char *pSource, mz_ulong source_len); | |
| 549 | int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, | |
| 550 | const unsigned char *pSource, mz_ulong source_len, int level); | |
| 551 | ||
| 552 | // mz_compressBound() returns a (very) conservative upper bound on the amount of | |
| 553 | // data that could be generated by calling mz_compress(). | |
| 554 | mz_ulong mz_compressBound(mz_ulong source_len); | |
| 555 | ||
| 556 | // Initializes a decompressor. | |
| 557 | int mz_inflateInit(mz_streamp pStream); | |
| 558 | ||
| 559 | // mz_inflateInit2() is like mz_inflateInit() with an additional option that | |
| 560 | // controls the window size and whether or not the stream has been wrapped with | |
| 561 | // a zlib header/footer: window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse | |
| 562 | // zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). | |
| 563 | int mz_inflateInit2(mz_streamp pStream, int window_bits); | |
| 564 | ||
| 565 | // Decompresses the input stream to the output, consuming only as much of the | |
| 566 | // input as needed, and writing as much to the output as possible. Parameters: | |
| 567 | // pStream is the stream to read from and write to. You must initialize/update | |
| 568 | // the next_in, avail_in, next_out, and avail_out members. flush may be | |
| 569 | // MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. On the first call, if flush is | |
| 570 | // MZ_FINISH it's assumed the input and output buffers are both sized large | |
| 571 | // enough to decompress the entire stream in a single call (this is slightly | |
| 572 | // faster). MZ_FINISH implies that there are no more source bytes available | |
| 573 | // beside what's already in the input buffer, and that the output buffer is | |
| 574 | // large enough to hold the rest of the decompressed data. | |
| 575 | // Return values: | |
| 576 | // MZ_OK on success. Either more input is needed but not available, and/or | |
| 577 | // there's more output to be written but the output buffer is full. | |
| 578 | // MZ_STREAM_END if all needed input has been consumed and all output bytes | |
| 579 | // have been written. For zlib streams, the adler-32 of the decompressed data | |
| 580 | // has also been verified. MZ_STREAM_ERROR if the stream is bogus. | |
| 581 | // MZ_DATA_ERROR if the deflate stream is invalid. | |
| 582 | // MZ_PARAM_ERROR if one of the parameters is invalid. | |
| 583 | // MZ_BUF_ERROR if no forward progress is possible because the input buffer is | |
| 584 | // empty but the inflater needs more input to continue, or if the output | |
| 585 | // buffer is not large enough. Call mz_inflate() again with more input data, | |
| 586 | // or with more room in the output buffer (except when using single call | |
| 587 | // decompression, described above). | |
| 588 | int mz_inflate(mz_streamp pStream, int flush); | |
| 589 | ||
| 590 | // Deinitializes a decompressor. | |
| 591 | int mz_inflateEnd(mz_streamp pStream); | |
| 592 | ||
| 593 | // Single-call decompression. | |
| 594 | // Returns MZ_OK on success, or one of the error codes from mz_inflate() on | |
| 595 | // failure. | |
| 596 | int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, | |
| 597 | const unsigned char *pSource, mz_ulong source_len); | |
| 598 | ||
| 599 | // Returns a string description of the specified error code, or NULL if the | |
| 600 | // error code is invalid. | |
| 601 | const char *mz_error(int err); | |
| 602 | ||
| 603 | // Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used | |
| 604 | // as a drop-in replacement for the subset of zlib that miniz.c supports. Define | |
| 605 | // MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib | |
| 606 | // in the same project. | |
| 607 | #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES | |
| 608 | typedef unsigned char Byte; | |
| 609 | typedef unsigned int uInt; | |
| 610 | typedef mz_ulong uLong; | |
| 611 | typedef Byte Bytef; | |
| 612 | typedef uInt uIntf; | |
| 613 | typedef char charf; | |
| 614 | typedef int intf; | |
| 615 | typedef void *voidpf; | |
| 616 | typedef uLong uLongf; | |
| 617 | typedef void *voidp; | |
| 618 | typedef void *const voidpc; | |
| 619 | #define Z_NULL 0 | |
| 620 | #define Z_NO_FLUSH MZ_NO_FLUSH | |
| 621 | #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH | |
| 622 | #define Z_SYNC_FLUSH MZ_SYNC_FLUSH | |
| 623 | #define Z_FULL_FLUSH MZ_FULL_FLUSH | |
| 624 | #define Z_FINISH MZ_FINISH | |
| 625 | #define Z_BLOCK MZ_BLOCK | |
| 626 | #define Z_OK MZ_OK | |
| 627 | #define Z_STREAM_END MZ_STREAM_END | |
| 628 | #define Z_NEED_DICT MZ_NEED_DICT | |
| 629 | #define Z_ERRNO MZ_ERRNO | |
| 630 | #define Z_STREAM_ERROR MZ_STREAM_ERROR | |
| 631 | #define Z_DATA_ERROR MZ_DATA_ERROR | |
| 632 | #define Z_MEM_ERROR MZ_MEM_ERROR | |
| 633 | #define Z_BUF_ERROR MZ_BUF_ERROR | |
| 634 | #define Z_VERSION_ERROR MZ_VERSION_ERROR | |
| 635 | #define Z_PARAM_ERROR MZ_PARAM_ERROR | |
| 636 | #define Z_NO_COMPRESSION MZ_NO_COMPRESSION | |
| 637 | #define Z_BEST_SPEED MZ_BEST_SPEED | |
| 638 | #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION | |
| 639 | #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION | |
| 640 | #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY | |
| 641 | #define Z_FILTERED MZ_FILTERED | |
| 642 | #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY | |
| 643 | #define Z_RLE MZ_RLE | |
| 644 | #define Z_FIXED MZ_FIXED | |
| 645 | #define Z_DEFLATED MZ_DEFLATED | |
| 646 | #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS | |
| 647 | #define alloc_func mz_alloc_func | |
| 648 | #define free_func mz_free_func | |
| 649 | #define internal_state mz_internal_state | |
| 650 | #define z_stream mz_stream | |
| 651 | #define deflateInit mz_deflateInit | |
| 652 | #define deflateInit2 mz_deflateInit2 | |
| 653 | #define deflateReset mz_deflateReset | |
| 654 | #define deflate mz_deflate | |
| 655 | #define deflateEnd mz_deflateEnd | |
| 656 | #define deflateBound mz_deflateBound | |
| 657 | #define compress mz_compress | |
| 658 | #define compress2 mz_compress2 | |
| 659 | #define compressBound mz_compressBound | |
| 660 | #define inflateInit mz_inflateInit | |
| 661 | #define inflateInit2 mz_inflateInit2 | |
| 662 | #define inflate mz_inflate | |
| 663 | #define inflateEnd mz_inflateEnd | |
| 664 | #define uncompress mz_uncompress | |
| 665 | #define crc32 mz_crc32 | |
| 666 | #define adler32 mz_adler32 | |
| 667 | #define MAX_WBITS 15 | |
| 668 | #define MAX_MEM_LEVEL 9 | |
| 669 | #define zError mz_error | |
| 670 | #define ZLIB_VERSION MZ_VERSION | |
| 671 | #define ZLIB_VERNUM MZ_VERNUM | |
| 672 | #define ZLIB_VER_MAJOR MZ_VER_MAJOR | |
| 673 | #define ZLIB_VER_MINOR MZ_VER_MINOR | |
| 674 | #define ZLIB_VER_REVISION MZ_VER_REVISION | |
| 675 | #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION | |
| 676 | #define zlibVersion mz_version | |
| 677 | #define zlib_version mz_version() | |
| 678 | #endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES | |
| 679 | ||
| 680 | #endif // MINIZ_NO_ZLIB_APIS | |
| 681 | ||
| 682 | // ------------------- Types and macros | |
| 683 | ||
| 684 | typedef unsigned char mz_uint8; | |
| 685 | typedef signed short mz_int16; | |
| 686 | typedef unsigned short mz_uint16; | |
| 687 | typedef unsigned int mz_uint32; | |
| 688 | typedef unsigned int mz_uint; | |
| 689 | typedef long long mz_int64; | |
| 690 | typedef unsigned long long mz_uint64; | |
| 691 | typedef int mz_bool; | |
| 692 | ||
| 693 | #define MZ_FALSE (0) | |
| 694 | #define MZ_TRUE (1) | |
| 695 | ||
| 696 | // An attempt to work around MSVC's spammy "warning C4127: conditional | |
| 697 | // expression is constant" message. | |
| 698 | #ifdef _MSC_VER | |
| 699 | #define MZ_MACRO_END while (0, 0) | |
| 700 | #else | |
| 701 | #define MZ_MACRO_END while (0) | |
| 702 | #endif | |
| 703 | ||
| 704 | // ------------------- ZIP archive reading/writing | |
| 705 | ||
| 706 | #ifndef MINIZ_NO_ARCHIVE_APIS | |
| 707 | ||
| 708 | enum { | |
| 709 | MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, | |
| 710 | MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, | |
| 711 | MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 | |
| 712 | }; | |
| 713 | ||
| 714 | typedef struct { | |
| 715 | mz_uint32 m_file_index; | |
| 716 | mz_uint32 m_central_dir_ofs; | |
| 717 | mz_uint16 m_version_made_by; | |
| 718 | mz_uint16 m_version_needed; | |
| 719 | mz_uint16 m_bit_flag; | |
| 720 | mz_uint16 m_method; | |
| 721 | #ifndef MINIZ_NO_TIME | |
| 722 | time_t m_time; | |
| 723 | #endif | |
| 724 | mz_uint32 m_crc32; | |
| 725 | mz_uint64 m_comp_size; | |
| 726 | mz_uint64 m_uncomp_size; | |
| 727 | mz_uint16 m_internal_attr; | |
| 728 | mz_uint32 m_external_attr; | |
| 729 | mz_uint64 m_local_header_ofs; | |
| 730 | mz_uint32 m_comment_size; | |
| 731 | char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; | |
| 732 | char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; | |
| 733 | } mz_zip_archive_file_stat; | |
| 734 | ||
| 735 | typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, | |
| 736 | void *pBuf, size_t n); | |
| 737 | typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, | |
| 738 | const void *pBuf, size_t n); | |
| 739 | typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); | |
| 740 | ||
| 741 | struct mz_zip_internal_state_tag; | |
| 742 | typedef struct mz_zip_internal_state_tag mz_zip_internal_state; | |
| 743 | ||
| 744 | typedef enum { | |
| 745 | MZ_ZIP_MODE_INVALID = 0, | |
| 746 | MZ_ZIP_MODE_READING = 1, | |
| 747 | MZ_ZIP_MODE_WRITING = 2, | |
| 748 | MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 | |
| 749 | } mz_zip_mode; | |
| 750 | ||
| 751 | typedef enum { | |
| 752 | MZ_ZIP_TYPE_INVALID = 0, | |
| 753 | MZ_ZIP_TYPE_USER, | |
| 754 | MZ_ZIP_TYPE_MEMORY, | |
| 755 | MZ_ZIP_TYPE_HEAP, | |
| 756 | MZ_ZIP_TYPE_FILE, | |
| 757 | MZ_ZIP_TYPE_CFILE, | |
| 758 | MZ_ZIP_TOTAL_TYPES | |
| 759 | } mz_zip_type; | |
| 760 | ||
| 761 | typedef struct { | |
| 762 | mz_uint64 m_archive_size; | |
| 763 | mz_uint64 m_central_directory_file_ofs; | |
| 764 | ||
| 765 | /* We only support up to UINT32_MAX files in zip64 mode. */ | |
| 766 | mz_uint32 m_total_files; | |
| 767 | mz_zip_mode m_zip_mode; | |
| 768 | mz_zip_type m_zip_type; | |
| 769 | mz_zip_error m_last_error; | |
| 770 | ||
| 771 | mz_uint64 m_file_offset_alignment; | |
| 772 | ||
| 773 | mz_alloc_func m_pAlloc; | |
| 774 | mz_free_func m_pFree; | |
| 775 | mz_realloc_func m_pRealloc; | |
| 776 | void *m_pAlloc_opaque; | |
| 777 | ||
| 778 | mz_file_read_func m_pRead; | |
| 779 | mz_file_write_func m_pWrite; | |
| 780 | mz_file_needs_keepalive m_pNeeds_keepalive; | |
| 781 | void *m_pIO_opaque; | |
| 782 | ||
| 783 | mz_zip_internal_state *m_pState; | |
| 784 | ||
| 785 | } mz_zip_archive; | |
| 786 | ||
| 787 | typedef enum { | |
| 788 | MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, | |
| 789 | MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, | |
| 790 | MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, | |
| 791 | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 | |
| 792 | } mz_zip_flags; | |
| 793 | ||
| 794 | // ZIP archive reading | |
| 795 | ||
| 796 | // Inits a ZIP archive reader. | |
| 797 | // These functions read and validate the archive's central directory. | |
| 798 | mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, | |
| 799 | mz_uint32 flags); | |
| 800 | mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, | |
| 801 | size_t size, mz_uint32 flags); | |
| 802 | ||
| 803 | #ifndef MINIZ_NO_STDIO | |
| 804 | mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, | |
| 805 | mz_uint32 flags); | |
| 806 | #endif | |
| 807 | ||
| 808 | // Returns the total number of files in the archive. | |
| 809 | mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); | |
| 810 | ||
| 811 | // Returns detailed information about an archive file entry. | |
| 812 | mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, | |
| 813 | mz_zip_archive_file_stat *pStat); | |
| 814 | ||
| 815 | // Determines if an archive file entry is a directory entry. | |
| 816 | mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, | |
| 817 | mz_uint file_index); | |
| 818 | mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, | |
| 819 | mz_uint file_index); | |
| 820 | ||
| 821 | // Retrieves the filename of an archive file entry. | |
| 822 | // Returns the number of bytes written to pFilename, or if filename_buf_size is | |
| 823 | // 0 this function returns the number of bytes needed to fully store the | |
| 824 | // filename. | |
| 825 | mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, | |
| 826 | char *pFilename, mz_uint filename_buf_size); | |
| 827 | ||
| 828 | // Attempts to locates a file in the archive's central directory. | |
| 829 | // Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH | |
| 830 | // Returns -1 if the file cannot be found. | |
| 831 | int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, | |
| 832 | const char *pComment, mz_uint flags); | |
| 833 | ||
| 834 | // Extracts a archive file to a memory buffer using no memory allocation. | |
| 835 | mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, | |
| 836 | mz_uint file_index, void *pBuf, | |
| 837 | size_t buf_size, mz_uint flags, | |
| 838 | void *pUser_read_buf, | |
| 839 | size_t user_read_buf_size); | |
| 840 | mz_bool mz_zip_reader_extract_file_to_mem_no_alloc( | |
| 841 | mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, | |
| 842 | mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); | |
| 843 | ||
| 844 | // Extracts a archive file to a memory buffer. | |
| 845 | mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, | |
| 846 | void *pBuf, size_t buf_size, | |
| 847 | mz_uint flags); | |
| 848 | mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, | |
| 849 | const char *pFilename, void *pBuf, | |
| 850 | size_t buf_size, mz_uint flags); | |
| 851 | ||
| 852 | // Extracts a archive file to a dynamically allocated heap buffer. | |
| 853 | void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, | |
| 854 | size_t *pSize, mz_uint flags); | |
| 855 | void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, | |
| 856 | const char *pFilename, size_t *pSize, | |
| 857 | mz_uint flags); | |
| 858 | ||
| 859 | // Extracts a archive file using a callback function to output the file's data. | |
| 860 | mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, | |
| 861 | mz_uint file_index, | |
| 862 | mz_file_write_func pCallback, | |
| 863 | void *pOpaque, mz_uint flags); | |
| 864 | mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, | |
| 865 | const char *pFilename, | |
| 866 | mz_file_write_func pCallback, | |
| 867 | void *pOpaque, mz_uint flags); | |
| 868 | ||
| 869 | #ifndef MINIZ_NO_STDIO | |
| 870 | // Extracts a archive file to a disk file and sets its last accessed and | |
| 871 | // modified times. This function only extracts files, not archive directory | |
| 872 | // records. | |
| 873 | mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, | |
| 874 | const char *pDst_filename, mz_uint flags); | |
| 875 | mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, | |
| 876 | const char *pArchive_filename, | |
| 877 | const char *pDst_filename, | |
| 878 | mz_uint flags); | |
| 879 | #endif | |
| 880 | ||
| 881 | // Ends archive reading, freeing all allocations, and closing the input archive | |
| 882 | // file if mz_zip_reader_init_file() was used. | |
| 883 | mz_bool mz_zip_reader_end(mz_zip_archive *pZip); | |
| 884 | ||
| 885 | // ZIP archive writing | |
| 886 | ||
| 887 | #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS | |
| 888 | ||
| 889 | // Inits a ZIP archive writer. | |
| 890 | mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); | |
| 891 | mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, | |
| 892 | size_t size_to_reserve_at_beginning, | |
| 893 | size_t initial_allocation_size); | |
| 894 | ||
| 895 | #ifndef MINIZ_NO_STDIO | |
| 896 | mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, | |
| 897 | mz_uint64 size_to_reserve_at_beginning); | |
| 898 | #endif | |
| 899 | ||
| 900 | // Converts a ZIP archive reader object into a writer object, to allow efficient | |
| 901 | // in-place file appends to occur on an existing archive. For archives opened | |
| 902 | // using mz_zip_reader_init_file, pFilename must be the archive's filename so it | |
| 903 | // can be reopened for writing. If the file can't be reopened, | |
| 904 | // mz_zip_reader_end() will be called. For archives opened using | |
| 905 | // mz_zip_reader_init_mem, the memory block must be growable using the realloc | |
| 906 | // callback (which defaults to realloc unless you've overridden it). Finally, | |
| 907 | // for archives opened using mz_zip_reader_init, the mz_zip_archive's user | |
| 908 | // provided m_pWrite function cannot be NULL. Note: In-place archive | |
| 909 | // modification is not recommended unless you know what you're doing, because if | |
| 910 | // execution stops or something goes wrong before the archive is finalized the | |
| 911 | // file's central directory will be hosed. | |
| 912 | mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, | |
| 913 | const char *pFilename); | |
| 914 | ||
| 915 | // Adds the contents of a memory buffer to an archive. These functions record | |
| 916 | // the current local time into the archive. To add a directory entry, call this | |
| 917 | // method with an archive name ending in a forwardslash with empty buffer. | |
| 918 | // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, | |
| 919 | // MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or | |
| 920 | // just set to MZ_DEFAULT_COMPRESSION. | |
| 921 | mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, | |
| 922 | const void *pBuf, size_t buf_size, | |
| 923 | mz_uint level_and_flags); | |
| 924 | mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, | |
| 925 | const char *pArchive_name, const void *pBuf, | |
| 926 | size_t buf_size, const void *pComment, | |
| 927 | mz_uint16 comment_size, | |
| 928 | mz_uint level_and_flags, mz_uint64 uncomp_size, | |
| 929 | mz_uint32 uncomp_crc32); | |
| 930 | ||
| 931 | #ifndef MINIZ_NO_STDIO | |
| 932 | // Adds the contents of a disk file to an archive. This function also records | |
| 933 | // the disk file's modified time into the archive. level_and_flags - compression | |
| 934 | // level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd | |
| 935 | // with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. | |
| 936 | mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, | |
| 937 | const char *pSrc_filename, const void *pComment, | |
| 938 | mz_uint16 comment_size, mz_uint level_and_flags, | |
| 939 | mz_uint32 ext_attributes); | |
| 940 | #endif | |
| 941 | ||
| 942 | // Adds a file to an archive by fully cloning the data from another archive. | |
| 943 | // This function fully clones the source file's compressed data (no | |
| 944 | // recompression), along with its full filename, extra data, and comment fields. | |
| 945 | mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, | |
| 946 | mz_zip_archive *pSource_zip, | |
| 947 | mz_uint file_index); | |
| 948 | ||
| 949 | // Finalizes the archive by writing the central directory records followed by | |
| 950 | // the end of central directory record. After an archive is finalized, the only | |
| 951 | // valid call on the mz_zip_archive struct is mz_zip_writer_end(). An archive | |
| 952 | // must be manually finalized by calling this function for it to be valid. | |
| 953 | mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); | |
| 954 | mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, | |
| 955 | size_t *pSize); | |
| 956 | ||
| 957 | // Ends archive writing, freeing all allocations, and closing the output file if | |
| 958 | // mz_zip_writer_init_file() was used. Note for the archive to be valid, it must | |
| 959 | // have been finalized before ending. | |
| 960 | mz_bool mz_zip_writer_end(mz_zip_archive *pZip); | |
| 961 | ||
| 962 | // Misc. high-level helper functions: | |
| 963 | ||
| 964 | // mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) | |
| 965 | // appends a memory blob to a ZIP archive. level_and_flags - compression level | |
| 966 | // (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero | |
| 967 | // or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. | |
| 968 | mz_bool mz_zip_add_mem_to_archive_file_in_place( | |
| 969 | const char *pZip_filename, const char *pArchive_name, const void *pBuf, | |
| 970 | size_t buf_size, const void *pComment, mz_uint16 comment_size, | |
| 971 | mz_uint level_and_flags); | |
| 972 | ||
| 973 | // Reads a single file from an archive into a heap block. | |
| 974 | // Returns NULL on failure. | |
| 975 | void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, | |
| 976 | const char *pArchive_name, | |
| 977 | size_t *pSize, mz_uint zip_flags); | |
| 978 | ||
| 979 | #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS | |
| 980 | ||
| 981 | #endif // #ifndef MINIZ_NO_ARCHIVE_APIS | |
| 982 | ||
| 983 | // ------------------- Low-level Decompression API Definitions | |
| 984 | ||
| 985 | // Decompression flags used by tinfl_decompress(). | |
| 986 | // TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and | |
| 987 | // ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the | |
| 988 | // input is a raw deflate stream. TINFL_FLAG_HAS_MORE_INPUT: If set, there are | |
| 989 | // more input bytes available beyond the end of the supplied input buffer. If | |
| 990 | // clear, the input buffer contains all remaining input. | |
| 991 | // TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large | |
| 992 | // enough to hold the entire decompressed stream. If clear, the output buffer is | |
| 993 | // at least the size of the dictionary (typically 32KB). | |
| 994 | // TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the | |
| 995 | // decompressed bytes. | |
| 996 | enum { | |
| 997 | TINFL_FLAG_PARSE_ZLIB_HEADER = 1, | |
| 998 | TINFL_FLAG_HAS_MORE_INPUT = 2, | |
| 999 | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, | |
| 1000 | TINFL_FLAG_COMPUTE_ADLER32 = 8 | |
| 1001 | }; | |
| 1002 | ||
| 1003 | // High level decompression functions: | |
| 1004 | // tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block | |
| 1005 | // allocated via malloc(). On entry: | |
| 1006 | // pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data | |
| 1007 | // to decompress. | |
| 1008 | // On return: | |
| 1009 | // Function returns a pointer to the decompressed data, or NULL on failure. | |
| 1010 | // *pOut_len will be set to the decompressed data's size, which could be larger | |
| 1011 | // than src_buf_len on uncompressible data. The caller must call mz_free() on | |
| 1012 | // the returned block when it's no longer needed. | |
| 1013 | void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, | |
| 1014 | size_t *pOut_len, int flags); | |
| 1015 | ||
| 1016 | // tinfl_decompress_mem_to_mem() decompresses a block in memory to another block | |
| 1017 | // in memory. Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the | |
| 1018 | // number of bytes written on success. | |
| 1019 | #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) | |
| 1020 | size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, | |
| 1021 | const void *pSrc_buf, size_t src_buf_len, | |
| 1022 | int flags); | |
| 1023 | ||
| 1024 | // tinfl_decompress_mem_to_callback() decompresses a block in memory to an | |
| 1025 | // internal 32KB buffer, and a user provided callback function will be called to | |
| 1026 | // flush the buffer. Returns 1 on success or 0 on failure. | |
| 1027 | typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); | |
| 1028 | int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, | |
| 1029 | tinfl_put_buf_func_ptr pPut_buf_func, | |
| 1030 | void *pPut_buf_user, int flags); | |
| 1031 | ||
| 1032 | struct tinfl_decompressor_tag; | |
| 1033 | typedef struct tinfl_decompressor_tag tinfl_decompressor; | |
| 1034 | ||
| 1035 | // Max size of LZ dictionary. | |
| 1036 | #define TINFL_LZ_DICT_SIZE 32768 | |
| 1037 | ||
| 1038 | // Return status. | |
| 1039 | typedef enum { | |
| 1040 | TINFL_STATUS_BAD_PARAM = -3, | |
| 1041 | TINFL_STATUS_ADLER32_MISMATCH = -2, | |
| 1042 | TINFL_STATUS_FAILED = -1, | |
| 1043 | TINFL_STATUS_DONE = 0, | |
| 1044 | TINFL_STATUS_NEEDS_MORE_INPUT = 1, | |
| 1045 | TINFL_STATUS_HAS_MORE_OUTPUT = 2 | |
| 1046 | } tinfl_status; | |
| 1047 | ||
| 1048 | // Initializes the decompressor to its initial state. | |
| 1049 | #define tinfl_init(r) \ | |
| 1050 | do { \ | |
| 1051 | (r)->m_state = 0; \ | |
| 1052 | } \ | |
| 1053 | MZ_MACRO_END | |
| 1054 | #define tinfl_get_adler32(r) (r)->m_check_adler32 | |
| 1055 | ||
| 1056 | // Main low-level decompressor coroutine function. This is the only function | |
| 1057 | // actually needed for decompression. All the other functions are just | |
| 1058 | // high-level helpers for improved usability. This is a universal API, i.e. it | |
| 1059 | // can be used as a building block to build any desired higher level | |
| 1060 | // decompression API. In the limit case, it can be called once per every byte | |
| 1061 | // input or output. | |
| 1062 | tinfl_status tinfl_decompress(tinfl_decompressor *r, | |
| 1063 | const mz_uint8 *pIn_buf_next, | |
| 1064 | size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, | |
| 1065 | mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, | |
| 1066 | const mz_uint32 decomp_flags); | |
| 1067 | ||
| 1068 | // Internal/private bits follow. | |
| 1069 | enum { | |
| 1070 | TINFL_MAX_HUFF_TABLES = 3, | |
| 1071 | TINFL_MAX_HUFF_SYMBOLS_0 = 288, | |
| 1072 | TINFL_MAX_HUFF_SYMBOLS_1 = 32, | |
| 1073 | TINFL_MAX_HUFF_SYMBOLS_2 = 19, | |
| 1074 | TINFL_FAST_LOOKUP_BITS = 10, | |
| 1075 | TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS | |
| 1076 | }; | |
| 1077 | ||
| 1078 | typedef struct { | |
| 1079 | mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; | |
| 1080 | mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], | |
| 1081 | m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; | |
| 1082 | } tinfl_huff_table; | |
| 1083 | ||
| 1084 | #if MINIZ_HAS_64BIT_REGISTERS | |
| 1085 | #define TINFL_USE_64BIT_BITBUF 1 | |
| 1086 | #endif | |
| 1087 | ||
| 1088 | #if TINFL_USE_64BIT_BITBUF | |
| 1089 | typedef mz_uint64 tinfl_bit_buf_t; | |
| 1090 | #define TINFL_BITBUF_SIZE (64) | |
| 1091 | #else | |
| 1092 | typedef mz_uint32 tinfl_bit_buf_t; | |
| 1093 | #define TINFL_BITBUF_SIZE (32) | |
| 1094 | #endif | |
| 1095 | ||
| 1096 | struct tinfl_decompressor_tag { | |
| 1097 | mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, | |
| 1098 | m_check_adler32, m_dist, m_counter, m_num_extra, | |
| 1099 | m_table_sizes[TINFL_MAX_HUFF_TABLES]; | |
| 1100 | tinfl_bit_buf_t m_bit_buf; | |
| 1101 | size_t m_dist_from_out_buf_start; | |
| 1102 | tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; | |
| 1103 | mz_uint8 m_raw_header[4], | |
| 1104 | m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; | |
| 1105 | }; | |
| 1106 | ||
| 1107 | // ------------------- Low-level Compression API Definitions | |
| 1108 | ||
| 1109 | // Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly | |
| 1110 | // slower, and raw/dynamic blocks will be output more frequently). | |
| 1111 | #define TDEFL_LESS_MEMORY 0 | |
| 1112 | ||
| 1113 | // tdefl_init() compression flags logically OR'd together (low 12 bits contain | |
| 1114 | // the max. number of probes per dictionary search): TDEFL_DEFAULT_MAX_PROBES: | |
| 1115 | // The compressor defaults to 128 dictionary probes per dictionary search. | |
| 1116 | // 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ | |
| 1117 | // (slowest/best compression). | |
| 1118 | enum { | |
| 1119 | TDEFL_HUFFMAN_ONLY = 0, | |
| 1120 | TDEFL_DEFAULT_MAX_PROBES = 128, | |
| 1121 | TDEFL_MAX_PROBES_MASK = 0xFFF | |
| 1122 | }; | |
| 1123 | ||
| 1124 | // TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before | |
| 1125 | // the deflate data, and the Adler-32 of the source data at the end. Otherwise, | |
| 1126 | // you'll get raw deflate data. TDEFL_COMPUTE_ADLER32: Always compute the | |
| 1127 | // adler-32 of the input data (even when not writing zlib headers). | |
| 1128 | // TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more | |
| 1129 | // efficient lazy parsing. TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to | |
| 1130 | // decrease the compressor's initialization time to the minimum, but the output | |
| 1131 | // may vary from run to run given the same input (depending on the contents of | |
| 1132 | // memory). TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a | |
| 1133 | // distance of 1) TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. | |
| 1134 | // TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. | |
| 1135 | // TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. | |
| 1136 | // The low 12 bits are reserved to control the max # of hash probes per | |
| 1137 | // dictionary lookup (see TDEFL_MAX_PROBES_MASK). | |
| 1138 | enum { | |
| 1139 | TDEFL_WRITE_ZLIB_HEADER = 0x01000, | |
| 1140 | TDEFL_COMPUTE_ADLER32 = 0x02000, | |
| 1141 | TDEFL_GREEDY_PARSING_FLAG = 0x04000, | |
| 1142 | TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, | |
| 1143 | TDEFL_RLE_MATCHES = 0x10000, | |
| 1144 | TDEFL_FILTER_MATCHES = 0x20000, | |
| 1145 | TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, | |
| 1146 | TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 | |
| 1147 | }; | |
| 1148 | ||
| 1149 | // High level compression functions: | |
| 1150 | // tdefl_compress_mem_to_heap() compresses a block in memory to a heap block | |
| 1151 | // allocated via malloc(). On entry: | |
| 1152 | // pSrc_buf, src_buf_len: Pointer and size of source block to compress. | |
| 1153 | // flags: The max match finder probes (default is 128) logically OR'd against | |
| 1154 | // the above flags. Higher probes are slower but improve compression. | |
| 1155 | // On return: | |
| 1156 | // Function returns a pointer to the compressed data, or NULL on failure. | |
| 1157 | // *pOut_len will be set to the compressed data's size, which could be larger | |
| 1158 | // than src_buf_len on uncompressible data. The caller must free() the returned | |
| 1159 | // block when it's no longer needed. | |
| 1160 | void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, | |
| 1161 | size_t *pOut_len, int flags); | |
| 1162 | ||
| 1163 | // tdefl_compress_mem_to_mem() compresses a block in memory to another block in | |
| 1164 | // memory. Returns 0 on failure. | |
| 1165 | size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, | |
| 1166 | const void *pSrc_buf, size_t src_buf_len, | |
| 1167 | int flags); | |
| 1168 | ||
| 1169 | // Compresses an image to a compressed PNG file in memory. | |
| 1170 | // On entry: | |
| 1171 | // pImage, w, h, and num_chans describe the image to compress. num_chans may be | |
| 1172 | // 1, 2, 3, or 4. The image pitch in bytes per scanline will be w*num_chans. | |
| 1173 | // The leftmost pixel on the top scanline is stored first in memory. level may | |
| 1174 | // range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, | |
| 1175 | // MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL If flip is | |
| 1176 | // true, the image will be flipped on the Y axis (useful for OpenGL apps). | |
| 1177 | // On return: | |
| 1178 | // Function returns a pointer to the compressed data, or NULL on failure. | |
| 1179 | // *pLen_out will be set to the size of the PNG image file. | |
| 1180 | // The caller must mz_free() the returned heap block (which will typically be | |
| 1181 | // larger than *pLen_out) when it's no longer needed. | |
| 1182 | void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, | |
| 1183 | int h, int num_chans, | |
| 1184 | size_t *pLen_out, | |
| 1185 | mz_uint level, mz_bool flip); | |
| 1186 | void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, | |
| 1187 | int num_chans, size_t *pLen_out); | |
| 1188 | ||
| 1189 | // Output stream interface. The compressor uses this interface to write | |
| 1190 | // compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. | |
| 1191 | typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, | |
| 1192 | void *pUser); | |
| 1193 | ||
| 1194 | // tdefl_compress_mem_to_output() compresses a block to an output stream. The | |
| 1195 | // above helpers use this function internally. | |
| 1196 | mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, | |
| 1197 | tdefl_put_buf_func_ptr pPut_buf_func, | |
| 1198 | void *pPut_buf_user, int flags); | |
| 1199 | ||
| 1200 | enum { | |
| 1201 | TDEFL_MAX_HUFF_TABLES = 3, | |
| 1202 | TDEFL_MAX_HUFF_SYMBOLS_0 = 288, | |
| 1203 | TDEFL_MAX_HUFF_SYMBOLS_1 = 32, | |
| 1204 | TDEFL_MAX_HUFF_SYMBOLS_2 = 19, | |
| 1205 | TDEFL_LZ_DICT_SIZE = 32768, | |
| 1206 | TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, | |
| 1207 | TDEFL_MIN_MATCH_LEN = 3, | |
| 1208 | TDEFL_MAX_MATCH_LEN = 258 | |
| 1209 | }; | |
| 1210 | ||
| 1211 | // TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed | |
| 1212 | // output block (using static/fixed Huffman codes). | |
| 1213 | #if TDEFL_LESS_MEMORY | |
| 1214 | enum { | |
| 1215 | TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, | |
| 1216 | TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, | |
| 1217 | TDEFL_MAX_HUFF_SYMBOLS = 288, | |
| 1218 | TDEFL_LZ_HASH_BITS = 12, | |
| 1219 | TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, | |
| 1220 | TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, | |
| 1221 | TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS | |
| 1222 | }; | |
| 1223 | #else | |
| 1224 | enum { | |
| 1225 | TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, | |
| 1226 | TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, | |
| 1227 | TDEFL_MAX_HUFF_SYMBOLS = 288, | |
| 1228 | TDEFL_LZ_HASH_BITS = 15, | |
| 1229 | TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, | |
| 1230 | TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, | |
| 1231 | TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS | |
| 1232 | }; | |
| 1233 | #endif | |
| 1234 | ||
| 1235 | // The low-level tdefl functions below may be used directly if the above helper | |
| 1236 | // functions aren't flexible enough. The low-level functions don't make any heap | |
| 1237 | // allocations, unlike the above helper functions. | |
| 1238 | typedef enum { | |
| 1239 | TDEFL_STATUS_BAD_PARAM = -2, | |
| 1240 | TDEFL_STATUS_PUT_BUF_FAILED = -1, | |
| 1241 | TDEFL_STATUS_OKAY = 0, | |
| 1242 | TDEFL_STATUS_DONE = 1, | |
| 1243 | } tdefl_status; | |
| 1244 | ||
| 1245 | // Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums | |
| 1246 | typedef enum { | |
| 1247 | TDEFL_NO_FLUSH = 0, | |
| 1248 | TDEFL_SYNC_FLUSH = 2, | |
| 1249 | TDEFL_FULL_FLUSH = 3, | |
| 1250 | TDEFL_FINISH = 4 | |
| 1251 | } tdefl_flush; | |
| 1252 | ||
| 1253 | // tdefl's compression state structure. | |
| 1254 | typedef struct { | |
| 1255 | tdefl_put_buf_func_ptr m_pPut_buf_func; | |
| 1256 | void *m_pPut_buf_user; | |
| 1257 | mz_uint m_flags, m_max_probes[2]; | |
| 1258 | int m_greedy_parsing; | |
| 1259 | mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; | |
| 1260 | mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; | |
| 1261 | mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, | |
| 1262 | m_bit_buffer; | |
| 1263 | mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, | |
| 1264 | m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, | |
| 1265 | m_wants_to_finish; | |
| 1266 | tdefl_status m_prev_return_status; | |
| 1267 | const void *m_pIn_buf; | |
| 1268 | void *m_pOut_buf; | |
| 1269 | size_t *m_pIn_buf_size, *m_pOut_buf_size; | |
| 1270 | tdefl_flush m_flush; | |
| 1271 | const mz_uint8 *m_pSrc; | |
| 1272 | size_t m_src_buf_left, m_out_buf_ofs; | |
| 1273 | mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; | |
| 1274 | mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; | |
| 1275 | mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; | |
| 1276 | mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; | |
| 1277 | mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; | |
| 1278 | mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; | |
| 1279 | mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; | |
| 1280 | mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; | |
| 1281 | } tdefl_compressor; | |
| 1282 | ||
| 1283 | // Initializes the compressor. | |
| 1284 | // There is no corresponding deinit() function because the tdefl API's do not | |
| 1285 | // dynamically allocate memory. pBut_buf_func: If NULL, output data will be | |
| 1286 | // supplied to the specified callback. In this case, the user should call the | |
| 1287 | // tdefl_compress_buffer() API for compression. If pBut_buf_func is NULL the | |
| 1288 | // user should always call the tdefl_compress() API. flags: See the above enums | |
| 1289 | // (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) | |
| 1290 | tdefl_status tdefl_init(tdefl_compressor *d, | |
| 1291 | tdefl_put_buf_func_ptr pPut_buf_func, | |
| 1292 | void *pPut_buf_user, int flags); | |
| 1293 | ||
| 1294 | // Compresses a block of data, consuming as much of the specified input buffer | |
| 1295 | // as possible, and writing as much compressed data to the specified output | |
| 1296 | // buffer as possible. | |
| 1297 | tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, | |
| 1298 | size_t *pIn_buf_size, void *pOut_buf, | |
| 1299 | size_t *pOut_buf_size, tdefl_flush flush); | |
| 1300 | ||
| 1301 | // tdefl_compress_buffer() is only usable when the tdefl_init() is called with a | |
| 1302 | // non-NULL tdefl_put_buf_func_ptr. tdefl_compress_buffer() always consumes the | |
| 1303 | // entire input buffer. | |
| 1304 | tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, | |
| 1305 | size_t in_buf_size, tdefl_flush flush); | |
| 1306 | ||
| 1307 | tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); | |
| 1308 | mz_uint32 tdefl_get_adler32(tdefl_compressor *d); | |
| 1309 | ||
| 1310 | // Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't | |
| 1311 | // defined, because it uses some of its macros. | |
| 1312 | #ifndef MINIZ_NO_ZLIB_APIS | |
| 1313 | // Create tdefl_compress() flags given zlib-style compression parameters. | |
| 1314 | // level may range from [0,10] (where 10 is absolute max compression, but may be | |
| 1315 | // much slower on some files) window_bits may be -15 (raw deflate) or 15 (zlib) | |
| 1316 | // strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, | |
| 1317 | // MZ_RLE, or MZ_FIXED | |
| 1318 | mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, | |
| 1319 | int strategy); | |
| 1320 | #endif // #ifndef MINIZ_NO_ZLIB_APIS | |
| 1321 | ||
| 1322 | #define MZ_UINT16_MAX (0xFFFFU) | |
| 1323 | #define MZ_UINT32_MAX (0xFFFFFFFFU) | |
| 1324 | ||
| 1325 | #ifdef __cplusplus | |
| 1326 | } | |
| 1327 | #endif | |
| 1328 | ||
| 1329 | #endif // MINIZ_HEADER_INCLUDED | |
| 1330 | ||
| 1331 | // ------------------- End of Header: Implementation follows. (If you only want | |
| 1332 | // the header, define MINIZ_HEADER_FILE_ONLY.) | |
| 1333 | ||
| 1334 | #ifndef MINIZ_HEADER_FILE_ONLY | |
| 1335 | ||
| 1336 | typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; | |
| 1337 | typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; | |
| 1338 | typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; | |
| 1339 | ||
| 1340 | #include <assert.h> | |
| 1341 | #include <string.h> | |
| 1342 | ||
| 1343 | #define MZ_ASSERT(x) assert(x) | |
| 1344 | ||
| 1345 | #ifdef MINIZ_NO_MALLOC | |
| 1346 | #define MZ_MALLOC(x) NULL | |
| 1347 | #define MZ_FREE(x) (void)x, ((void)0) | |
| 1348 | #define MZ_REALLOC(p, x) NULL | |
| 1349 | #else | |
| 1350 | #define MZ_MALLOC(x) malloc(x) | |
| 1351 | #define MZ_FREE(x) free(x) | |
| 1352 | #define MZ_REALLOC(p, x) realloc(p, x) | |
| 1353 | #endif | |
| 1354 | ||
| 1355 | #define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) | |
| 1356 | #define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) | |
| 1357 | #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) | |
| 1358 | ||
| 1359 | #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN | |
| 1360 | #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) | |
| 1361 | #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) | |
| 1362 | #else | |
| 1363 | #define MZ_READ_LE16(p) \ | |
| 1364 | ((mz_uint32)(((const mz_uint8 *)(p))[0]) | \ | |
| 1365 | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) | |
| 1366 | #define MZ_READ_LE32(p) \ | |
| 1367 | ((mz_uint32)(((const mz_uint8 *)(p))[0]) | \ | |
| 1368 | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | \ | |
| 1369 | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | \ | |
| 1370 | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) | |
| 1371 | #endif | |
| 1372 | ||
| 1373 | #define MZ_READ_LE64(p) \ | |
| 1374 | (((mz_uint64)MZ_READ_LE32(p)) | \ | |
| 1375 | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) \ | |
| 1376 | << 32U)) | |
| 1377 | ||
| 1378 | #ifdef _MSC_VER | |
| 1379 | #define MZ_FORCEINLINE __forceinline | |
| 1380 | #elif defined(__GNUC__) | |
| 1381 | #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) | |
| 1382 | #else | |
| 1383 | #define MZ_FORCEINLINE inline | |
| 1384 | #endif | |
| 1385 | ||
| 1386 | #ifdef __cplusplus | |
| 1387 | extern "C" { | |
| 1388 | #endif | |
| 1389 | ||
| 1390 | // ------------------- zlib-style API's | |
| 1391 | ||
| 1392 | mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { | |
| 1393 | mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); | |
| 1394 | size_t block_len = buf_len % 5552; | |
| 1395 | if (!ptr) | |
| 1396 | return MZ_ADLER32_INIT; | |
| 1397 | while (buf_len) { | |
| 1398 | for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { | |
| 1399 | s1 += ptr[0], s2 += s1; | |
| 1400 | s1 += ptr[1], s2 += s1; | |
| 1401 | s1 += ptr[2], s2 += s1; | |
| 1402 | s1 += ptr[3], s2 += s1; | |
| 1403 | s1 += ptr[4], s2 += s1; | |
| 1404 | s1 += ptr[5], s2 += s1; | |
| 1405 | s1 += ptr[6], s2 += s1; | |
| 1406 | s1 += ptr[7], s2 += s1; | |
| 1407 | } | |
| 1408 | for (; i < block_len; ++i) | |
| 1409 | s1 += *ptr++, s2 += s1; | |
| 1410 | s1 %= 65521U, s2 %= 65521U; | |
| 1411 | buf_len -= block_len; | |
| 1412 | block_len = 5552; | |
| 1413 | } | |
| 1414 | return (s2 << 16) + s1; | |
| 1415 | } | |
| 1416 | ||
| 1417 | // Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C | |
| 1418 | // implementation that balances processor cache usage against speed": | |
| 1419 | // http://www.geocities.com/malbrain/ | |
| 1420 | mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { | |
| 1421 | static const mz_uint32 s_crc32[16] = { | |
| 1422 | 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, | |
| 1423 | 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, | |
| 1424 | 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c}; | |
| 1425 | mz_uint32 crcu32 = (mz_uint32)crc; | |
| 1426 | if (!ptr) | |
| 1427 | return MZ_CRC32_INIT; | |
| 1428 | crcu32 = ~crcu32; | |
| 1429 | while (buf_len--) { | |
| 1430 | mz_uint8 b = *ptr++; | |
| 1431 | crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; | |
| 1432 | crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; | |
| 1433 | } | |
| 1434 | return ~crcu32; | |
| 1435 | } | |
| 1436 | ||
| 1437 | void mz_free(void *p) { MZ_FREE(p); } | |
| 1438 | ||
| 1439 | #ifndef MINIZ_NO_ZLIB_APIS | |
| 1440 | ||
| 1441 | static void *def_alloc_func(void *opaque, size_t items, size_t size) { | |
| 1442 | (void)opaque, (void)items, (void)size; | |
| 1443 | return MZ_MALLOC(items * size); | |
| 1444 | } | |
| 1445 | static void def_free_func(void *opaque, void *address) { | |
| 1446 | (void)opaque, (void)address; | |
| 1447 | MZ_FREE(address); | |
| 1448 | } | |
| 1449 | static void *def_realloc_func(void *opaque, void *address, size_t items, | |
| 1450 | size_t size) { | |
| 1451 | (void)opaque, (void)address, (void)items, (void)size; | |
| 1452 | return MZ_REALLOC(address, items * size); | |
| 1453 | } | |
| 1454 | ||
| 1455 | const char *mz_version(void) { return MZ_VERSION; } | |
| 1456 | ||
| 1457 | int mz_deflateInit(mz_streamp pStream, int level) { | |
| 1458 | return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, | |
| 1459 | MZ_DEFAULT_STRATEGY); | |
| 1460 | } | |
| 1461 | ||
| 1462 | int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, | |
| 1463 | int mem_level, int strategy) { | |
| 1464 | tdefl_compressor *pComp; | |
| 1465 | mz_uint comp_flags = | |
| 1466 | TDEFL_COMPUTE_ADLER32 | | |
| 1467 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); | |
| 1468 | ||
| 1469 | if (!pStream) | |
| 1470 | return MZ_STREAM_ERROR; | |
| 1471 | if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || | |
| 1472 | ((window_bits != MZ_DEFAULT_WINDOW_BITS) && | |
| 1473 | (-window_bits != MZ_DEFAULT_WINDOW_BITS))) | |
| 1474 | return MZ_PARAM_ERROR; | |
| 1475 | ||
| 1476 | pStream->data_type = 0; | |
| 1477 | pStream->adler = MZ_ADLER32_INIT; | |
| 1478 | pStream->msg = NULL; | |
| 1479 | pStream->reserved = 0; | |
| 1480 | pStream->total_in = 0; | |
| 1481 | pStream->total_out = 0; | |
| 1482 | if (!pStream->zalloc) | |
| 1483 | pStream->zalloc = def_alloc_func; | |
| 1484 | if (!pStream->zfree) | |
| 1485 | pStream->zfree = def_free_func; | |
| 1486 | ||
| 1487 | pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, | |
| 1488 | sizeof(tdefl_compressor)); | |
| 1489 | if (!pComp) | |
| 1490 | return MZ_MEM_ERROR; | |
| 1491 | ||
| 1492 | pStream->state = (struct mz_internal_state *)pComp; | |
| 1493 | ||
| 1494 | if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { | |
| 1495 | mz_deflateEnd(pStream); | |
| 1496 | return MZ_PARAM_ERROR; | |
| 1497 | } | |
| 1498 | ||
| 1499 | return MZ_OK; | |
| 1500 | } | |
| 1501 | ||
| 1502 | int mz_deflateReset(mz_streamp pStream) { | |
| 1503 | if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || | |
| 1504 | (!pStream->zfree)) | |
| 1505 | return MZ_STREAM_ERROR; | |
| 1506 | pStream->total_in = pStream->total_out = 0; | |
| 1507 | tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, | |
| 1508 | ((tdefl_compressor *)pStream->state)->m_flags); | |
| 1509 | return MZ_OK; | |
| 1510 | } | |
| 1511 | ||
| 1512 | int mz_deflate(mz_streamp pStream, int flush) { | |
| 1513 | size_t in_bytes, out_bytes; | |
| 1514 | mz_ulong orig_total_in, orig_total_out; | |
| 1515 | int mz_status = MZ_OK; | |
| 1516 | ||
| 1517 | if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || | |
| 1518 | (!pStream->next_out)) | |
| 1519 | return MZ_STREAM_ERROR; | |
| 1520 | if (!pStream->avail_out) | |
| 1521 | return MZ_BUF_ERROR; | |
| 1522 | ||
| 1523 | if (flush == MZ_PARTIAL_FLUSH) | |
| 1524 | flush = MZ_SYNC_FLUSH; | |
| 1525 | ||
| 1526 | if (((tdefl_compressor *)pStream->state)->m_prev_return_status == | |
| 1527 | TDEFL_STATUS_DONE) | |
| 1528 | return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; | |
| 1529 | ||
| 1530 | orig_total_in = pStream->total_in; | |
| 1531 | orig_total_out = pStream->total_out; | |
| 1532 | for (;;) { | |
| 1533 | tdefl_status defl_status; | |
| 1534 | in_bytes = pStream->avail_in; | |
| 1535 | out_bytes = pStream->avail_out; | |
| 1536 | ||
| 1537 | defl_status = tdefl_compress((tdefl_compressor *)pStream->state, | |
| 1538 | pStream->next_in, &in_bytes, pStream->next_out, | |
| 1539 | &out_bytes, (tdefl_flush)flush); | |
| 1540 | pStream->next_in += (mz_uint)in_bytes; | |
| 1541 | pStream->avail_in -= (mz_uint)in_bytes; | |
| 1542 | pStream->total_in += (mz_uint)in_bytes; | |
| 1543 | pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); | |
| 1544 | ||
| 1545 | pStream->next_out += (mz_uint)out_bytes; | |
| 1546 | pStream->avail_out -= (mz_uint)out_bytes; | |
| 1547 | pStream->total_out += (mz_uint)out_bytes; | |
| 1548 | ||
| 1549 | if (defl_status < 0) { | |
| 1550 | mz_status = MZ_STREAM_ERROR; | |
| 1551 | break; | |
| 1552 | } else if (defl_status == TDEFL_STATUS_DONE) { | |
| 1553 | mz_status = MZ_STREAM_END; | |
| 1554 | break; | |
| 1555 | } else if (!pStream->avail_out) | |
| 1556 | break; | |
| 1557 | else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { | |
| 1558 | if ((flush) || (pStream->total_in != orig_total_in) || | |
| 1559 | (pStream->total_out != orig_total_out)) | |
| 1560 | break; | |
| 1561 | return MZ_BUF_ERROR; // Can't make forward progress without some input. | |
| 1562 | } | |
| 1563 | } | |
| 1564 | return mz_status; | |
| 1565 | } | |
| 1566 | ||
| 1567 | int mz_deflateEnd(mz_streamp pStream) { | |
| 1568 | if (!pStream) | |
| 1569 | return MZ_STREAM_ERROR; | |
| 1570 | if (pStream->state) { | |
| 1571 | pStream->zfree(pStream->opaque, pStream->state); | |
| 1572 | pStream->state = NULL; | |
| 1573 | } | |
| 1574 | return MZ_OK; | |
| 1575 | } | |
| 1576 | ||
| 1577 | mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { | |
| 1578 | (void)pStream; | |
| 1579 | // This is really over conservative. (And lame, but it's actually pretty | |
| 1580 | // tricky to compute a true upper bound given the way tdefl's blocking works.) | |
| 1581 | return MZ_MAX(128 + (source_len * 110) / 100, | |
| 1582 | 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); | |
| 1583 | } | |
| 1584 | ||
| 1585 | int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, | |
| 1586 | const unsigned char *pSource, mz_ulong source_len, int level) { | |
| 1587 | int status; | |
| 1588 | mz_stream stream; | |
| 1589 | memset(&stream, 0, sizeof(stream)); | |
| 1590 | ||
| 1591 | // In case mz_ulong is 64-bits (argh I hate longs). | |
| 1592 | if ((source_len | *pDest_len) > 0xFFFFFFFFU) | |
| 1593 | return MZ_PARAM_ERROR; | |
| 1594 | ||
| 1595 | stream.next_in = pSource; | |
| 1596 | stream.avail_in = (mz_uint32)source_len; | |
| 1597 | stream.next_out = pDest; | |
| 1598 | stream.avail_out = (mz_uint32)*pDest_len; | |
| 1599 | ||
| 1600 | status = mz_deflateInit(&stream, level); | |
| 1601 | if (status != MZ_OK) | |
| 1602 | return status; | |
| 1603 | ||
| 1604 | status = mz_deflate(&stream, MZ_FINISH); | |
| 1605 | if (status != MZ_STREAM_END) { | |
| 1606 | mz_deflateEnd(&stream); | |
| 1607 | return (status == MZ_OK) ? MZ_BUF_ERROR : status; | |
| 1608 | } | |
| 1609 | ||
| 1610 | *pDest_len = stream.total_out; | |
| 1611 | return mz_deflateEnd(&stream); | |
| 1612 | } | |
| 1613 | ||
| 1614 | int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, | |
| 1615 | const unsigned char *pSource, mz_ulong source_len) { | |
| 1616 | return mz_compress2(pDest, pDest_len, pSource, source_len, | |
| 1617 | MZ_DEFAULT_COMPRESSION); | |
| 1618 | } | |
| 1619 | ||
| 1620 | mz_ulong mz_compressBound(mz_ulong source_len) { | |
| 1621 | return mz_deflateBound(NULL, source_len); | |
| 1622 | } | |
| 1623 | ||
| 1624 | typedef struct { | |
| 1625 | tinfl_decompressor m_decomp; | |
| 1626 | mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; | |
| 1627 | int m_window_bits; | |
| 1628 | mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; | |
| 1629 | tinfl_status m_last_status; | |
| 1630 | } inflate_state; | |
| 1631 | ||
| 1632 | int mz_inflateInit2(mz_streamp pStream, int window_bits) { | |
| 1633 | inflate_state *pDecomp; | |
| 1634 | if (!pStream) | |
| 1635 | return MZ_STREAM_ERROR; | |
| 1636 | if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && | |
| 1637 | (-window_bits != MZ_DEFAULT_WINDOW_BITS)) | |
| 1638 | return MZ_PARAM_ERROR; | |
| 1639 | ||
| 1640 | pStream->data_type = 0; | |
| 1641 | pStream->adler = 0; | |
| 1642 | pStream->msg = NULL; | |
| 1643 | pStream->total_in = 0; | |
| 1644 | pStream->total_out = 0; | |
| 1645 | pStream->reserved = 0; | |
| 1646 | if (!pStream->zalloc) | |
| 1647 | pStream->zalloc = def_alloc_func; | |
| 1648 | if (!pStream->zfree) | |
| 1649 | pStream->zfree = def_free_func; | |
| 1650 | ||
| 1651 | pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, | |
| 1652 | sizeof(inflate_state)); | |
| 1653 | if (!pDecomp) | |
| 1654 | return MZ_MEM_ERROR; | |
| 1655 | ||
| 1656 | pStream->state = (struct mz_internal_state *)pDecomp; | |
| 1657 | ||
| 1658 | tinfl_init(&pDecomp->m_decomp); | |
| 1659 | pDecomp->m_dict_ofs = 0; | |
| 1660 | pDecomp->m_dict_avail = 0; | |
| 1661 | pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; | |
| 1662 | pDecomp->m_first_call = 1; | |
| 1663 | pDecomp->m_has_flushed = 0; | |
| 1664 | pDecomp->m_window_bits = window_bits; | |
| 1665 | ||
| 1666 | return MZ_OK; | |
| 1667 | } | |
| 1668 | ||
| 1669 | int mz_inflateInit(mz_streamp pStream) { | |
| 1670 | return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); | |
| 1671 | } | |
| 1672 | ||
| 1673 | int mz_inflate(mz_streamp pStream, int flush) { | |
| 1674 | inflate_state *pState; | |
| 1675 | mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; | |
| 1676 | size_t in_bytes, out_bytes, orig_avail_in; | |
| 1677 | tinfl_status status; | |
| 1678 | ||
| 1679 | if ((!pStream) || (!pStream->state)) | |
| 1680 | return MZ_STREAM_ERROR; | |
| 1681 | if (flush == MZ_PARTIAL_FLUSH) | |
| 1682 | flush = MZ_SYNC_FLUSH; | |
| 1683 | if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) | |
| 1684 | return MZ_STREAM_ERROR; | |
| 1685 | ||
| 1686 | pState = (inflate_state *)pStream->state; | |
| 1687 | if (pState->m_window_bits > 0) | |
| 1688 | decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; | |
| 1689 | orig_avail_in = pStream->avail_in; | |
| 1690 | ||
| 1691 | first_call = pState->m_first_call; | |
| 1692 | pState->m_first_call = 0; | |
| 1693 | if (pState->m_last_status < 0) | |
| 1694 | return MZ_DATA_ERROR; | |
| 1695 | ||
| 1696 | if (pState->m_has_flushed && (flush != MZ_FINISH)) | |
| 1697 | return MZ_STREAM_ERROR; | |
| 1698 | pState->m_has_flushed |= (flush == MZ_FINISH); | |
| 1699 | ||
| 1700 | if ((flush == MZ_FINISH) && (first_call)) { | |
| 1701 | // MZ_FINISH on the first call implies that the input and output buffers are | |
| 1702 | // large enough to hold the entire compressed/decompressed file. | |
| 1703 | decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; | |
| 1704 | in_bytes = pStream->avail_in; | |
| 1705 | out_bytes = pStream->avail_out; | |
| 1706 | status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, | |
| 1707 | pStream->next_out, pStream->next_out, &out_bytes, | |
| 1708 | decomp_flags); | |
| 1709 | pState->m_last_status = status; | |
| 1710 | pStream->next_in += (mz_uint)in_bytes; | |
| 1711 | pStream->avail_in -= (mz_uint)in_bytes; | |
| 1712 | pStream->total_in += (mz_uint)in_bytes; | |
| 1713 | pStream->adler = tinfl_get_adler32(&pState->m_decomp); | |
| 1714 | pStream->next_out += (mz_uint)out_bytes; | |
| 1715 | pStream->avail_out -= (mz_uint)out_bytes; | |
| 1716 | pStream->total_out += (mz_uint)out_bytes; | |
| 1717 | ||
| 1718 | if (status < 0) | |
| 1719 | return MZ_DATA_ERROR; | |
| 1720 | else if (status != TINFL_STATUS_DONE) { | |
| 1721 | pState->m_last_status = TINFL_STATUS_FAILED; | |
| 1722 | return MZ_BUF_ERROR; | |
| 1723 | } | |
| 1724 | return MZ_STREAM_END; | |
| 1725 | } | |
| 1726 | // flush != MZ_FINISH then we must assume there's more input. | |
| 1727 | if (flush != MZ_FINISH) | |
| 1728 | decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; | |
| 1729 | ||
| 1730 | if (pState->m_dict_avail) { | |
| 1731 | n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); | |
| 1732 | memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); | |
| 1733 | pStream->next_out += n; | |
| 1734 | pStream->avail_out -= n; | |
| 1735 | pStream->total_out += n; | |
| 1736 | pState->m_dict_avail -= n; | |
| 1737 | pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); | |
| 1738 | return ((pState->m_last_status == TINFL_STATUS_DONE) && | |
| 1739 | (!pState->m_dict_avail)) | |
| 1740 | ? MZ_STREAM_END | |
| 1741 | : MZ_OK; | |
| 1742 | } | |
| 1743 | ||
| 1744 | for (;;) { | |
| 1745 | in_bytes = pStream->avail_in; | |
| 1746 | out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; | |
| 1747 | ||
| 1748 | status = tinfl_decompress( | |
| 1749 | &pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, | |
| 1750 | pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); | |
| 1751 | pState->m_last_status = status; | |
| 1752 | ||
| 1753 | pStream->next_in += (mz_uint)in_bytes; | |
| 1754 | pStream->avail_in -= (mz_uint)in_bytes; | |
| 1755 | pStream->total_in += (mz_uint)in_bytes; | |
| 1756 | pStream->adler = tinfl_get_adler32(&pState->m_decomp); | |
| 1757 | ||
| 1758 | pState->m_dict_avail = (mz_uint)out_bytes; | |
| 1759 | ||
| 1760 | n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); | |
| 1761 | memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); | |
| 1762 | pStream->next_out += n; | |
| 1763 | pStream->avail_out -= n; | |
| 1764 | pStream->total_out += n; | |
| 1765 | pState->m_dict_avail -= n; | |
| 1766 | pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); | |
| 1767 | ||
| 1768 | if (status < 0) | |
| 1769 | return MZ_DATA_ERROR; // Stream is corrupted (there could be some | |
| 1770 | // uncompressed data left in the output dictionary - | |
| 1771 | // oh well). | |
| 1772 | else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) | |
| 1773 | return MZ_BUF_ERROR; // Signal caller that we can't make forward progress | |
| 1774 | // without supplying more input or by setting flush | |
| 1775 | // to MZ_FINISH. | |
| 1776 | else if (flush == MZ_FINISH) { | |
| 1777 | // The output buffer MUST be large to hold the remaining uncompressed data | |
| 1778 | // when flush==MZ_FINISH. | |
| 1779 | if (status == TINFL_STATUS_DONE) | |
| 1780 | return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; | |
| 1781 | // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's | |
| 1782 | // at least 1 more byte on the way. If there's no more room left in the | |
| 1783 | // output buffer then something is wrong. | |
| 1784 | else if (!pStream->avail_out) | |
| 1785 | return MZ_BUF_ERROR; | |
| 1786 | } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || | |
| 1787 | (!pStream->avail_out) || (pState->m_dict_avail)) | |
| 1788 | break; | |
| 1789 | } | |
| 1790 | ||
| 1791 | return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) | |
| 1792 | ? MZ_STREAM_END | |
| 1793 | : MZ_OK; | |
| 1794 | } | |
| 1795 | ||
| 1796 | int mz_inflateEnd(mz_streamp pStream) { | |
| 1797 | if (!pStream) | |
| 1798 | return MZ_STREAM_ERROR; | |
| 1799 | if (pStream->state) { | |
| 1800 | pStream->zfree(pStream->opaque, pStream->state); | |
| 1801 | pStream->state = NULL; | |
| 1802 | } | |
| 1803 | return MZ_OK; | |
| 1804 | } | |
| 1805 | ||
| 1806 | int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, | |
| 1807 | const unsigned char *pSource, mz_ulong source_len) { | |
| 1808 | mz_stream stream; | |
| 1809 | int status; | |
| 1810 | memset(&stream, 0, sizeof(stream)); | |
| 1811 | ||
| 1812 | // In case mz_ulong is 64-bits (argh I hate longs). | |
| 1813 | if ((source_len | *pDest_len) > 0xFFFFFFFFU) | |
| 1814 | return MZ_PARAM_ERROR; | |
| 1815 | ||
| 1816 | stream.next_in = pSource; | |
| 1817 | stream.avail_in = (mz_uint32)source_len; | |
| 1818 | stream.next_out = pDest; | |
| 1819 | stream.avail_out = (mz_uint32)*pDest_len; | |
| 1820 | ||
| 1821 | status = mz_inflateInit(&stream); | |
| 1822 | if (status != MZ_OK) | |
| 1823 | return status; | |
| 1824 | ||
| 1825 | status = mz_inflate(&stream, MZ_FINISH); | |
| 1826 | if (status != MZ_STREAM_END) { | |
| 1827 | mz_inflateEnd(&stream); | |
| 1828 | return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR | |
| 1829 | : status; | |
| 1830 | } | |
| 1831 | *pDest_len = stream.total_out; | |
| 1832 | ||
| 1833 | return mz_inflateEnd(&stream); | |
| 1834 | } | |
| 1835 | ||
| 1836 | const char *mz_error(int err) { | |
| 1837 | static struct { | |
| 1838 | int m_err; | |
| 1839 | const char *m_pDesc; | |
| 1840 | } s_error_descs[] = {{MZ_OK, ""}, | |
| 1841 | {MZ_STREAM_END, "stream end"}, | |
| 1842 | {MZ_NEED_DICT, "need dictionary"}, | |
| 1843 | {MZ_ERRNO, "file error"}, | |
| 1844 | {MZ_STREAM_ERROR, "stream error"}, | |
| 1845 | {MZ_DATA_ERROR, "data error"}, | |
| 1846 | {MZ_MEM_ERROR, "out of memory"}, | |
| 1847 | {MZ_BUF_ERROR, "buf error"}, | |
| 1848 | {MZ_VERSION_ERROR, "version error"}, | |
| 1849 | {MZ_PARAM_ERROR, "parameter error"}}; | |
| 1850 | mz_uint i; | |
| 1851 | for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) | |
| 1852 | if (s_error_descs[i].m_err == err) | |
| 1853 | return s_error_descs[i].m_pDesc; | |
| 1854 | return NULL; | |
| 1855 | } | |
| 1856 | ||
| 1857 | #endif // MINIZ_NO_ZLIB_APIS | |
| 1858 | ||
| 1859 | // ------------------- Low-level Decompression (completely independent from all | |
| 1860 | // compression API's) | |
| 1861 | ||
| 1862 | #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) | |
| 1863 | #define TINFL_MEMSET(p, c, l) memset(p, c, l) | |
| 1864 | ||
| 1865 | #define TINFL_CR_BEGIN \ | |
| 1866 | switch (r->m_state) { \ | |
| 1867 | case 0: | |
| 1868 | #define TINFL_CR_RETURN(state_index, result) \ | |
| 1869 | do { \ | |
| 1870 | status = result; \ | |
| 1871 | r->m_state = state_index; \ | |
| 1872 | goto common_exit; \ | |
| 1873 | case state_index:; \ | |
| 1874 | } \ | |
| 1875 | MZ_MACRO_END | |
| 1876 | #define TINFL_CR_RETURN_FOREVER(state_index, result) \ | |
| 1877 | do { \ | |
| 1878 | for (;;) { \ | |
| 1879 | TINFL_CR_RETURN(state_index, result); \ | |
| 1880 | } \ | |
| 1881 | } \ | |
| 1882 | MZ_MACRO_END | |
| 1883 | #define TINFL_CR_FINISH } | |
| 1884 | ||
| 1885 | // TODO: If the caller has indicated that there's no more input, and we attempt | |
| 1886 | // to read beyond the input buf, then something is wrong with the input because | |
| 1887 | // the inflator never reads ahead more than it needs to. Currently | |
| 1888 | // TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. | |
| 1889 | #define TINFL_GET_BYTE(state_index, c) \ | |
| 1890 | do { \ | |
| 1891 | if (pIn_buf_cur >= pIn_buf_end) { \ | |
| 1892 | for (;;) { \ | |
| 1893 | if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ | |
| 1894 | TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ | |
| 1895 | if (pIn_buf_cur < pIn_buf_end) { \ | |
| 1896 | c = *pIn_buf_cur++; \ | |
| 1897 | break; \ | |
| 1898 | } \ | |
| 1899 | } else { \ | |
| 1900 | c = 0; \ | |
| 1901 | break; \ | |
| 1902 | } \ | |
| 1903 | } \ | |
| 1904 | } else \ | |
| 1905 | c = *pIn_buf_cur++; \ | |
| 1906 | } \ | |
| 1907 | MZ_MACRO_END | |
| 1908 | ||
| 1909 | #define TINFL_NEED_BITS(state_index, n) \ | |
| 1910 | do { \ | |
| 1911 | mz_uint c; \ | |
| 1912 | TINFL_GET_BYTE(state_index, c); \ | |
| 1913 | bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ | |
| 1914 | num_bits += 8; \ | |
| 1915 | } while (num_bits < (mz_uint)(n)) | |
| 1916 | #define TINFL_SKIP_BITS(state_index, n) \ | |
| 1917 | do { \ | |
| 1918 | if (num_bits < (mz_uint)(n)) { \ | |
| 1919 | TINFL_NEED_BITS(state_index, n); \ | |
| 1920 | } \ | |
| 1921 | bit_buf >>= (n); \ | |
| 1922 | num_bits -= (n); \ | |
| 1923 | } \ | |
| 1924 | MZ_MACRO_END | |
| 1925 | #define TINFL_GET_BITS(state_index, b, n) \ | |
| 1926 | do { \ | |
| 1927 | if (num_bits < (mz_uint)(n)) { \ | |
| 1928 | TINFL_NEED_BITS(state_index, n); \ | |
| 1929 | } \ | |
| 1930 | b = bit_buf & ((1 << (n)) - 1); \ | |
| 1931 | bit_buf >>= (n); \ | |
| 1932 | num_bits -= (n); \ | |
| 1933 | } \ | |
| 1934 | MZ_MACRO_END | |
| 1935 | ||
| 1936 | // TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes | |
| 1937 | // remaining in the input buffer falls below 2. It reads just enough bytes from | |
| 1938 | // the input stream that are needed to decode the next Huffman code (and | |
| 1939 | // absolutely no more). It works by trying to fully decode a Huffman code by | |
| 1940 | // using whatever bits are currently present in the bit buffer. If this fails, | |
| 1941 | // it reads another byte, and tries again until it succeeds or until the bit | |
| 1942 | // buffer contains >=15 bits (deflate's max. Huffman code size). | |
| 1943 | #define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ | |
| 1944 | do { \ | |
| 1945 | temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ | |
| 1946 | if (temp >= 0) { \ | |
| 1947 | code_len = temp >> 9; \ | |
| 1948 | if ((code_len) && (num_bits >= code_len)) \ | |
| 1949 | break; \ | |
| 1950 | } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ | |
| 1951 | code_len = TINFL_FAST_LOOKUP_BITS; \ | |
| 1952 | do { \ | |
| 1953 | temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ | |
| 1954 | } while ((temp < 0) && (num_bits >= (code_len + 1))); \ | |
| 1955 | if (temp >= 0) \ | |
| 1956 | break; \ | |
| 1957 | } \ | |
| 1958 | TINFL_GET_BYTE(state_index, c); \ | |
| 1959 | bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ | |
| 1960 | num_bits += 8; \ | |
| 1961 | } while (num_bits < 15); | |
| 1962 | ||
| 1963 | // TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex | |
| 1964 | // than you would initially expect because the zlib API expects the decompressor | |
| 1965 | // to never read beyond the final byte of the deflate stream. (In other words, | |
| 1966 | // when this macro wants to read another byte from the input, it REALLY needs | |
| 1967 | // another byte in order to fully decode the next Huffman code.) Handling this | |
| 1968 | // properly is particularly important on raw deflate (non-zlib) streams, which | |
| 1969 | // aren't followed by a byte aligned adler-32. The slow path is only executed at | |
| 1970 | // the very end of the input buffer. | |
| 1971 | #define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ | |
| 1972 | do { \ | |
| 1973 | int temp; \ | |
| 1974 | mz_uint code_len, c; \ | |
| 1975 | if (num_bits < 15) { \ | |
| 1976 | if ((pIn_buf_end - pIn_buf_cur) < 2) { \ | |
| 1977 | TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ | |
| 1978 | } else { \ | |
| 1979 | bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | \ | |
| 1980 | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ | |
| 1981 | pIn_buf_cur += 2; \ | |
| 1982 | num_bits += 16; \ | |
| 1983 | } \ | |
| 1984 | } \ | |
| 1985 | if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= \ | |
| 1986 | 0) \ | |
| 1987 | code_len = temp >> 9, temp &= 511; \ | |
| 1988 | else { \ | |
| 1989 | code_len = TINFL_FAST_LOOKUP_BITS; \ | |
| 1990 | do { \ | |
| 1991 | temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ | |
| 1992 | } while (temp < 0); \ | |
| 1993 | } \ | |
| 1994 | sym = temp; \ | |
| 1995 | bit_buf >>= code_len; \ | |
| 1996 | num_bits -= code_len; \ | |
| 1997 | } \ | |
| 1998 | MZ_MACRO_END | |
| 1999 | ||
| 2000 | tinfl_status tinfl_decompress(tinfl_decompressor *r, | |
| 2001 | const mz_uint8 *pIn_buf_next, | |
| 2002 | size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, | |
| 2003 | mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, | |
| 2004 | const mz_uint32 decomp_flags) { | |
| 2005 | static const int s_length_base[31] = { | |
| 2006 | 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, | |
| 2007 | 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; | |
| 2008 | static const int s_length_extra[31] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | |
| 2009 | 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, | |
| 2010 | 4, 4, 5, 5, 5, 5, 0, 0, 0}; | |
| 2011 | static const int s_dist_base[32] = { | |
| 2012 | 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, | |
| 2013 | 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, | |
| 2014 | 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; | |
| 2015 | static const int s_dist_extra[32] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, | |
| 2016 | 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, | |
| 2017 | 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; | |
| 2018 | static const mz_uint8 s_length_dezigzag[19] = { | |
| 2019 | 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; | |
| 2020 | static const int s_min_table_sizes[3] = {257, 1, 4}; | |
| 2021 | ||
| 2022 | tinfl_status status = TINFL_STATUS_FAILED; | |
| 2023 | mz_uint32 num_bits, dist, counter, num_extra; | |
| 2024 | tinfl_bit_buf_t bit_buf; | |
| 2025 | const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = | |
| 2026 | pIn_buf_next + *pIn_buf_size; | |
| 2027 | mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = | |
| 2028 | pOut_buf_next + *pOut_buf_size; | |
| 2029 | size_t out_buf_size_mask = | |
| 2030 | (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) | |
| 2031 | ? (size_t)-1 | |
| 2032 | : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, | |
| 2033 | dist_from_out_buf_start; | |
| 2034 | ||
| 2035 | // Ensure the output buffer's size is a power of 2, unless the output buffer | |
| 2036 | // is large enough to hold the entire output file (in which case it doesn't | |
| 2037 | // matter). | |
| 2038 | if (((out_buf_size_mask + 1) & out_buf_size_mask) || | |
| 2039 | (pOut_buf_next < pOut_buf_start)) { | |
| 2040 | *pIn_buf_size = *pOut_buf_size = 0; | |
| 2041 | return TINFL_STATUS_BAD_PARAM; | |
| 2042 | } | |
| 2043 | ||
| 2044 | num_bits = r->m_num_bits; | |
| 2045 | bit_buf = r->m_bit_buf; | |
| 2046 | dist = r->m_dist; | |
| 2047 | counter = r->m_counter; | |
| 2048 | num_extra = r->m_num_extra; | |
| 2049 | dist_from_out_buf_start = r->m_dist_from_out_buf_start; | |
| 2050 | TINFL_CR_BEGIN | |
| 2051 | ||
| 2052 | bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; | |
| 2053 | r->m_z_adler32 = r->m_check_adler32 = 1; | |
| 2054 | if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { | |
| 2055 | TINFL_GET_BYTE(1, r->m_zhdr0); | |
| 2056 | TINFL_GET_BYTE(2, r->m_zhdr1); | |
| 2057 | counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || | |
| 2058 | (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); | |
| 2059 | if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) | |
| 2060 | counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || | |
| 2061 | ((out_buf_size_mask + 1) < | |
| 2062 | (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); | |
| 2063 | if (counter) { | |
| 2064 | TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); | |
| 2065 | } | |
| 2066 | } | |
| 2067 | ||
| 2068 | do { | |
| 2069 | TINFL_GET_BITS(3, r->m_final, 3); | |
| 2070 | r->m_type = r->m_final >> 1; | |
| 2071 | if (r->m_type == 0) { | |
| 2072 | TINFL_SKIP_BITS(5, num_bits & 7); | |
| 2073 | for (counter = 0; counter < 4; ++counter) { | |
| 2074 | if (num_bits) | |
| 2075 | TINFL_GET_BITS(6, r->m_raw_header[counter], 8); | |
| 2076 | else | |
| 2077 | TINFL_GET_BYTE(7, r->m_raw_header[counter]); | |
| 2078 | } | |
| 2079 | if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != | |
| 2080 | (mz_uint)(0xFFFF ^ | |
| 2081 | (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { | |
| 2082 | TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); | |
| 2083 | } | |
| 2084 | while ((counter) && (num_bits)) { | |
| 2085 | TINFL_GET_BITS(51, dist, 8); | |
| 2086 | while (pOut_buf_cur >= pOut_buf_end) { | |
| 2087 | TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); | |
| 2088 | } | |
| 2089 | *pOut_buf_cur++ = (mz_uint8)dist; | |
| 2090 | counter--; | |
| 2091 | } | |
| 2092 | while (counter) { | |
| 2093 | size_t n; | |
| 2094 | while (pOut_buf_cur >= pOut_buf_end) { | |
| 2095 | TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); | |
| 2096 | } | |
| 2097 | while (pIn_buf_cur >= pIn_buf_end) { | |
| 2098 | if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { | |
| 2099 | TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); | |
| 2100 | } else { | |
| 2101 | TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); | |
| 2102 | } | |
| 2103 | } | |
| 2104 | n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), | |
| 2105 | (size_t)(pIn_buf_end - pIn_buf_cur)), | |
| 2106 | counter); | |
| 2107 | TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); | |
| 2108 | pIn_buf_cur += n; | |
| 2109 | pOut_buf_cur += n; | |
| 2110 | counter -= (mz_uint)n; | |
| 2111 | } | |
| 2112 | } else if (r->m_type == 3) { | |
| 2113 | TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); | |
| 2114 | } else { | |
| 2115 | if (r->m_type == 1) { | |
| 2116 | mz_uint8 *p = r->m_tables[0].m_code_size; | |
| 2117 | mz_uint i; | |
| 2118 | r->m_table_sizes[0] = 288; | |
| 2119 | r->m_table_sizes[1] = 32; | |
| 2120 | TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); | |
| 2121 | for (i = 0; i <= 143; ++i) | |
| 2122 | *p++ = 8; | |
| 2123 | for (; i <= 255; ++i) | |
| 2124 | *p++ = 9; | |
| 2125 | for (; i <= 279; ++i) | |
| 2126 | *p++ = 7; | |
| 2127 | for (; i <= 287; ++i) | |
| 2128 | *p++ = 8; | |
| 2129 | } else { | |
| 2130 | for (counter = 0; counter < 3; counter++) { | |
| 2131 | TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); | |
| 2132 | r->m_table_sizes[counter] += s_min_table_sizes[counter]; | |
| 2133 | } | |
| 2134 | MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); | |
| 2135 | for (counter = 0; counter < r->m_table_sizes[2]; counter++) { | |
| 2136 | mz_uint s; | |
| 2137 | TINFL_GET_BITS(14, s, 3); | |
| 2138 | r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; | |
| 2139 | } | |
| 2140 | r->m_table_sizes[2] = 19; | |
| 2141 | } | |
| 2142 | for (; (int)r->m_type >= 0; r->m_type--) { | |
| 2143 | int tree_next, tree_cur; | |
| 2144 | tinfl_huff_table *pTable; | |
| 2145 | mz_uint i, j, used_syms, total, sym_index, next_code[17], | |
| 2146 | total_syms[16]; | |
| 2147 | pTable = &r->m_tables[r->m_type]; | |
| 2148 | MZ_CLEAR_OBJ(total_syms); | |
| 2149 | MZ_CLEAR_OBJ(pTable->m_look_up); | |
| 2150 | MZ_CLEAR_OBJ(pTable->m_tree); | |
| 2151 | for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) | |
| 2152 | total_syms[pTable->m_code_size[i]]++; | |
| 2153 | used_syms = 0, total = 0; | |
| 2154 | next_code[0] = next_code[1] = 0; | |
| 2155 | for (i = 1; i <= 15; ++i) { | |
| 2156 | used_syms += total_syms[i]; | |
| 2157 | next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); | |
| 2158 | } | |
| 2159 | if ((65536 != total) && (used_syms > 1)) { | |
| 2160 | TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); | |
| 2161 | } | |
| 2162 | for (tree_next = -1, sym_index = 0; | |
| 2163 | sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { | |
| 2164 | mz_uint rev_code = 0, l, cur_code, | |
| 2165 | code_size = pTable->m_code_size[sym_index]; | |
| 2166 | if (!code_size) | |
| 2167 | continue; | |
| 2168 | cur_code = next_code[code_size]++; | |
| 2169 | for (l = code_size; l > 0; l--, cur_code >>= 1) | |
| 2170 | rev_code = (rev_code << 1) | (cur_code & 1); | |
| 2171 | if (code_size <= TINFL_FAST_LOOKUP_BITS) { | |
| 2172 | mz_int16 k = (mz_int16)((code_size << 9) | sym_index); | |
| 2173 | while (rev_code < TINFL_FAST_LOOKUP_SIZE) { | |
| 2174 | pTable->m_look_up[rev_code] = k; | |
| 2175 | rev_code += (1 << code_size); | |
| 2176 | } | |
| 2177 | continue; | |
| 2178 | } | |
| 2179 | if (0 == | |
| 2180 | (tree_cur = pTable->m_look_up[rev_code & | |
| 2181 | (TINFL_FAST_LOOKUP_SIZE - 1)])) { | |
| 2182 | pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = | |
| 2183 | (mz_int16)tree_next; | |
| 2184 | tree_cur = tree_next; | |
| 2185 | tree_next -= 2; | |
| 2186 | } | |
| 2187 | rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); | |
| 2188 | for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { | |
| 2189 | tree_cur -= ((rev_code >>= 1) & 1); | |
| 2190 | if (!pTable->m_tree[-tree_cur - 1]) { | |
| 2191 | pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; | |
| 2192 | tree_cur = tree_next; | |
| 2193 | tree_next -= 2; | |
| 2194 | } else | |
| 2195 | tree_cur = pTable->m_tree[-tree_cur - 1]; | |
| 2196 | } | |
| 2197 | rev_code >>= 1; | |
| 2198 | tree_cur -= (rev_code & 1); | |
| 2199 | pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; | |
| 2200 | } | |
| 2201 | if (r->m_type == 2) { | |
| 2202 | for (counter = 0; | |
| 2203 | counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { | |
| 2204 | mz_uint s; | |
| 2205 | TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); | |
| 2206 | if (dist < 16) { | |
| 2207 | r->m_len_codes[counter++] = (mz_uint8)dist; | |
| 2208 | continue; | |
| 2209 | } | |
| 2210 | if ((dist == 16) && (!counter)) { | |
| 2211 | TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); | |
| 2212 | } | |
| 2213 | num_extra = "\02\03\07"[dist - 16]; | |
| 2214 | TINFL_GET_BITS(18, s, num_extra); | |
| 2215 | s += "\03\03\013"[dist - 16]; | |
| 2216 | TINFL_MEMSET(r->m_len_codes + counter, | |
| 2217 | (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); | |
| 2218 | counter += s; | |
| 2219 | } | |
| 2220 | if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { | |
| 2221 | TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); | |
| 2222 | } | |
| 2223 | TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, | |
| 2224 | r->m_table_sizes[0]); | |
| 2225 | TINFL_MEMCPY(r->m_tables[1].m_code_size, | |
| 2226 | r->m_len_codes + r->m_table_sizes[0], | |
| 2227 | r->m_table_sizes[1]); | |
| 2228 | } | |
| 2229 | } | |
| 2230 | for (;;) { | |
| 2231 | mz_uint8 *pSrc; | |
| 2232 | for (;;) { | |
| 2233 | if (((pIn_buf_end - pIn_buf_cur) < 4) || | |
| 2234 | ((pOut_buf_end - pOut_buf_cur) < 2)) { | |
| 2235 | TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); | |
| 2236 | if (counter >= 256) | |
| 2237 | break; | |
| 2238 | while (pOut_buf_cur >= pOut_buf_end) { | |
| 2239 | TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); | |
| 2240 | } | |
| 2241 | *pOut_buf_cur++ = (mz_uint8)counter; | |
| 2242 | } else { | |
| 2243 | int sym2; | |
| 2244 | mz_uint code_len; | |
| 2245 | #if TINFL_USE_64BIT_BITBUF | |
| 2246 | if (num_bits < 30) { | |
| 2247 | bit_buf |= | |
| 2248 | (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); | |
| 2249 | pIn_buf_cur += 4; | |
| 2250 | num_bits += 32; | |
| 2251 | } | |
| 2252 | #else | |
| 2253 | if (num_bits < 15) { | |
| 2254 | bit_buf |= | |
| 2255 | (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); | |
| 2256 | pIn_buf_cur += 2; | |
| 2257 | num_bits += 16; | |
| 2258 | } | |
| 2259 | #endif | |
| 2260 | if ((sym2 = | |
| 2261 | r->m_tables[0] | |
| 2262 | .m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= | |
| 2263 | 0) | |
| 2264 | code_len = sym2 >> 9; | |
| 2265 | else { | |
| 2266 | code_len = TINFL_FAST_LOOKUP_BITS; | |
| 2267 | do { | |
| 2268 | sym2 = r->m_tables[0] | |
| 2269 | .m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; | |
| 2270 | } while (sym2 < 0); | |
| 2271 | } | |
| 2272 | counter = sym2; | |
| 2273 | bit_buf >>= code_len; | |
| 2274 | num_bits -= code_len; | |
| 2275 | if (counter & 256) | |
| 2276 | break; | |
| 2277 | ||
| 2278 | #if !TINFL_USE_64BIT_BITBUF | |
| 2279 | if (num_bits < 15) { | |
| 2280 | bit_buf |= | |
| 2281 | (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); | |
| 2282 | pIn_buf_cur += 2; | |
| 2283 | num_bits += 16; | |
| 2284 | } | |
| 2285 | #endif | |
| 2286 | if ((sym2 = | |
| 2287 | r->m_tables[0] | |
| 2288 | .m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= | |
| 2289 | 0) | |
| 2290 | code_len = sym2 >> 9; | |
| 2291 | else { | |
| 2292 | code_len = TINFL_FAST_LOOKUP_BITS; | |
| 2293 | do { | |
| 2294 | sym2 = r->m_tables[0] | |
| 2295 | .m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; | |
| 2296 | } while (sym2 < 0); | |
| 2297 | } | |
| 2298 | bit_buf >>= code_len; | |
| 2299 | num_bits -= code_len; | |
| 2300 | ||
| 2301 | pOut_buf_cur[0] = (mz_uint8)counter; | |
| 2302 | if (sym2 & 256) { | |
| 2303 | pOut_buf_cur++; | |
| 2304 | counter = sym2; | |
| 2305 | break; | |
| 2306 | } | |
| 2307 | pOut_buf_cur[1] = (mz_uint8)sym2; | |
| 2308 | pOut_buf_cur += 2; | |
| 2309 | } | |
| 2310 | } | |
| 2311 | if ((counter &= 511) == 256) | |
| 2312 | break; | |
| 2313 | ||
| 2314 | num_extra = s_length_extra[counter - 257]; | |
| 2315 | counter = s_length_base[counter - 257]; | |
| 2316 | if (num_extra) { | |
| 2317 | mz_uint extra_bits; | |
| 2318 | TINFL_GET_BITS(25, extra_bits, num_extra); | |
| 2319 | counter += extra_bits; | |
| 2320 | } | |
| 2321 | ||
| 2322 | TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); | |
| 2323 | num_extra = s_dist_extra[dist]; | |
| 2324 | dist = s_dist_base[dist]; | |
| 2325 | if (num_extra) { | |
| 2326 | mz_uint extra_bits; | |
| 2327 | TINFL_GET_BITS(27, extra_bits, num_extra); | |
| 2328 | dist += extra_bits; | |
| 2329 | } | |
| 2330 | ||
| 2331 | dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; | |
| 2332 | if ((dist > dist_from_out_buf_start) && | |
| 2333 | (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { | |
| 2334 | TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); | |
| 2335 | } | |
| 2336 | ||
| 2337 | pSrc = pOut_buf_start + | |
| 2338 | ((dist_from_out_buf_start - dist) & out_buf_size_mask); | |
| 2339 | ||
| 2340 | if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { | |
| 2341 | while (counter--) { | |
| 2342 | while (pOut_buf_cur >= pOut_buf_end) { | |
| 2343 | TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); | |
| 2344 | } | |
| 2345 | *pOut_buf_cur++ = | |
| 2346 | pOut_buf_start[(dist_from_out_buf_start++ - dist) & | |
| 2347 | out_buf_size_mask]; | |
| 2348 | } | |
| 2349 | continue; | |
| 2350 | } | |
| 2351 | #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES | |
| 2352 | else if ((counter >= 9) && (counter <= dist)) { | |
| 2353 | const mz_uint8 *pSrc_end = pSrc + (counter & ~7); | |
| 2354 | do { | |
| 2355 | ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; | |
| 2356 | ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; | |
| 2357 | pOut_buf_cur += 8; | |
| 2358 | } while ((pSrc += 8) < pSrc_end); | |
| 2359 | if ((counter &= 7) < 3) { | |
| 2360 | if (counter) { | |
| 2361 | pOut_buf_cur[0] = pSrc[0]; | |
| 2362 | if (counter > 1) | |
| 2363 | pOut_buf_cur[1] = pSrc[1]; | |
| 2364 | pOut_buf_cur += counter; | |
| 2365 | } | |
| 2366 | continue; | |
| 2367 | } | |
| 2368 | } | |
| 2369 | #endif | |
| 2370 | do { | |
| 2371 | pOut_buf_cur[0] = pSrc[0]; | |
| 2372 | pOut_buf_cur[1] = pSrc[1]; | |
| 2373 | pOut_buf_cur[2] = pSrc[2]; | |
| 2374 | pOut_buf_cur += 3; | |
| 2375 | pSrc += 3; | |
| 2376 | } while ((int)(counter -= 3) > 2); | |
| 2377 | if ((int)counter > 0) { | |
| 2378 | pOut_buf_cur[0] = pSrc[0]; | |
| 2379 | if ((int)counter > 1) | |
| 2380 | pOut_buf_cur[1] = pSrc[1]; | |
| 2381 | pOut_buf_cur += counter; | |
| 2382 | } | |
| 2383 | } | |
| 2384 | } | |
| 2385 | } while (!(r->m_final & 1)); | |
| 2386 | if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { | |
| 2387 | TINFL_SKIP_BITS(32, num_bits & 7); | |
| 2388 | for (counter = 0; counter < 4; ++counter) { | |
| 2389 | mz_uint s; | |
| 2390 | if (num_bits) | |
| 2391 | TINFL_GET_BITS(41, s, 8); | |
| 2392 | else | |
| 2393 | TINFL_GET_BYTE(42, s); | |
| 2394 | r->m_z_adler32 = (r->m_z_adler32 << 8) | s; | |
| 2395 | } | |
| 2396 | } | |
| 2397 | TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); | |
| 2398 | TINFL_CR_FINISH | |
| 2399 | ||
| 2400 | common_exit: | |
| 2401 | r->m_num_bits = num_bits; | |
| 2402 | r->m_bit_buf = bit_buf; | |
| 2403 | r->m_dist = dist; | |
| 2404 | r->m_counter = counter; | |
| 2405 | r->m_num_extra = num_extra; | |
| 2406 | r->m_dist_from_out_buf_start = dist_from_out_buf_start; | |
| 2407 | *pIn_buf_size = pIn_buf_cur - pIn_buf_next; | |
| 2408 | *pOut_buf_size = pOut_buf_cur - pOut_buf_next; | |
| 2409 | if ((decomp_flags & | |
| 2410 | (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && | |
| 2411 | (status >= 0)) { | |
| 2412 | const mz_uint8 *ptr = pOut_buf_next; | |
| 2413 | size_t buf_len = *pOut_buf_size; | |
| 2414 | mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, | |
| 2415 | s2 = r->m_check_adler32 >> 16; | |
| 2416 | size_t block_len = buf_len % 5552; | |
| 2417 | while (buf_len) { | |
| 2418 | for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { | |
| 2419 | s1 += ptr[0], s2 += s1; | |
| 2420 | s1 += ptr[1], s2 += s1; | |
| 2421 | s1 += ptr[2], s2 += s1; | |
| 2422 | s1 += ptr[3], s2 += s1; | |
| 2423 | s1 += ptr[4], s2 += s1; | |
| 2424 | s1 += ptr[5], s2 += s1; | |
| 2425 | s1 += ptr[6], s2 += s1; | |
| 2426 | s1 += ptr[7], s2 += s1; | |
| 2427 | } | |
| 2428 | for (; i < block_len; ++i) | |
| 2429 | s1 += *ptr++, s2 += s1; | |
| 2430 | s1 %= 65521U, s2 %= 65521U; | |
| 2431 | buf_len -= block_len; | |
| 2432 | block_len = 5552; | |
| 2433 | } | |
| 2434 | r->m_check_adler32 = (s2 << 16) + s1; | |
| 2435 | if ((status == TINFL_STATUS_DONE) && | |
| 2436 | (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && | |
| 2437 | (r->m_check_adler32 != r->m_z_adler32)) | |
| 2438 | status = TINFL_STATUS_ADLER32_MISMATCH; | |
| 2439 | } | |
| 2440 | return status; | |
| 2441 | } | |
| 2442 | ||
| 2443 | // Higher level helper functions. | |
| 2444 | void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, | |
| 2445 | size_t *pOut_len, int flags) { | |
| 2446 | tinfl_decompressor decomp; | |
| 2447 | void *pBuf = NULL, *pNew_buf; | |
| 2448 | size_t src_buf_ofs = 0, out_buf_capacity = 0; | |
| 2449 | *pOut_len = 0; | |
| 2450 | tinfl_init(&decomp); | |
| 2451 | for (;;) { | |
| 2452 | size_t src_buf_size = src_buf_len - src_buf_ofs, | |
| 2453 | dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; | |
| 2454 | tinfl_status status = tinfl_decompress( | |
| 2455 | &decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, | |
| 2456 | (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, | |
| 2457 | &dst_buf_size, | |
| 2458 | (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | | |
| 2459 | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); | |
| 2460 | if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { | |
| 2461 | MZ_FREE(pBuf); | |
| 2462 | *pOut_len = 0; | |
| 2463 | return NULL; | |
| 2464 | } | |
| 2465 | src_buf_ofs += src_buf_size; | |
| 2466 | *pOut_len += dst_buf_size; | |
| 2467 | if (status == TINFL_STATUS_DONE) | |
| 2468 | break; | |
| 2469 | new_out_buf_capacity = out_buf_capacity * 2; | |
| 2470 | if (new_out_buf_capacity < 128) | |
| 2471 | new_out_buf_capacity = 128; | |
| 2472 | pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); | |
| 2473 | if (!pNew_buf) { | |
| 2474 | MZ_FREE(pBuf); | |
| 2475 | *pOut_len = 0; | |
| 2476 | return NULL; | |
| 2477 | } | |
| 2478 | pBuf = pNew_buf; | |
| 2479 | out_buf_capacity = new_out_buf_capacity; | |
| 2480 | } | |
| 2481 | return pBuf; | |
| 2482 | } | |
| 2483 | ||
| 2484 | size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, | |
| 2485 | const void *pSrc_buf, size_t src_buf_len, | |
| 2486 | int flags) { | |
| 2487 | tinfl_decompressor decomp; | |
| 2488 | tinfl_status status; | |
| 2489 | tinfl_init(&decomp); | |
| 2490 | status = | |
| 2491 | tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, | |
| 2492 | (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, | |
| 2493 | (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | | |
| 2494 | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); | |
| 2495 | return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED | |
| 2496 | : out_buf_len; | |
| 2497 | } | |
| 2498 | ||
| 2499 | int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, | |
| 2500 | tinfl_put_buf_func_ptr pPut_buf_func, | |
| 2501 | void *pPut_buf_user, int flags) { | |
| 2502 | int result = 0; | |
| 2503 | tinfl_decompressor decomp; | |
| 2504 | mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); | |
| 2505 | size_t in_buf_ofs = 0, dict_ofs = 0; | |
| 2506 | if (!pDict) | |
| 2507 | return TINFL_STATUS_FAILED; | |
| 2508 | tinfl_init(&decomp); | |
| 2509 | for (;;) { | |
| 2510 | size_t in_buf_size = *pIn_buf_size - in_buf_ofs, | |
| 2511 | dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; | |
| 2512 | tinfl_status status = | |
| 2513 | tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, | |
| 2514 | &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, | |
| 2515 | (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | | |
| 2516 | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); | |
| 2517 | in_buf_ofs += in_buf_size; | |
| 2518 | if ((dst_buf_size) && | |
| 2519 | (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) | |
| 2520 | break; | |
| 2521 | if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { | |
| 2522 | result = (status == TINFL_STATUS_DONE); | |
| 2523 | break; | |
| 2524 | } | |
| 2525 | dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); | |
| 2526 | } | |
| 2527 | MZ_FREE(pDict); | |
| 2528 | *pIn_buf_size = in_buf_ofs; | |
| 2529 | return result; | |
| 2530 | } | |
| 2531 | ||
| 2532 | // ------------------- Low-level Compression (independent from all decompression | |
| 2533 | // API's) | |
| 2534 | ||
| 2535 | // Purposely making these tables static for faster init and thread safety. | |
| 2536 | static const mz_uint16 s_tdefl_len_sym[256] = { | |
| 2537 | 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, | |
| 2538 | 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, | |
| 2539 | 272, 272, 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, | |
| 2540 | 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, | |
| 2541 | 276, 276, 276, 276, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, | |
| 2542 | 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, | |
| 2543 | 278, 278, 278, 278, 278, 278, 279, 279, 279, 279, 279, 279, 279, 279, 279, | |
| 2544 | 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, | |
| 2545 | 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, 281, 281, 281, | |
| 2546 | 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, | |
| 2547 | 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 282, 282, 282, 282, 282, | |
| 2548 | 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, | |
| 2549 | 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 283, 283, 283, | |
| 2550 | 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, | |
| 2551 | 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, | |
| 2552 | 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, | |
| 2553 | 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, | |
| 2554 | 285}; | |
| 2555 | ||
| 2556 | static const mz_uint8 s_tdefl_len_extra[256] = { | |
| 2557 | 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, | |
| 2558 | 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, | |
| 2559 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, | |
| 2560 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, | |
| 2561 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, | |
| 2562 | 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | |
| 2563 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | |
| 2564 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | |
| 2565 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | |
| 2566 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | |
| 2567 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0}; | |
| 2568 | ||
| 2569 | static const mz_uint8 s_tdefl_small_dist_sym[512] = { | |
| 2570 | 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, | |
| 2571 | 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, | |
| 2572 | 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, | |
| 2573 | 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, | |
| 2574 | 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, | |
| 2575 | 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, | |
| 2576 | 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, | |
| 2577 | 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, | |
| 2578 | 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, | |
| 2579 | 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, | |
| 2580 | 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, | |
| 2581 | 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, | |
| 2582 | 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, | |
| 2583 | 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, | |
| 2584 | 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, | |
| 2585 | 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, | |
| 2586 | 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, | |
| 2587 | 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, | |
| 2588 | 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, | |
| 2589 | 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, | |
| 2590 | 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, | |
| 2591 | 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, | |
| 2592 | 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, | |
| 2593 | 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, | |
| 2594 | 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, | |
| 2595 | 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, | |
| 2596 | 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17}; | |
| 2597 | ||
| 2598 | static const mz_uint8 s_tdefl_small_dist_extra[512] = { | |
| 2599 | 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, | |
| 2600 | 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, | |
| 2601 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | |
| 2602 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | |
| 2603 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | |
| 2604 | 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | |
| 2605 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | |
| 2606 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | |
| 2607 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | |
| 2608 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | |
| 2609 | 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
| 2610 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
| 2611 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
| 2612 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
| 2613 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
| 2614 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
| 2615 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
| 2616 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
| 2617 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
| 2618 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | |
| 2619 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; | |
| 2620 | ||
| 2621 | static const mz_uint8 s_tdefl_large_dist_sym[128] = { | |
| 2622 | 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, | |
| 2623 | 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, | |
| 2624 | 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, | |
| 2625 | 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, | |
| 2626 | 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, | |
| 2627 | 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, | |
| 2628 | 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29}; | |
| 2629 | ||
| 2630 | static const mz_uint8 s_tdefl_large_dist_extra[128] = { | |
| 2631 | 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, | |
| 2632 | 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, | |
| 2633 | 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, | |
| 2634 | 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, | |
| 2635 | 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, | |
| 2636 | 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, | |
| 2637 | 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; | |
| 2638 | ||
| 2639 | // Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted | |
| 2640 | // values. | |
| 2641 | typedef struct { | |
| 2642 | mz_uint16 m_key, m_sym_index; | |
| 2643 | } tdefl_sym_freq; | |
| 2644 | static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, | |
| 2645 | tdefl_sym_freq *pSyms0, | |
| 2646 | tdefl_sym_freq *pSyms1) { | |
| 2647 | mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; | |
| 2648 | tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; | |
| 2649 | MZ_CLEAR_OBJ(hist); | |
| 2650 | for (i = 0; i < num_syms; i++) { | |
| 2651 | mz_uint freq = pSyms0[i].m_key; | |
| 2652 | hist[freq & 0xFF]++; | |
| 2653 | hist[256 + ((freq >> 8) & 0xFF)]++; | |
| 2654 | } | |
| 2655 | while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) | |
| 2656 | total_passes--; | |
| 2657 | for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { | |
| 2658 | const mz_uint32 *pHist = &hist[pass << 8]; | |
| 2659 | mz_uint offsets[256], cur_ofs = 0; | |
| 2660 | for (i = 0; i < 256; i++) { | |
| 2661 | offsets[i] = cur_ofs; | |
| 2662 | cur_ofs += pHist[i]; | |
| 2663 | } | |
| 2664 | for (i = 0; i < num_syms; i++) | |
| 2665 | pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = | |
| 2666 | pCur_syms[i]; | |
| 2667 | { | |
| 2668 | tdefl_sym_freq *t = pCur_syms; | |
| 2669 | pCur_syms = pNew_syms; | |
| 2670 | pNew_syms = t; | |
| 2671 | } | |
| 2672 | } | |
| 2673 | return pCur_syms; | |
| 2674 | } | |
| 2675 | ||
| 2676 | // tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, | |
| 2677 | // alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. | |
| 2678 | static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { | |
| 2679 | int root, leaf, next, avbl, used, dpth; | |
| 2680 | if (n == 0) | |
| 2681 | return; | |
| 2682 | else if (n == 1) { | |
| 2683 | A[0].m_key = 1; | |
| 2684 | return; | |
| 2685 | } | |
| 2686 | A[0].m_key += A[1].m_key; | |
| 2687 | root = 0; | |
| 2688 | leaf = 2; | |
| 2689 | for (next = 1; next < n - 1; next++) { | |
| 2690 | if (leaf >= n || A[root].m_key < A[leaf].m_key) { | |
| 2691 | A[next].m_key = A[root].m_key; | |
| 2692 | A[root++].m_key = (mz_uint16)next; | |
| 2693 | } else | |
| 2694 | A[next].m_key = A[leaf++].m_key; | |
| 2695 | if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { | |
| 2696 | A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); | |
| 2697 | A[root++].m_key = (mz_uint16)next; | |
| 2698 | } else | |
| 2699 | A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); | |
| 2700 | } | |
| 2701 | A[n - 2].m_key = 0; | |
| 2702 | for (next = n - 3; next >= 0; next--) | |
| 2703 | A[next].m_key = A[A[next].m_key].m_key + 1; | |
| 2704 | avbl = 1; | |
| 2705 | used = dpth = 0; | |
| 2706 | root = n - 2; | |
| 2707 | next = n - 1; | |
| 2708 | while (avbl > 0) { | |
| 2709 | while (root >= 0 && (int)A[root].m_key == dpth) { | |
| 2710 | used++; | |
| 2711 | root--; | |
| 2712 | } | |
| 2713 | while (avbl > used) { | |
| 2714 | A[next--].m_key = (mz_uint16)(dpth); | |
| 2715 | avbl--; | |
| 2716 | } | |
| 2717 | avbl = 2 * used; | |
| 2718 | dpth++; | |
| 2719 | used = 0; | |
| 2720 | } | |
| 2721 | } | |
| 2722 | ||
| 2723 | // Limits canonical Huffman code table's max code size. | |
| 2724 | enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; | |
| 2725 | static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, | |
| 2726 | int code_list_len, | |
| 2727 | int max_code_size) { | |
| 2728 | int i; | |
| 2729 | mz_uint32 total = 0; | |
| 2730 | if (code_list_len <= 1) | |
| 2731 | return; | |
| 2732 | for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) | |
| 2733 | pNum_codes[max_code_size] += pNum_codes[i]; | |
| 2734 | for (i = max_code_size; i > 0; i--) | |
| 2735 | total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); | |
| 2736 | while (total != (1UL << max_code_size)) { | |
| 2737 | pNum_codes[max_code_size]--; | |
| 2738 | for (i = max_code_size - 1; i > 0; i--) | |
| 2739 | if (pNum_codes[i]) { | |
| 2740 | pNum_codes[i]--; | |
| 2741 | pNum_codes[i + 1] += 2; | |
| 2742 | break; | |
| 2743 | } | |
| 2744 | total--; | |
| 2745 | } | |
| 2746 | } | |
| 2747 | ||
| 2748 | static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, | |
| 2749 | int table_len, int code_size_limit, | |
| 2750 | int static_table) { | |
| 2751 | int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; | |
| 2752 | mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; | |
| 2753 | MZ_CLEAR_OBJ(num_codes); | |
| 2754 | if (static_table) { | |
| 2755 | for (i = 0; i < table_len; i++) | |
| 2756 | num_codes[d->m_huff_code_sizes[table_num][i]]++; | |
| 2757 | } else { | |
| 2758 | tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], | |
| 2759 | *pSyms; | |
| 2760 | int num_used_syms = 0; | |
| 2761 | const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; | |
| 2762 | for (i = 0; i < table_len; i++) | |
| 2763 | if (pSym_count[i]) { | |
| 2764 | syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; | |
| 2765 | syms0[num_used_syms++].m_sym_index = (mz_uint16)i; | |
| 2766 | } | |
| 2767 | ||
| 2768 | pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); | |
| 2769 | tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); | |
| 2770 | ||
| 2771 | for (i = 0; i < num_used_syms; i++) | |
| 2772 | num_codes[pSyms[i].m_key]++; | |
| 2773 | ||
| 2774 | tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, | |
| 2775 | code_size_limit); | |
| 2776 | ||
| 2777 | MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); | |
| 2778 | MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); | |
| 2779 | for (i = 1, j = num_used_syms; i <= code_size_limit; i++) | |
| 2780 | for (l = num_codes[i]; l > 0; l--) | |
| 2781 | d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); | |
| 2782 | } | |
| 2783 | ||
| 2784 | next_code[1] = 0; | |
| 2785 | for (j = 0, i = 2; i <= code_size_limit; i++) | |
| 2786 | next_code[i] = j = ((j + num_codes[i - 1]) << 1); | |
| 2787 | ||
| 2788 | for (i = 0; i < table_len; i++) { | |
| 2789 | mz_uint rev_code = 0, code, code_size; | |
| 2790 | if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) | |
| 2791 | continue; | |
| 2792 | code = next_code[code_size]++; | |
| 2793 | for (l = code_size; l > 0; l--, code >>= 1) | |
| 2794 | rev_code = (rev_code << 1) | (code & 1); | |
| 2795 | d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; | |
| 2796 | } | |
| 2797 | } | |
| 2798 | ||
| 2799 | #define TDEFL_PUT_BITS(b, l) \ | |
| 2800 | do { \ | |
| 2801 | mz_uint bits = b; \ | |
| 2802 | mz_uint len = l; \ | |
| 2803 | MZ_ASSERT(bits <= ((1U << len) - 1U)); \ | |
| 2804 | d->m_bit_buffer |= (bits << d->m_bits_in); \ | |
| 2805 | d->m_bits_in += len; \ | |
| 2806 | while (d->m_bits_in >= 8) { \ | |
| 2807 | if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ | |
| 2808 | *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ | |
| 2809 | d->m_bit_buffer >>= 8; \ | |
| 2810 | d->m_bits_in -= 8; \ | |
| 2811 | } \ | |
| 2812 | } \ | |
| 2813 | MZ_MACRO_END | |
| 2814 | ||
| 2815 | #define TDEFL_RLE_PREV_CODE_SIZE() \ | |
| 2816 | { \ | |
| 2817 | if (rle_repeat_count) { \ | |
| 2818 | if (rle_repeat_count < 3) { \ | |
| 2819 | d->m_huff_count[2][prev_code_size] = (mz_uint16)( \ | |
| 2820 | d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ | |
| 2821 | while (rle_repeat_count--) \ | |
| 2822 | packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ | |
| 2823 | } else { \ | |
| 2824 | d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ | |
| 2825 | packed_code_sizes[num_packed_code_sizes++] = 16; \ | |
| 2826 | packed_code_sizes[num_packed_code_sizes++] = \ | |
| 2827 | (mz_uint8)(rle_repeat_count - 3); \ | |
| 2828 | } \ | |
| 2829 | rle_repeat_count = 0; \ | |
| 2830 | } \ | |
| 2831 | } | |
| 2832 | ||
| 2833 | #define TDEFL_RLE_ZERO_CODE_SIZE() \ | |
| 2834 | { \ | |
| 2835 | if (rle_z_count) { \ | |
| 2836 | if (rle_z_count < 3) { \ | |
| 2837 | d->m_huff_count[2][0] = \ | |
| 2838 | (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ | |
| 2839 | while (rle_z_count--) \ | |
| 2840 | packed_code_sizes[num_packed_code_sizes++] = 0; \ | |
| 2841 | } else if (rle_z_count <= 10) { \ | |
| 2842 | d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ | |
| 2843 | packed_code_sizes[num_packed_code_sizes++] = 17; \ | |
| 2844 | packed_code_sizes[num_packed_code_sizes++] = \ | |
| 2845 | (mz_uint8)(rle_z_count - 3); \ | |
| 2846 | } else { \ | |
| 2847 | d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ | |
| 2848 | packed_code_sizes[num_packed_code_sizes++] = 18; \ | |
| 2849 | packed_code_sizes[num_packed_code_sizes++] = \ | |
| 2850 | (mz_uint8)(rle_z_count - 11); \ | |
| 2851 | } \ | |
| 2852 | rle_z_count = 0; \ | |
| 2853 | } \ | |
| 2854 | } | |
| 2855 | ||
| 2856 | static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { | |
| 2857 | 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; | |
| 2858 | ||
| 2859 | static void tdefl_start_dynamic_block(tdefl_compressor *d) { | |
| 2860 | int num_lit_codes, num_dist_codes, num_bit_lengths; | |
| 2861 | mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, | |
| 2862 | rle_repeat_count, packed_code_sizes_index; | |
| 2863 | mz_uint8 | |
| 2864 | code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], | |
| 2865 | packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], | |
| 2866 | prev_code_size = 0xFF; | |
| 2867 | ||
| 2868 | d->m_huff_count[0][256] = 1; | |
| 2869 | ||
| 2870 | tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); | |
| 2871 | tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); | |
| 2872 | ||
| 2873 | for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) | |
| 2874 | if (d->m_huff_code_sizes[0][num_lit_codes - 1]) | |
| 2875 | break; | |
| 2876 | for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) | |
| 2877 | if (d->m_huff_code_sizes[1][num_dist_codes - 1]) | |
| 2878 | break; | |
| 2879 | ||
| 2880 | memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], | |
| 2881 | sizeof(mz_uint8) * num_lit_codes); | |
| 2882 | memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], | |
| 2883 | sizeof(mz_uint8) * num_dist_codes); | |
| 2884 | total_code_sizes_to_pack = num_lit_codes + num_dist_codes; | |
| 2885 | num_packed_code_sizes = 0; | |
| 2886 | rle_z_count = 0; | |
| 2887 | rle_repeat_count = 0; | |
| 2888 | ||
| 2889 | memset(&d->m_huff_count[2][0], 0, | |
| 2890 | sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); | |
| 2891 | for (i = 0; i < total_code_sizes_to_pack; i++) { | |
| 2892 | mz_uint8 code_size = code_sizes_to_pack[i]; | |
| 2893 | if (!code_size) { | |
| 2894 | TDEFL_RLE_PREV_CODE_SIZE(); | |
| 2895 | if (++rle_z_count == 138) { | |
| 2896 | TDEFL_RLE_ZERO_CODE_SIZE(); | |
| 2897 | } | |
| 2898 | } else { | |
| 2899 | TDEFL_RLE_ZERO_CODE_SIZE(); | |
| 2900 | if (code_size != prev_code_size) { | |
| 2901 | TDEFL_RLE_PREV_CODE_SIZE(); | |
| 2902 | d->m_huff_count[2][code_size] = | |
| 2903 | (mz_uint16)(d->m_huff_count[2][code_size] + 1); | |
| 2904 | packed_code_sizes[num_packed_code_sizes++] = code_size; | |
| 2905 | } else if (++rle_repeat_count == 6) { | |
| 2906 | TDEFL_RLE_PREV_CODE_SIZE(); | |
| 2907 | } | |
| 2908 | } | |
| 2909 | prev_code_size = code_size; | |
| 2910 | } | |
| 2911 | if (rle_repeat_count) { | |
| 2912 | TDEFL_RLE_PREV_CODE_SIZE(); | |
| 2913 | } else { | |
| 2914 | TDEFL_RLE_ZERO_CODE_SIZE(); | |
| 2915 | } | |
| 2916 | ||
| 2917 | tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); | |
| 2918 | ||
| 2919 | TDEFL_PUT_BITS(2, 2); | |
| 2920 | ||
| 2921 | TDEFL_PUT_BITS(num_lit_codes - 257, 5); | |
| 2922 | TDEFL_PUT_BITS(num_dist_codes - 1, 5); | |
| 2923 | ||
| 2924 | for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) | |
| 2925 | if (d->m_huff_code_sizes | |
| 2926 | [2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) | |
| 2927 | break; | |
| 2928 | num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); | |
| 2929 | TDEFL_PUT_BITS(num_bit_lengths - 4, 4); | |
| 2930 | for (i = 0; (int)i < num_bit_lengths; i++) | |
| 2931 | TDEFL_PUT_BITS( | |
| 2932 | d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); | |
| 2933 | ||
| 2934 | for (packed_code_sizes_index = 0; | |
| 2935 | packed_code_sizes_index < num_packed_code_sizes;) { | |
| 2936 | mz_uint code = packed_code_sizes[packed_code_sizes_index++]; | |
| 2937 | MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); | |
| 2938 | TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); | |
| 2939 | if (code >= 16) | |
| 2940 | TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], | |
| 2941 | "\02\03\07"[code - 16]); | |
| 2942 | } | |
| 2943 | } | |
| 2944 | ||
| 2945 | static void tdefl_start_static_block(tdefl_compressor *d) { | |
| 2946 | mz_uint i; | |
| 2947 | mz_uint8 *p = &d->m_huff_code_sizes[0][0]; | |
| 2948 | ||
| 2949 | for (i = 0; i <= 143; ++i) | |
| 2950 | *p++ = 8; | |
| 2951 | for (; i <= 255; ++i) | |
| 2952 | *p++ = 9; | |
| 2953 | for (; i <= 279; ++i) | |
| 2954 | *p++ = 7; | |
| 2955 | for (; i <= 287; ++i) | |
| 2956 | *p++ = 8; | |
| 2957 | ||
| 2958 | memset(d->m_huff_code_sizes[1], 5, 32); | |
| 2959 | ||
| 2960 | tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); | |
| 2961 | tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); | |
| 2962 | ||
| 2963 | TDEFL_PUT_BITS(1, 2); | |
| 2964 | } | |
| 2965 | ||
| 2966 | static const mz_uint mz_bitmasks[17] = { | |
| 2967 | 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, | |
| 2968 | 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; | |
| 2969 | ||
| 2970 | #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && \ | |
| 2971 | MINIZ_HAS_64BIT_REGISTERS | |
| 2972 | static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { | |
| 2973 | mz_uint flags; | |
| 2974 | mz_uint8 *pLZ_codes; | |
| 2975 | mz_uint8 *pOutput_buf = d->m_pOutput_buf; | |
| 2976 | mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; | |
| 2977 | mz_uint64 bit_buffer = d->m_bit_buffer; | |
| 2978 | mz_uint bits_in = d->m_bits_in; | |
| 2979 | ||
| 2980 | #define TDEFL_PUT_BITS_FAST(b, l) \ | |
| 2981 | { \ | |
| 2982 | bit_buffer |= (((mz_uint64)(b)) << bits_in); \ | |
| 2983 | bits_in += (l); \ | |
| 2984 | } | |
| 2985 | ||
| 2986 | flags = 1; | |
| 2987 | for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; | |
| 2988 | flags >>= 1) { | |
| 2989 | if (flags == 1) | |
| 2990 | flags = *pLZ_codes++ | 0x100; | |
| 2991 | ||
| 2992 | if (flags & 1) { | |
| 2993 | mz_uint s0, s1, n0, n1, sym, num_extra_bits; | |
| 2994 | mz_uint match_len = pLZ_codes[0], | |
| 2995 | match_dist = *(const mz_uint16 *)(pLZ_codes + 1); | |
| 2996 | pLZ_codes += 3; | |
| 2997 | ||
| 2998 | MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); | |
| 2999 | TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], | |
| 3000 | d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); | |
| 3001 | TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], | |
| 3002 | s_tdefl_len_extra[match_len]); | |
| 3003 | ||
| 3004 | // This sequence coaxes MSVC into using cmov's vs. jmp's. | |
| 3005 | s0 = s_tdefl_small_dist_sym[match_dist & 511]; | |
| 3006 | n0 = s_tdefl_small_dist_extra[match_dist & 511]; | |
| 3007 | s1 = s_tdefl_large_dist_sym[match_dist >> 8]; | |
| 3008 | n1 = s_tdefl_large_dist_extra[match_dist >> 8]; | |
| 3009 | sym = (match_dist < 512) ? s0 : s1; | |
| 3010 | num_extra_bits = (match_dist < 512) ? n0 : n1; | |
| 3011 | ||
| 3012 | MZ_ASSERT(d->m_huff_code_sizes[1][sym]); | |
| 3013 | TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], | |
| 3014 | d->m_huff_code_sizes[1][sym]); | |
| 3015 | TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], | |
| 3016 | num_extra_bits); | |
| 3017 | } else { | |
| 3018 | mz_uint lit = *pLZ_codes++; | |
| 3019 | MZ_ASSERT(d->m_huff_code_sizes[0][lit]); | |
| 3020 | TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], | |
| 3021 | d->m_huff_code_sizes[0][lit]); | |
| 3022 | ||
| 3023 | if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { | |
| 3024 | flags >>= 1; | |
| 3025 | lit = *pLZ_codes++; | |
| 3026 | MZ_ASSERT(d->m_huff_code_sizes[0][lit]); | |
| 3027 | TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], | |
| 3028 | d->m_huff_code_sizes[0][lit]); | |
| 3029 | ||
| 3030 | if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { | |
| 3031 | flags >>= 1; | |
| 3032 | lit = *pLZ_codes++; | |
| 3033 | MZ_ASSERT(d->m_huff_code_sizes[0][lit]); | |
| 3034 | TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], | |
| 3035 | d->m_huff_code_sizes[0][lit]); | |
| 3036 | } | |
| 3037 | } | |
| 3038 | } | |
| 3039 | ||
| 3040 | if (pOutput_buf >= d->m_pOutput_buf_end) | |
| 3041 | return MZ_FALSE; | |
| 3042 | ||
| 3043 | *(mz_uint64 *)pOutput_buf = bit_buffer; | |
| 3044 | pOutput_buf += (bits_in >> 3); | |
| 3045 | bit_buffer >>= (bits_in & ~7); | |
| 3046 | bits_in &= 7; | |
| 3047 | } | |
| 3048 | ||
| 3049 | #undef TDEFL_PUT_BITS_FAST | |
| 3050 | ||
| 3051 | d->m_pOutput_buf = pOutput_buf; | |
| 3052 | d->m_bits_in = 0; | |
| 3053 | d->m_bit_buffer = 0; | |
| 3054 | ||
| 3055 | while (bits_in) { | |
| 3056 | mz_uint32 n = MZ_MIN(bits_in, 16); | |
| 3057 | TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); | |
| 3058 | bit_buffer >>= n; | |
| 3059 | bits_in -= n; | |
| 3060 | } | |
| 3061 | ||
| 3062 | TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); | |
| 3063 | ||
| 3064 | return (d->m_pOutput_buf < d->m_pOutput_buf_end); | |
| 3065 | } | |
| 3066 | #else | |
| 3067 | static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { | |
| 3068 | mz_uint flags; | |
| 3069 | mz_uint8 *pLZ_codes; | |
| 3070 | ||
| 3071 | flags = 1; | |
| 3072 | for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; | |
| 3073 | flags >>= 1) { | |
| 3074 | if (flags == 1) | |
| 3075 | flags = *pLZ_codes++ | 0x100; | |
| 3076 | if (flags & 1) { | |
| 3077 | mz_uint sym, num_extra_bits; | |
| 3078 | mz_uint match_len = pLZ_codes[0], | |
| 3079 | match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); | |
| 3080 | pLZ_codes += 3; | |
| 3081 | ||
| 3082 | MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); | |
| 3083 | TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], | |
| 3084 | d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); | |
| 3085 | TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], | |
| 3086 | s_tdefl_len_extra[match_len]); | |
| 3087 | ||
| 3088 | if (match_dist < 512) { | |
| 3089 | sym = s_tdefl_small_dist_sym[match_dist]; | |
| 3090 | num_extra_bits = s_tdefl_small_dist_extra[match_dist]; | |
| 3091 | } else { | |
| 3092 | sym = s_tdefl_large_dist_sym[match_dist >> 8]; | |
| 3093 | num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; | |
| 3094 | } | |
| 3095 | TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); | |
| 3096 | TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); | |
| 3097 | } else { | |
| 3098 | mz_uint lit = *pLZ_codes++; | |
| 3099 | MZ_ASSERT(d->m_huff_code_sizes[0][lit]); | |
| 3100 | TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); | |
| 3101 | } | |
| 3102 | } | |
| 3103 | ||
| 3104 | TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); | |
| 3105 | ||
| 3106 | return (d->m_pOutput_buf < d->m_pOutput_buf_end); | |
| 3107 | } | |
| 3108 | #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && | |
| 3109 | // MINIZ_HAS_64BIT_REGISTERS | |
| 3110 | ||
| 3111 | static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { | |
| 3112 | if (static_block) | |
| 3113 | tdefl_start_static_block(d); | |
| 3114 | else | |
| 3115 | tdefl_start_dynamic_block(d); | |
| 3116 | return tdefl_compress_lz_codes(d); | |
| 3117 | } | |
| 3118 | ||
| 3119 | static int tdefl_flush_block(tdefl_compressor *d, int flush) { | |
| 3120 | mz_uint saved_bit_buf, saved_bits_in; | |
| 3121 | mz_uint8 *pSaved_output_buf; | |
| 3122 | mz_bool comp_block_succeeded = MZ_FALSE; | |
| 3123 | int n, use_raw_block = | |
| 3124 | ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && | |
| 3125 | (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; | |
| 3126 | mz_uint8 *pOutput_buf_start = | |
| 3127 | ((d->m_pPut_buf_func == NULL) && | |
| 3128 | ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) | |
| 3129 | ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) | |
| 3130 | : d->m_output_buf; | |
| 3131 | ||
| 3132 | d->m_pOutput_buf = pOutput_buf_start; | |
| 3133 | d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; | |
| 3134 | ||
| 3135 | MZ_ASSERT(!d->m_output_flush_remaining); | |
| 3136 | d->m_output_flush_ofs = 0; | |
| 3137 | d->m_output_flush_remaining = 0; | |
| 3138 | ||
| 3139 | *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); | |
| 3140 | d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); | |
| 3141 | ||
| 3142 | if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { | |
| 3143 | TDEFL_PUT_BITS(0x78, 8); | |
| 3144 | TDEFL_PUT_BITS(0x01, 8); | |
| 3145 | } | |
| 3146 | ||
| 3147 | TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); | |
| 3148 | ||
| 3149 | pSaved_output_buf = d->m_pOutput_buf; | |
| 3150 | saved_bit_buf = d->m_bit_buffer; | |
| 3151 | saved_bits_in = d->m_bits_in; | |
| 3152 | ||
| 3153 | if (!use_raw_block) | |
| 3154 | comp_block_succeeded = | |
| 3155 | tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || | |
| 3156 | (d->m_total_lz_bytes < 48)); | |
| 3157 | ||
| 3158 | // If the block gets expanded, forget the current contents of the output | |
| 3159 | // buffer and send a raw block instead. | |
| 3160 | if (((use_raw_block) || | |
| 3161 | ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= | |
| 3162 | d->m_total_lz_bytes))) && | |
| 3163 | ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) { | |
| 3164 | mz_uint i; | |
| 3165 | d->m_pOutput_buf = pSaved_output_buf; | |
| 3166 | d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; | |
| 3167 | TDEFL_PUT_BITS(0, 2); | |
| 3168 | if (d->m_bits_in) { | |
| 3169 | TDEFL_PUT_BITS(0, 8 - d->m_bits_in); | |
| 3170 | } | |
| 3171 | for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { | |
| 3172 | TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); | |
| 3173 | } | |
| 3174 | for (i = 0; i < d->m_total_lz_bytes; ++i) { | |
| 3175 | TDEFL_PUT_BITS( | |
| 3176 | d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], | |
| 3177 | 8); | |
| 3178 | } | |
| 3179 | } | |
| 3180 | // Check for the extremely unlikely (if not impossible) case of the compressed | |
| 3181 | // block not fitting into the output buffer when using dynamic codes. | |
| 3182 | else if (!comp_block_succeeded) { | |
| 3183 | d->m_pOutput_buf = pSaved_output_buf; | |
| 3184 | d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; | |
| 3185 | tdefl_compress_block(d, MZ_TRUE); | |
| 3186 | } | |
| 3187 | ||
| 3188 | if (flush) { | |
| 3189 | if (flush == TDEFL_FINISH) { | |
| 3190 | if (d->m_bits_in) { | |
| 3191 | TDEFL_PUT_BITS(0, 8 - d->m_bits_in); | |
| 3192 | } | |
| 3193 | if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { | |
| 3194 | mz_uint i, a = d->m_adler32; | |
| 3195 | for (i = 0; i < 4; i++) { | |
| 3196 | TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); | |
| 3197 | a <<= 8; | |
| 3198 | } | |
| 3199 | } | |
| 3200 | } else { | |
| 3201 | mz_uint i, z = 0; | |
| 3202 | TDEFL_PUT_BITS(0, 3); | |
| 3203 | if (d->m_bits_in) { | |
| 3204 | TDEFL_PUT_BITS(0, 8 - d->m_bits_in); | |
| 3205 | } | |
| 3206 | for (i = 2; i; --i, z ^= 0xFFFF) { | |
| 3207 | TDEFL_PUT_BITS(z & 0xFFFF, 16); | |
| 3208 | } | |
| 3209 | } | |
| 3210 | } | |
| 3211 | ||
| 3212 | MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); | |
| 3213 | ||
| 3214 | memset(&d->m_huff_count[0][0], 0, | |
| 3215 | sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); | |
| 3216 | memset(&d->m_huff_count[1][0], 0, | |
| 3217 | sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); | |
| 3218 | ||
| 3219 | d->m_pLZ_code_buf = d->m_lz_code_buf + 1; | |
| 3220 | d->m_pLZ_flags = d->m_lz_code_buf; | |
| 3221 | d->m_num_flags_left = 8; | |
| 3222 | d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; | |
| 3223 | d->m_total_lz_bytes = 0; | |
| 3224 | d->m_block_index++; | |
| 3225 | ||
| 3226 | if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { | |
| 3227 | if (d->m_pPut_buf_func) { | |
| 3228 | *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; | |
| 3229 | if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) | |
| 3230 | return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); | |
| 3231 | } else if (pOutput_buf_start == d->m_output_buf) { | |
| 3232 | int bytes_to_copy = (int)MZ_MIN( | |
| 3233 | (size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); | |
| 3234 | memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, | |
| 3235 | bytes_to_copy); | |
| 3236 | d->m_out_buf_ofs += bytes_to_copy; | |
| 3237 | if ((n -= bytes_to_copy) != 0) { | |
| 3238 | d->m_output_flush_ofs = bytes_to_copy; | |
| 3239 | d->m_output_flush_remaining = n; | |
| 3240 | } | |
| 3241 | } else { | |
| 3242 | d->m_out_buf_ofs += n; | |
| 3243 | } | |
| 3244 | } | |
| 3245 | ||
| 3246 | return d->m_output_flush_remaining; | |
| 3247 | } | |
| 3248 | ||
| 3249 | #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES | |
| 3250 | #define TDEFL_READ_UNALIGNED_WORD(p) ((p)[0] | (p)[1] << 8) | |
| 3251 | static MZ_FORCEINLINE void | |
| 3252 | tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, | |
| 3253 | mz_uint max_match_len, mz_uint *pMatch_dist, | |
| 3254 | mz_uint *pMatch_len) { | |
| 3255 | mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, | |
| 3256 | match_len = *pMatch_len, probe_pos = pos, next_probe_pos, | |
| 3257 | probe_len; | |
| 3258 | mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; | |
| 3259 | const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; | |
| 3260 | mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), | |
| 3261 | s01 = *s; | |
| 3262 | MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); | |
| 3263 | if (max_match_len <= match_len) | |
| 3264 | return; | |
| 3265 | for (;;) { | |
| 3266 | for (;;) { | |
| 3267 | if (--num_probes_left == 0) | |
| 3268 | return; | |
| 3269 | #define TDEFL_PROBE \ | |
| 3270 | next_probe_pos = d->m_next[probe_pos]; \ | |
| 3271 | if ((!next_probe_pos) || \ | |
| 3272 | ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ | |
| 3273 | return; \ | |
| 3274 | probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ | |
| 3275 | if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ | |
| 3276 | break; | |
| 3277 | TDEFL_PROBE; | |
| 3278 | TDEFL_PROBE; | |
| 3279 | TDEFL_PROBE; | |
| 3280 | } | |
| 3281 | if (!dist) | |
| 3282 | break; | |
| 3283 | q = (const mz_uint16 *)(d->m_dict + probe_pos); | |
| 3284 | if (*q != s01) | |
| 3285 | continue; | |
| 3286 | p = s; | |
| 3287 | probe_len = 32; | |
| 3288 | do { | |
| 3289 | } while ((*(++p) == *(++q)) && (*(++p) == *(++q)) && (*(++p) == *(++q)) && | |
| 3290 | (*(++p) == *(++q)) && (--probe_len > 0)); | |
| 3291 | if (!probe_len) { | |
| 3292 | *pMatch_dist = dist; | |
| 3293 | *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); | |
| 3294 | break; | |
| 3295 | } else if ((probe_len = ((mz_uint)(p - s) * 2) + | |
| 3296 | (mz_uint)(*(const mz_uint8 *)p == | |
| 3297 | *(const mz_uint8 *)q)) > match_len) { | |
| 3298 | *pMatch_dist = dist; | |
| 3299 | if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == | |
| 3300 | max_match_len) | |
| 3301 | break; | |
| 3302 | c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); | |
| 3303 | } | |
| 3304 | } | |
| 3305 | } | |
| 3306 | #else | |
| 3307 | static MZ_FORCEINLINE void | |
| 3308 | tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, | |
| 3309 | mz_uint max_match_len, mz_uint *pMatch_dist, | |
| 3310 | mz_uint *pMatch_len) { | |
| 3311 | mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, | |
| 3312 | match_len = *pMatch_len, probe_pos = pos, next_probe_pos, | |
| 3313 | probe_len; | |
| 3314 | mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; | |
| 3315 | const mz_uint8 *s = d->m_dict + pos, *p, *q; | |
| 3316 | mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; | |
| 3317 | MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); | |
| 3318 | if (max_match_len <= match_len) | |
| 3319 | return; | |
| 3320 | for (;;) { | |
| 3321 | for (;;) { | |
| 3322 | if (--num_probes_left == 0) | |
| 3323 | return; | |
| 3324 | #define TDEFL_PROBE \ | |
| 3325 | next_probe_pos = d->m_next[probe_pos]; \ | |
| 3326 | if ((!next_probe_pos) || \ | |
| 3327 | ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ | |
| 3328 | return; \ | |
| 3329 | probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ | |
| 3330 | if ((d->m_dict[probe_pos + match_len] == c0) && \ | |
| 3331 | (d->m_dict[probe_pos + match_len - 1] == c1)) \ | |
| 3332 | break; | |
| 3333 | TDEFL_PROBE; | |
| 3334 | TDEFL_PROBE; | |
| 3335 | TDEFL_PROBE; | |
| 3336 | } | |
| 3337 | if (!dist) | |
| 3338 | break; | |
| 3339 | p = s; | |
| 3340 | q = d->m_dict + probe_pos; | |
| 3341 | for (probe_len = 0; probe_len < max_match_len; probe_len++) | |
| 3342 | if (*p++ != *q++) | |
| 3343 | break; | |
| 3344 | if (probe_len > match_len) { | |
| 3345 | *pMatch_dist = dist; | |
| 3346 | if ((*pMatch_len = match_len = probe_len) == max_match_len) | |
| 3347 | return; | |
| 3348 | c0 = d->m_dict[pos + match_len]; | |
| 3349 | c1 = d->m_dict[pos + match_len - 1]; | |
| 3350 | } | |
| 3351 | } | |
| 3352 | } | |
| 3353 | #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES | |
| 3354 | ||
| 3355 | #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN | |
| 3356 | static mz_bool tdefl_compress_fast(tdefl_compressor *d) { | |
| 3357 | // Faster, minimally featured LZRW1-style match+parse loop with better | |
| 3358 | // register utilization. Intended for applications where raw throughput is | |
| 3359 | // valued more highly than ratio. | |
| 3360 | mz_uint lookahead_pos = d->m_lookahead_pos, | |
| 3361 | lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, | |
| 3362 | total_lz_bytes = d->m_total_lz_bytes, | |
| 3363 | num_flags_left = d->m_num_flags_left; | |
| 3364 | mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; | |
| 3365 | mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; | |
| 3366 | ||
| 3367 | while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { | |
| 3368 | const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; | |
| 3369 | mz_uint dst_pos = | |
| 3370 | (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; | |
| 3371 | mz_uint num_bytes_to_process = (mz_uint)MZ_MIN( | |
| 3372 | d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); | |
| 3373 | d->m_src_buf_left -= num_bytes_to_process; | |
| 3374 | lookahead_size += num_bytes_to_process; | |
| 3375 | ||
| 3376 | while (num_bytes_to_process) { | |
| 3377 | mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); | |
| 3378 | memcpy(d->m_dict + dst_pos, d->m_pSrc, n); | |
| 3379 | if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) | |
| 3380 | memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, | |
| 3381 | MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); | |
| 3382 | d->m_pSrc += n; | |
| 3383 | dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; | |
| 3384 | num_bytes_to_process -= n; | |
| 3385 | } | |
| 3386 | ||
| 3387 | dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); | |
| 3388 | if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) | |
| 3389 | break; | |
| 3390 | ||
| 3391 | while (lookahead_size >= 4) { | |
| 3392 | mz_uint cur_match_dist, cur_match_len = 1; | |
| 3393 | mz_uint8 *pCur_dict = d->m_dict + cur_pos; | |
| 3394 | mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; | |
| 3395 | mz_uint hash = | |
| 3396 | (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & | |
| 3397 | TDEFL_LEVEL1_HASH_SIZE_MASK; | |
| 3398 | mz_uint probe_pos = d->m_hash[hash]; | |
| 3399 | d->m_hash[hash] = (mz_uint16)lookahead_pos; | |
| 3400 | ||
| 3401 | if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= | |
| 3402 | dict_size) && | |
| 3403 | ((mz_uint32)( | |
| 3404 | *(d->m_dict + (probe_pos & TDEFL_LZ_DICT_SIZE_MASK)) | | |
| 3405 | (*(d->m_dict + ((probe_pos & TDEFL_LZ_DICT_SIZE_MASK) + 1)) | |
| 3406 | << 8) | | |
| 3407 | (*(d->m_dict + ((probe_pos & TDEFL_LZ_DICT_SIZE_MASK) + 2)) | |
| 3408 | << 16)) == first_trigram)) { | |
| 3409 | const mz_uint16 *p = (const mz_uint16 *)pCur_dict; | |
| 3410 | const mz_uint16 *q = | |
| 3411 | (const mz_uint16 *)(d->m_dict + | |
| 3412 | (probe_pos & TDEFL_LZ_DICT_SIZE_MASK)); | |
| 3413 | mz_uint32 probe_len = 32; | |
| 3414 | do { | |
| 3415 | } while ((*(++p) == *(++q)) && (*(++p) == *(++q)) && | |
| 3416 | (*(++p) == *(++q)) && (*(++p) == *(++q)) && (--probe_len > 0)); | |
| 3417 | cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + | |
| 3418 | (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); | |
| 3419 | if (!probe_len) | |
| 3420 | cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; | |
| 3421 | ||
| 3422 | if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || | |
| 3423 | ((cur_match_len == TDEFL_MIN_MATCH_LEN) && | |
| 3424 | (cur_match_dist >= 8U * 1024U))) { | |
| 3425 | cur_match_len = 1; | |
| 3426 | *pLZ_code_buf++ = (mz_uint8)first_trigram; | |
| 3427 | *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); | |
| 3428 | d->m_huff_count[0][(mz_uint8)first_trigram]++; | |
| 3429 | } else { | |
| 3430 | mz_uint32 s0, s1; | |
| 3431 | cur_match_len = MZ_MIN(cur_match_len, lookahead_size); | |
| 3432 | ||
| 3433 | MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && | |
| 3434 | (cur_match_dist >= 1) && | |
| 3435 | (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); | |
| 3436 | ||
| 3437 | cur_match_dist--; | |
| 3438 | ||
| 3439 | pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); | |
| 3440 | *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; | |
| 3441 | pLZ_code_buf += 3; | |
| 3442 | *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); | |
| 3443 | ||
| 3444 | s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; | |
| 3445 | s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; | |
| 3446 | d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; | |
| 3447 | ||
| 3448 | d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - | |
| 3449 | TDEFL_MIN_MATCH_LEN]]++; | |
| 3450 | } | |
| 3451 | } else { | |
| 3452 | *pLZ_code_buf++ = (mz_uint8)first_trigram; | |
| 3453 | *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); | |
| 3454 | d->m_huff_count[0][(mz_uint8)first_trigram]++; | |
| 3455 | } | |
| 3456 | ||
| 3457 | if (--num_flags_left == 0) { | |
| 3458 | num_flags_left = 8; | |
| 3459 | pLZ_flags = pLZ_code_buf++; | |
| 3460 | } | |
| 3461 | ||
| 3462 | total_lz_bytes += cur_match_len; | |
| 3463 | lookahead_pos += cur_match_len; | |
| 3464 | dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); | |
| 3465 | cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; | |
| 3466 | MZ_ASSERT(lookahead_size >= cur_match_len); | |
| 3467 | lookahead_size -= cur_match_len; | |
| 3468 | ||
| 3469 | if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { | |
| 3470 | int n; | |
| 3471 | d->m_lookahead_pos = lookahead_pos; | |
| 3472 | d->m_lookahead_size = lookahead_size; | |
| 3473 | d->m_dict_size = dict_size; | |
| 3474 | d->m_total_lz_bytes = total_lz_bytes; | |
| 3475 | d->m_pLZ_code_buf = pLZ_code_buf; | |
| 3476 | d->m_pLZ_flags = pLZ_flags; | |
| 3477 | d->m_num_flags_left = num_flags_left; | |
| 3478 | if ((n = tdefl_flush_block(d, 0)) != 0) | |
| 3479 | return (n < 0) ? MZ_FALSE : MZ_TRUE; | |
| 3480 | total_lz_bytes = d->m_total_lz_bytes; | |
| 3481 | pLZ_code_buf = d->m_pLZ_code_buf; | |
| 3482 | pLZ_flags = d->m_pLZ_flags; | |
| 3483 | num_flags_left = d->m_num_flags_left; | |
| 3484 | } | |
| 3485 | } | |
| 3486 | ||
| 3487 | while (lookahead_size) { | |
| 3488 | mz_uint8 lit = d->m_dict[cur_pos]; | |
| 3489 | ||
| 3490 | total_lz_bytes++; | |
| 3491 | *pLZ_code_buf++ = lit; | |
| 3492 | *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); | |
| 3493 | if (--num_flags_left == 0) { | |
| 3494 | num_flags_left = 8; | |
| 3495 | pLZ_flags = pLZ_code_buf++; | |
| 3496 | } | |
| 3497 | ||
| 3498 | d->m_huff_count[0][lit]++; | |
| 3499 | ||
| 3500 | lookahead_pos++; | |
| 3501 | dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); | |
| 3502 | cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; | |
| 3503 | lookahead_size--; | |
| 3504 | ||
| 3505 | if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { | |
| 3506 | int n; | |
| 3507 | d->m_lookahead_pos = lookahead_pos; | |
| 3508 | d->m_lookahead_size = lookahead_size; | |
| 3509 | d->m_dict_size = dict_size; | |
| 3510 | d->m_total_lz_bytes = total_lz_bytes; | |
| 3511 | d->m_pLZ_code_buf = pLZ_code_buf; | |
| 3512 | d->m_pLZ_flags = pLZ_flags; | |
| 3513 | d->m_num_flags_left = num_flags_left; | |
| 3514 | if ((n = tdefl_flush_block(d, 0)) != 0) | |
| 3515 | return (n < 0) ? MZ_FALSE : MZ_TRUE; | |
| 3516 | total_lz_bytes = d->m_total_lz_bytes; | |
| 3517 | pLZ_code_buf = d->m_pLZ_code_buf; | |
| 3518 | pLZ_flags = d->m_pLZ_flags; | |
| 3519 | num_flags_left = d->m_num_flags_left; | |
| 3520 | } | |
| 3521 | } | |
| 3522 | } | |
| 3523 | ||
| 3524 | d->m_lookahead_pos = lookahead_pos; | |
| 3525 | d->m_lookahead_size = lookahead_size; | |
| 3526 | d->m_dict_size = dict_size; | |
| 3527 | d->m_total_lz_bytes = total_lz_bytes; | |
| 3528 | d->m_pLZ_code_buf = pLZ_code_buf; | |
| 3529 | d->m_pLZ_flags = pLZ_flags; | |
| 3530 | d->m_num_flags_left = num_flags_left; | |
| 3531 | return MZ_TRUE; | |
| 3532 | } | |
| 3533 | #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN | |
| 3534 | ||
| 3535 | static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, | |
| 3536 | mz_uint8 lit) { | |
| 3537 | d->m_total_lz_bytes++; | |
| 3538 | *d->m_pLZ_code_buf++ = lit; | |
| 3539 | *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); | |
| 3540 | if (--d->m_num_flags_left == 0) { | |
| 3541 | d->m_num_flags_left = 8; | |
| 3542 | d->m_pLZ_flags = d->m_pLZ_code_buf++; | |
| 3543 | } | |
| 3544 | d->m_huff_count[0][lit]++; | |
| 3545 | } | |
| 3546 | ||
| 3547 | static MZ_FORCEINLINE void | |
| 3548 | tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { | |
| 3549 | mz_uint32 s0, s1; | |
| 3550 | ||
| 3551 | MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && | |
| 3552 | (match_dist <= TDEFL_LZ_DICT_SIZE)); | |
| 3553 | ||
| 3554 | d->m_total_lz_bytes += match_len; | |
| 3555 | ||
| 3556 | d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); | |
| 3557 | ||
| 3558 | match_dist -= 1; | |
| 3559 | d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); | |
| 3560 | d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); | |
| 3561 | d->m_pLZ_code_buf += 3; | |
| 3562 | ||
| 3563 | *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); | |
| 3564 | if (--d->m_num_flags_left == 0) { | |
| 3565 | d->m_num_flags_left = 8; | |
| 3566 | d->m_pLZ_flags = d->m_pLZ_code_buf++; | |
| 3567 | } | |
| 3568 | ||
| 3569 | s0 = s_tdefl_small_dist_sym[match_dist & 511]; | |
| 3570 | s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; | |
| 3571 | d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; | |
| 3572 | ||
| 3573 | if (match_len >= TDEFL_MIN_MATCH_LEN) | |
| 3574 | d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; | |
| 3575 | } | |
| 3576 | ||
| 3577 | static mz_bool tdefl_compress_normal(tdefl_compressor *d) { | |
| 3578 | const mz_uint8 *pSrc = d->m_pSrc; | |
| 3579 | size_t src_buf_left = d->m_src_buf_left; | |
| 3580 | tdefl_flush flush = d->m_flush; | |
| 3581 | ||
| 3582 | while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { | |
| 3583 | mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; | |
| 3584 | // Update dictionary and hash chains. Keeps the lookahead size equal to | |
| 3585 | // TDEFL_MAX_MATCH_LEN. | |
| 3586 | if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { | |
| 3587 | mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & | |
| 3588 | TDEFL_LZ_DICT_SIZE_MASK, | |
| 3589 | ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; | |
| 3590 | mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] | |
| 3591 | << TDEFL_LZ_HASH_SHIFT) ^ | |
| 3592 | d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; | |
| 3593 | mz_uint num_bytes_to_process = (mz_uint)MZ_MIN( | |
| 3594 | src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); | |
| 3595 | const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; | |
| 3596 | src_buf_left -= num_bytes_to_process; | |
| 3597 | d->m_lookahead_size += num_bytes_to_process; | |
| 3598 | while (pSrc != pSrc_end) { | |
| 3599 | mz_uint8 c = *pSrc++; | |
| 3600 | d->m_dict[dst_pos] = c; | |
| 3601 | if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) | |
| 3602 | d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; | |
| 3603 | hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); | |
| 3604 | d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; | |
| 3605 | d->m_hash[hash] = (mz_uint16)(ins_pos); | |
| 3606 | dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; | |
| 3607 | ins_pos++; | |
| 3608 | } | |
| 3609 | } else { | |
| 3610 | while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { | |
| 3611 | mz_uint8 c = *pSrc++; | |
| 3612 | mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & | |
| 3613 | TDEFL_LZ_DICT_SIZE_MASK; | |
| 3614 | src_buf_left--; | |
| 3615 | d->m_dict[dst_pos] = c; | |
| 3616 | if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) | |
| 3617 | d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; | |
| 3618 | if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { | |
| 3619 | mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; | |
| 3620 | mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] | |
| 3621 | << (TDEFL_LZ_HASH_SHIFT * 2)) ^ | |
| 3622 | (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] | |
| 3623 | << TDEFL_LZ_HASH_SHIFT) ^ | |
| 3624 | c) & | |
| 3625 | (TDEFL_LZ_HASH_SIZE - 1); | |
| 3626 | d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; | |
| 3627 | d->m_hash[hash] = (mz_uint16)(ins_pos); | |
| 3628 | } | |
| 3629 | } | |
| 3630 | } | |
| 3631 | d->m_dict_size = | |
| 3632 | MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); | |
| 3633 | if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) | |
| 3634 | break; | |
| 3635 | ||
| 3636 | // Simple lazy/greedy parsing state machine. | |
| 3637 | len_to_move = 1; | |
| 3638 | cur_match_dist = 0; | |
| 3639 | cur_match_len = | |
| 3640 | d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); | |
| 3641 | cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; | |
| 3642 | if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { | |
| 3643 | if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { | |
| 3644 | mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; | |
| 3645 | cur_match_len = 0; | |
| 3646 | while (cur_match_len < d->m_lookahead_size) { | |
| 3647 | if (d->m_dict[cur_pos + cur_match_len] != c) | |
| 3648 | break; | |
| 3649 | cur_match_len++; | |
| 3650 | } | |
| 3651 | if (cur_match_len < TDEFL_MIN_MATCH_LEN) | |
| 3652 | cur_match_len = 0; | |
| 3653 | else | |
| 3654 | cur_match_dist = 1; | |
| 3655 | } | |
| 3656 | } else { | |
| 3657 | tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, | |
| 3658 | d->m_lookahead_size, &cur_match_dist, &cur_match_len); | |
| 3659 | } | |
| 3660 | if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && | |
| 3661 | (cur_match_dist >= 8U * 1024U)) || | |
| 3662 | (cur_pos == cur_match_dist) || | |
| 3663 | ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { | |
| 3664 | cur_match_dist = cur_match_len = 0; | |
| 3665 | } | |
| 3666 | if (d->m_saved_match_len) { | |
| 3667 | if (cur_match_len > d->m_saved_match_len) { | |
| 3668 | tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); | |
| 3669 | if (cur_match_len >= 128) { | |
| 3670 | tdefl_record_match(d, cur_match_len, cur_match_dist); | |
| 3671 | d->m_saved_match_len = 0; | |
| 3672 | len_to_move = cur_match_len; | |
| 3673 | } else { | |
| 3674 | d->m_saved_lit = d->m_dict[cur_pos]; | |
| 3675 | d->m_saved_match_dist = cur_match_dist; | |
| 3676 | d->m_saved_match_len = cur_match_len; | |
| 3677 | } | |
| 3678 | } else { | |
| 3679 | tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); | |
| 3680 | len_to_move = d->m_saved_match_len - 1; | |
| 3681 | d->m_saved_match_len = 0; | |
| 3682 | } | |
| 3683 | } else if (!cur_match_dist) | |
| 3684 | tdefl_record_literal(d, | |
| 3685 | d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); | |
| 3686 | else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || | |
| 3687 | (cur_match_len >= 128)) { | |
| 3688 | tdefl_record_match(d, cur_match_len, cur_match_dist); | |
| 3689 | len_to_move = cur_match_len; | |
| 3690 | } else { | |
| 3691 | d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; | |
| 3692 | d->m_saved_match_dist = cur_match_dist; | |
| 3693 | d->m_saved_match_len = cur_match_len; | |
| 3694 | } | |
| 3695 | // Move the lookahead forward by len_to_move bytes. | |
| 3696 | d->m_lookahead_pos += len_to_move; | |
| 3697 | MZ_ASSERT(d->m_lookahead_size >= len_to_move); | |
| 3698 | d->m_lookahead_size -= len_to_move; | |
| 3699 | d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); | |
| 3700 | // Check if it's time to flush the current LZ codes to the internal output | |
| 3701 | // buffer. | |
| 3702 | if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || | |
| 3703 | ((d->m_total_lz_bytes > 31 * 1024) && | |
| 3704 | (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= | |
| 3705 | d->m_total_lz_bytes) || | |
| 3706 | (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) { | |
| 3707 | int n; | |
| 3708 | d->m_pSrc = pSrc; | |
| 3709 | d->m_src_buf_left = src_buf_left; | |
| 3710 | if ((n = tdefl_flush_block(d, 0)) != 0) | |
| 3711 | return (n < 0) ? MZ_FALSE : MZ_TRUE; | |
| 3712 | } | |
| 3713 | } | |
| 3714 | ||
| 3715 | d->m_pSrc = pSrc; | |
| 3716 | d->m_src_buf_left = src_buf_left; | |
| 3717 | return MZ_TRUE; | |
| 3718 | } | |
| 3719 | ||
| 3720 | static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { | |
| 3721 | if (d->m_pIn_buf_size) { | |
| 3722 | *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; | |
| 3723 | } | |
| 3724 | ||
| 3725 | if (d->m_pOut_buf_size) { | |
| 3726 | size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, | |
| 3727 | d->m_output_flush_remaining); | |
| 3728 | memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, | |
| 3729 | d->m_output_buf + d->m_output_flush_ofs, n); | |
| 3730 | d->m_output_flush_ofs += (mz_uint)n; | |
| 3731 | d->m_output_flush_remaining -= (mz_uint)n; | |
| 3732 | d->m_out_buf_ofs += n; | |
| 3733 | ||
| 3734 | *d->m_pOut_buf_size = d->m_out_buf_ofs; | |
| 3735 | } | |
| 3736 | ||
| 3737 | return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE | |
| 3738 | : TDEFL_STATUS_OKAY; | |
| 3739 | } | |
| 3740 | ||
| 3741 | tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, | |
| 3742 | size_t *pIn_buf_size, void *pOut_buf, | |
| 3743 | size_t *pOut_buf_size, tdefl_flush flush) { | |
| 3744 | if (!d) { | |
| 3745 | if (pIn_buf_size) | |
| 3746 | *pIn_buf_size = 0; | |
| 3747 | if (pOut_buf_size) | |
| 3748 | *pOut_buf_size = 0; | |
| 3749 | return TDEFL_STATUS_BAD_PARAM; | |
| 3750 | } | |
| 3751 | ||
| 3752 | d->m_pIn_buf = pIn_buf; | |
| 3753 | d->m_pIn_buf_size = pIn_buf_size; | |
| 3754 | d->m_pOut_buf = pOut_buf; | |
| 3755 | d->m_pOut_buf_size = pOut_buf_size; | |
| 3756 | d->m_pSrc = (const mz_uint8 *)(pIn_buf); | |
| 3757 | d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; | |
| 3758 | d->m_out_buf_ofs = 0; | |
| 3759 | d->m_flush = flush; | |
| 3760 | ||
| 3761 | if (((d->m_pPut_buf_func != NULL) == | |
| 3762 | ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || | |
| 3763 | (d->m_prev_return_status != TDEFL_STATUS_OKAY) || | |
| 3764 | (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || | |
| 3765 | (pIn_buf_size && *pIn_buf_size && !pIn_buf) || | |
| 3766 | (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { | |
| 3767 | if (pIn_buf_size) | |
| 3768 | *pIn_buf_size = 0; | |
| 3769 | if (pOut_buf_size) | |
| 3770 | *pOut_buf_size = 0; | |
| 3771 | return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); | |
| 3772 | } | |
| 3773 | d->m_wants_to_finish |= (flush == TDEFL_FINISH); | |
| 3774 | ||
| 3775 | if ((d->m_output_flush_remaining) || (d->m_finished)) | |
| 3776 | return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); | |
| 3777 | ||
| 3778 | #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN | |
| 3779 | if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && | |
| 3780 | ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && | |
| 3781 | ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | | |
| 3782 | TDEFL_RLE_MATCHES)) == 0)) { | |
| 3783 | if (!tdefl_compress_fast(d)) | |
| 3784 | return d->m_prev_return_status; | |
| 3785 | } else | |
| 3786 | #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN | |
| 3787 | { | |
| 3788 | if (!tdefl_compress_normal(d)) | |
| 3789 | return d->m_prev_return_status; | |
| 3790 | } | |
| 3791 | ||
| 3792 | if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && | |
| 3793 | (pIn_buf)) | |
| 3794 | d->m_adler32 = | |
| 3795 | (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, | |
| 3796 | d->m_pSrc - (const mz_uint8 *)pIn_buf); | |
| 3797 | ||
| 3798 | if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && | |
| 3799 | (!d->m_output_flush_remaining)) { | |
| 3800 | if (tdefl_flush_block(d, flush) < 0) | |
| 3801 | return d->m_prev_return_status; | |
| 3802 | d->m_finished = (flush == TDEFL_FINISH); | |
| 3803 | if (flush == TDEFL_FULL_FLUSH) { | |
| 3804 | MZ_CLEAR_OBJ(d->m_hash); | |
| 3805 | MZ_CLEAR_OBJ(d->m_next); | |
| 3806 | d->m_dict_size = 0; | |
| 3807 | } | |
| 3808 | } | |
| 3809 | ||
| 3810 | return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); | |
| 3811 | } | |
| 3812 | ||
| 3813 | tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, | |
| 3814 | size_t in_buf_size, tdefl_flush flush) { | |
| 3815 | MZ_ASSERT(d->m_pPut_buf_func); | |
| 3816 | return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); | |
| 3817 | } | |
| 3818 | ||
| 3819 | tdefl_status tdefl_init(tdefl_compressor *d, | |
| 3820 | tdefl_put_buf_func_ptr pPut_buf_func, | |
| 3821 | void *pPut_buf_user, int flags) { | |
| 3822 | d->m_pPut_buf_func = pPut_buf_func; | |
| 3823 | d->m_pPut_buf_user = pPut_buf_user; | |
| 3824 | d->m_flags = (mz_uint)(flags); | |
| 3825 | d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; | |
| 3826 | d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; | |
| 3827 | d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; | |
| 3828 | if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) | |
| 3829 | MZ_CLEAR_OBJ(d->m_hash); | |
| 3830 | d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = | |
| 3831 | d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; | |
| 3832 | d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = | |
| 3833 | d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; | |
| 3834 | d->m_pLZ_code_buf = d->m_lz_code_buf + 1; | |
| 3835 | d->m_pLZ_flags = d->m_lz_code_buf; | |
| 3836 | d->m_num_flags_left = 8; | |
| 3837 | d->m_pOutput_buf = d->m_output_buf; | |
| 3838 | d->m_pOutput_buf_end = d->m_output_buf; | |
| 3839 | d->m_prev_return_status = TDEFL_STATUS_OKAY; | |
| 3840 | d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; | |
| 3841 | d->m_adler32 = 1; | |
| 3842 | d->m_pIn_buf = NULL; | |
| 3843 | d->m_pOut_buf = NULL; | |
| 3844 | d->m_pIn_buf_size = NULL; | |
| 3845 | d->m_pOut_buf_size = NULL; | |
| 3846 | d->m_flush = TDEFL_NO_FLUSH; | |
| 3847 | d->m_pSrc = NULL; | |
| 3848 | d->m_src_buf_left = 0; | |
| 3849 | d->m_out_buf_ofs = 0; | |
| 3850 | memset(&d->m_huff_count[0][0], 0, | |
| 3851 | sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); | |
| 3852 | memset(&d->m_huff_count[1][0], 0, | |
| 3853 | sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); | |
| 3854 | return TDEFL_STATUS_OKAY; | |
| 3855 | } | |
| 3856 | ||
| 3857 | tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { | |
| 3858 | return d->m_prev_return_status; | |
| 3859 | } | |
| 3860 | ||
| 3861 | mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } | |
| 3862 | ||
| 3863 | mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, | |
| 3864 | tdefl_put_buf_func_ptr pPut_buf_func, | |
| 3865 | void *pPut_buf_user, int flags) { | |
| 3866 | tdefl_compressor *pComp; | |
| 3867 | mz_bool succeeded; | |
| 3868 | if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) | |
| 3869 | return MZ_FALSE; | |
| 3870 | pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); | |
| 3871 | if (!pComp) | |
| 3872 | return MZ_FALSE; | |
| 3873 | succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == | |
| 3874 | TDEFL_STATUS_OKAY); | |
| 3875 | succeeded = | |
| 3876 | succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == | |
| 3877 | TDEFL_STATUS_DONE); | |
| 3878 | MZ_FREE(pComp); | |
| 3879 | return succeeded; | |
| 3880 | } | |
| 3881 | ||
| 3882 | typedef struct { | |
| 3883 | size_t m_size, m_capacity; | |
| 3884 | mz_uint8 *m_pBuf; | |
| 3885 | mz_bool m_expandable; | |
| 3886 | } tdefl_output_buffer; | |
| 3887 | ||
| 3888 | static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, | |
| 3889 | void *pUser) { | |
| 3890 | tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; | |
| 3891 | size_t new_size = p->m_size + len; | |
| 3892 | if (new_size > p->m_capacity) { | |
| 3893 | size_t new_capacity = p->m_capacity; | |
| 3894 | mz_uint8 *pNew_buf; | |
| 3895 | if (!p->m_expandable) | |
| 3896 | return MZ_FALSE; | |
| 3897 | do { | |
| 3898 | new_capacity = MZ_MAX(128U, new_capacity << 1U); | |
| 3899 | } while (new_size > new_capacity); | |
| 3900 | pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); | |
| 3901 | if (!pNew_buf) | |
| 3902 | return MZ_FALSE; | |
| 3903 | p->m_pBuf = pNew_buf; | |
| 3904 | p->m_capacity = new_capacity; | |
| 3905 | } | |
| 3906 | memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); | |
| 3907 | p->m_size = new_size; | |
| 3908 | return MZ_TRUE; | |
| 3909 | } | |
| 3910 | ||
| 3911 | void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, | |
| 3912 | size_t *pOut_len, int flags) { | |
| 3913 | tdefl_output_buffer out_buf; | |
| 3914 | MZ_CLEAR_OBJ(out_buf); | |
| 3915 | if (!pOut_len) | |
| 3916 | return MZ_FALSE; | |
| 3917 | else | |
| 3918 | *pOut_len = 0; | |
| 3919 | out_buf.m_expandable = MZ_TRUE; | |
| 3920 | if (!tdefl_compress_mem_to_output( | |
| 3921 | pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) | |
| 3922 | return NULL; | |
| 3923 | *pOut_len = out_buf.m_size; | |
| 3924 | return out_buf.m_pBuf; | |
| 3925 | } | |
| 3926 | ||
| 3927 | size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, | |
| 3928 | const void *pSrc_buf, size_t src_buf_len, | |
| 3929 | int flags) { | |
| 3930 | tdefl_output_buffer out_buf; | |
| 3931 | MZ_CLEAR_OBJ(out_buf); | |
| 3932 | if (!pOut_buf) | |
| 3933 | return 0; | |
| 3934 | out_buf.m_pBuf = (mz_uint8 *)pOut_buf; | |
| 3935 | out_buf.m_capacity = out_buf_len; | |
| 3936 | if (!tdefl_compress_mem_to_output( | |
| 3937 | pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) | |
| 3938 | return 0; | |
| 3939 | return out_buf.m_size; | |
| 3940 | } | |
| 3941 | ||
| 3942 | #ifndef MINIZ_NO_ZLIB_APIS | |
| 3943 | static const mz_uint s_tdefl_num_probes[11] = {0, 1, 6, 32, 16, 32, | |
| 3944 | 128, 256, 512, 768, 1500}; | |
| 3945 | ||
| 3946 | // level may actually range from [0,10] (10 is a "hidden" max level, where we | |
| 3947 | // want a bit more compression and it's fine if throughput to fall off a cliff | |
| 3948 | // on some files). | |
| 3949 | mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, | |
| 3950 | int strategy) { | |
| 3951 | mz_uint comp_flags = | |
| 3952 | s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | | |
| 3953 | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); | |
| 3954 | if (window_bits > 0) | |
| 3955 | comp_flags |= TDEFL_WRITE_ZLIB_HEADER; | |
| 3956 | ||
| 3957 | if (!level) | |
| 3958 | comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; | |
| 3959 | else if (strategy == MZ_FILTERED) | |
| 3960 | comp_flags |= TDEFL_FILTER_MATCHES; | |
| 3961 | else if (strategy == MZ_HUFFMAN_ONLY) | |
| 3962 | comp_flags &= ~TDEFL_MAX_PROBES_MASK; | |
| 3963 | else if (strategy == MZ_FIXED) | |
| 3964 | comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; | |
| 3965 | else if (strategy == MZ_RLE) | |
| 3966 | comp_flags |= TDEFL_RLE_MATCHES; | |
| 3967 | ||
| 3968 | return comp_flags; | |
| 3969 | } | |
| 3970 | #endif // MINIZ_NO_ZLIB_APIS | |
| 3971 | ||
| 3972 | #ifdef _MSC_VER | |
| 3973 | #pragma warning(push) | |
| 3974 | #pragma warning(disable : 4204) // nonstandard extension used : non-constant | |
| 3975 | // aggregate initializer (also supported by GNU | |
| 3976 | // C and C99, so no big deal) | |
| 3977 | #endif | |
| 3978 | ||
| 3979 | // Simple PNG writer function by Alex Evans, 2011. Released into the public | |
| 3980 | // domain: https://gist.github.com/908299, more context at | |
| 3981 | // http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. | |
| 3982 | // This is actually a modification of Alex's original code so PNG files | |
| 3983 | // generated by this function pass pngcheck. | |
| 3984 | void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, | |
| 3985 | int h, int num_chans, | |
| 3986 | size_t *pLen_out, | |
| 3987 | mz_uint level, mz_bool flip) { | |
| 3988 | // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was | |
| 3989 | // defined. | |
| 3990 | static const mz_uint s_tdefl_png_num_probes[11] = { | |
| 3991 | 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500}; | |
| 3992 | tdefl_compressor *pComp = | |
| 3993 | (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); | |
| 3994 | tdefl_output_buffer out_buf; | |
| 3995 | int i, bpl = w * num_chans, y, z; | |
| 3996 | mz_uint32 c; | |
| 3997 | *pLen_out = 0; | |
| 3998 | if (!pComp) | |
| 3999 | return NULL; | |
| 4000 | MZ_CLEAR_OBJ(out_buf); | |
| 4001 | out_buf.m_expandable = MZ_TRUE; | |
| 4002 | out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); | |
| 4003 | if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) { | |
| 4004 | MZ_FREE(pComp); | |
| 4005 | return NULL; | |
| 4006 | } | |
| 4007 | // write dummy header | |
| 4008 | for (z = 41; z; --z) | |
| 4009 | tdefl_output_buffer_putter(&z, 1, &out_buf); | |
| 4010 | // compress image data | |
| 4011 | tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, | |
| 4012 | s_tdefl_png_num_probes[MZ_MIN(10, level)] | | |
| 4013 | TDEFL_WRITE_ZLIB_HEADER); | |
| 4014 | for (y = 0; y < h; ++y) { | |
| 4015 | tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); | |
| 4016 | tdefl_compress_buffer(pComp, | |
| 4017 | (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, | |
| 4018 | bpl, TDEFL_NO_FLUSH); | |
| 4019 | } | |
| 4020 | if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != | |
| 4021 | TDEFL_STATUS_DONE) { | |
| 4022 | MZ_FREE(pComp); | |
| 4023 | MZ_FREE(out_buf.m_pBuf); | |
| 4024 | return NULL; | |
| 4025 | } | |
| 4026 | // write real header | |
| 4027 | *pLen_out = out_buf.m_size - 41; | |
| 4028 | { | |
| 4029 | static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; | |
| 4030 | mz_uint8 pnghdr[41] = {0x89, | |
| 4031 | 0x50, | |
| 4032 | 0x4e, | |
| 4033 | 0x47, | |
| 4034 | 0x0d, | |
| 4035 | 0x0a, | |
| 4036 | 0x1a, | |
| 4037 | 0x0a, | |
| 4038 | 0x00, | |
| 4039 | 0x00, | |
| 4040 | 0x00, | |
| 4041 | 0x0d, | |
| 4042 | 0x49, | |
| 4043 | 0x48, | |
| 4044 | 0x44, | |
| 4045 | 0x52, | |
| 4046 | 0, | |
| 4047 | 0, | |
| 4048 | (mz_uint8)(w >> 8), | |
| 4049 | (mz_uint8)w, | |
| 4050 | 0, | |
| 4051 | 0, | |
| 4052 | (mz_uint8)(h >> 8), | |
| 4053 | (mz_uint8)h, | |
| 4054 | 8, | |
| 4055 | chans[num_chans], | |
| 4056 | 0, | |
| 4057 | 0, | |
| 4058 | 0, | |
| 4059 | 0, | |
| 4060 | 0, | |
| 4061 | 0, | |
| 4062 | 0, | |
| 4063 | (mz_uint8)(*pLen_out >> 24), | |
| 4064 | (mz_uint8)(*pLen_out >> 16), | |
| 4065 | (mz_uint8)(*pLen_out >> 8), | |
| 4066 | (mz_uint8)*pLen_out, | |
| 4067 | 0x49, | |
| 4068 | 0x44, | |
| 4069 | 0x41, | |
| 4070 | 0x54}; | |
| 4071 | c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); | |
| 4072 | for (i = 0; i < 4; ++i, c <<= 8) | |
| 4073 | ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); | |
| 4074 | memcpy(out_buf.m_pBuf, pnghdr, 41); | |
| 4075 | } | |
| 4076 | // write footer (IDAT CRC-32, followed by IEND chunk) | |
| 4077 | if (!tdefl_output_buffer_putter( | |
| 4078 | "\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { | |
| 4079 | *pLen_out = 0; | |
| 4080 | MZ_FREE(pComp); | |
| 4081 | MZ_FREE(out_buf.m_pBuf); | |
| 4082 | return NULL; | |
| 4083 | } | |
| 4084 | c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, | |
| 4085 | *pLen_out + 4); | |
| 4086 | for (i = 0; i < 4; ++i, c <<= 8) | |
| 4087 | (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); | |
| 4088 | // compute final size of file, grab compressed data buffer and return | |
| 4089 | *pLen_out += 57; | |
| 4090 | MZ_FREE(pComp); | |
| 4091 | return out_buf.m_pBuf; | |
| 4092 | } | |
| 4093 | void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, | |
| 4094 | int num_chans, size_t *pLen_out) { | |
| 4095 | // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we | |
| 4096 | // can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's | |
| 4097 | // where #defined out) | |
| 4098 | return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, | |
| 4099 | pLen_out, 6, MZ_FALSE); | |
| 4100 | } | |
| 4101 | ||
| 4102 | #ifdef _MSC_VER | |
| 4103 | #pragma warning(pop) | |
| 4104 | #endif | |
| 4105 | ||
| 4106 | // ------------------- .ZIP archive reading | |
| 4107 | ||
| 4108 | #ifndef MINIZ_NO_ARCHIVE_APIS | |
| 4109 | ||
| 4110 | #ifdef MINIZ_NO_STDIO | |
| 4111 | #define MZ_FILE void * | |
| 4112 | #else | |
| 4113 | #include <stdio.h> | |
| 4114 | #include <sys/stat.h> | |
| 4115 | ||
| 4116 | #if defined(_MSC_VER) || defined(__MINGW32__) | |
| 4117 | ||
| 4118 | #include <windows.h> | |
| 4119 | ||
| 4120 | static wchar_t *str2wstr(const char *str) { | |
| 4121 | int len = strlen(str) + 1; | |
| 4122 | wchar_t *wstr = (wchar_t*)malloc(len * sizeof(wchar_t)); | |
| 4123 | MultiByteToWideChar(CP_UTF8, 0, str, len * sizeof(char), wstr, len); | |
| 4124 | return wstr; | |
| 4125 | } | |
| 4126 | ||
| 4127 | static FILE *mz_fopen(const char *pFilename, const char *pMode) { | |
| 4128 | wchar_t *wFilename = str2wstr(pFilename); | |
| 4129 | wchar_t *wMode = str2wstr(pMode); | |
| 4130 | FILE *pFile = _wfopen(wFilename, wMode); | |
| 4131 | ||
| 4132 | free(wFilename); | |
| 4133 | free(wMode); | |
| 4134 | ||
| 4135 | return pFile; | |
| 4136 | } | |
| 4137 | ||
| 4138 | static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { | |
| 4139 | wchar_t *wPath = str2wstr(pPath); | |
| 4140 | wchar_t *wMode = str2wstr(pMode); | |
| 4141 | FILE *pFile = _wfreopen(wPath, wMode, pStream); | |
| 4142 | ||
| 4143 | free(wPath); | |
| 4144 | free(wMode); | |
| 4145 | ||
| 4146 | return pFile; | |
| 4147 | } | |
| 4148 | ||
| 4149 | #ifndef MINIZ_NO_TIME | |
| 4150 | #include <sys/utime.h> | |
| 4151 | #endif | |
| 4152 | #define MZ_FILE FILE | |
| 4153 | #define MZ_FOPEN mz_fopen | |
| 4154 | #define MZ_FCLOSE fclose | |
| 4155 | #define MZ_FREAD fread | |
| 4156 | #define MZ_FWRITE fwrite | |
| 4157 | // ZIG_ANDROID_MOD: Zig's mingw64 doesn't have _ftelli64/_fseeki64 | |
| 4158 | #if defined(__MINGW64__) | |
| 4159 | #define MZ_FTELL64 ftello | |
| 4160 | #define MZ_FSEEK64 fseeko | |
| 4161 | #else | |
| 4162 | #define MZ_FTELL64 _ftelli64 | |
| 4163 | #define MZ_FSEEK64 _fseeki64 | |
| 4164 | #endif | |
| 4165 | #define MZ_FILE_STAT_STRUCT _stat | |
| 4166 | #define MZ_FILE_STAT _stat | |
| 4167 | #define MZ_FFLUSH fflush | |
| 4168 | #define MZ_FREOPEN mz_freopen | |
| 4169 | #define MZ_DELETE_FILE remove | |
| 4170 | #elif defined(__MINGW32__) | |
| 4171 | #ifndef MINIZ_NO_TIME | |
| 4172 | #include <sys/utime.h> | |
| 4173 | #endif | |
| 4174 | #define MZ_FILE FILE | |
| 4175 | #define MZ_FOPEN(f, m) mz_fopen | |
| 4176 | #define MZ_FCLOSE fclose | |
| 4177 | #define MZ_FREAD fread | |
| 4178 | #define MZ_FWRITE fwrite | |
| 4179 | #define MZ_FTELL64 ftell | |
| 4180 | #define MZ_FSEEK64 fseek | |
| 4181 | #define MZ_FILE_STAT_STRUCT _stat | |
| 4182 | #define MZ_FILE_STAT _stat | |
| 4183 | #define MZ_FFLUSH fflush | |
| 4184 | #define MZ_FREOPEN(f, m, s) mz_freopen | |
| 4185 | #define MZ_DELETE_FILE remove | |
| 4186 | #elif defined(__TINYC__) | |
| 4187 | #ifndef MINIZ_NO_TIME | |
| 4188 | #include <sys/utime.h> | |
| 4189 | #endif | |
| 4190 | #define MZ_FILE FILE | |
| 4191 | #define MZ_FOPEN(f, m) fopen(f, m) | |
| 4192 | #define MZ_FCLOSE fclose | |
| 4193 | #define MZ_FREAD fread | |
| 4194 | #define MZ_FWRITE fwrite | |
| 4195 | #define MZ_FTELL64 ftell | |
| 4196 | #define MZ_FSEEK64 fseek | |
| 4197 | #define MZ_FILE_STAT_STRUCT stat | |
| 4198 | #define MZ_FILE_STAT stat | |
| 4199 | #define MZ_FFLUSH fflush | |
| 4200 | #define MZ_FREOPEN(f, m, s) freopen(f, m, s) | |
| 4201 | #define MZ_DELETE_FILE remove | |
| 4202 | #elif defined(__GNUC__) && _LARGEFILE64_SOURCE | |
| 4203 | #ifndef MINIZ_NO_TIME | |
| 4204 | #include <utime.h> | |
| 4205 | #endif | |
| 4206 | #define MZ_FILE FILE | |
| 4207 | #define MZ_FOPEN(f, m) fopen64(f, m) | |
| 4208 | #define MZ_FCLOSE fclose | |
| 4209 | #define MZ_FREAD fread | |
| 4210 | #define MZ_FWRITE fwrite | |
| 4211 | #define MZ_FTELL64 ftello64 | |
| 4212 | #define MZ_FSEEK64 fseeko64 | |
| 4213 | #define MZ_FILE_STAT_STRUCT stat64 | |
| 4214 | #define MZ_FILE_STAT stat64 | |
| 4215 | #define MZ_FFLUSH fflush | |
| 4216 | #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) | |
| 4217 | #define MZ_DELETE_FILE remove | |
| 4218 | #else | |
| 4219 | #ifndef MINIZ_NO_TIME | |
| 4220 | #include <utime.h> | |
| 4221 | #endif | |
| 4222 | #define MZ_FILE FILE | |
| 4223 | #define MZ_FOPEN(f, m) fopen(f, m) | |
| 4224 | #define MZ_FCLOSE fclose | |
| 4225 | #define MZ_FREAD fread | |
| 4226 | #define MZ_FWRITE fwrite | |
| 4227 | #if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L | |
| 4228 | #define MZ_FTELL64 ftello | |
| 4229 | #define MZ_FSEEK64 fseeko | |
| 4230 | #else | |
| 4231 | #define MZ_FTELL64 ftell | |
| 4232 | #define MZ_FSEEK64 fseek | |
| 4233 | #endif | |
| 4234 | #define MZ_FILE_STAT_STRUCT stat | |
| 4235 | #define MZ_FILE_STAT stat | |
| 4236 | #define MZ_FFLUSH fflush | |
| 4237 | #define MZ_FREOPEN(f, m, s) freopen(f, m, s) | |
| 4238 | #define MZ_DELETE_FILE remove | |
| 4239 | #endif // #ifdef _MSC_VER | |
| 4240 | #endif // #ifdef MINIZ_NO_STDIO | |
| 4241 | ||
| 4242 | #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) | |
| 4243 | ||
| 4244 | // Various ZIP archive enums. To completely avoid cross platform compiler | |
| 4245 | // alignment and platform endian issues, miniz.c doesn't use structs for any of | |
| 4246 | // this stuff. | |
| 4247 | enum { | |
| 4248 | // ZIP archive identifiers and record sizes | |
| 4249 | MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, | |
| 4250 | MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, | |
| 4251 | MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, | |
| 4252 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, | |
| 4253 | MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, | |
| 4254 | MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, | |
| 4255 | ||
| 4256 | /* ZIP64 archive identifier and record sizes */ | |
| 4257 | MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, | |
| 4258 | MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, | |
| 4259 | MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, | |
| 4260 | MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, | |
| 4261 | MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, | |
| 4262 | MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, | |
| 4263 | MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, | |
| 4264 | MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, | |
| 4265 | ||
| 4266 | // Central directory header record offsets | |
| 4267 | MZ_ZIP_CDH_SIG_OFS = 0, | |
| 4268 | MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, | |
| 4269 | MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, | |
| 4270 | MZ_ZIP_CDH_BIT_FLAG_OFS = 8, | |
| 4271 | MZ_ZIP_CDH_METHOD_OFS = 10, | |
| 4272 | MZ_ZIP_CDH_FILE_TIME_OFS = 12, | |
| 4273 | MZ_ZIP_CDH_FILE_DATE_OFS = 14, | |
| 4274 | MZ_ZIP_CDH_CRC32_OFS = 16, | |
| 4275 | MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, | |
| 4276 | MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, | |
| 4277 | MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, | |
| 4278 | MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, | |
| 4279 | MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, | |
| 4280 | MZ_ZIP_CDH_DISK_START_OFS = 34, | |
| 4281 | MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, | |
| 4282 | MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, | |
| 4283 | MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, | |
| 4284 | // Local directory header offsets | |
| 4285 | MZ_ZIP_LDH_SIG_OFS = 0, | |
| 4286 | MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, | |
| 4287 | MZ_ZIP_LDH_BIT_FLAG_OFS = 6, | |
| 4288 | MZ_ZIP_LDH_METHOD_OFS = 8, | |
| 4289 | MZ_ZIP_LDH_FILE_TIME_OFS = 10, | |
| 4290 | MZ_ZIP_LDH_FILE_DATE_OFS = 12, | |
| 4291 | MZ_ZIP_LDH_CRC32_OFS = 14, | |
| 4292 | MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, | |
| 4293 | MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, | |
| 4294 | MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, | |
| 4295 | MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, | |
| 4296 | // End of central directory offsets | |
| 4297 | MZ_ZIP_ECDH_SIG_OFS = 0, | |
| 4298 | MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, | |
| 4299 | MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, | |
| 4300 | MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, | |
| 4301 | MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, | |
| 4302 | MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, | |
| 4303 | MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, | |
| 4304 | MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, | |
| 4305 | ||
| 4306 | /* ZIP64 End of central directory locator offsets */ | |
| 4307 | MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ | |
| 4308 | MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ | |
| 4309 | MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ | |
| 4310 | MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ | |
| 4311 | ||
| 4312 | /* ZIP64 End of central directory header offsets */ | |
| 4313 | MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ | |
| 4314 | MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ | |
| 4315 | MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ | |
| 4316 | MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ | |
| 4317 | MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ | |
| 4318 | MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ | |
| 4319 | MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ | |
| 4320 | MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ | |
| 4321 | MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ | |
| 4322 | MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ | |
| 4323 | MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, | |
| 4324 | MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, | |
| 4325 | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, | |
| 4326 | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, | |
| 4327 | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, | |
| 4328 | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, | |
| 4329 | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 | |
| 4330 | }; | |
| 4331 | ||
| 4332 | typedef struct { | |
| 4333 | void *m_p; | |
| 4334 | size_t m_size, m_capacity; | |
| 4335 | mz_uint m_element_size; | |
| 4336 | } mz_zip_array; | |
| 4337 | ||
| 4338 | struct mz_zip_internal_state_tag { | |
| 4339 | mz_zip_array m_central_dir; | |
| 4340 | mz_zip_array m_central_dir_offsets; | |
| 4341 | mz_zip_array m_sorted_central_dir_offsets; | |
| 4342 | ||
| 4343 | /* The flags passed in when the archive is initially opened. */ | |
| 4344 | uint32_t m_init_flags; | |
| 4345 | ||
| 4346 | /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. | |
| 4347 | */ | |
| 4348 | mz_bool m_zip64; | |
| 4349 | ||
| 4350 | /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 | |
| 4351 | * will also be slammed to true too, even if we didn't find a zip64 end of | |
| 4352 | * central dir header, etc.) */ | |
| 4353 | mz_bool m_zip64_has_extended_info_fields; | |
| 4354 | ||
| 4355 | /* These fields are used by the file, FILE, memory, and memory/heap read/write | |
| 4356 | * helpers. */ | |
| 4357 | MZ_FILE *m_pFile; | |
| 4358 | mz_uint64 m_file_archive_start_ofs; | |
| 4359 | ||
| 4360 | void *m_pMem; | |
| 4361 | size_t m_mem_size; | |
| 4362 | size_t m_mem_capacity; | |
| 4363 | }; | |
| 4364 | ||
| 4365 | #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) \ | |
| 4366 | (array_ptr)->m_element_size = element_size | |
| 4367 | #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) \ | |
| 4368 | ((element_type *)((array_ptr)->m_p))[index] | |
| 4369 | ||
| 4370 | static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, | |
| 4371 | mz_zip_array *pArray) { | |
| 4372 | pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); | |
| 4373 | memset(pArray, 0, sizeof(mz_zip_array)); | |
| 4374 | } | |
| 4375 | ||
| 4376 | static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, | |
| 4377 | mz_zip_array *pArray, | |
| 4378 | size_t min_new_capacity, | |
| 4379 | mz_uint growing) { | |
| 4380 | void *pNew_p; | |
| 4381 | size_t new_capacity = min_new_capacity; | |
| 4382 | MZ_ASSERT(pArray->m_element_size); | |
| 4383 | if (pArray->m_capacity >= min_new_capacity) | |
| 4384 | return MZ_TRUE; | |
| 4385 | if (growing) { | |
| 4386 | new_capacity = MZ_MAX(1, pArray->m_capacity); | |
| 4387 | while (new_capacity < min_new_capacity) | |
| 4388 | new_capacity *= 2; | |
| 4389 | } | |
| 4390 | if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, | |
| 4391 | pArray->m_element_size, new_capacity))) | |
| 4392 | return MZ_FALSE; | |
| 4393 | pArray->m_p = pNew_p; | |
| 4394 | pArray->m_capacity = new_capacity; | |
| 4395 | return MZ_TRUE; | |
| 4396 | } | |
| 4397 | ||
| 4398 | static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, | |
| 4399 | mz_zip_array *pArray, | |
| 4400 | size_t new_capacity, | |
| 4401 | mz_uint growing) { | |
| 4402 | if (new_capacity > pArray->m_capacity) { | |
| 4403 | if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) | |
| 4404 | return MZ_FALSE; | |
| 4405 | } | |
| 4406 | return MZ_TRUE; | |
| 4407 | } | |
| 4408 | ||
| 4409 | static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, | |
| 4410 | mz_zip_array *pArray, | |
| 4411 | size_t new_size, | |
| 4412 | mz_uint growing) { | |
| 4413 | if (new_size > pArray->m_capacity) { | |
| 4414 | if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) | |
| 4415 | return MZ_FALSE; | |
| 4416 | } | |
| 4417 | pArray->m_size = new_size; | |
| 4418 | return MZ_TRUE; | |
| 4419 | } | |
| 4420 | ||
| 4421 | static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, | |
| 4422 | mz_zip_array *pArray, | |
| 4423 | size_t n) { | |
| 4424 | return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); | |
| 4425 | } | |
| 4426 | ||
| 4427 | static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, | |
| 4428 | mz_zip_array *pArray, | |
| 4429 | const void *pElements, | |
| 4430 | size_t n) { | |
| 4431 | if (0 == n) | |
| 4432 | return MZ_TRUE; | |
| 4433 | if (!pElements) | |
| 4434 | return MZ_FALSE; | |
| 4435 | ||
| 4436 | size_t orig_size = pArray->m_size; | |
| 4437 | if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) | |
| 4438 | return MZ_FALSE; | |
| 4439 | memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, | |
| 4440 | pElements, n * pArray->m_element_size); | |
| 4441 | return MZ_TRUE; | |
| 4442 | } | |
| 4443 | ||
| 4444 | #ifndef MINIZ_NO_TIME | |
| 4445 | static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) { | |
| 4446 | struct tm tm; | |
| 4447 | memset(&tm, 0, sizeof(tm)); | |
| 4448 | tm.tm_isdst = -1; | |
| 4449 | tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; | |
| 4450 | tm.tm_mon = ((dos_date >> 5) & 15) - 1; | |
| 4451 | tm.tm_mday = dos_date & 31; | |
| 4452 | tm.tm_hour = (dos_time >> 11) & 31; | |
| 4453 | tm.tm_min = (dos_time >> 5) & 63; | |
| 4454 | tm.tm_sec = (dos_time << 1) & 62; | |
| 4455 | return mktime(&tm); | |
| 4456 | } | |
| 4457 | ||
| 4458 | #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS | |
| 4459 | static void mz_zip_time_t_to_dos_time(time_t time, mz_uint16 *pDOS_time, | |
| 4460 | mz_uint16 *pDOS_date) { | |
| 4461 | #ifdef _MSC_VER | |
| 4462 | struct tm tm_struct; | |
| 4463 | struct tm *tm = &tm_struct; | |
| 4464 | errno_t err = localtime_s(tm, &time); | |
| 4465 | if (err) { | |
| 4466 | *pDOS_date = 0; | |
| 4467 | *pDOS_time = 0; | |
| 4468 | return; | |
| 4469 | } | |
| 4470 | #else | |
| 4471 | struct tm *tm = localtime(&time); | |
| 4472 | #endif /* #ifdef _MSC_VER */ | |
| 4473 | ||
| 4474 | *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + | |
| 4475 | ((tm->tm_sec) >> 1)); | |
| 4476 | *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + | |
| 4477 | ((tm->tm_mon + 1) << 5) + tm->tm_mday); | |
| 4478 | } | |
| 4479 | #endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ | |
| 4480 | ||
| 4481 | #ifndef MINIZ_NO_STDIO | |
| 4482 | #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS | |
| 4483 | static mz_bool mz_zip_get_file_modified_time(const char *pFilename, | |
| 4484 | time_t *pTime) { | |
| 4485 | struct MZ_FILE_STAT_STRUCT file_stat; | |
| 4486 | ||
| 4487 | /* On Linux with x86 glibc, this call will fail on large files (I think >= | |
| 4488 | * 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ | |
| 4489 | if (MZ_FILE_STAT(pFilename, &file_stat) != 0) | |
| 4490 | return MZ_FALSE; | |
| 4491 | ||
| 4492 | *pTime = file_stat.st_mtime; | |
| 4493 | ||
| 4494 | return MZ_TRUE; | |
| 4495 | } | |
| 4496 | #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ | |
| 4497 | ||
| 4498 | static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, | |
| 4499 | time_t modified_time) { | |
| 4500 | struct utimbuf t; | |
| 4501 | ||
| 4502 | memset(&t, 0, sizeof(t)); | |
| 4503 | t.actime = access_time; | |
| 4504 | t.modtime = modified_time; | |
| 4505 | ||
| 4506 | return !utime(pFilename, &t); | |
| 4507 | } | |
| 4508 | #endif /* #ifndef MINIZ_NO_STDIO */ | |
| 4509 | #endif /* #ifndef MINIZ_NO_TIME */ | |
| 4510 | ||
| 4511 | static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, | |
| 4512 | mz_zip_error err_num) { | |
| 4513 | if (pZip) | |
| 4514 | pZip->m_last_error = err_num; | |
| 4515 | return MZ_FALSE; | |
| 4516 | } | |
| 4517 | ||
| 4518 | static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, | |
| 4519 | mz_uint32 flags) { | |
| 4520 | (void)flags; | |
| 4521 | if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) | |
| 4522 | return MZ_FALSE; | |
| 4523 | ||
| 4524 | if (!pZip->m_pAlloc) | |
| 4525 | pZip->m_pAlloc = def_alloc_func; | |
| 4526 | if (!pZip->m_pFree) | |
| 4527 | pZip->m_pFree = def_free_func; | |
| 4528 | if (!pZip->m_pRealloc) | |
| 4529 | pZip->m_pRealloc = def_realloc_func; | |
| 4530 | ||
| 4531 | pZip->m_zip_mode = MZ_ZIP_MODE_READING; | |
| 4532 | pZip->m_archive_size = 0; | |
| 4533 | pZip->m_central_directory_file_ofs = 0; | |
| 4534 | pZip->m_total_files = 0; | |
| 4535 | ||
| 4536 | if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc( | |
| 4537 | pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) | |
| 4538 | return MZ_FALSE; | |
| 4539 | memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); | |
| 4540 | MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, | |
| 4541 | sizeof(mz_uint8)); | |
| 4542 | MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, | |
| 4543 | sizeof(mz_uint32)); | |
| 4544 | MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, | |
| 4545 | sizeof(mz_uint32)); | |
| 4546 | return MZ_TRUE; | |
| 4547 | } | |
| 4548 | ||
| 4549 | static MZ_FORCEINLINE mz_bool | |
| 4550 | mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, | |
| 4551 | const mz_zip_array *pCentral_dir_offsets, | |
| 4552 | mz_uint l_index, mz_uint r_index) { | |
| 4553 | const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( | |
| 4554 | pCentral_dir_array, mz_uint8, | |
| 4555 | MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, | |
| 4556 | l_index)), | |
| 4557 | *pE; | |
| 4558 | const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT( | |
| 4559 | pCentral_dir_array, mz_uint8, | |
| 4560 | MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); | |
| 4561 | mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), | |
| 4562 | r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); | |
| 4563 | mz_uint8 l = 0, r = 0; | |
| 4564 | pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; | |
| 4565 | pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; | |
| 4566 | pE = pL + MZ_MIN(l_len, r_len); | |
| 4567 | while (pL < pE) { | |
| 4568 | if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) | |
| 4569 | break; | |
| 4570 | pL++; | |
| 4571 | pR++; | |
| 4572 | } | |
| 4573 | return (pL == pE) ? (l_len < r_len) : (l < r); | |
| 4574 | } | |
| 4575 | ||
| 4576 | #define MZ_SWAP_UINT32(a, b) \ | |
| 4577 | do { \ | |
| 4578 | mz_uint32 t = a; \ | |
| 4579 | a = b; \ | |
| 4580 | b = t; \ | |
| 4581 | } \ | |
| 4582 | MZ_MACRO_END | |
| 4583 | ||
| 4584 | // Heap sort of lowercased filenames, used to help accelerate plain central | |
| 4585 | // directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), | |
| 4586 | // but it could allocate memory.) | |
| 4587 | static void | |
| 4588 | mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { | |
| 4589 | mz_zip_internal_state *pState = pZip->m_pState; | |
| 4590 | const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; | |
| 4591 | const mz_zip_array *pCentral_dir = &pState->m_central_dir; | |
| 4592 | mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT( | |
| 4593 | &pState->m_sorted_central_dir_offsets, mz_uint32, 0); | |
| 4594 | const int size = pZip->m_total_files; | |
| 4595 | int start = (size - 2) >> 1, end; | |
| 4596 | while (start >= 0) { | |
| 4597 | int child, root = start; | |
| 4598 | for (;;) { | |
| 4599 | if ((child = (root << 1) + 1) >= size) | |
| 4600 | break; | |
| 4601 | child += | |
| 4602 | (((child + 1) < size) && | |
| 4603 | (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, | |
| 4604 | pIndices[child], pIndices[child + 1]))); | |
| 4605 | if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, | |
| 4606 | pIndices[root], pIndices[child])) | |
| 4607 | break; | |
| 4608 | MZ_SWAP_UINT32(pIndices[root], pIndices[child]); | |
| 4609 | root = child; | |
| 4610 | } | |
| 4611 | start--; | |
| 4612 | } | |
| 4613 | ||
| 4614 | end = size - 1; | |
| 4615 | while (end > 0) { | |
| 4616 | int child, root = 0; | |
| 4617 | MZ_SWAP_UINT32(pIndices[end], pIndices[0]); | |
| 4618 | for (;;) { | |
| 4619 | if ((child = (root << 1) + 1) >= end) | |
| 4620 | break; | |
| 4621 | child += | |
| 4622 | (((child + 1) < end) && | |
| 4623 | mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, | |
| 4624 | pIndices[child], pIndices[child + 1])); | |
| 4625 | if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, | |
| 4626 | pIndices[root], pIndices[child])) | |
| 4627 | break; | |
| 4628 | MZ_SWAP_UINT32(pIndices[root], pIndices[child]); | |
| 4629 | root = child; | |
| 4630 | } | |
| 4631 | end--; | |
| 4632 | } | |
| 4633 | } | |
| 4634 | ||
| 4635 | static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, | |
| 4636 | mz_uint32 record_sig, | |
| 4637 | mz_uint32 record_size, | |
| 4638 | mz_int64 *pOfs) { | |
| 4639 | mz_int64 cur_file_ofs; | |
| 4640 | mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; | |
| 4641 | mz_uint8 *pBuf = (mz_uint8 *)buf_u32; | |
| 4642 | ||
| 4643 | /* Basic sanity checks - reject files which are too small */ | |
| 4644 | if (pZip->m_archive_size < record_size) | |
| 4645 | return MZ_FALSE; | |
| 4646 | ||
| 4647 | /* Find the record by scanning the file from the end towards the beginning. */ | |
| 4648 | cur_file_ofs = | |
| 4649 | MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); | |
| 4650 | for (;;) { | |
| 4651 | int i, | |
| 4652 | n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); | |
| 4653 | ||
| 4654 | if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) | |
| 4655 | return MZ_FALSE; | |
| 4656 | ||
| 4657 | for (i = n - 4; i >= 0; --i) { | |
| 4658 | mz_uint s = MZ_READ_LE32(pBuf + i); | |
| 4659 | if (s == record_sig) { | |
| 4660 | if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) | |
| 4661 | break; | |
| 4662 | } | |
| 4663 | } | |
| 4664 | ||
| 4665 | if (i >= 0) { | |
| 4666 | cur_file_ofs += i; | |
| 4667 | break; | |
| 4668 | } | |
| 4669 | ||
| 4670 | /* Give up if we've searched the entire file, or we've gone back "too far" | |
| 4671 | * (~64kb) */ | |
| 4672 | if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= | |
| 4673 | (MZ_UINT16_MAX + record_size))) | |
| 4674 | return MZ_FALSE; | |
| 4675 | ||
| 4676 | cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); | |
| 4677 | } | |
| 4678 | ||
| 4679 | *pOfs = cur_file_ofs; | |
| 4680 | return MZ_TRUE; | |
| 4681 | } | |
| 4682 | ||
| 4683 | static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, | |
| 4684 | mz_uint flags) { | |
| 4685 | mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, | |
| 4686 | cdir_disk_index = 0; | |
| 4687 | mz_uint64 cdir_ofs = 0; | |
| 4688 | mz_int64 cur_file_ofs = 0; | |
| 4689 | const mz_uint8 *p; | |
| 4690 | ||
| 4691 | mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; | |
| 4692 | mz_uint8 *pBuf = (mz_uint8 *)buf_u32; | |
| 4693 | mz_bool sort_central_dir = | |
| 4694 | ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); | |
| 4695 | mz_uint32 zip64_end_of_central_dir_locator_u32 | |
| 4696 | [(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / | |
| 4697 | sizeof(mz_uint32)]; | |
| 4698 | mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; | |
| 4699 | ||
| 4700 | mz_uint32 zip64_end_of_central_dir_header_u32 | |
| 4701 | [(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / | |
| 4702 | sizeof(mz_uint32)]; | |
| 4703 | mz_uint8 *pZip64_end_of_central_dir = | |
| 4704 | (mz_uint8 *)zip64_end_of_central_dir_header_u32; | |
| 4705 | ||
| 4706 | mz_uint64 zip64_end_of_central_dir_ofs = 0; | |
| 4707 | ||
| 4708 | /* Basic sanity checks - reject files which are too small, and check the first | |
| 4709 | * 4 bytes of the file to make sure a local header is there. */ | |
| 4710 | if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) | |
| 4711 | return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); | |
| 4712 | ||
| 4713 | if (!mz_zip_reader_locate_header_sig( | |
| 4714 | pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, | |
| 4715 | MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) | |
| 4716 | return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); | |
| 4717 | ||
| 4718 | /* Read and verify the end of central directory record. */ | |
| 4719 | if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, | |
| 4720 | MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != | |
| 4721 | MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) | |
| 4722 | return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); | |
| 4723 | ||
| 4724 | if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != | |
| 4725 | MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) | |
| 4726 | return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); | |
| 4727 | ||
| 4728 | if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + | |
| 4729 | MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) { | |
| 4730 | if (pZip->m_pRead(pZip->m_pIO_opaque, | |
| 4731 | cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, | |
| 4732 | pZip64_locator, | |
| 4733 | MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == | |
| 4734 | MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { | |
| 4735 | if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == | |
| 4736 | MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) { | |
| 4737 | zip64_end_of_central_dir_ofs = MZ_READ_LE64( | |
| 4738 | pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); | |
| 4739 | if (zip64_end_of_central_dir_ofs > | |
| 4740 | (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) | |
| 4741 | return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); | |
| 4742 | ||
| 4743 | if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, | |
| 4744 | pZip64_end_of_central_dir, | |
| 4745 | MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == | |
| 4746 | MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { | |
| 4747 | if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == | |
| 4748 | MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { | |
| 4749 | pZip->m_pState->m_zip64 = MZ_TRUE; | |
| 4750 | } | |
| 4751 | } | |
| 4752 | } | |
| 4753 | } | |
| 4754 | } | |
| 4755 | ||
| 4756 | pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); | |
| 4757 | cdir_entries_on_this_disk = | |
| 4758 | MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); | |
| 4759 | num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); | |
| 4760 | cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); | |
| 4761 | cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); | |
| 4762 | cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); | |
| 4763 | ||
| 4764 | if (pZip->m_pState->m_zip64) { | |
| 4765 | mz_uint32 zip64_total_num_of_disks = | |
| 4766 | MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); | |
| 4767 | mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64( | |
| 4768 | pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); | |
| 4769 | mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64( | |
| 4770 | pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); | |
| 4771 | mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64( | |
| 4772 | pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); | |
| 4773 | mz_uint64 zip64_size_of_central_directory = | |
| 4774 | MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); | |
| 4775 | ||
| 4776 | if (zip64_size_of_end_of_central_dir_record < | |
| 4777 | (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) | |
| 4778 | return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); | |
| 4779 | ||
| 4780 | if (zip64_total_num_of_disks != 1U) | |
| 4781 | return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); | |
| 4782 | ||
| 4783 | /* Check for miniz's practical limits */ | |
| 4784 | if (zip64_cdir_total_entries > MZ_UINT32_MAX) | |
| 4785 | return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); | |
| 4786 | ||
| 4787 | pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; | |
| 4788 | ||
| 4789 | if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) | |
| 4790 | return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); | |
| 4791 | ||
| 4792 | cdir_entries_on_this_disk = | |
| 4793 | (mz_uint32)zip64_cdir_total_entries_on_this_disk; | |
| 4794 | ||
| 4795 | /* Check for miniz's current practical limits (sorry, this should be enough | |
| 4796 | * for millions of files) */ | |
| 4797 | if (zip64_size_of_central_directory > MZ_UINT32_MAX) | |
| 4798 | return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); | |
| 4799 | ||
| 4800 | cdir_size = (mz_uint32)zip64_size_of_central_directory; | |
| 4801 | ||
| 4802 | num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + | |
| 4803 | MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); | |
| 4804 | ||
| 4805 | cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + | |
| 4806 | MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); | |
| 4807 | ||
| 4808 | cdir_ofs = | |
| 4809 | MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); | |
| 4810 | } | |
| 4811 | ||
| 4812 | if (pZip->m_total_files != cdir_entries_on_this_disk) | |
| 4813 | return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); | |
| 4814 | ||
| 4815 | if (((num_this_disk | cdir_disk_index) != 0) && | |
| 4816 | ((num_this_disk != 1) || (cdir_disk_index != 1))) | |
| 4817 | return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); | |
| 4818 | ||
| 4819 | if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) | |
| 4820 | return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); | |
| 4821 | ||
| 4822 | if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) | |
| 4823 | return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); | |
| 4824 | ||
| 4825 | pZip->m_central_directory_file_ofs = cdir_ofs; | |
| 4826 | ||
| 4827 | if (pZip->m_total_files) { | |
| 4828 | mz_uint i, n; | |
| 4829 | /* Read the entire central directory into a heap block, and allocate another | |
| 4830 | * heap block to hold the unsorted central dir file record offsets, and | |
| 4831 | * possibly another to hold the sorted indices. */ | |
| 4832 | if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, | |
| 4833 | MZ_FALSE)) || | |
| 4834 | (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, | |
| 4835 | pZip->m_total_files, MZ_FALSE))) | |
| 4836 | return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); | |
| 4837 | ||
| 4838 | if (sort_central_dir) { | |
| 4839 | if (!mz_zip_array_resize(pZip, | |
| 4840 | &pZip->m_pState->m_sorted_central_dir_offsets, | |
| 4841 | pZip->m_total_files, MZ_FALSE)) | |
| 4842 | return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); | |
| 4843 | } | |
| 4844 | ||
| 4845 | if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, | |
| 4846 | pZip->m_pState->m_central_dir.m_p, | |
| 4847 | cdir_size) != cdir_size) | |
| 4848 | return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); | |
| 4849 | ||
| 4850 | /* Now create an index into the central directory file records, do some | |
| 4851 | * basic sanity checking on each record */ | |
| 4852 | p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; | |
| 4853 | for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { | |
| 4854 | mz_uint total_header_size, disk_index, bit_flags, filename_size, | |
| 4855 | ext_data_size; | |
| 4856 | mz_uint64 comp_size, decomp_size, local_header_ofs; | |
| 4857 | ||
| 4858 | if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || | |
| 4859 | (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) | |
| 4860 | return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); | |
| 4861 | ||
| 4862 | MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, | |
| 4863 | i) = | |
| 4864 | (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); | |
| 4865 | ||
| 4866 | if (sort_central_dir) | |
| 4867 | MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, | |
| 4868 | mz_uint32, i) = i; | |
| 4869 | ||
| 4870 | comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); | |
| 4871 | decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); | |
| 4872 | local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); | |
| 4873 | filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); | |
| 4874 | ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); | |
| 4875 | ||
| 4876 | if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && | |
| 4877 | (ext_data_size) && | |
| 4878 | (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == | |
| 4879 | MZ_UINT32_MAX)) { | |
| 4880 | /* Attempt to find zip64 extended information field in the entry's extra | |
| 4881 | * data */ | |
| 4882 | mz_uint32 extra_size_remaining = ext_data_size; | |
| 4883 | ||
| 4884 | if (extra_size_remaining) { | |
| 4885 | const mz_uint8 *pExtra_data; | |
| 4886 | void *buf = NULL; | |
| 4887 | ||
| 4888 | if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > | |
| 4889 | n) { | |
| 4890 | buf = MZ_MALLOC(ext_data_size); | |
| 4891 | if (buf == NULL) | |
| 4892 | return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); | |
| 4893 | ||
| 4894 | if (pZip->m_pRead(pZip->m_pIO_opaque, | |
| 4895 | cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + | |
| 4896 | filename_size, | |
| 4897 | buf, ext_data_size) != ext_data_size) { | |
| 4898 | MZ_FREE(buf); | |
| 4899 | return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); | |
| 4900 | } | |
| 4901 | ||
| 4902 | pExtra_data = (mz_uint8 *)buf; | |
| 4903 | } else { | |
| 4904 | pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; | |
| 4905 | } | |
| 4906 | ||
| 4907 | do { | |
| 4908 | mz_uint32 field_id; | |
| 4909 | mz_uint32 field_data_size; | |
| 4910 | ||
| 4911 | if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { | |
| 4912 | MZ_FREE(buf); | |
| 4913 | return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); | |
| 4914 | } | |
| 4915 | ||
| 4916 | field_id = MZ_READ_LE16(pExtra_data); | |
| 4917 | field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); | |
| 4918 | ||
| 4919 | if ((field_data_size + sizeof(mz_uint16) * 2) > | |
| 4920 | extra_size_remaining) { | |
| 4921 | MZ_FREE(buf); | |
| 4922 | return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); | |
| 4923 | } | |
| 4924 | ||
| 4925 | if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { | |
| 4926 | /* Ok, the archive didn't have any zip64 headers but it uses a | |
| 4927 | * zip64 extended information field so mark it as zip64 anyway | |
| 4928 | * (this can occur with infozip's zip util when it reads | |
| 4929 | * compresses files from stdin). */ | |
| 4930 | pZip->m_pState->m_zip64 = MZ_TRUE; | |
| 4931 | pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; | |
| 4932 | break; | |
| 4933 | } | |
| 4934 | ||
| 4935 | pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; | |
| 4936 | extra_size_remaining = | |
| 4937 | extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; | |
| 4938 | } while (extra_size_remaining); | |
| 4939 | ||
| 4940 | MZ_FREE(buf); | |
| 4941 | } | |
| 4942 | } | |
| 4943 | ||
| 4944 | /* I've seen archives that aren't marked as zip64 that uses zip64 ext | |
| 4945 | * data, argh */ | |
| 4946 | if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { | |
| 4947 | if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && | |
| 4948 | (decomp_size != comp_size)) || | |
| 4949 | (decomp_size && !comp_size)) | |
| 4950 | return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); | |
| 4951 | } | |
| 4952 | ||
| 4953 | disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); | |
| 4954 | if ((disk_index == MZ_UINT16_MAX) || | |
| 4955 | ((disk_index != num_this_disk) && (disk_index != 1))) | |
| 4956 | return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); | |
| 4957 | ||
| 4958 | if (comp_size != MZ_UINT32_MAX) { | |
| 4959 | if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + | |
| 4960 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) | |
| 4961 | return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); | |
| 4962 | } | |
| 4963 | ||
| 4964 | bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); | |
| 4965 | if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) | |
| 4966 | return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); | |
| 4967 | ||
| 4968 | if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + | |
| 4969 | MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + | |
| 4970 | MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + | |
| 4971 | MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > | |
| 4972 | n) | |
| 4973 | return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); | |
| 4974 | ||
| 4975 | n -= total_header_size; | |
| 4976 | p += total_header_size; | |
| 4977 | } | |
| 4978 | } | |
| 4979 | ||
| 4980 | if (sort_central_dir) | |
| 4981 | mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); | |
| 4982 | ||
| 4983 | return MZ_TRUE; | |
| 4984 | } | |
| 4985 | ||
| 4986 | mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, | |
| 4987 | mz_uint32 flags) { | |
| 4988 | if ((!pZip) || (!pZip->m_pRead)) | |
| 4989 | return MZ_FALSE; | |
| 4990 | if (!mz_zip_reader_init_internal(pZip, flags)) | |
| 4991 | return MZ_FALSE; | |
| 4992 | pZip->m_archive_size = size; | |
| 4993 | if (!mz_zip_reader_read_central_dir(pZip, flags)) { | |
| 4994 | mz_zip_reader_end(pZip); | |
| 4995 | return MZ_FALSE; | |
| 4996 | } | |
| 4997 | return MZ_TRUE; | |
| 4998 | } | |
| 4999 | ||
| 5000 | static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, | |
| 5001 | void *pBuf, size_t n) { | |
| 5002 | mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; | |
| 5003 | size_t s = (file_ofs >= pZip->m_archive_size) | |
| 5004 | ? 0 | |
| 5005 | : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); | |
| 5006 | memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); | |
| 5007 | return s; | |
| 5008 | } | |
| 5009 | ||
| 5010 | mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, | |
| 5011 | size_t size, mz_uint32 flags) { | |
| 5012 | if (!mz_zip_reader_init_internal(pZip, flags)) | |
| 5013 | return MZ_FALSE; | |
| 5014 | pZip->m_archive_size = size; | |
| 5015 | pZip->m_pRead = mz_zip_mem_read_func; | |
| 5016 | pZip->m_pIO_opaque = pZip; | |
| 5017 | #ifdef __cplusplus | |
| 5018 | pZip->m_pState->m_pMem = const_cast<void *>(pMem); | |
| 5019 | #else | |
| 5020 | pZip->m_pState->m_pMem = (void *)pMem; | |
| 5021 | #endif | |
| 5022 | pZip->m_pState->m_mem_size = size; | |
| 5023 | if (!mz_zip_reader_read_central_dir(pZip, flags)) { | |
| 5024 | mz_zip_reader_end(pZip); | |
| 5025 | return MZ_FALSE; | |
| 5026 | } | |
| 5027 | return MZ_TRUE; | |
| 5028 | } | |
| 5029 | ||
| 5030 | #ifndef MINIZ_NO_STDIO | |
| 5031 | static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, | |
| 5032 | void *pBuf, size_t n) { | |
| 5033 | mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; | |
| 5034 | mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); | |
| 5035 | if (((mz_int64)file_ofs < 0) || | |
| 5036 | (((cur_ofs != (mz_int64)file_ofs)) && | |
| 5037 | (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) | |
| 5038 | return 0; | |
| 5039 | return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); | |
| 5040 | } | |
| 5041 | ||
| 5042 | mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, | |
| 5043 | mz_uint32 flags) { | |
| 5044 | mz_uint64 file_size; | |
| 5045 | MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); | |
| 5046 | if (!pFile) | |
| 5047 | return MZ_FALSE; | |
| 5048 | if (MZ_FSEEK64(pFile, 0, SEEK_END)) { | |
| 5049 | MZ_FCLOSE(pFile); | |
| 5050 | return MZ_FALSE; | |
| 5051 | } | |
| 5052 | file_size = MZ_FTELL64(pFile); | |
| 5053 | if (!mz_zip_reader_init_internal(pZip, flags)) { | |
| 5054 | MZ_FCLOSE(pFile); | |
| 5055 | return MZ_FALSE; | |
| 5056 | } | |
| 5057 | pZip->m_pRead = mz_zip_file_read_func; | |
| 5058 | pZip->m_pIO_opaque = pZip; | |
| 5059 | pZip->m_pState->m_pFile = pFile; | |
| 5060 | pZip->m_archive_size = file_size; | |
| 5061 | if (!mz_zip_reader_read_central_dir(pZip, flags)) { | |
| 5062 | mz_zip_reader_end(pZip); | |
| 5063 | return MZ_FALSE; | |
| 5064 | } | |
| 5065 | return MZ_TRUE; | |
| 5066 | } | |
| 5067 | #endif // #ifndef MINIZ_NO_STDIO | |
| 5068 | ||
| 5069 | mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { | |
| 5070 | return pZip ? pZip->m_total_files : 0; | |
| 5071 | } | |
| 5072 | ||
| 5073 | static MZ_FORCEINLINE const mz_uint8 * | |
| 5074 | mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { | |
| 5075 | if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || | |
| 5076 | (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) | |
| 5077 | return NULL; | |
| 5078 | return &MZ_ZIP_ARRAY_ELEMENT( | |
| 5079 | &pZip->m_pState->m_central_dir, mz_uint8, | |
| 5080 | MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, | |
| 5081 | file_index)); | |
| 5082 | } | |
| 5083 | ||
| 5084 | mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, | |
| 5085 | mz_uint file_index) { | |
| 5086 | mz_uint m_bit_flag; | |
| 5087 | const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); | |
| 5088 | if (!p) | |
| 5089 | return MZ_FALSE; | |
| 5090 | m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); | |
| 5091 | return (m_bit_flag & 1); | |
| 5092 | } | |
| 5093 | ||
| 5094 | mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, | |
| 5095 | mz_uint file_index) { | |
| 5096 | mz_uint filename_len, external_attr; | |
| 5097 | const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); | |
| 5098 | if (!p) | |
| 5099 | return MZ_FALSE; | |
| 5100 | ||
| 5101 | // First see if the filename ends with a '/' character. | |
| 5102 | filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); | |
| 5103 | if (filename_len) { | |
| 5104 | if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') | |
| 5105 | return MZ_TRUE; | |
| 5106 | } | |
| 5107 | ||
| 5108 | // Bugfix: This code was also checking if the internal attribute was non-zero, | |
| 5109 | // which wasn't correct. Most/all zip writers (hopefully) set DOS | |
| 5110 | // file/directory attributes in the low 16-bits, so check for the DOS | |
| 5111 | // directory flag and ignore the source OS ID in the created by field. | |
| 5112 | // FIXME: Remove this check? Is it necessary - we already check the filename. | |
| 5113 | external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); | |
| 5114 | if ((external_attr & 0x10) != 0) | |
| 5115 | return MZ_TRUE; | |
| 5116 | ||
| 5117 | return MZ_FALSE; | |
| 5118 | } | |
| 5119 | ||
| 5120 | mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, | |
| 5121 | mz_zip_archive_file_stat *pStat) { | |
| 5122 | mz_uint n; | |
| 5123 | const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); | |
| 5124 | if ((!p) || (!pStat)) | |
| 5125 | return MZ_FALSE; | |
| 5126 | ||
| 5127 | // Unpack the central directory record. | |
| 5128 | pStat->m_file_index = file_index; | |
| 5129 | pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT( | |
| 5130 | &pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); | |
| 5131 | pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); | |
| 5132 | pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); | |
| 5133 | pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); | |
| 5134 | pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); | |
| 5135 | #ifndef MINIZ_NO_TIME | |
| 5136 | pStat->m_time = | |
| 5137 | mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), | |
| 5138 | MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); | |
| 5139 | #endif | |
| 5140 | pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); | |
| 5141 | pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); | |
| 5142 | pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); | |
| 5143 | pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); | |
| 5144 | pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); | |
| 5145 | pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); | |
| 5146 | ||
| 5147 | // Copy as much of the filename and comment as possible. | |
| 5148 | n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); | |
| 5149 | n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); | |
| 5150 | memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); | |
| 5151 | pStat->m_filename[n] = '\0'; | |
| 5152 | ||
| 5153 | n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); | |
| 5154 | n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); | |
| 5155 | pStat->m_comment_size = n; | |
| 5156 | memcpy(pStat->m_comment, | |
| 5157 | p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + | |
| 5158 | MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + | |
| 5159 | MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), | |
| 5160 | n); | |
| 5161 | pStat->m_comment[n] = '\0'; | |
| 5162 | ||
| 5163 | return MZ_TRUE; | |
| 5164 | } | |
| 5165 | ||
| 5166 | mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, | |
| 5167 | char *pFilename, mz_uint filename_buf_size) { | |
| 5168 | mz_uint n; | |
| 5169 | const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); | |
| 5170 | if (!p) { | |
| 5171 | if (filename_buf_size) | |
| 5172 | pFilename[0] = '\0'; | |
| 5173 | return 0; | |
| 5174 | } | |
| 5175 | n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); | |
| 5176 | if (filename_buf_size) { | |
| 5177 | n = MZ_MIN(n, filename_buf_size - 1); | |
| 5178 | memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); | |
| 5179 | pFilename[n] = '\0'; | |
| 5180 | } | |
| 5181 | return n + 1; | |
| 5182 | } | |
| 5183 | ||
| 5184 | static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, | |
| 5185 | const char *pB, | |
| 5186 | mz_uint len, | |
| 5187 | mz_uint flags) { | |
| 5188 | mz_uint i; | |
| 5189 | if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) | |
| 5190 | return 0 == memcmp(pA, pB, len); | |
| 5191 | for (i = 0; i < len; ++i) | |
| 5192 | if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) | |
| 5193 | return MZ_FALSE; | |
| 5194 | return MZ_TRUE; | |
| 5195 | } | |
| 5196 | ||
| 5197 | static MZ_FORCEINLINE int | |
| 5198 | mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, | |
| 5199 | const mz_zip_array *pCentral_dir_offsets, | |
| 5200 | mz_uint l_index, const char *pR, mz_uint r_len) { | |
| 5201 | const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( | |
| 5202 | pCentral_dir_array, mz_uint8, | |
| 5203 | MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, | |
| 5204 | l_index)), | |
| 5205 | *pE; | |
| 5206 | mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); | |
| 5207 | mz_uint8 l = 0, r = 0; | |
| 5208 | pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; | |
| 5209 | pE = pL + MZ_MIN(l_len, r_len); | |
| 5210 | while (pL < pE) { | |
| 5211 | if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) | |
| 5212 | break; | |
| 5213 | pL++; | |
| 5214 | pR++; | |
| 5215 | } | |
| 5216 | return (pL == pE) ? (int)(l_len - r_len) : (l - r); | |
| 5217 | } | |
| 5218 | ||
| 5219 | static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, | |
| 5220 | const char *pFilename) { | |
| 5221 | mz_zip_internal_state *pState = pZip->m_pState; | |
| 5222 | const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; | |
| 5223 | const mz_zip_array *pCentral_dir = &pState->m_central_dir; | |
| 5224 | mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT( | |
| 5225 | &pState->m_sorted_central_dir_offsets, mz_uint32, 0); | |
| 5226 | const int size = pZip->m_total_files; | |
| 5227 | const mz_uint filename_len = (mz_uint)strlen(pFilename); | |
| 5228 | int l = 0, h = size - 1; | |
| 5229 | while (l <= h) { | |
| 5230 | int m = (l + h) >> 1, file_index = pIndices[m], | |
| 5231 | comp = | |
| 5232 | mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, | |
| 5233 | file_index, pFilename, filename_len); | |
| 5234 | if (!comp) | |
| 5235 | return file_index; | |
| 5236 | else if (comp < 0) | |
| 5237 | l = m + 1; | |
| 5238 | else | |
| 5239 | h = m - 1; | |
| 5240 | } | |
| 5241 | return -1; | |
| 5242 | } | |
| 5243 | ||
| 5244 | int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, | |
| 5245 | const char *pComment, mz_uint flags) { | |
| 5246 | mz_uint file_index; | |
| 5247 | size_t name_len, comment_len; | |
| 5248 | if ((!pZip) || (!pZip->m_pState) || (!pName) || | |
| 5249 | (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) | |
| 5250 | return -1; | |
| 5251 | if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && | |
| 5252 | (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) | |
| 5253 | return mz_zip_reader_locate_file_binary_search(pZip, pName); | |
| 5254 | name_len = strlen(pName); | |
| 5255 | if (name_len > 0xFFFF) | |
| 5256 | return -1; | |
| 5257 | comment_len = pComment ? strlen(pComment) : 0; | |
| 5258 | if (comment_len > 0xFFFF) | |
| 5259 | return -1; | |
| 5260 | for (file_index = 0; file_index < pZip->m_total_files; file_index++) { | |
| 5261 | const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT( | |
| 5262 | &pZip->m_pState->m_central_dir, mz_uint8, | |
| 5263 | MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, | |
| 5264 | file_index)); | |
| 5265 | mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); | |
| 5266 | const char *pFilename = | |
| 5267 | (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; | |
| 5268 | if (filename_len < name_len) | |
| 5269 | continue; | |
| 5270 | if (comment_len) { | |
| 5271 | mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), | |
| 5272 | file_comment_len = | |
| 5273 | MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); | |
| 5274 | const char *pFile_comment = pFilename + filename_len + file_extra_len; | |
| 5275 | if ((file_comment_len != comment_len) || | |
| 5276 | (!mz_zip_reader_string_equal(pComment, pFile_comment, | |
| 5277 | file_comment_len, flags))) | |
| 5278 | continue; | |
| 5279 | } | |
| 5280 | if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { | |
| 5281 | int ofs = filename_len - 1; | |
| 5282 | do { | |
| 5283 | if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || | |
| 5284 | (pFilename[ofs] == ':')) | |
| 5285 | break; | |
| 5286 | } while (--ofs >= 0); | |
| 5287 | ofs++; | |
| 5288 | pFilename += ofs; | |
| 5289 | filename_len -= ofs; | |
| 5290 | } | |
| 5291 | if ((filename_len == name_len) && | |
| 5292 | (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) | |
| 5293 | return file_index; | |
| 5294 | } | |
| 5295 | return -1; | |
| 5296 | } | |
| 5297 | ||
| 5298 | mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, | |
| 5299 | mz_uint file_index, void *pBuf, | |
| 5300 | size_t buf_size, mz_uint flags, | |
| 5301 | void *pUser_read_buf, | |
| 5302 | size_t user_read_buf_size) { | |
| 5303 | int status = TINFL_STATUS_DONE; | |
| 5304 | mz_uint64 needed_size, cur_file_ofs, comp_remaining, | |
| 5305 | out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; | |
| 5306 | mz_zip_archive_file_stat file_stat; | |
| 5307 | void *pRead_buf; | |
| 5308 | mz_uint32 | |
| 5309 | local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / | |
| 5310 | sizeof(mz_uint32)]; | |
| 5311 | mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; | |
| 5312 | tinfl_decompressor inflator; | |
| 5313 | ||
| 5314 | if ((buf_size) && (!pBuf)) | |
| 5315 | return MZ_FALSE; | |
| 5316 | ||
| 5317 | if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) | |
| 5318 | return MZ_FALSE; | |
| 5319 | ||
| 5320 | // Empty file, or a directory (but not always a directory - I've seen odd zips | |
| 5321 | // with directories that have compressed data which inflates to 0 bytes) | |
| 5322 | if (!file_stat.m_comp_size) | |
| 5323 | return MZ_TRUE; | |
| 5324 | ||
| 5325 | // Entry is a subdirectory (I've seen old zips with dir entries which have | |
| 5326 | // compressed deflate data which inflates to 0 bytes, but these entries claim | |
| 5327 | // to uncompress to 512 bytes in the headers). I'm torn how to handle this | |
| 5328 | // case - should it fail instead? | |
| 5329 | if (mz_zip_reader_is_file_a_directory(pZip, file_index)) | |
| 5330 | return MZ_TRUE; | |
| 5331 | ||
| 5332 | // Encryption and patch files are not supported. | |
| 5333 | if (file_stat.m_bit_flag & (1 | 32)) | |
| 5334 | return MZ_FALSE; | |
| 5335 | ||
| 5336 | // This function only supports stored and deflate. | |
| 5337 | if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && | |
| 5338 | (file_stat.m_method != MZ_DEFLATED)) | |
| 5339 | return MZ_FALSE; | |
| 5340 | ||
| 5341 | // Ensure supplied output buffer is large enough. | |
| 5342 | needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size | |
| 5343 | : file_stat.m_uncomp_size; | |
| 5344 | if (buf_size < needed_size) | |
| 5345 | return MZ_FALSE; | |
| 5346 | ||
| 5347 | // Read and parse the local directory entry. | |
| 5348 | cur_file_ofs = file_stat.m_local_header_ofs; | |
| 5349 | if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, | |
| 5350 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != | |
| 5351 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE) | |
| 5352 | return MZ_FALSE; | |
| 5353 | if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) | |
| 5354 | return MZ_FALSE; | |
| 5355 | ||
| 5356 | cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + | |
| 5357 | MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + | |
| 5358 | MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); | |
| 5359 | if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) | |
| 5360 | return MZ_FALSE; | |
| 5361 | ||
| 5362 | if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { | |
| 5363 | // The file is stored or the caller has requested the compressed data. | |
| 5364 | if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, | |
| 5365 | (size_t)needed_size) != needed_size) | |
| 5366 | return MZ_FALSE; | |
| 5367 | return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || | |
| 5368 | (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, | |
| 5369 | (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); | |
| 5370 | } | |
| 5371 | ||
| 5372 | // Decompress the file either directly from memory or from a file input | |
| 5373 | // buffer. | |
| 5374 | tinfl_init(&inflator); | |
| 5375 | ||
| 5376 | if (pZip->m_pState->m_pMem) { | |
| 5377 | // Read directly from the archive in memory. | |
| 5378 | pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; | |
| 5379 | read_buf_size = read_buf_avail = file_stat.m_comp_size; | |
| 5380 | comp_remaining = 0; | |
| 5381 | } else if (pUser_read_buf) { | |
| 5382 | // Use a user provided read buffer. | |
| 5383 | if (!user_read_buf_size) | |
| 5384 | return MZ_FALSE; | |
| 5385 | pRead_buf = (mz_uint8 *)pUser_read_buf; | |
| 5386 | read_buf_size = user_read_buf_size; | |
| 5387 | read_buf_avail = 0; | |
| 5388 | comp_remaining = file_stat.m_comp_size; | |
| 5389 | } else { | |
| 5390 | // Temporarily allocate a read buffer. | |
| 5391 | read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); | |
| 5392 | if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) | |
| 5393 | return MZ_FALSE; | |
| 5394 | ||
| 5395 | if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, | |
| 5396 | (size_t)read_buf_size))) | |
| 5397 | return MZ_FALSE; | |
| 5398 | read_buf_avail = 0; | |
| 5399 | comp_remaining = file_stat.m_comp_size; | |
| 5400 | } | |
| 5401 | ||
| 5402 | do { | |
| 5403 | size_t in_buf_size, | |
| 5404 | out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); | |
| 5405 | if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { | |
| 5406 | read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); | |
| 5407 | if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, | |
| 5408 | (size_t)read_buf_avail) != read_buf_avail) { | |
| 5409 | status = TINFL_STATUS_FAILED; | |
| 5410 | break; | |
| 5411 | } | |
| 5412 | cur_file_ofs += read_buf_avail; | |
| 5413 | comp_remaining -= read_buf_avail; | |
| 5414 | read_buf_ofs = 0; | |
| 5415 | } | |
| 5416 | in_buf_size = (size_t)read_buf_avail; | |
| 5417 | status = tinfl_decompress( | |
| 5418 | &inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, | |
| 5419 | (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, | |
| 5420 | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | | |
| 5421 | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); | |
| 5422 | read_buf_avail -= in_buf_size; | |
| 5423 | read_buf_ofs += in_buf_size; | |
| 5424 | out_buf_ofs += out_buf_size; | |
| 5425 | } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); | |
| 5426 | ||
| 5427 | if (status == TINFL_STATUS_DONE) { | |
| 5428 | // Make sure the entire file was decompressed, and check its CRC. | |
| 5429 | if ((out_buf_ofs != file_stat.m_uncomp_size) || | |
| 5430 | (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, | |
| 5431 | (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) | |
| 5432 | status = TINFL_STATUS_FAILED; | |
| 5433 | } | |
| 5434 | ||
| 5435 | if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) | |
| 5436 | pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); | |
| 5437 | ||
| 5438 | return status == TINFL_STATUS_DONE; | |
| 5439 | } | |
| 5440 | ||
| 5441 | mz_bool mz_zip_reader_extract_file_to_mem_no_alloc( | |
| 5442 | mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, | |
| 5443 | mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { | |
| 5444 | int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); | |
| 5445 | if (file_index < 0) | |
| 5446 | return MZ_FALSE; | |
| 5447 | return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, | |
| 5448 | flags, pUser_read_buf, | |
| 5449 | user_read_buf_size); | |
| 5450 | } | |
| 5451 | ||
| 5452 | mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, | |
| 5453 | void *pBuf, size_t buf_size, | |
| 5454 | mz_uint flags) { | |
| 5455 | return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, | |
| 5456 | flags, NULL, 0); | |
| 5457 | } | |
| 5458 | ||
| 5459 | mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, | |
| 5460 | const char *pFilename, void *pBuf, | |
| 5461 | size_t buf_size, mz_uint flags) { | |
| 5462 | return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, | |
| 5463 | buf_size, flags, NULL, 0); | |
| 5464 | } | |
| 5465 | ||
| 5466 | void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, | |
| 5467 | size_t *pSize, mz_uint flags) { | |
| 5468 | mz_uint64 comp_size, uncomp_size, alloc_size; | |
| 5469 | const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); | |
| 5470 | void *pBuf; | |
| 5471 | ||
| 5472 | if (pSize) | |
| 5473 | *pSize = 0; | |
| 5474 | if (!p) | |
| 5475 | return NULL; | |
| 5476 | ||
| 5477 | comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); | |
| 5478 | uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); | |
| 5479 | ||
| 5480 | alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; | |
| 5481 | if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) | |
| 5482 | return NULL; | |
| 5483 | if (NULL == | |
| 5484 | (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) | |
| 5485 | return NULL; | |
| 5486 | ||
| 5487 | if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, | |
| 5488 | flags)) { | |
| 5489 | pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); | |
| 5490 | return NULL; | |
| 5491 | } | |
| 5492 | ||
| 5493 | if (pSize) | |
| 5494 | *pSize = (size_t)alloc_size; | |
| 5495 | return pBuf; | |
| 5496 | } | |
| 5497 | ||
| 5498 | void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, | |
| 5499 | const char *pFilename, size_t *pSize, | |
| 5500 | mz_uint flags) { | |
| 5501 | int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); | |
| 5502 | if (file_index < 0) { | |
| 5503 | if (pSize) | |
| 5504 | *pSize = 0; | |
| 5505 | return MZ_FALSE; | |
| 5506 | } | |
| 5507 | return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); | |
| 5508 | } | |
| 5509 | ||
| 5510 | mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, | |
| 5511 | mz_uint file_index, | |
| 5512 | mz_file_write_func pCallback, | |
| 5513 | void *pOpaque, mz_uint flags) { | |
| 5514 | int status = TINFL_STATUS_DONE; | |
| 5515 | mz_uint file_crc32 = MZ_CRC32_INIT; | |
| 5516 | mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, | |
| 5517 | out_buf_ofs = 0, cur_file_ofs; | |
| 5518 | mz_zip_archive_file_stat file_stat; | |
| 5519 | void *pRead_buf = NULL; | |
| 5520 | void *pWrite_buf = NULL; | |
| 5521 | mz_uint32 | |
| 5522 | local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / | |
| 5523 | sizeof(mz_uint32)]; | |
| 5524 | mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; | |
| 5525 | ||
| 5526 | if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) | |
| 5527 | return MZ_FALSE; | |
| 5528 | ||
| 5529 | // Empty file, or a directory (but not always a directory - I've seen odd zips | |
| 5530 | // with directories that have compressed data which inflates to 0 bytes) | |
| 5531 | if (!file_stat.m_comp_size) | |
| 5532 | return MZ_TRUE; | |
| 5533 | ||
| 5534 | // Entry is a subdirectory (I've seen old zips with dir entries which have | |
| 5535 | // compressed deflate data which inflates to 0 bytes, but these entries claim | |
| 5536 | // to uncompress to 512 bytes in the headers). I'm torn how to handle this | |
| 5537 | // case - should it fail instead? | |
| 5538 | if (mz_zip_reader_is_file_a_directory(pZip, file_index)) | |
| 5539 | return MZ_TRUE; | |
| 5540 | ||
| 5541 | // Encryption and patch files are not supported. | |
| 5542 | if (file_stat.m_bit_flag & (1 | 32)) | |
| 5543 | return MZ_FALSE; | |
| 5544 | ||
| 5545 | // This function only supports stored and deflate. | |
| 5546 | if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && | |
| 5547 | (file_stat.m_method != MZ_DEFLATED)) | |
| 5548 | return MZ_FALSE; | |
| 5549 | ||
| 5550 | // Read and parse the local directory entry. | |
| 5551 | cur_file_ofs = file_stat.m_local_header_ofs; | |
| 5552 | if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, | |
| 5553 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != | |
| 5554 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE) | |
| 5555 | return MZ_FALSE; | |
| 5556 | if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) | |
| 5557 | return MZ_FALSE; | |
| 5558 | ||
| 5559 | cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + | |
| 5560 | MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + | |
| 5561 | MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); | |
| 5562 | if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) | |
| 5563 | return MZ_FALSE; | |
| 5564 | ||
| 5565 | // Decompress the file either directly from memory or from a file input | |
| 5566 | // buffer. | |
| 5567 | if (pZip->m_pState->m_pMem) { | |
| 5568 | pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; | |
| 5569 | read_buf_size = read_buf_avail = file_stat.m_comp_size; | |
| 5570 | comp_remaining = 0; | |
| 5571 | } else { | |
| 5572 | read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); | |
| 5573 | if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, | |
| 5574 | (size_t)read_buf_size))) | |
| 5575 | return MZ_FALSE; | |
| 5576 | read_buf_avail = 0; | |
| 5577 | comp_remaining = file_stat.m_comp_size; | |
| 5578 | } | |
| 5579 | ||
| 5580 | if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { | |
| 5581 | // The file is stored or the caller has requested the compressed data. | |
| 5582 | if (pZip->m_pState->m_pMem) { | |
| 5583 | if (((sizeof(size_t) == sizeof(mz_uint32))) && | |
| 5584 | (file_stat.m_comp_size > 0xFFFFFFFF)) | |
| 5585 | return MZ_FALSE; | |
| 5586 | ||
| 5587 | if (pCallback(pOpaque, out_buf_ofs, pRead_buf, | |
| 5588 | (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) | |
| 5589 | status = TINFL_STATUS_FAILED; | |
| 5590 | else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) | |
| 5591 | file_crc32 = | |
| 5592 | (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, | |
| 5593 | (size_t)file_stat.m_comp_size); | |
| 5594 | // cur_file_ofs += file_stat.m_comp_size; | |
| 5595 | out_buf_ofs += file_stat.m_comp_size; | |
| 5596 | // comp_remaining = 0; | |
| 5597 | } else { | |
| 5598 | while (comp_remaining) { | |
| 5599 | read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); | |
| 5600 | if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, | |
| 5601 | (size_t)read_buf_avail) != read_buf_avail) { | |
| 5602 | status = TINFL_STATUS_FAILED; | |
| 5603 | break; | |
| 5604 | } | |
| 5605 | ||
| 5606 | if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) | |
| 5607 | file_crc32 = (mz_uint32)mz_crc32( | |
| 5608 | file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); | |
| 5609 | ||
| 5610 | if (pCallback(pOpaque, out_buf_ofs, pRead_buf, | |
| 5611 | (size_t)read_buf_avail) != read_buf_avail) { | |
| 5612 | status = TINFL_STATUS_FAILED; | |
| 5613 | break; | |
| 5614 | } | |
| 5615 | cur_file_ofs += read_buf_avail; | |
| 5616 | out_buf_ofs += read_buf_avail; | |
| 5617 | comp_remaining -= read_buf_avail; | |
| 5618 | } | |
| 5619 | } | |
| 5620 | } else { | |
| 5621 | tinfl_decompressor inflator; | |
| 5622 | tinfl_init(&inflator); | |
| 5623 | ||
| 5624 | if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, | |
| 5625 | TINFL_LZ_DICT_SIZE))) | |
| 5626 | status = TINFL_STATUS_FAILED; | |
| 5627 | else { | |
| 5628 | do { | |
| 5629 | mz_uint8 *pWrite_buf_cur = | |
| 5630 | (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); | |
| 5631 | size_t in_buf_size, | |
| 5632 | out_buf_size = | |
| 5633 | TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); | |
| 5634 | if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { | |
| 5635 | read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); | |
| 5636 | if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, | |
| 5637 | (size_t)read_buf_avail) != read_buf_avail) { | |
| 5638 | status = TINFL_STATUS_FAILED; | |
| 5639 | break; | |
| 5640 | } | |
| 5641 | cur_file_ofs += read_buf_avail; | |
| 5642 | comp_remaining -= read_buf_avail; | |
| 5643 | read_buf_ofs = 0; | |
| 5644 | } | |
| 5645 | ||
| 5646 | in_buf_size = (size_t)read_buf_avail; | |
| 5647 | status = tinfl_decompress( | |
| 5648 | &inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, | |
| 5649 | (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, | |
| 5650 | comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); | |
| 5651 | read_buf_avail -= in_buf_size; | |
| 5652 | read_buf_ofs += in_buf_size; | |
| 5653 | ||
| 5654 | if (out_buf_size) { | |
| 5655 | if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != | |
| 5656 | out_buf_size) { | |
| 5657 | status = TINFL_STATUS_FAILED; | |
| 5658 | break; | |
| 5659 | } | |
| 5660 | file_crc32 = | |
| 5661 | (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); | |
| 5662 | if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { | |
| 5663 | status = TINFL_STATUS_FAILED; | |
| 5664 | break; | |
| 5665 | } | |
| 5666 | } | |
| 5667 | } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || | |
| 5668 | (status == TINFL_STATUS_HAS_MORE_OUTPUT)); | |
| 5669 | } | |
| 5670 | } | |
| 5671 | ||
| 5672 | if ((status == TINFL_STATUS_DONE) && | |
| 5673 | (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { | |
| 5674 | // Make sure the entire file was decompressed, and check its CRC. | |
| 5675 | if ((out_buf_ofs != file_stat.m_uncomp_size) || | |
| 5676 | (file_crc32 != file_stat.m_crc32)) | |
| 5677 | status = TINFL_STATUS_FAILED; | |
| 5678 | } | |
| 5679 | ||
| 5680 | if (!pZip->m_pState->m_pMem) | |
| 5681 | pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); | |
| 5682 | if (pWrite_buf) | |
| 5683 | pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); | |
| 5684 | ||
| 5685 | return status == TINFL_STATUS_DONE; | |
| 5686 | } | |
| 5687 | ||
| 5688 | mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, | |
| 5689 | const char *pFilename, | |
| 5690 | mz_file_write_func pCallback, | |
| 5691 | void *pOpaque, mz_uint flags) { | |
| 5692 | int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); | |
| 5693 | if (file_index < 0) | |
| 5694 | return MZ_FALSE; | |
| 5695 | return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, | |
| 5696 | flags); | |
| 5697 | } | |
| 5698 | ||
| 5699 | #ifndef MINIZ_NO_STDIO | |
| 5700 | static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, | |
| 5701 | const void *pBuf, size_t n) { | |
| 5702 | (void)ofs; | |
| 5703 | return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); | |
| 5704 | } | |
| 5705 | ||
| 5706 | mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, | |
| 5707 | const char *pDst_filename, | |
| 5708 | mz_uint flags) { | |
| 5709 | mz_bool status; | |
| 5710 | mz_zip_archive_file_stat file_stat; | |
| 5711 | MZ_FILE *pFile; | |
| 5712 | if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) | |
| 5713 | return MZ_FALSE; | |
| 5714 | ||
| 5715 | pFile = MZ_FOPEN(pDst_filename, "wb"); | |
| 5716 | if (!pFile) | |
| 5717 | return MZ_FALSE; | |
| 5718 | status = mz_zip_reader_extract_to_callback( | |
| 5719 | pZip, file_index, mz_zip_file_write_callback, pFile, flags); | |
| 5720 | if (MZ_FCLOSE(pFile) == EOF) | |
| 5721 | return MZ_FALSE; | |
| 5722 | #ifndef MINIZ_NO_TIME | |
| 5723 | if (status) { | |
| 5724 | mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); | |
| 5725 | } | |
| 5726 | #endif | |
| 5727 | ||
| 5728 | return status; | |
| 5729 | } | |
| 5730 | #endif // #ifndef MINIZ_NO_STDIO | |
| 5731 | ||
| 5732 | mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { | |
| 5733 | if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || | |
| 5734 | (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) | |
| 5735 | return MZ_FALSE; | |
| 5736 | ||
| 5737 | mz_zip_internal_state *pState = pZip->m_pState; | |
| 5738 | pZip->m_pState = NULL; | |
| 5739 | mz_zip_array_clear(pZip, &pState->m_central_dir); | |
| 5740 | mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); | |
| 5741 | mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); | |
| 5742 | ||
| 5743 | #ifndef MINIZ_NO_STDIO | |
| 5744 | if (pState->m_pFile) { | |
| 5745 | MZ_FCLOSE(pState->m_pFile); | |
| 5746 | pState->m_pFile = NULL; | |
| 5747 | } | |
| 5748 | #endif // #ifndef MINIZ_NO_STDIO | |
| 5749 | ||
| 5750 | pZip->m_pFree(pZip->m_pAlloc_opaque, pState); | |
| 5751 | ||
| 5752 | pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; | |
| 5753 | ||
| 5754 | return MZ_TRUE; | |
| 5755 | } | |
| 5756 | ||
| 5757 | #ifndef MINIZ_NO_STDIO | |
| 5758 | mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, | |
| 5759 | const char *pArchive_filename, | |
| 5760 | const char *pDst_filename, | |
| 5761 | mz_uint flags) { | |
| 5762 | int file_index = | |
| 5763 | mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); | |
| 5764 | if (file_index < 0) | |
| 5765 | return MZ_FALSE; | |
| 5766 | return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); | |
| 5767 | } | |
| 5768 | #endif | |
| 5769 | ||
| 5770 | // ------------------- .ZIP archive writing | |
| 5771 | ||
| 5772 | #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS | |
| 5773 | ||
| 5774 | static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { | |
| 5775 | p[0] = (mz_uint8)v; | |
| 5776 | p[1] = (mz_uint8)(v >> 8); | |
| 5777 | } | |
| 5778 | static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { | |
| 5779 | p[0] = (mz_uint8)v; | |
| 5780 | p[1] = (mz_uint8)(v >> 8); | |
| 5781 | p[2] = (mz_uint8)(v >> 16); | |
| 5782 | p[3] = (mz_uint8)(v >> 24); | |
| 5783 | } | |
| 5784 | #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) | |
| 5785 | #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) | |
| 5786 | ||
| 5787 | mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { | |
| 5788 | if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || | |
| 5789 | (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) | |
| 5790 | return MZ_FALSE; | |
| 5791 | ||
| 5792 | if (pZip->m_file_offset_alignment) { | |
| 5793 | // Ensure user specified file offset alignment is a power of 2. | |
| 5794 | if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) | |
| 5795 | return MZ_FALSE; | |
| 5796 | } | |
| 5797 | ||
| 5798 | if (!pZip->m_pAlloc) | |
| 5799 | pZip->m_pAlloc = def_alloc_func; | |
| 5800 | if (!pZip->m_pFree) | |
| 5801 | pZip->m_pFree = def_free_func; | |
| 5802 | if (!pZip->m_pRealloc) | |
| 5803 | pZip->m_pRealloc = def_realloc_func; | |
| 5804 | ||
| 5805 | pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; | |
| 5806 | pZip->m_archive_size = existing_size; | |
| 5807 | pZip->m_central_directory_file_ofs = 0; | |
| 5808 | pZip->m_total_files = 0; | |
| 5809 | ||
| 5810 | if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc( | |
| 5811 | pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) | |
| 5812 | return MZ_FALSE; | |
| 5813 | memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); | |
| 5814 | MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, | |
| 5815 | sizeof(mz_uint8)); | |
| 5816 | MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, | |
| 5817 | sizeof(mz_uint32)); | |
| 5818 | MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, | |
| 5819 | sizeof(mz_uint32)); | |
| 5820 | return MZ_TRUE; | |
| 5821 | } | |
| 5822 | ||
| 5823 | static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, | |
| 5824 | const void *pBuf, size_t n) { | |
| 5825 | mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; | |
| 5826 | mz_zip_internal_state *pState = pZip->m_pState; | |
| 5827 | mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); | |
| 5828 | ||
| 5829 | if ((!n) || | |
| 5830 | ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) | |
| 5831 | return 0; | |
| 5832 | ||
| 5833 | if (new_size > pState->m_mem_capacity) { | |
| 5834 | void *pNew_block; | |
| 5835 | size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); | |
| 5836 | while (new_capacity < new_size) | |
| 5837 | new_capacity *= 2; | |
| 5838 | if (NULL == (pNew_block = pZip->m_pRealloc( | |
| 5839 | pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) | |
| 5840 | return 0; | |
| 5841 | pState->m_pMem = pNew_block; | |
| 5842 | pState->m_mem_capacity = new_capacity; | |
| 5843 | } | |
| 5844 | memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); | |
| 5845 | pState->m_mem_size = (size_t)new_size; | |
| 5846 | return n; | |
| 5847 | } | |
| 5848 | ||
| 5849 | mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, | |
| 5850 | size_t size_to_reserve_at_beginning, | |
| 5851 | size_t initial_allocation_size) { | |
| 5852 | pZip->m_pWrite = mz_zip_heap_write_func; | |
| 5853 | pZip->m_pIO_opaque = pZip; | |
| 5854 | if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) | |
| 5855 | return MZ_FALSE; | |
| 5856 | if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, | |
| 5857 | size_to_reserve_at_beginning))) { | |
| 5858 | if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc( | |
| 5859 | pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { | |
| 5860 | mz_zip_writer_end(pZip); | |
| 5861 | return MZ_FALSE; | |
| 5862 | } | |
| 5863 | pZip->m_pState->m_mem_capacity = initial_allocation_size; | |
| 5864 | } | |
| 5865 | return MZ_TRUE; | |
| 5866 | } | |
| 5867 | ||
| 5868 | #ifndef MINIZ_NO_STDIO | |
| 5869 | static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, | |
| 5870 | const void *pBuf, size_t n) { | |
| 5871 | mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; | |
| 5872 | mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); | |
| 5873 | if (((mz_int64)file_ofs < 0) || | |
| 5874 | (((cur_ofs != (mz_int64)file_ofs)) && | |
| 5875 | (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) | |
| 5876 | return 0; | |
| 5877 | return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); | |
| 5878 | } | |
| 5879 | ||
| 5880 | mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, | |
| 5881 | mz_uint64 size_to_reserve_at_beginning) { | |
| 5882 | MZ_FILE *pFile; | |
| 5883 | pZip->m_pWrite = mz_zip_file_write_func; | |
| 5884 | pZip->m_pIO_opaque = pZip; | |
| 5885 | if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) | |
| 5886 | return MZ_FALSE; | |
| 5887 | if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) { | |
| 5888 | mz_zip_writer_end(pZip); | |
| 5889 | return MZ_FALSE; | |
| 5890 | } | |
| 5891 | pZip->m_pState->m_pFile = pFile; | |
| 5892 | if (size_to_reserve_at_beginning) { | |
| 5893 | mz_uint64 cur_ofs = 0; | |
| 5894 | char buf[4096]; | |
| 5895 | MZ_CLEAR_OBJ(buf); | |
| 5896 | do { | |
| 5897 | size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); | |
| 5898 | if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { | |
| 5899 | mz_zip_writer_end(pZip); | |
| 5900 | return MZ_FALSE; | |
| 5901 | } | |
| 5902 | cur_ofs += n; | |
| 5903 | size_to_reserve_at_beginning -= n; | |
| 5904 | } while (size_to_reserve_at_beginning); | |
| 5905 | } | |
| 5906 | return MZ_TRUE; | |
| 5907 | } | |
| 5908 | #endif // #ifndef MINIZ_NO_STDIO | |
| 5909 | ||
| 5910 | mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, | |
| 5911 | const char *pFilename) { | |
| 5912 | mz_zip_internal_state *pState; | |
| 5913 | if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) | |
| 5914 | return MZ_FALSE; | |
| 5915 | // No sense in trying to write to an archive that's already at the support max | |
| 5916 | // size | |
| 5917 | if ((pZip->m_total_files == 0xFFFF) || | |
| 5918 | ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + | |
| 5919 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) | |
| 5920 | return MZ_FALSE; | |
| 5921 | ||
| 5922 | pState = pZip->m_pState; | |
| 5923 | ||
| 5924 | if (pState->m_pFile) { | |
| 5925 | #ifdef MINIZ_NO_STDIO | |
| 5926 | pFilename; | |
| 5927 | return MZ_FALSE; | |
| 5928 | #else | |
| 5929 | // Archive is being read from stdio - try to reopen as writable. | |
| 5930 | if (pZip->m_pIO_opaque != pZip) | |
| 5931 | return MZ_FALSE; | |
| 5932 | if (!pFilename) | |
| 5933 | return MZ_FALSE; | |
| 5934 | pZip->m_pWrite = mz_zip_file_write_func; | |
| 5935 | if (NULL == | |
| 5936 | (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { | |
| 5937 | // The mz_zip_archive is now in a bogus state because pState->m_pFile is | |
| 5938 | // NULL, so just close it. | |
| 5939 | mz_zip_reader_end(pZip); | |
| 5940 | return MZ_FALSE; | |
| 5941 | } | |
| 5942 | #endif // #ifdef MINIZ_NO_STDIO | |
| 5943 | } else if (pState->m_pMem) { | |
| 5944 | // Archive lives in a memory block. Assume it's from the heap that we can | |
| 5945 | // resize using the realloc callback. | |
| 5946 | if (pZip->m_pIO_opaque != pZip) | |
| 5947 | return MZ_FALSE; | |
| 5948 | pState->m_mem_capacity = pState->m_mem_size; | |
| 5949 | pZip->m_pWrite = mz_zip_heap_write_func; | |
| 5950 | } | |
| 5951 | // Archive is being read via a user provided read function - make sure the | |
| 5952 | // user has specified a write function too. | |
| 5953 | else if (!pZip->m_pWrite) | |
| 5954 | return MZ_FALSE; | |
| 5955 | ||
| 5956 | // Start writing new files at the archive's current central directory | |
| 5957 | // location. | |
| 5958 | pZip->m_archive_size = pZip->m_central_directory_file_ofs; | |
| 5959 | pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; | |
| 5960 | pZip->m_central_directory_file_ofs = 0; | |
| 5961 | ||
| 5962 | return MZ_TRUE; | |
| 5963 | } | |
| 5964 | ||
| 5965 | mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, | |
| 5966 | const void *pBuf, size_t buf_size, | |
| 5967 | mz_uint level_and_flags) { | |
| 5968 | return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, | |
| 5969 | level_and_flags, 0, 0); | |
| 5970 | } | |
| 5971 | ||
| 5972 | typedef struct { | |
| 5973 | mz_zip_archive *m_pZip; | |
| 5974 | mz_uint64 m_cur_archive_file_ofs; | |
| 5975 | mz_uint64 m_comp_size; | |
| 5976 | } mz_zip_writer_add_state; | |
| 5977 | ||
| 5978 | static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, | |
| 5979 | void *pUser) { | |
| 5980 | mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; | |
| 5981 | if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, | |
| 5982 | pState->m_cur_archive_file_ofs, pBuf, | |
| 5983 | len) != len) | |
| 5984 | return MZ_FALSE; | |
| 5985 | pState->m_cur_archive_file_ofs += len; | |
| 5986 | pState->m_comp_size += len; | |
| 5987 | return MZ_TRUE; | |
| 5988 | } | |
| 5989 | ||
| 5990 | static mz_bool mz_zip_writer_create_local_dir_header( | |
| 5991 | mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, | |
| 5992 | mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, | |
| 5993 | mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, | |
| 5994 | mz_uint16 dos_time, mz_uint16 dos_date) { | |
| 5995 | (void)pZip; | |
| 5996 | memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); | |
| 5997 | MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); | |
| 5998 | MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); | |
| 5999 | MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); | |
| 6000 | MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); | |
| 6001 | MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); | |
| 6002 | MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); | |
| 6003 | MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); | |
| 6004 | MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); | |
| 6005 | MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); | |
| 6006 | MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); | |
| 6007 | MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); | |
| 6008 | return MZ_TRUE; | |
| 6009 | } | |
| 6010 | ||
| 6011 | static mz_bool mz_zip_writer_create_central_dir_header( | |
| 6012 | mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, | |
| 6013 | mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, | |
| 6014 | mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, | |
| 6015 | mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, | |
| 6016 | mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { | |
| 6017 | (void)pZip; | |
| 6018 | mz_uint16 version_made_by = 10 * MZ_VER_MAJOR + MZ_VER_MINOR; | |
| 6019 | version_made_by |= (MZ_PLATFORM << 8); | |
| 6020 | ||
| 6021 | memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); | |
| 6022 | MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); | |
| 6023 | MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_MADE_BY_OFS, version_made_by); | |
| 6024 | MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); | |
| 6025 | MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); | |
| 6026 | MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); | |
| 6027 | MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); | |
| 6028 | MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); | |
| 6029 | MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); | |
| 6030 | MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); | |
| 6031 | MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); | |
| 6032 | MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); | |
| 6033 | MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); | |
| 6034 | MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); | |
| 6035 | MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); | |
| 6036 | MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); | |
| 6037 | return MZ_TRUE; | |
| 6038 | } | |
| 6039 | ||
| 6040 | static mz_bool mz_zip_writer_add_to_central_dir( | |
| 6041 | mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, | |
| 6042 | const void *pExtra, mz_uint16 extra_size, const void *pComment, | |
| 6043 | mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, | |
| 6044 | mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, | |
| 6045 | mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, | |
| 6046 | mz_uint32 ext_attributes) { | |
| 6047 | mz_zip_internal_state *pState = pZip->m_pState; | |
| 6048 | mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; | |
| 6049 | size_t orig_central_dir_size = pState->m_central_dir.m_size; | |
| 6050 | mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; | |
| 6051 | ||
| 6052 | // No zip64 support yet | |
| 6053 | if ((local_header_ofs > 0xFFFFFFFF) || | |
| 6054 | (((mz_uint64)pState->m_central_dir.m_size + | |
| 6055 | MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + | |
| 6056 | comment_size) > 0xFFFFFFFF)) | |
| 6057 | return MZ_FALSE; | |
| 6058 | ||
| 6059 | if (!mz_zip_writer_create_central_dir_header( | |
| 6060 | pZip, central_dir_header, filename_size, extra_size, comment_size, | |
| 6061 | uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, | |
| 6062 | dos_date, local_header_ofs, ext_attributes)) | |
| 6063 | return MZ_FALSE; | |
| 6064 | ||
| 6065 | if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, | |
| 6066 | MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || | |
| 6067 | (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, | |
| 6068 | filename_size)) || | |
| 6069 | (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, | |
| 6070 | extra_size)) || | |
| 6071 | (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, | |
| 6072 | comment_size)) || | |
| 6073 | (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, | |
| 6074 | &central_dir_ofs, 1))) { | |
| 6075 | // Try to push the central directory array back into its original state. | |
| 6076 | mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, | |
| 6077 | MZ_FALSE); | |
| 6078 | return MZ_FALSE; | |
| 6079 | } | |
| 6080 | ||
| 6081 | return MZ_TRUE; | |
| 6082 | } | |
| 6083 | ||
| 6084 | static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { | |
| 6085 | // Basic ZIP archive filename validity checks: Valid filenames cannot start | |
| 6086 | // with a forward slash, cannot contain a drive letter, and cannot use | |
| 6087 | // DOS-style backward slashes. | |
| 6088 | if (*pArchive_name == '/') | |
| 6089 | return MZ_FALSE; | |
| 6090 | while (*pArchive_name) { | |
| 6091 | if ((*pArchive_name == '\\') || (*pArchive_name == ':')) | |
| 6092 | return MZ_FALSE; | |
| 6093 | pArchive_name++; | |
| 6094 | } | |
| 6095 | return MZ_TRUE; | |
| 6096 | } | |
| 6097 | ||
| 6098 | static mz_uint | |
| 6099 | mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { | |
| 6100 | mz_uint32 n; | |
| 6101 | if (!pZip->m_file_offset_alignment) | |
| 6102 | return 0; | |
| 6103 | n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); | |
| 6104 | return (pZip->m_file_offset_alignment - n) & | |
| 6105 | (pZip->m_file_offset_alignment - 1); | |
| 6106 | } | |
| 6107 | ||
| 6108 | static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, | |
| 6109 | mz_uint64 cur_file_ofs, mz_uint32 n) { | |
| 6110 | char buf[4096]; | |
| 6111 | memset(buf, 0, MZ_MIN(sizeof(buf), n)); | |
| 6112 | while (n) { | |
| 6113 | mz_uint32 s = MZ_MIN(sizeof(buf), n); | |
| 6114 | if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) | |
| 6115 | return MZ_FALSE; | |
| 6116 | cur_file_ofs += s; | |
| 6117 | n -= s; | |
| 6118 | } | |
| 6119 | return MZ_TRUE; | |
| 6120 | } | |
| 6121 | ||
| 6122 | mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, | |
| 6123 | const char *pArchive_name, const void *pBuf, | |
| 6124 | size_t buf_size, const void *pComment, | |
| 6125 | mz_uint16 comment_size, | |
| 6126 | mz_uint level_and_flags, mz_uint64 uncomp_size, | |
| 6127 | mz_uint32 uncomp_crc32) { | |
| 6128 | mz_uint32 ext_attributes = 0; | |
| 6129 | mz_uint16 method = 0, dos_time = 0, dos_date = 0; | |
| 6130 | mz_uint level, num_alignment_padding_bytes; | |
| 6131 | mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, comp_size = 0; | |
| 6132 | size_t archive_name_size; | |
| 6133 | mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; | |
| 6134 | tdefl_compressor *pComp = NULL; | |
| 6135 | mz_bool store_data_uncompressed; | |
| 6136 | mz_zip_internal_state *pState; | |
| 6137 | ||
| 6138 | if ((int)level_and_flags < 0) | |
| 6139 | level_and_flags = MZ_DEFAULT_LEVEL; | |
| 6140 | level = level_and_flags & 0xF; | |
| 6141 | store_data_uncompressed = | |
| 6142 | ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); | |
| 6143 | ||
| 6144 | if ((!pZip) || (!pZip->m_pState) || | |
| 6145 | (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || | |
| 6146 | (!pArchive_name) || ((comment_size) && (!pComment)) || | |
| 6147 | (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) | |
| 6148 | return MZ_FALSE; | |
| 6149 | ||
| 6150 | local_dir_header_ofs = cur_archive_file_ofs = pZip->m_archive_size; | |
| 6151 | pState = pZip->m_pState; | |
| 6152 | ||
| 6153 | if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) | |
| 6154 | return MZ_FALSE; | |
| 6155 | // No zip64 support yet | |
| 6156 | if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) | |
| 6157 | return MZ_FALSE; | |
| 6158 | if (!mz_zip_writer_validate_archive_name(pArchive_name)) | |
| 6159 | return MZ_FALSE; | |
| 6160 | ||
| 6161 | #ifndef MINIZ_NO_TIME | |
| 6162 | { | |
| 6163 | time_t cur_time; | |
| 6164 | time(&cur_time); | |
| 6165 | mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); | |
| 6166 | } | |
| 6167 | #endif // #ifndef MINIZ_NO_TIME | |
| 6168 | ||
| 6169 | archive_name_size = strlen(pArchive_name); | |
| 6170 | if (archive_name_size > 0xFFFF) | |
| 6171 | return MZ_FALSE; | |
| 6172 | ||
| 6173 | num_alignment_padding_bytes = | |
| 6174 | mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); | |
| 6175 | ||
| 6176 | // no zip64 support yet | |
| 6177 | if ((pZip->m_total_files == 0xFFFF) || | |
| 6178 | ((pZip->m_archive_size + num_alignment_padding_bytes + | |
| 6179 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + | |
| 6180 | comment_size + archive_name_size) > 0xFFFFFFFF)) | |
| 6181 | return MZ_FALSE; | |
| 6182 | ||
| 6183 | if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { | |
| 6184 | // Set DOS Subdirectory attribute bit. | |
| 6185 | ext_attributes |= 0x10; | |
| 6186 | // Subdirectories cannot contain data. | |
| 6187 | if ((buf_size) || (uncomp_size)) | |
| 6188 | return MZ_FALSE; | |
| 6189 | } | |
| 6190 | ||
| 6191 | // Try to do any allocations before writing to the archive, so if an | |
| 6192 | // allocation fails the file remains unmodified. (A good idea if we're doing | |
| 6193 | // an in-place modification.) | |
| 6194 | if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, | |
| 6195 | MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + | |
| 6196 | archive_name_size + comment_size)) || | |
| 6197 | (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) | |
| 6198 | return MZ_FALSE; | |
| 6199 | ||
| 6200 | if ((!store_data_uncompressed) && (buf_size)) { | |
| 6201 | if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc( | |
| 6202 | pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) | |
| 6203 | return MZ_FALSE; | |
| 6204 | } | |
| 6205 | ||
| 6206 | if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, | |
| 6207 | num_alignment_padding_bytes + | |
| 6208 | sizeof(local_dir_header))) { | |
| 6209 | pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); | |
| 6210 | return MZ_FALSE; | |
| 6211 | } | |
| 6212 | local_dir_header_ofs += num_alignment_padding_bytes; | |
| 6213 | if (pZip->m_file_offset_alignment) { | |
| 6214 | MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == | |
| 6215 | 0); | |
| 6216 | } | |
| 6217 | cur_archive_file_ofs += | |
| 6218 | num_alignment_padding_bytes + sizeof(local_dir_header); | |
| 6219 | ||
| 6220 | MZ_CLEAR_OBJ(local_dir_header); | |
| 6221 | if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, | |
| 6222 | archive_name_size) != archive_name_size) { | |
| 6223 | pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); | |
| 6224 | return MZ_FALSE; | |
| 6225 | } | |
| 6226 | cur_archive_file_ofs += archive_name_size; | |
| 6227 | ||
| 6228 | if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { | |
| 6229 | uncomp_crc32 = | |
| 6230 | (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); | |
| 6231 | uncomp_size = buf_size; | |
| 6232 | if (uncomp_size <= 3) { | |
| 6233 | level = 0; | |
| 6234 | store_data_uncompressed = MZ_TRUE; | |
| 6235 | } | |
| 6236 | } | |
| 6237 | ||
| 6238 | if (store_data_uncompressed) { | |
| 6239 | if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, | |
| 6240 | buf_size) != buf_size) { | |
| 6241 | pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); | |
| 6242 | return MZ_FALSE; | |
| 6243 | } | |
| 6244 | ||
| 6245 | cur_archive_file_ofs += buf_size; | |
| 6246 | comp_size = buf_size; | |
| 6247 | ||
| 6248 | if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) | |
| 6249 | method = MZ_DEFLATED; | |
| 6250 | } else if (buf_size) { | |
| 6251 | mz_zip_writer_add_state state; | |
| 6252 | ||
| 6253 | state.m_pZip = pZip; | |
| 6254 | state.m_cur_archive_file_ofs = cur_archive_file_ofs; | |
| 6255 | state.m_comp_size = 0; | |
| 6256 | ||
| 6257 | if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, | |
| 6258 | tdefl_create_comp_flags_from_zip_params( | |
| 6259 | level, -15, MZ_DEFAULT_STRATEGY)) != | |
| 6260 | TDEFL_STATUS_OKAY) || | |
| 6261 | (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != | |
| 6262 | TDEFL_STATUS_DONE)) { | |
| 6263 | pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); | |
| 6264 | return MZ_FALSE; | |
| 6265 | } | |
| 6266 | ||
| 6267 | comp_size = state.m_comp_size; | |
| 6268 | cur_archive_file_ofs = state.m_cur_archive_file_ofs; | |
| 6269 | ||
| 6270 | method = MZ_DEFLATED; | |
| 6271 | } | |
| 6272 | ||
| 6273 | pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); | |
| 6274 | pComp = NULL; | |
| 6275 | ||
| 6276 | // no zip64 support yet | |
| 6277 | if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) | |
| 6278 | return MZ_FALSE; | |
| 6279 | ||
| 6280 | if (!mz_zip_writer_create_local_dir_header( | |
| 6281 | pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, | |
| 6282 | comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) | |
| 6283 | return MZ_FALSE; | |
| 6284 | ||
| 6285 | if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, | |
| 6286 | sizeof(local_dir_header)) != sizeof(local_dir_header)) | |
| 6287 | return MZ_FALSE; | |
| 6288 | ||
| 6289 | if (!mz_zip_writer_add_to_central_dir( | |
| 6290 | pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, | |
| 6291 | comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, | |
| 6292 | dos_time, dos_date, local_dir_header_ofs, ext_attributes)) | |
| 6293 | return MZ_FALSE; | |
| 6294 | ||
| 6295 | pZip->m_total_files++; | |
| 6296 | pZip->m_archive_size = cur_archive_file_ofs; | |
| 6297 | ||
| 6298 | return MZ_TRUE; | |
| 6299 | } | |
| 6300 | ||
| 6301 | #ifndef MINIZ_NO_STDIO | |
| 6302 | mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, | |
| 6303 | const char *pSrc_filename, const void *pComment, | |
| 6304 | mz_uint16 comment_size, mz_uint level_and_flags, | |
| 6305 | mz_uint32 ext_attributes) { | |
| 6306 | mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; | |
| 6307 | mz_uint16 method = 0, dos_time = 0, dos_date = 0; | |
| 6308 | #ifndef MINIZ_NO_TIME | |
| 6309 | time_t file_modified_time; | |
| 6310 | #endif | |
| 6311 | ||
| 6312 | mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, uncomp_size = 0, | |
| 6313 | comp_size = 0; | |
| 6314 | size_t archive_name_size; | |
| 6315 | mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; | |
| 6316 | MZ_FILE *pSrc_file = NULL; | |
| 6317 | ||
| 6318 | if ((int)level_and_flags < 0) | |
| 6319 | level_and_flags = MZ_DEFAULT_LEVEL; | |
| 6320 | level = level_and_flags & 0xF; | |
| 6321 | ||
| 6322 | if ((!pZip) || (!pZip->m_pState) || | |
| 6323 | (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || | |
| 6324 | ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) | |
| 6325 | return MZ_FALSE; | |
| 6326 | ||
| 6327 | local_dir_header_ofs = cur_archive_file_ofs = pZip->m_archive_size; | |
| 6328 | ||
| 6329 | if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) | |
| 6330 | return MZ_FALSE; | |
| 6331 | if (!mz_zip_writer_validate_archive_name(pArchive_name)) | |
| 6332 | return MZ_FALSE; | |
| 6333 | ||
| 6334 | archive_name_size = strlen(pArchive_name); | |
| 6335 | if (archive_name_size > 0xFFFF) | |
| 6336 | return MZ_FALSE; | |
| 6337 | ||
| 6338 | num_alignment_padding_bytes = | |
| 6339 | mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); | |
| 6340 | ||
| 6341 | // no zip64 support yet | |
| 6342 | if ((pZip->m_total_files == 0xFFFF) || | |
| 6343 | ((pZip->m_archive_size + num_alignment_padding_bytes + | |
| 6344 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + | |
| 6345 | comment_size + archive_name_size) > 0xFFFFFFFF)) | |
| 6346 | return MZ_FALSE; | |
| 6347 | ||
| 6348 | #ifndef MINIZ_NO_TIME | |
| 6349 | memset(&file_modified_time, 0, sizeof(file_modified_time)); | |
| 6350 | if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) | |
| 6351 | return MZ_FALSE; | |
| 6352 | mz_zip_time_t_to_dos_time(file_modified_time, &dos_time, &dos_date); | |
| 6353 | #endif | |
| 6354 | ||
| 6355 | pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); | |
| 6356 | if (!pSrc_file) | |
| 6357 | return MZ_FALSE; | |
| 6358 | MZ_FSEEK64(pSrc_file, 0, SEEK_END); | |
| 6359 | uncomp_size = MZ_FTELL64(pSrc_file); | |
| 6360 | MZ_FSEEK64(pSrc_file, 0, SEEK_SET); | |
| 6361 | ||
| 6362 | if (uncomp_size > 0xFFFFFFFF) { | |
| 6363 | // No zip64 support yet | |
| 6364 | MZ_FCLOSE(pSrc_file); | |
| 6365 | return MZ_FALSE; | |
| 6366 | } | |
| 6367 | if (uncomp_size <= 3) | |
| 6368 | level = 0; | |
| 6369 | ||
| 6370 | if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, | |
| 6371 | num_alignment_padding_bytes + | |
| 6372 | sizeof(local_dir_header))) { | |
| 6373 | MZ_FCLOSE(pSrc_file); | |
| 6374 | return MZ_FALSE; | |
| 6375 | } | |
| 6376 | local_dir_header_ofs += num_alignment_padding_bytes; | |
| 6377 | if (pZip->m_file_offset_alignment) { | |
| 6378 | MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == | |
| 6379 | 0); | |
| 6380 | } | |
| 6381 | cur_archive_file_ofs += | |
| 6382 | num_alignment_padding_bytes + sizeof(local_dir_header); | |
| 6383 | ||
| 6384 | MZ_CLEAR_OBJ(local_dir_header); | |
| 6385 | if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, | |
| 6386 | archive_name_size) != archive_name_size) { | |
| 6387 | MZ_FCLOSE(pSrc_file); | |
| 6388 | return MZ_FALSE; | |
| 6389 | } | |
| 6390 | cur_archive_file_ofs += archive_name_size; | |
| 6391 | ||
| 6392 | if (uncomp_size) { | |
| 6393 | mz_uint64 uncomp_remaining = uncomp_size; | |
| 6394 | void *pRead_buf = | |
| 6395 | pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); | |
| 6396 | if (!pRead_buf) { | |
| 6397 | MZ_FCLOSE(pSrc_file); | |
| 6398 | return MZ_FALSE; | |
| 6399 | } | |
| 6400 | ||
| 6401 | if (!level) { | |
| 6402 | while (uncomp_remaining) { | |
| 6403 | mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); | |
| 6404 | if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || | |
| 6405 | (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, | |
| 6406 | n) != n)) { | |
| 6407 | pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); | |
| 6408 | MZ_FCLOSE(pSrc_file); | |
| 6409 | return MZ_FALSE; | |
| 6410 | } | |
| 6411 | uncomp_crc32 = | |
| 6412 | (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); | |
| 6413 | uncomp_remaining -= n; | |
| 6414 | cur_archive_file_ofs += n; | |
| 6415 | } | |
| 6416 | comp_size = uncomp_size; | |
| 6417 | } else { | |
| 6418 | mz_bool result = MZ_FALSE; | |
| 6419 | mz_zip_writer_add_state state; | |
| 6420 | tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc( | |
| 6421 | pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); | |
| 6422 | if (!pComp) { | |
| 6423 | pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); | |
| 6424 | MZ_FCLOSE(pSrc_file); | |
| 6425 | return MZ_FALSE; | |
| 6426 | } | |
| 6427 | ||
| 6428 | state.m_pZip = pZip; | |
| 6429 | state.m_cur_archive_file_ofs = cur_archive_file_ofs; | |
| 6430 | state.m_comp_size = 0; | |
| 6431 | ||
| 6432 | if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, | |
| 6433 | tdefl_create_comp_flags_from_zip_params( | |
| 6434 | level, -15, MZ_DEFAULT_STRATEGY)) != | |
| 6435 | TDEFL_STATUS_OKAY) { | |
| 6436 | pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); | |
| 6437 | pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); | |
| 6438 | MZ_FCLOSE(pSrc_file); | |
| 6439 | return MZ_FALSE; | |
| 6440 | } | |
| 6441 | ||
| 6442 | for (;;) { | |
| 6443 | size_t in_buf_size = | |
| 6444 | (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); | |
| 6445 | tdefl_status status; | |
| 6446 | ||
| 6447 | if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) | |
| 6448 | break; | |
| 6449 | ||
| 6450 | uncomp_crc32 = (mz_uint32)mz_crc32( | |
| 6451 | uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); | |
| 6452 | uncomp_remaining -= in_buf_size; | |
| 6453 | ||
| 6454 | status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, | |
| 6455 | uncomp_remaining ? TDEFL_NO_FLUSH | |
| 6456 | : TDEFL_FINISH); | |
| 6457 | if (status == TDEFL_STATUS_DONE) { | |
| 6458 | result = MZ_TRUE; | |
| 6459 | break; | |
| 6460 | } else if (status != TDEFL_STATUS_OKAY) | |
| 6461 | break; | |
| 6462 | } | |
| 6463 | ||
| 6464 | pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); | |
| 6465 | ||
| 6466 | if (!result) { | |
| 6467 | pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); | |
| 6468 | MZ_FCLOSE(pSrc_file); | |
| 6469 | return MZ_FALSE; | |
| 6470 | } | |
| 6471 | ||
| 6472 | comp_size = state.m_comp_size; | |
| 6473 | cur_archive_file_ofs = state.m_cur_archive_file_ofs; | |
| 6474 | ||
| 6475 | method = MZ_DEFLATED; | |
| 6476 | } | |
| 6477 | ||
| 6478 | pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); | |
| 6479 | } | |
| 6480 | ||
| 6481 | MZ_FCLOSE(pSrc_file); | |
| 6482 | pSrc_file = NULL; | |
| 6483 | ||
| 6484 | // no zip64 support yet | |
| 6485 | if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) | |
| 6486 | return MZ_FALSE; | |
| 6487 | ||
| 6488 | if (!mz_zip_writer_create_local_dir_header( | |
| 6489 | pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, | |
| 6490 | comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) | |
| 6491 | return MZ_FALSE; | |
| 6492 | ||
| 6493 | if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, | |
| 6494 | sizeof(local_dir_header)) != sizeof(local_dir_header)) | |
| 6495 | return MZ_FALSE; | |
| 6496 | ||
| 6497 | if (!mz_zip_writer_add_to_central_dir( | |
| 6498 | pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, | |
| 6499 | comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, | |
| 6500 | dos_time, dos_date, local_dir_header_ofs, ext_attributes)) | |
| 6501 | return MZ_FALSE; | |
| 6502 | ||
| 6503 | pZip->m_total_files++; | |
| 6504 | pZip->m_archive_size = cur_archive_file_ofs; | |
| 6505 | ||
| 6506 | return MZ_TRUE; | |
| 6507 | } | |
| 6508 | #endif // #ifndef MINIZ_NO_STDIO | |
| 6509 | ||
| 6510 | mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, | |
| 6511 | mz_zip_archive *pSource_zip, | |
| 6512 | mz_uint file_index) { | |
| 6513 | mz_uint n, bit_flags, num_alignment_padding_bytes; | |
| 6514 | mz_uint64 comp_bytes_remaining, local_dir_header_ofs; | |
| 6515 | mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; | |
| 6516 | mz_uint32 | |
| 6517 | local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / | |
| 6518 | sizeof(mz_uint32)]; | |
| 6519 | mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; | |
| 6520 | mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; | |
| 6521 | size_t orig_central_dir_size; | |
| 6522 | mz_zip_internal_state *pState; | |
| 6523 | void *pBuf; | |
| 6524 | const mz_uint8 *pSrc_central_header; | |
| 6525 | ||
| 6526 | if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) | |
| 6527 | return MZ_FALSE; | |
| 6528 | if (NULL == | |
| 6529 | (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) | |
| 6530 | return MZ_FALSE; | |
| 6531 | pState = pZip->m_pState; | |
| 6532 | ||
| 6533 | num_alignment_padding_bytes = | |
| 6534 | mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); | |
| 6535 | ||
| 6536 | // no zip64 support yet | |
| 6537 | if ((pZip->m_total_files == 0xFFFF) || | |
| 6538 | ((pZip->m_archive_size + num_alignment_padding_bytes + | |
| 6539 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > | |
| 6540 | 0xFFFFFFFF)) | |
| 6541 | return MZ_FALSE; | |
| 6542 | ||
| 6543 | cur_src_file_ofs = | |
| 6544 | MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); | |
| 6545 | cur_dst_file_ofs = pZip->m_archive_size; | |
| 6546 | ||
| 6547 | if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, | |
| 6548 | pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != | |
| 6549 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE) | |
| 6550 | return MZ_FALSE; | |
| 6551 | if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) | |
| 6552 | return MZ_FALSE; | |
| 6553 | cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; | |
| 6554 | ||
| 6555 | if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, | |
| 6556 | num_alignment_padding_bytes)) | |
| 6557 | return MZ_FALSE; | |
| 6558 | cur_dst_file_ofs += num_alignment_padding_bytes; | |
| 6559 | local_dir_header_ofs = cur_dst_file_ofs; | |
| 6560 | if (pZip->m_file_offset_alignment) { | |
| 6561 | MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == | |
| 6562 | 0); | |
| 6563 | } | |
| 6564 | ||
| 6565 | if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, | |
| 6566 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != | |
| 6567 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE) | |
| 6568 | return MZ_FALSE; | |
| 6569 | cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; | |
| 6570 | ||
| 6571 | n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + | |
| 6572 | MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); | |
| 6573 | comp_bytes_remaining = | |
| 6574 | n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); | |
| 6575 | ||
| 6576 | if (NULL == | |
| 6577 | (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, | |
| 6578 | (size_t)MZ_MAX(sizeof(mz_uint32) * 4, | |
| 6579 | MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, | |
| 6580 | comp_bytes_remaining))))) | |
| 6581 | return MZ_FALSE; | |
| 6582 | ||
| 6583 | while (comp_bytes_remaining) { | |
| 6584 | n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); | |
| 6585 | if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, | |
| 6586 | n) != n) { | |
| 6587 | pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); | |
| 6588 | return MZ_FALSE; | |
| 6589 | } | |
| 6590 | cur_src_file_ofs += n; | |
| 6591 | ||
| 6592 | if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { | |
| 6593 | pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); | |
| 6594 | return MZ_FALSE; | |
| 6595 | } | |
| 6596 | cur_dst_file_ofs += n; | |
| 6597 | ||
| 6598 | comp_bytes_remaining -= n; | |
| 6599 | } | |
| 6600 | ||
| 6601 | bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); | |
| 6602 | if (bit_flags & 8) { | |
| 6603 | // Copy data descriptor | |
| 6604 | if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, | |
| 6605 | sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { | |
| 6606 | pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); | |
| 6607 | return MZ_FALSE; | |
| 6608 | } | |
| 6609 | ||
| 6610 | n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); | |
| 6611 | if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { | |
| 6612 | pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); | |
| 6613 | return MZ_FALSE; | |
| 6614 | } | |
| 6615 | ||
| 6616 | // cur_src_file_ofs += n; | |
| 6617 | cur_dst_file_ofs += n; | |
| 6618 | } | |
| 6619 | pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); | |
| 6620 | ||
| 6621 | // no zip64 support yet | |
| 6622 | if (cur_dst_file_ofs > 0xFFFFFFFF) | |
| 6623 | return MZ_FALSE; | |
| 6624 | ||
| 6625 | orig_central_dir_size = pState->m_central_dir.m_size; | |
| 6626 | ||
| 6627 | memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); | |
| 6628 | MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, | |
| 6629 | local_dir_header_ofs); | |
| 6630 | if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, | |
| 6631 | MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) | |
| 6632 | return MZ_FALSE; | |
| 6633 | ||
| 6634 | n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + | |
| 6635 | MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + | |
| 6636 | MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); | |
| 6637 | if (!mz_zip_array_push_back( | |
| 6638 | pZip, &pState->m_central_dir, | |
| 6639 | pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) { | |
| 6640 | mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, | |
| 6641 | MZ_FALSE); | |
| 6642 | return MZ_FALSE; | |
| 6643 | } | |
| 6644 | ||
| 6645 | if (pState->m_central_dir.m_size > 0xFFFFFFFF) | |
| 6646 | return MZ_FALSE; | |
| 6647 | n = (mz_uint32)orig_central_dir_size; | |
| 6648 | if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { | |
| 6649 | mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, | |
| 6650 | MZ_FALSE); | |
| 6651 | return MZ_FALSE; | |
| 6652 | } | |
| 6653 | ||
| 6654 | pZip->m_total_files++; | |
| 6655 | pZip->m_archive_size = cur_dst_file_ofs; | |
| 6656 | ||
| 6657 | return MZ_TRUE; | |
| 6658 | } | |
| 6659 | ||
| 6660 | mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { | |
| 6661 | mz_zip_internal_state *pState; | |
| 6662 | mz_uint64 central_dir_ofs, central_dir_size; | |
| 6663 | mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; | |
| 6664 | ||
| 6665 | if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) | |
| 6666 | return MZ_FALSE; | |
| 6667 | ||
| 6668 | pState = pZip->m_pState; | |
| 6669 | ||
| 6670 | // no zip64 support yet | |
| 6671 | if ((pZip->m_total_files > 0xFFFF) || | |
| 6672 | ((pZip->m_archive_size + pState->m_central_dir.m_size + | |
| 6673 | MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) | |
| 6674 | return MZ_FALSE; | |
| 6675 | ||
| 6676 | central_dir_ofs = 0; | |
| 6677 | central_dir_size = 0; | |
| 6678 | if (pZip->m_total_files) { | |
| 6679 | // Write central directory | |
| 6680 | central_dir_ofs = pZip->m_archive_size; | |
| 6681 | central_dir_size = pState->m_central_dir.m_size; | |
| 6682 | pZip->m_central_directory_file_ofs = central_dir_ofs; | |
| 6683 | if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, | |
| 6684 | pState->m_central_dir.m_p, | |
| 6685 | (size_t)central_dir_size) != central_dir_size) | |
| 6686 | return MZ_FALSE; | |
| 6687 | pZip->m_archive_size += central_dir_size; | |
| 6688 | } | |
| 6689 | ||
| 6690 | // Write end of central directory record | |
| 6691 | MZ_CLEAR_OBJ(hdr); | |
| 6692 | MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, | |
| 6693 | MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); | |
| 6694 | MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, | |
| 6695 | pZip->m_total_files); | |
| 6696 | MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); | |
| 6697 | MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); | |
| 6698 | MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); | |
| 6699 | ||
| 6700 | if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, | |
| 6701 | sizeof(hdr)) != sizeof(hdr)) | |
| 6702 | return MZ_FALSE; | |
| 6703 | #ifndef MINIZ_NO_STDIO | |
| 6704 | if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) | |
| 6705 | return MZ_FALSE; | |
| 6706 | #endif // #ifndef MINIZ_NO_STDIO | |
| 6707 | ||
| 6708 | pZip->m_archive_size += sizeof(hdr); | |
| 6709 | ||
| 6710 | pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; | |
| 6711 | return MZ_TRUE; | |
| 6712 | } | |
| 6713 | ||
| 6714 | mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, | |
| 6715 | size_t *pSize) { | |
| 6716 | if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) | |
| 6717 | return MZ_FALSE; | |
| 6718 | if (pZip->m_pWrite != mz_zip_heap_write_func) | |
| 6719 | return MZ_FALSE; | |
| 6720 | if (!mz_zip_writer_finalize_archive(pZip)) | |
| 6721 | return MZ_FALSE; | |
| 6722 | ||
| 6723 | *pBuf = pZip->m_pState->m_pMem; | |
| 6724 | *pSize = pZip->m_pState->m_mem_size; | |
| 6725 | pZip->m_pState->m_pMem = NULL; | |
| 6726 | pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; | |
| 6727 | return MZ_TRUE; | |
| 6728 | } | |
| 6729 | ||
| 6730 | mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { | |
| 6731 | mz_zip_internal_state *pState; | |
| 6732 | mz_bool status = MZ_TRUE; | |
| 6733 | if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || | |
| 6734 | ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && | |
| 6735 | (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) | |
| 6736 | return MZ_FALSE; | |
| 6737 | ||
| 6738 | pState = pZip->m_pState; | |
| 6739 | pZip->m_pState = NULL; | |
| 6740 | mz_zip_array_clear(pZip, &pState->m_central_dir); | |
| 6741 | mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); | |
| 6742 | mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); | |
| 6743 | ||
| 6744 | #ifndef MINIZ_NO_STDIO | |
| 6745 | if (pState->m_pFile) { | |
| 6746 | MZ_FCLOSE(pState->m_pFile); | |
| 6747 | pState->m_pFile = NULL; | |
| 6748 | } | |
| 6749 | #endif // #ifndef MINIZ_NO_STDIO | |
| 6750 | ||
| 6751 | if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { | |
| 6752 | pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); | |
| 6753 | pState->m_pMem = NULL; | |
| 6754 | } | |
| 6755 | ||
| 6756 | pZip->m_pFree(pZip->m_pAlloc_opaque, pState); | |
| 6757 | pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; | |
| 6758 | return status; | |
| 6759 | } | |
| 6760 | ||
| 6761 | #ifndef MINIZ_NO_STDIO | |
| 6762 | mz_bool mz_zip_add_mem_to_archive_file_in_place( | |
| 6763 | const char *pZip_filename, const char *pArchive_name, const void *pBuf, | |
| 6764 | size_t buf_size, const void *pComment, mz_uint16 comment_size, | |
| 6765 | mz_uint level_and_flags) { | |
| 6766 | mz_bool status, created_new_archive = MZ_FALSE; | |
| 6767 | mz_zip_archive zip_archive; | |
| 6768 | struct MZ_FILE_STAT_STRUCT file_stat; | |
| 6769 | MZ_CLEAR_OBJ(zip_archive); | |
| 6770 | if ((int)level_and_flags < 0) | |
| 6771 | level_and_flags = MZ_DEFAULT_LEVEL; | |
| 6772 | if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || | |
| 6773 | ((comment_size) && (!pComment)) || | |
| 6774 | ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) | |
| 6775 | return MZ_FALSE; | |
| 6776 | if (!mz_zip_writer_validate_archive_name(pArchive_name)) | |
| 6777 | return MZ_FALSE; | |
| 6778 | if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { | |
| 6779 | // Create a new archive. | |
| 6780 | if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) | |
| 6781 | return MZ_FALSE; | |
| 6782 | created_new_archive = MZ_TRUE; | |
| 6783 | } else { | |
| 6784 | // Append to an existing archive. | |
| 6785 | if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, | |
| 6786 | level_and_flags | | |
| 6787 | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) | |
| 6788 | return MZ_FALSE; | |
| 6789 | if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) { | |
| 6790 | mz_zip_reader_end(&zip_archive); | |
| 6791 | return MZ_FALSE; | |
| 6792 | } | |
| 6793 | } | |
| 6794 | status = | |
| 6795 | mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, | |
| 6796 | pComment, comment_size, level_and_flags, 0, 0); | |
| 6797 | // Always finalize, even if adding failed for some reason, so we have a valid | |
| 6798 | // central directory. (This may not always succeed, but we can try.) | |
| 6799 | if (!mz_zip_writer_finalize_archive(&zip_archive)) | |
| 6800 | status = MZ_FALSE; | |
| 6801 | if (!mz_zip_writer_end(&zip_archive)) | |
| 6802 | status = MZ_FALSE; | |
| 6803 | if ((!status) && (created_new_archive)) { | |
| 6804 | // It's a new archive and something went wrong, so just delete it. | |
| 6805 | int ignoredStatus = MZ_DELETE_FILE(pZip_filename); | |
| 6806 | (void)ignoredStatus; | |
| 6807 | } | |
| 6808 | return status; | |
| 6809 | } | |
| 6810 | ||
| 6811 | void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, | |
| 6812 | const char *pArchive_name, | |
| 6813 | size_t *pSize, mz_uint flags) { | |
| 6814 | int file_index; | |
| 6815 | mz_zip_archive zip_archive; | |
| 6816 | void *p = NULL; | |
| 6817 | ||
| 6818 | if (pSize) | |
| 6819 | *pSize = 0; | |
| 6820 | ||
| 6821 | if ((!pZip_filename) || (!pArchive_name)) | |
| 6822 | return NULL; | |
| 6823 | ||
| 6824 | MZ_CLEAR_OBJ(zip_archive); | |
| 6825 | if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, | |
| 6826 | flags | | |
| 6827 | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) | |
| 6828 | return NULL; | |
| 6829 | ||
| 6830 | if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, | |
| 6831 | flags)) >= 0) | |
| 6832 | p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); | |
| 6833 | ||
| 6834 | mz_zip_reader_end(&zip_archive); | |
| 6835 | return p; | |
| 6836 | } | |
| 6837 | ||
| 6838 | #endif // #ifndef MINIZ_NO_STDIO | |
| 6839 | ||
| 6840 | #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS | |
| 6841 | ||
| 6842 | #endif // #ifndef MINIZ_NO_ARCHIVE_APIS | |
| 6843 | ||
| 6844 | #ifdef __cplusplus | |
| 6845 | } | |
| 6846 | #endif | |
| 6847 | ||
| 6848 | #endif // MINIZ_HEADER_FILE_ONLY | |
| 6849 | ||
| 6850 | /* | |
| 6851 | This is free and unencumbered software released into the public domain. | |
| 6852 | ||
| 6853 | Anyone is free to copy, modify, publish, use, compile, sell, or | |
| 6854 | distribute this software, either in source code form or as a compiled | |
| 6855 | binary, for any purpose, commercial or non-commercial, and by any | |
| 6856 | means. | |
| 6857 | ||
| 6858 | In jurisdictions that recognize copyright laws, the author or authors | |
| 6859 | of this software dedicate any and all copyright interest in the | |
| 6860 | software to the public domain. We make this dedication for the benefit | |
| 6861 | of the public at large and to the detriment of our heirs and | |
| 6862 | successors. We intend this dedication to be an overt act of | |
| 6863 | relinquishment in perpetuity of all present and future rights to this | |
| 6864 | software under copyright law. | |
| 6865 | ||
| 6866 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 6867 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 6868 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
| 6869 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 6870 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 6871 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 6872 | OTHER DEALINGS IN THE SOFTWARE. | |
| 6873 | ||
| 6874 | For more information, please refer to <http://unlicense.org/> | |
| 6875 | */ |
test/standalone/issue_9812/vendor/kuba-zip/zip.c created+1622| ... | ... | @@ -0,0 +1,1622 @@ |
| 1 | /* | |
| 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 3 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 4 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
| 5 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 6 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 7 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 8 | * OTHER DEALINGS IN THE SOFTWARE. | |
| 9 | */ | |
| 10 | #define __STDC_WANT_LIB_EXT1__ 1 | |
| 11 | ||
| 12 | #include <errno.h> | |
| 13 | #include <sys/stat.h> | |
| 14 | #include <time.h> | |
| 15 | ||
| 16 | #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ | |
| 17 | defined(__MINGW32__) | |
| 18 | /* Win32, DOS, MSVC, MSVS */ | |
| 19 | #include <direct.h> | |
| 20 | ||
| 21 | #define MKDIR(DIRNAME) _mkdir(DIRNAME) | |
| 22 | #define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL) | |
| 23 | #define HAS_DEVICE(P) \ | |
| 24 | ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ | |
| 25 | (P)[1] == ':') | |
| 26 | #define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0) | |
| 27 | ||
| 28 | #else | |
| 29 | ||
| 30 | #include <unistd.h> // needed for symlink() | |
| 31 | ||
| 32 | #define MKDIR(DIRNAME) mkdir(DIRNAME, 0755) | |
| 33 | #define STRCLONE(STR) ((STR) ? strdup(STR) : NULL) | |
| 34 | ||
| 35 | #endif | |
| 36 | ||
| 37 | #ifdef __MINGW32__ | |
| 38 | #include <sys/types.h> | |
| 39 | #include <unistd.h> | |
| 40 | #endif | |
| 41 | ||
| 42 | #include "miniz.h" | |
| 43 | #include "zip.h" | |
| 44 | ||
| 45 | #ifdef _MSC_VER | |
| 46 | #include <io.h> | |
| 47 | ||
| 48 | #define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0)) | |
| 49 | #define fileno _fileno | |
| 50 | #endif | |
| 51 | ||
| 52 | #ifndef HAS_DEVICE | |
| 53 | #define HAS_DEVICE(P) 0 | |
| 54 | #endif | |
| 55 | ||
| 56 | #ifndef FILESYSTEM_PREFIX_LEN | |
| 57 | #define FILESYSTEM_PREFIX_LEN(P) 0 | |
| 58 | #endif | |
| 59 | ||
| 60 | #ifndef ISSLASH | |
| 61 | #define ISSLASH(C) ((C) == '/' || (C) == '\\') | |
| 62 | #endif | |
| 63 | ||
| 64 | #define CLEANUP(ptr) \ | |
| 65 | do { \ | |
| 66 | if (ptr) { \ | |
| 67 | free((void *)ptr); \ | |
| 68 | ptr = NULL; \ | |
| 69 | } \ | |
| 70 | } while (0) | |
| 71 | ||
| 72 | struct zip_entry_t { | |
| 73 | int index; | |
| 74 | char *name; | |
| 75 | mz_uint64 uncomp_size; | |
| 76 | mz_uint64 comp_size; | |
| 77 | mz_uint32 uncomp_crc32; | |
| 78 | mz_uint64 offset; | |
| 79 | mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; | |
| 80 | mz_uint64 header_offset; | |
| 81 | mz_uint16 method; | |
| 82 | mz_zip_writer_add_state state; | |
| 83 | tdefl_compressor comp; | |
| 84 | mz_uint32 external_attr; | |
| 85 | time_t m_time; | |
| 86 | }; | |
| 87 | ||
| 88 | struct zip_t { | |
| 89 | mz_zip_archive archive; | |
| 90 | mz_uint level; | |
| 91 | struct zip_entry_t entry; | |
| 92 | }; | |
| 93 | ||
| 94 | enum zip_modify_t { | |
| 95 | MZ_KEEP = 0, | |
| 96 | MZ_DELETE = 1, | |
| 97 | MZ_MOVE = 2, | |
| 98 | }; | |
| 99 | ||
| 100 | struct zip_entry_mark_t { | |
| 101 | int file_index; | |
| 102 | enum zip_modify_t type; | |
| 103 | mz_uint64 m_local_header_ofs; | |
| 104 | mz_uint64 lf_length; | |
| 105 | }; | |
| 106 | ||
| 107 | static const char *const zip_errlist[30] = { | |
| 108 | NULL, | |
| 109 | "not initialized\0", | |
| 110 | "invalid entry name\0", | |
| 111 | "entry not found\0", | |
| 112 | "invalid zip mode\0", | |
| 113 | "invalid compression level\0", | |
| 114 | "no zip 64 support\0", | |
| 115 | "memset error\0", | |
| 116 | "cannot write data to entry\0", | |
| 117 | "cannot initialize tdefl compressor\0", | |
| 118 | "invalid index\0", | |
| 119 | "header not found\0", | |
| 120 | "cannot flush tdefl buffer\0", | |
| 121 | "cannot write entry header\0", | |
| 122 | "cannot create entry header\0", | |
| 123 | "cannot write to central dir\0", | |
| 124 | "cannot open file\0", | |
| 125 | "invalid entry type\0", | |
| 126 | "extracting data using no memory allocation\0", | |
| 127 | "file not found\0", | |
| 128 | "no permission\0", | |
| 129 | "out of memory\0", | |
| 130 | "invalid zip archive name\0", | |
| 131 | "make dir error\0", | |
| 132 | "symlink error\0", | |
| 133 | "close archive error\0", | |
| 134 | "capacity size too small\0", | |
| 135 | "fseek error\0", | |
| 136 | "fread error\0", | |
| 137 | "fwrite error\0", | |
| 138 | }; | |
| 139 | ||
| 140 | const char *zip_strerror(int errnum) { | |
| 141 | errnum = -errnum; | |
| 142 | if (errnum <= 0 || errnum >= 30) { | |
| 143 | return NULL; | |
| 144 | } | |
| 145 | ||
| 146 | return zip_errlist[errnum]; | |
| 147 | } | |
| 148 | ||
| 149 | static const char *zip_basename(const char *name) { | |
| 150 | char const *p; | |
| 151 | char const *base = name += FILESYSTEM_PREFIX_LEN(name); | |
| 152 | int all_slashes = 1; | |
| 153 | ||
| 154 | for (p = name; *p; p++) { | |
| 155 | if (ISSLASH(*p)) | |
| 156 | base = p + 1; | |
| 157 | else | |
| 158 | all_slashes = 0; | |
| 159 | } | |
| 160 | ||
| 161 | /* If NAME is all slashes, arrange to return `/'. */ | |
| 162 | if (*base == '\0' && ISSLASH(*name) && all_slashes) | |
| 163 | --base; | |
| 164 | ||
| 165 | return base; | |
| 166 | } | |
| 167 | ||
| 168 | static int zip_mkpath(char *path) { | |
| 169 | char *p; | |
| 170 | char npath[MAX_PATH + 1]; | |
| 171 | int len = 0; | |
| 172 | int has_device = HAS_DEVICE(path); | |
| 173 | ||
| 174 | memset(npath, 0, MAX_PATH + 1); | |
| 175 | if (has_device) { | |
| 176 | // only on windows | |
| 177 | npath[0] = path[0]; | |
| 178 | npath[1] = path[1]; | |
| 179 | len = 2; | |
| 180 | } | |
| 181 | for (p = path + len; *p && len < MAX_PATH; p++) { | |
| 182 | if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) { | |
| 183 | #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ | |
| 184 | defined(__MINGW32__) | |
| 185 | #else | |
| 186 | if ('\\' == *p) { | |
| 187 | *p = '/'; | |
| 188 | } | |
| 189 | #endif | |
| 190 | ||
| 191 | if (MKDIR(npath) == -1) { | |
| 192 | if (errno != EEXIST) { | |
| 193 | return ZIP_EMKDIR; | |
| 194 | } | |
| 195 | } | |
| 196 | } | |
| 197 | npath[len++] = *p; | |
| 198 | } | |
| 199 | ||
| 200 | return 0; | |
| 201 | } | |
| 202 | ||
| 203 | static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) { | |
| 204 | char c; | |
| 205 | size_t i; | |
| 206 | char *rpl = (char *)calloc((1 + n), sizeof(char)); | |
| 207 | char *begin = rpl; | |
| 208 | if (!rpl) { | |
| 209 | return NULL; | |
| 210 | } | |
| 211 | ||
| 212 | for (i = 0; (i < n) && (c = *str++); ++i) { | |
| 213 | if (c == oldchar) { | |
| 214 | c = newchar; | |
| 215 | } | |
| 216 | *rpl++ = c; | |
| 217 | } | |
| 218 | ||
| 219 | return begin; | |
| 220 | } | |
| 221 | ||
| 222 | static char *zip_name_normalize(char *name, char *const nname, size_t len) { | |
| 223 | size_t offn = 0; | |
| 224 | size_t offnn = 0, ncpy = 0; | |
| 225 | ||
| 226 | if (name == NULL || nname == NULL || len <= 0) { | |
| 227 | return NULL; | |
| 228 | } | |
| 229 | // skip trailing '/' | |
| 230 | while (ISSLASH(*name)) | |
| 231 | name++; | |
| 232 | ||
| 233 | for (; offn < len; offn++) { | |
| 234 | if (ISSLASH(name[offn])) { | |
| 235 | if (ncpy > 0 && strcmp(&nname[offnn], ".\0") && | |
| 236 | strcmp(&nname[offnn], "..\0")) { | |
| 237 | offnn += ncpy; | |
| 238 | nname[offnn++] = name[offn]; // append '/' | |
| 239 | } | |
| 240 | ncpy = 0; | |
| 241 | } else { | |
| 242 | nname[offnn + ncpy] = name[offn]; | |
| 243 | ncpy++; | |
| 244 | } | |
| 245 | } | |
| 246 | ||
| 247 | // at the end, extra check what we've already copied | |
| 248 | if (ncpy == 0 || !strcmp(&nname[offnn], ".\0") || | |
| 249 | !strcmp(&nname[offnn], "..\0")) { | |
| 250 | nname[offnn] = 0; | |
| 251 | } | |
| 252 | return nname; | |
| 253 | } | |
| 254 | ||
| 255 | static mz_bool zip_name_match(const char *name1, const char *name2) { | |
| 256 | int len2 = strlen(name2); | |
| 257 | char *nname2 = zip_strrpl(name2, len2, '\\', '/'); | |
| 258 | if (!nname2) { | |
| 259 | return MZ_FALSE; | |
| 260 | } | |
| 261 | ||
| 262 | mz_bool res = (strcmp(name1, nname2) == 0) ? MZ_TRUE : MZ_FALSE; | |
| 263 | CLEANUP(nname2); | |
| 264 | return res; | |
| 265 | } | |
| 266 | ||
| 267 | static int zip_archive_truncate(mz_zip_archive *pzip) { | |
| 268 | mz_zip_internal_state *pState = pzip->m_pState; | |
| 269 | mz_uint64 file_size = pzip->m_archive_size; | |
| 270 | if ((pzip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { | |
| 271 | return 0; | |
| 272 | } | |
| 273 | if (pzip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) { | |
| 274 | if (pState->m_pFile) { | |
| 275 | int fd = fileno(pState->m_pFile); | |
| 276 | return ftruncate(fd, file_size); | |
| 277 | } | |
| 278 | } | |
| 279 | return 0; | |
| 280 | } | |
| 281 | ||
| 282 | static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir, | |
| 283 | int (*on_extract)(const char *filename, | |
| 284 | void *arg), | |
| 285 | void *arg) { | |
| 286 | int err = 0; | |
| 287 | mz_uint i, n; | |
| 288 | char path[MAX_PATH + 1]; | |
| 289 | char symlink_to[MAX_PATH + 1]; | |
| 290 | mz_zip_archive_file_stat info; | |
| 291 | size_t dirlen = 0; | |
| 292 | mz_uint32 xattr = 0; | |
| 293 | ||
| 294 | memset(path, 0, sizeof(path)); | |
| 295 | memset(symlink_to, 0, sizeof(symlink_to)); | |
| 296 | ||
| 297 | dirlen = strlen(dir); | |
| 298 | if (dirlen + 1 > MAX_PATH) { | |
| 299 | return ZIP_EINVENTNAME; | |
| 300 | } | |
| 301 | ||
| 302 | memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); | |
| 303 | ||
| 304 | #if defined(_MSC_VER) | |
| 305 | strcpy_s(path, MAX_PATH, dir); | |
| 306 | #else | |
| 307 | strcpy(path, dir); | |
| 308 | #endif | |
| 309 | ||
| 310 | if (!ISSLASH(path[dirlen - 1])) { | |
| 311 | #if defined(_WIN32) || defined(__WIN32__) | |
| 312 | path[dirlen] = '\\'; | |
| 313 | #else | |
| 314 | path[dirlen] = '/'; | |
| 315 | #endif | |
| 316 | ++dirlen; | |
| 317 | } | |
| 318 | ||
| 319 | // Get and print information about each file in the archive. | |
| 320 | n = mz_zip_reader_get_num_files(zip_archive); | |
| 321 | for (i = 0; i < n; ++i) { | |
| 322 | if (!mz_zip_reader_file_stat(zip_archive, i, &info)) { | |
| 323 | // Cannot get information about zip archive; | |
| 324 | err = ZIP_ENOENT; | |
| 325 | goto out; | |
| 326 | } | |
| 327 | ||
| 328 | if (!zip_name_normalize(info.m_filename, info.m_filename, | |
| 329 | strlen(info.m_filename))) { | |
| 330 | // Cannot normalize file name; | |
| 331 | err = ZIP_EINVENTNAME; | |
| 332 | goto out; | |
| 333 | } | |
| 334 | #if defined(_MSC_VER) | |
| 335 | strncpy_s(&path[dirlen], MAX_PATH - dirlen, info.m_filename, | |
| 336 | MAX_PATH - dirlen); | |
| 337 | #else | |
| 338 | strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen); | |
| 339 | #endif | |
| 340 | err = zip_mkpath(path); | |
| 341 | if (err < 0) { | |
| 342 | // Cannot make a path | |
| 343 | goto out; | |
| 344 | } | |
| 345 | ||
| 346 | if ((((info.m_version_made_by >> 8) == 3) || | |
| 347 | ((info.m_version_made_by >> 8) == | |
| 348 | 19)) // if zip is produced on Unix or macOS (3 and 19 from | |
| 349 | // section 4.4.2.2 of zip standard) | |
| 350 | && info.m_external_attr & | |
| 351 | (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 | |
| 352 | // is directory) | |
| 353 | #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ | |
| 354 | defined(__MINGW32__) | |
| 355 | #else | |
| 356 | if (info.m_uncomp_size > MAX_PATH || | |
| 357 | !mz_zip_reader_extract_to_mem_no_alloc(zip_archive, i, symlink_to, | |
| 358 | MAX_PATH, 0, NULL, 0)) { | |
| 359 | err = ZIP_EMEMNOALLOC; | |
| 360 | goto out; | |
| 361 | } | |
| 362 | symlink_to[info.m_uncomp_size] = '\0'; | |
| 363 | if (symlink(symlink_to, path) != 0) { | |
| 364 | err = ZIP_ESYMLINK; | |
| 365 | goto out; | |
| 366 | } | |
| 367 | #endif | |
| 368 | } else { | |
| 369 | if (!mz_zip_reader_is_file_a_directory(zip_archive, i)) { | |
| 370 | if (!mz_zip_reader_extract_to_file(zip_archive, i, path, 0)) { | |
| 371 | // Cannot extract zip archive to file | |
| 372 | err = ZIP_ENOFILE; | |
| 373 | goto out; | |
| 374 | } | |
| 375 | } | |
| 376 | ||
| 377 | #if defined(_MSC_VER) | |
| 378 | (void)xattr; // unused | |
| 379 | #else | |
| 380 | xattr = (info.m_external_attr >> 16) & 0xFFFF; | |
| 381 | if (xattr > 0) { | |
| 382 | if (chmod(path, (mode_t)xattr) < 0) { | |
| 383 | err = ZIP_ENOPERM; | |
| 384 | goto out; | |
| 385 | } | |
| 386 | } | |
| 387 | #endif | |
| 388 | } | |
| 389 | ||
| 390 | if (on_extract) { | |
| 391 | if (on_extract(path, arg) < 0) { | |
| 392 | goto out; | |
| 393 | } | |
| 394 | } | |
| 395 | } | |
| 396 | ||
| 397 | out: | |
| 398 | // Close the archive, freeing any resources it was using | |
| 399 | if (!mz_zip_reader_end(zip_archive)) { | |
| 400 | // Cannot end zip reader | |
| 401 | err = ZIP_ECLSZIP; | |
| 402 | } | |
| 403 | return err; | |
| 404 | } | |
| 405 | ||
| 406 | static inline void zip_archive_finalize(mz_zip_archive *pzip) { | |
| 407 | mz_zip_writer_finalize_archive(pzip); | |
| 408 | zip_archive_truncate(pzip); | |
| 409 | } | |
| 410 | ||
| 411 | static int zip_entry_mark(struct zip_t *zip, | |
| 412 | struct zip_entry_mark_t *entry_mark, int n, | |
| 413 | char *const entries[], const size_t len) { | |
| 414 | int err = 0; | |
| 415 | if (!zip || !entry_mark || !entries) { | |
| 416 | return ZIP_ENOINIT; | |
| 417 | } | |
| 418 | ||
| 419 | mz_zip_archive_file_stat file_stat; | |
| 420 | mz_uint64 d_pos = ~0; | |
| 421 | for (int i = 0; i < n; ++i) { | |
| 422 | ||
| 423 | if ((err = zip_entry_openbyindex(zip, i))) { | |
| 424 | return err; | |
| 425 | } | |
| 426 | ||
| 427 | mz_bool name_matches = MZ_FALSE; | |
| 428 | for (int j = 0; j < (const int)len; ++j) { | |
| 429 | if (zip_name_match(zip->entry.name, entries[j])) { | |
| 430 | name_matches = MZ_TRUE; | |
| 431 | break; | |
| 432 | } | |
| 433 | } | |
| 434 | if (name_matches) { | |
| 435 | entry_mark[i].type = MZ_DELETE; | |
| 436 | } else { | |
| 437 | entry_mark[i].type = MZ_KEEP; | |
| 438 | } | |
| 439 | ||
| 440 | if (!mz_zip_reader_file_stat(&zip->archive, i, &file_stat)) { | |
| 441 | return ZIP_ENOENT; | |
| 442 | } | |
| 443 | ||
| 444 | zip_entry_close(zip); | |
| 445 | ||
| 446 | entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs; | |
| 447 | entry_mark[i].file_index = -1; | |
| 448 | entry_mark[i].lf_length = 0; | |
| 449 | if ((entry_mark[i].type) == MZ_DELETE && | |
| 450 | (d_pos > entry_mark[i].m_local_header_ofs)) { | |
| 451 | d_pos = entry_mark[i].m_local_header_ofs; | |
| 452 | } | |
| 453 | } | |
| 454 | for (int i = 0; i < n; ++i) { | |
| 455 | if ((entry_mark[i].m_local_header_ofs > d_pos) && | |
| 456 | (entry_mark[i].type != MZ_DELETE)) { | |
| 457 | entry_mark[i].type = MZ_MOVE; | |
| 458 | } | |
| 459 | } | |
| 460 | return err; | |
| 461 | } | |
| 462 | ||
| 463 | static int zip_index_next(mz_uint64 *local_header_ofs_array, int cur_index) { | |
| 464 | int new_index = 0; | |
| 465 | for (int i = cur_index - 1; i >= 0; --i) { | |
| 466 | if (local_header_ofs_array[cur_index] > local_header_ofs_array[i]) { | |
| 467 | new_index = i + 1; | |
| 468 | return new_index; | |
| 469 | } | |
| 470 | } | |
| 471 | return new_index; | |
| 472 | } | |
| 473 | ||
| 474 | static int zip_sort(mz_uint64 *local_header_ofs_array, int cur_index) { | |
| 475 | int nxt_index = zip_index_next(local_header_ofs_array, cur_index); | |
| 476 | ||
| 477 | if (nxt_index != cur_index) { | |
| 478 | mz_uint64 temp = local_header_ofs_array[cur_index]; | |
| 479 | for (int i = cur_index; i > nxt_index; i--) { | |
| 480 | local_header_ofs_array[i] = local_header_ofs_array[i - 1]; | |
| 481 | } | |
| 482 | local_header_ofs_array[nxt_index] = temp; | |
| 483 | } | |
| 484 | return nxt_index; | |
| 485 | } | |
| 486 | ||
| 487 | static int zip_index_update(struct zip_entry_mark_t *entry_mark, int last_index, | |
| 488 | int nxt_index) { | |
| 489 | for (int j = 0; j < last_index; j++) { | |
| 490 | if (entry_mark[j].file_index >= nxt_index) { | |
| 491 | entry_mark[j].file_index += 1; | |
| 492 | } | |
| 493 | } | |
| 494 | entry_mark[nxt_index].file_index = last_index; | |
| 495 | return 0; | |
| 496 | } | |
| 497 | ||
| 498 | static int zip_entry_finalize(struct zip_t *zip, | |
| 499 | struct zip_entry_mark_t *entry_mark, | |
| 500 | const int n) { | |
| 501 | ||
| 502 | mz_uint64 *local_header_ofs_array = (mz_uint64 *)calloc(n, sizeof(mz_uint64)); | |
| 503 | if (!local_header_ofs_array) { | |
| 504 | return ZIP_EOOMEM; | |
| 505 | } | |
| 506 | ||
| 507 | for (int i = 0; i < n; ++i) { | |
| 508 | local_header_ofs_array[i] = entry_mark[i].m_local_header_ofs; | |
| 509 | int index = zip_sort(local_header_ofs_array, i); | |
| 510 | ||
| 511 | if (index != i) { | |
| 512 | zip_index_update(entry_mark, i, index); | |
| 513 | } | |
| 514 | entry_mark[i].file_index = index; | |
| 515 | } | |
| 516 | ||
| 517 | mz_uint64 *length = (mz_uint64 *)calloc(n, sizeof(mz_uint64)); | |
| 518 | if (!length) { | |
| 519 | CLEANUP(local_header_ofs_array); | |
| 520 | return ZIP_EOOMEM; | |
| 521 | } | |
| 522 | for (int i = 0; i < n - 1; i++) { | |
| 523 | length[i] = local_header_ofs_array[i + 1] - local_header_ofs_array[i]; | |
| 524 | } | |
| 525 | length[n - 1] = zip->archive.m_archive_size - local_header_ofs_array[n - 1]; | |
| 526 | ||
| 527 | for (int i = 0; i < n; i++) { | |
| 528 | entry_mark[i].lf_length = length[entry_mark[i].file_index]; | |
| 529 | } | |
| 530 | ||
| 531 | CLEANUP(length); | |
| 532 | CLEANUP(local_header_ofs_array); | |
| 533 | return 0; | |
| 534 | } | |
| 535 | ||
| 536 | static int zip_entry_set(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, | |
| 537 | int n, char *const entries[], const size_t len) { | |
| 538 | int err = 0; | |
| 539 | ||
| 540 | if ((err = zip_entry_mark(zip, entry_mark, n, entries, len)) < 0) { | |
| 541 | return err; | |
| 542 | } | |
| 543 | if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) { | |
| 544 | return err; | |
| 545 | } | |
| 546 | return 0; | |
| 547 | } | |
| 548 | ||
| 549 | static mz_int64 zip_file_move(MZ_FILE *m_pFile, const mz_uint64 to, | |
| 550 | const mz_uint64 from, const mz_uint64 length, | |
| 551 | mz_uint8 *move_buf, | |
| 552 | const mz_int64 capacity_size) { | |
| 553 | if ((mz_int64)length > capacity_size) { | |
| 554 | return ZIP_ECAPSIZE; | |
| 555 | } | |
| 556 | if (MZ_FSEEK64(m_pFile, from, SEEK_SET)) { | |
| 557 | MZ_FCLOSE(m_pFile); | |
| 558 | return ZIP_EFSEEK; | |
| 559 | } | |
| 560 | ||
| 561 | if (fread(move_buf, 1, length, m_pFile) != length) { | |
| 562 | MZ_FCLOSE(m_pFile); | |
| 563 | return ZIP_EFREAD; | |
| 564 | } | |
| 565 | if (MZ_FSEEK64(m_pFile, to, SEEK_SET)) { | |
| 566 | MZ_FCLOSE(m_pFile); | |
| 567 | return ZIP_EFSEEK; | |
| 568 | } | |
| 569 | if (fwrite(move_buf, 1, length, m_pFile) != length) { | |
| 570 | MZ_FCLOSE(m_pFile); | |
| 571 | return ZIP_EFWRITE; | |
| 572 | } | |
| 573 | return (mz_int64)length; | |
| 574 | } | |
| 575 | ||
| 576 | static mz_int64 zip_files_move(MZ_FILE *m_pFile, mz_uint64 writen_num, | |
| 577 | mz_uint64 read_num, mz_uint64 length) { | |
| 578 | int n = 0; | |
| 579 | const mz_int64 page_size = 1 << 12; // 4K | |
| 580 | mz_uint8 *move_buf = (mz_uint8 *)calloc(1, page_size); | |
| 581 | if (move_buf == NULL) { | |
| 582 | return ZIP_EOOMEM; | |
| 583 | } | |
| 584 | ||
| 585 | mz_int64 moved_length = 0; | |
| 586 | mz_int64 move_count = 0; | |
| 587 | while ((mz_int64)length > 0) { | |
| 588 | move_count = ((mz_int64)length >= page_size) ? page_size : (mz_int64)length; | |
| 589 | n = zip_file_move(m_pFile, writen_num, read_num, move_count, move_buf, | |
| 590 | page_size); | |
| 591 | if (n < 0) { | |
| 592 | moved_length = n; | |
| 593 | goto cleanup; | |
| 594 | } | |
| 595 | ||
| 596 | if (n != move_count) { | |
| 597 | goto cleanup; | |
| 598 | } | |
| 599 | ||
| 600 | writen_num += move_count; | |
| 601 | read_num += move_count; | |
| 602 | length -= move_count; | |
| 603 | moved_length += move_count; | |
| 604 | } | |
| 605 | ||
| 606 | cleanup: | |
| 607 | CLEANUP(move_buf); | |
| 608 | return moved_length; | |
| 609 | } | |
| 610 | ||
| 611 | static int zip_central_dir_move(mz_zip_internal_state *pState, int begin, | |
| 612 | int end, int entry_num) { | |
| 613 | if (begin == entry_num) { | |
| 614 | return 0; | |
| 615 | } | |
| 616 | ||
| 617 | mz_uint64 l_size = 0; | |
| 618 | mz_uint64 r_size = 0; | |
| 619 | mz_uint64 d_size = 0; | |
| 620 | mz_uint8 *next = NULL; | |
| 621 | mz_uint8 *deleted = &MZ_ZIP_ARRAY_ELEMENT( | |
| 622 | &pState->m_central_dir, mz_uint8, | |
| 623 | MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, begin)); | |
| 624 | l_size = (mz_uint32)(deleted - (mz_uint8 *)(pState->m_central_dir.m_p)); | |
| 625 | if (end == entry_num) { | |
| 626 | r_size = 0; | |
| 627 | } else { | |
| 628 | next = &MZ_ZIP_ARRAY_ELEMENT( | |
| 629 | &pState->m_central_dir, mz_uint8, | |
| 630 | MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, end)); | |
| 631 | r_size = pState->m_central_dir.m_size - | |
| 632 | (mz_uint32)(next - (mz_uint8 *)(pState->m_central_dir.m_p)); | |
| 633 | d_size = next - deleted; | |
| 634 | } | |
| 635 | ||
| 636 | if (l_size == 0) { | |
| 637 | memmove(pState->m_central_dir.m_p, next, r_size); | |
| 638 | pState->m_central_dir.m_p = MZ_REALLOC(pState->m_central_dir.m_p, r_size); | |
| 639 | for (int i = end; i < entry_num; i++) { | |
| 640 | MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -= | |
| 641 | d_size; | |
| 642 | } | |
| 643 | } | |
| 644 | ||
| 645 | if (l_size * r_size != 0) { | |
| 646 | memmove(deleted, next, r_size); | |
| 647 | for (int i = end; i < entry_num; i++) { | |
| 648 | MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -= | |
| 649 | d_size; | |
| 650 | } | |
| 651 | } | |
| 652 | ||
| 653 | pState->m_central_dir.m_size = l_size + r_size; | |
| 654 | return 0; | |
| 655 | } | |
| 656 | ||
| 657 | static int zip_central_dir_delete(mz_zip_internal_state *pState, | |
| 658 | int *deleted_entry_index_array, | |
| 659 | int entry_num) { | |
| 660 | int i = 0; | |
| 661 | int begin = 0; | |
| 662 | int end = 0; | |
| 663 | int d_num = 0; | |
| 664 | while (i < entry_num) { | |
| 665 | while ((!deleted_entry_index_array[i]) && (i < entry_num)) { | |
| 666 | i++; | |
| 667 | } | |
| 668 | begin = i; | |
| 669 | ||
| 670 | while ((deleted_entry_index_array[i]) && (i < entry_num)) { | |
| 671 | i++; | |
| 672 | } | |
| 673 | end = i; | |
| 674 | zip_central_dir_move(pState, begin, end, entry_num); | |
| 675 | } | |
| 676 | ||
| 677 | i = 0; | |
| 678 | while (i < entry_num) { | |
| 679 | while ((!deleted_entry_index_array[i]) && (i < entry_num)) { | |
| 680 | i++; | |
| 681 | } | |
| 682 | begin = i; | |
| 683 | if (begin == entry_num) { | |
| 684 | break; | |
| 685 | } | |
| 686 | while ((deleted_entry_index_array[i]) && (i < entry_num)) { | |
| 687 | i++; | |
| 688 | } | |
| 689 | end = i; | |
| 690 | int k = 0; | |
| 691 | for (int j = end; j < entry_num; j++) { | |
| 692 | MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, | |
| 693 | begin + k) = | |
| 694 | (mz_uint32)MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, | |
| 695 | mz_uint32, j); | |
| 696 | k++; | |
| 697 | } | |
| 698 | d_num += end - begin; | |
| 699 | } | |
| 700 | ||
| 701 | pState->m_central_dir_offsets.m_size = | |
| 702 | sizeof(mz_uint32) * (entry_num - d_num); | |
| 703 | return 0; | |
| 704 | } | |
| 705 | ||
| 706 | static int zip_entries_delete_mark(struct zip_t *zip, | |
| 707 | struct zip_entry_mark_t *entry_mark, | |
| 708 | int entry_num) { | |
| 709 | mz_uint64 writen_num = 0; | |
| 710 | mz_uint64 read_num = 0; | |
| 711 | mz_uint64 deleted_length = 0; | |
| 712 | mz_uint64 move_length = 0; | |
| 713 | int i = 0; | |
| 714 | int deleted_entry_num = 0; | |
| 715 | int n = 0; | |
| 716 | ||
| 717 | mz_bool *deleted_entry_flag_array = | |
| 718 | (mz_bool *)calloc(entry_num, sizeof(mz_bool)); | |
| 719 | if (deleted_entry_flag_array == NULL) { | |
| 720 | return ZIP_EOOMEM; | |
| 721 | } | |
| 722 | ||
| 723 | mz_zip_internal_state *pState = zip->archive.m_pState; | |
| 724 | zip->archive.m_zip_mode = MZ_ZIP_MODE_WRITING; | |
| 725 | ||
| 726 | if (MZ_FSEEK64(pState->m_pFile, 0, SEEK_SET)) { | |
| 727 | CLEANUP(deleted_entry_flag_array); | |
| 728 | return ZIP_ENOENT; | |
| 729 | } | |
| 730 | ||
| 731 | while (i < entry_num) { | |
| 732 | while ((entry_mark[i].type == MZ_KEEP) && (i < entry_num)) { | |
| 733 | writen_num += entry_mark[i].lf_length; | |
| 734 | read_num = writen_num; | |
| 735 | i++; | |
| 736 | } | |
| 737 | ||
| 738 | while ((entry_mark[i].type == MZ_DELETE) && (i < entry_num)) { | |
| 739 | deleted_entry_flag_array[i] = MZ_TRUE; | |
| 740 | read_num += entry_mark[i].lf_length; | |
| 741 | deleted_length += entry_mark[i].lf_length; | |
| 742 | i++; | |
| 743 | deleted_entry_num++; | |
| 744 | } | |
| 745 | ||
| 746 | while ((entry_mark[i].type == MZ_MOVE) && (i < entry_num)) { | |
| 747 | move_length += entry_mark[i].lf_length; | |
| 748 | mz_uint8 *p = &MZ_ZIP_ARRAY_ELEMENT( | |
| 749 | &pState->m_central_dir, mz_uint8, | |
| 750 | MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i)); | |
| 751 | if (!p) { | |
| 752 | CLEANUP(deleted_entry_flag_array); | |
| 753 | return ZIP_ENOENT; | |
| 754 | } | |
| 755 | mz_uint32 offset = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); | |
| 756 | offset -= (mz_uint32)deleted_length; | |
| 757 | MZ_WRITE_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS, offset); | |
| 758 | i++; | |
| 759 | } | |
| 760 | ||
| 761 | n = zip_files_move(pState->m_pFile, writen_num, read_num, move_length); | |
| 762 | if (n != (mz_int64)move_length) { | |
| 763 | CLEANUP(deleted_entry_flag_array); | |
| 764 | return n; | |
| 765 | } | |
| 766 | writen_num += move_length; | |
| 767 | read_num += move_length; | |
| 768 | } | |
| 769 | ||
| 770 | zip->archive.m_archive_size -= deleted_length; | |
| 771 | zip->archive.m_total_files = entry_num - deleted_entry_num; | |
| 772 | ||
| 773 | zip_central_dir_delete(pState, deleted_entry_flag_array, entry_num); | |
| 774 | CLEANUP(deleted_entry_flag_array); | |
| 775 | ||
| 776 | return deleted_entry_num; | |
| 777 | } | |
| 778 | ||
| 779 | struct zip_t *zip_open(const char *zipname, int level, char mode) { | |
| 780 | struct zip_t *zip = NULL; | |
| 781 | ||
| 782 | if (!zipname || strlen(zipname) < 1) { | |
| 783 | // zip_t archive name is empty or NULL | |
| 784 | goto cleanup; | |
| 785 | } | |
| 786 | ||
| 787 | if (level < 0) | |
| 788 | level = MZ_DEFAULT_LEVEL; | |
| 789 | if ((level & 0xF) > MZ_UBER_COMPRESSION) { | |
| 790 | // Wrong compression level | |
| 791 | goto cleanup; | |
| 792 | } | |
| 793 | ||
| 794 | zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); | |
| 795 | if (!zip) | |
| 796 | goto cleanup; | |
| 797 | ||
| 798 | zip->level = (mz_uint)level; | |
| 799 | switch (mode) { | |
| 800 | case 'w': | |
| 801 | // Create a new archive. | |
| 802 | if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) { | |
| 803 | // Cannot initialize zip_archive writer | |
| 804 | goto cleanup; | |
| 805 | } | |
| 806 | break; | |
| 807 | ||
| 808 | case 'r': | |
| 809 | case 'a': | |
| 810 | case 'd': | |
| 811 | if (!mz_zip_reader_init_file( | |
| 812 | &(zip->archive), zipname, | |
| 813 | zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { | |
| 814 | // An archive file does not exist or cannot initialize | |
| 815 | // zip_archive reader | |
| 816 | goto cleanup; | |
| 817 | } | |
| 818 | if ((mode == 'a' || mode == 'd') && | |
| 819 | !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) { | |
| 820 | mz_zip_reader_end(&(zip->archive)); | |
| 821 | goto cleanup; | |
| 822 | } | |
| 823 | break; | |
| 824 | ||
| 825 | default: | |
| 826 | goto cleanup; | |
| 827 | } | |
| 828 | ||
| 829 | return zip; | |
| 830 | ||
| 831 | cleanup: | |
| 832 | CLEANUP(zip); | |
| 833 | return NULL; | |
| 834 | } | |
| 835 | ||
| 836 | void zip_close(struct zip_t *zip) { | |
| 837 | if (zip) { | |
| 838 | // Always finalize, even if adding failed for some reason, so we have a | |
| 839 | // valid central directory. | |
| 840 | mz_zip_writer_finalize_archive(&(zip->archive)); | |
| 841 | zip_archive_truncate(&(zip->archive)); | |
| 842 | mz_zip_writer_end(&(zip->archive)); | |
| 843 | mz_zip_reader_end(&(zip->archive)); | |
| 844 | ||
| 845 | CLEANUP(zip); | |
| 846 | } | |
| 847 | } | |
| 848 | ||
| 849 | int zip_is64(struct zip_t *zip) { | |
| 850 | if (!zip || !zip->archive.m_pState) { | |
| 851 | // zip_t handler or zip state is not initialized | |
| 852 | return ZIP_ENOINIT; | |
| 853 | } | |
| 854 | ||
| 855 | return (int)zip->archive.m_pState->m_zip64; | |
| 856 | } | |
| 857 | ||
| 858 | int zip_entry_open(struct zip_t *zip, const char *entryname) { | |
| 859 | size_t entrylen = 0; | |
| 860 | mz_zip_archive *pzip = NULL; | |
| 861 | mz_uint num_alignment_padding_bytes, level; | |
| 862 | mz_zip_archive_file_stat stats; | |
| 863 | int err = 0; | |
| 864 | ||
| 865 | if (!zip) { | |
| 866 | return ZIP_ENOINIT; | |
| 867 | } | |
| 868 | ||
| 869 | if (!entryname) { | |
| 870 | return ZIP_EINVENTNAME; | |
| 871 | } | |
| 872 | ||
| 873 | entrylen = strlen(entryname); | |
| 874 | if (entrylen == 0) { | |
| 875 | return ZIP_EINVENTNAME; | |
| 876 | } | |
| 877 | ||
| 878 | /* | |
| 879 | .ZIP File Format Specification Version: 6.3.3 | |
| 880 | ||
| 881 | 4.4.17.1 The name of the file, with optional relative path. | |
| 882 | The path stored MUST not contain a drive or | |
| 883 | device letter, or a leading slash. All slashes | |
| 884 | MUST be forward slashes '/' as opposed to | |
| 885 | backwards slashes '\' for compatibility with Amiga | |
| 886 | and UNIX file systems etc. If input came from standard | |
| 887 | input, there is no file name field. | |
| 888 | */ | |
| 889 | if (zip->entry.name) { | |
| 890 | CLEANUP(zip->entry.name); | |
| 891 | } | |
| 892 | zip->entry.name = zip_strrpl(entryname, entrylen, '\\', '/'); | |
| 893 | if (!zip->entry.name) { | |
| 894 | // Cannot parse zip entry name | |
| 895 | return ZIP_EINVENTNAME; | |
| 896 | } | |
| 897 | ||
| 898 | pzip = &(zip->archive); | |
| 899 | if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { | |
| 900 | zip->entry.index = | |
| 901 | mz_zip_reader_locate_file(pzip, zip->entry.name, NULL, 0); | |
| 902 | if (zip->entry.index < 0) { | |
| 903 | err = ZIP_ENOENT; | |
| 904 | goto cleanup; | |
| 905 | } | |
| 906 | ||
| 907 | if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) { | |
| 908 | err = ZIP_ENOENT; | |
| 909 | goto cleanup; | |
| 910 | } | |
| 911 | ||
| 912 | zip->entry.comp_size = stats.m_comp_size; | |
| 913 | zip->entry.uncomp_size = stats.m_uncomp_size; | |
| 914 | zip->entry.uncomp_crc32 = stats.m_crc32; | |
| 915 | zip->entry.offset = stats.m_central_dir_ofs; | |
| 916 | zip->entry.header_offset = stats.m_local_header_ofs; | |
| 917 | zip->entry.method = stats.m_method; | |
| 918 | zip->entry.external_attr = stats.m_external_attr; | |
| 919 | #ifndef MINIZ_NO_TIME | |
| 920 | zip->entry.m_time = stats.m_time; | |
| 921 | #endif | |
| 922 | ||
| 923 | return 0; | |
| 924 | } | |
| 925 | ||
| 926 | zip->entry.index = (int)zip->archive.m_total_files; | |
| 927 | zip->entry.comp_size = 0; | |
| 928 | zip->entry.uncomp_size = 0; | |
| 929 | zip->entry.uncomp_crc32 = MZ_CRC32_INIT; | |
| 930 | zip->entry.offset = zip->archive.m_archive_size; | |
| 931 | zip->entry.header_offset = zip->archive.m_archive_size; | |
| 932 | memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); | |
| 933 | zip->entry.method = 0; | |
| 934 | ||
| 935 | // UNIX or APPLE | |
| 936 | #if MZ_PLATFORM == 3 || MZ_PLATFORM == 19 | |
| 937 | // regular file with rw-r--r-- persmissions | |
| 938 | zip->entry.external_attr = (mz_uint32)(0100644) << 16; | |
| 939 | #else | |
| 940 | zip->entry.external_attr = 0; | |
| 941 | #endif | |
| 942 | ||
| 943 | num_alignment_padding_bytes = | |
| 944 | mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); | |
| 945 | ||
| 946 | if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) { | |
| 947 | // Invalid zip mode | |
| 948 | err = ZIP_EINVMODE; | |
| 949 | goto cleanup; | |
| 950 | } | |
| 951 | if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) { | |
| 952 | // Invalid zip compression level | |
| 953 | err = ZIP_EINVLVL; | |
| 954 | goto cleanup; | |
| 955 | } | |
| 956 | // no zip64 support yet | |
| 957 | if ((pzip->m_total_files == 0xFFFF) || | |
| 958 | ((pzip->m_archive_size + num_alignment_padding_bytes + | |
| 959 | MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + | |
| 960 | entrylen) > 0xFFFFFFFF)) { | |
| 961 | // No zip64 support yet | |
| 962 | err = ZIP_ENOSUP64; | |
| 963 | goto cleanup; | |
| 964 | } | |
| 965 | if (!mz_zip_writer_write_zeros(pzip, zip->entry.offset, | |
| 966 | num_alignment_padding_bytes + | |
| 967 | sizeof(zip->entry.header))) { | |
| 968 | // Cannot memset zip entry header | |
| 969 | err = ZIP_EMEMSET; | |
| 970 | goto cleanup; | |
| 971 | } | |
| 972 | ||
| 973 | zip->entry.header_offset += num_alignment_padding_bytes; | |
| 974 | if (pzip->m_file_offset_alignment) { | |
| 975 | MZ_ASSERT( | |
| 976 | (zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0); | |
| 977 | } | |
| 978 | zip->entry.offset += num_alignment_padding_bytes + sizeof(zip->entry.header); | |
| 979 | ||
| 980 | if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name, | |
| 981 | entrylen) != entrylen) { | |
| 982 | // Cannot write data to zip entry | |
| 983 | err = ZIP_EWRTENT; | |
| 984 | goto cleanup; | |
| 985 | } | |
| 986 | ||
| 987 | zip->entry.offset += entrylen; | |
| 988 | level = zip->level & 0xF; | |
| 989 | if (level) { | |
| 990 | zip->entry.state.m_pZip = pzip; | |
| 991 | zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset; | |
| 992 | zip->entry.state.m_comp_size = 0; | |
| 993 | ||
| 994 | if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, | |
| 995 | &(zip->entry.state), | |
| 996 | (int)tdefl_create_comp_flags_from_zip_params( | |
| 997 | (int)level, -15, MZ_DEFAULT_STRATEGY)) != | |
| 998 | TDEFL_STATUS_OKAY) { | |
| 999 | // Cannot initialize the zip compressor | |
| 1000 | err = ZIP_ETDEFLINIT; | |
| 1001 | goto cleanup; | |
| 1002 | } | |
| 1003 | } | |
| 1004 | ||
| 1005 | zip->entry.m_time = time(NULL); | |
| 1006 | ||
| 1007 | return 0; | |
| 1008 | ||
| 1009 | cleanup: | |
| 1010 | CLEANUP(zip->entry.name); | |
| 1011 | return err; | |
| 1012 | } | |
| 1013 | ||
| 1014 | int zip_entry_openbyindex(struct zip_t *zip, int index) { | |
| 1015 | mz_zip_archive *pZip = NULL; | |
| 1016 | mz_zip_archive_file_stat stats; | |
| 1017 | mz_uint namelen; | |
| 1018 | const mz_uint8 *pHeader; | |
| 1019 | const char *pFilename; | |
| 1020 | ||
| 1021 | if (!zip) { | |
| 1022 | // zip_t handler is not initialized | |
| 1023 | return ZIP_ENOINIT; | |
| 1024 | } | |
| 1025 | ||
| 1026 | pZip = &(zip->archive); | |
| 1027 | if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) { | |
| 1028 | // open by index requires readonly mode | |
| 1029 | return ZIP_EINVMODE; | |
| 1030 | } | |
| 1031 | ||
| 1032 | if (index < 0 || (mz_uint)index >= pZip->m_total_files) { | |
| 1033 | // index out of range | |
| 1034 | return ZIP_EINVIDX; | |
| 1035 | } | |
| 1036 | ||
| 1037 | if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT( | |
| 1038 | &pZip->m_pState->m_central_dir, mz_uint8, | |
| 1039 | MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, | |
| 1040 | mz_uint32, index)))) { | |
| 1041 | // cannot find header in central directory | |
| 1042 | return ZIP_ENOHDR; | |
| 1043 | } | |
| 1044 | ||
| 1045 | namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); | |
| 1046 | pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; | |
| 1047 | ||
| 1048 | /* | |
| 1049 | .ZIP File Format Specification Version: 6.3.3 | |
| 1050 | ||
| 1051 | 4.4.17.1 The name of the file, with optional relative path. | |
| 1052 | The path stored MUST not contain a drive or | |
| 1053 | device letter, or a leading slash. All slashes | |
| 1054 | MUST be forward slashes '/' as opposed to | |
| 1055 | backwards slashes '\' for compatibility with Amiga | |
| 1056 | and UNIX file systems etc. If input came from standard | |
| 1057 | input, there is no file name field. | |
| 1058 | */ | |
| 1059 | if (zip->entry.name) { | |
| 1060 | CLEANUP(zip->entry.name); | |
| 1061 | } | |
| 1062 | zip->entry.name = zip_strrpl(pFilename, namelen, '\\', '/'); | |
| 1063 | if (!zip->entry.name) { | |
| 1064 | // local entry name is NULL | |
| 1065 | return ZIP_EINVENTNAME; | |
| 1066 | } | |
| 1067 | ||
| 1068 | if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) { | |
| 1069 | return ZIP_ENOENT; | |
| 1070 | } | |
| 1071 | ||
| 1072 | zip->entry.index = index; | |
| 1073 | zip->entry.comp_size = stats.m_comp_size; | |
| 1074 | zip->entry.uncomp_size = stats.m_uncomp_size; | |
| 1075 | zip->entry.uncomp_crc32 = stats.m_crc32; | |
| 1076 | zip->entry.offset = stats.m_central_dir_ofs; | |
| 1077 | zip->entry.header_offset = stats.m_local_header_ofs; | |
| 1078 | zip->entry.method = stats.m_method; | |
| 1079 | zip->entry.external_attr = stats.m_external_attr; | |
| 1080 | #ifndef MINIZ_NO_TIME | |
| 1081 | zip->entry.m_time = stats.m_time; | |
| 1082 | #endif | |
| 1083 | ||
| 1084 | return 0; | |
| 1085 | } | |
| 1086 | ||
| 1087 | int zip_entry_close(struct zip_t *zip) { | |
| 1088 | mz_zip_archive *pzip = NULL; | |
| 1089 | mz_uint level; | |
| 1090 | tdefl_status done; | |
| 1091 | mz_uint16 entrylen; | |
| 1092 | mz_uint16 dos_time = 0, dos_date = 0; | |
| 1093 | int err = 0; | |
| 1094 | ||
| 1095 | if (!zip) { | |
| 1096 | // zip_t handler is not initialized | |
| 1097 | err = ZIP_ENOINIT; | |
| 1098 | goto cleanup; | |
| 1099 | } | |
| 1100 | ||
| 1101 | pzip = &(zip->archive); | |
| 1102 | if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { | |
| 1103 | goto cleanup; | |
| 1104 | } | |
| 1105 | ||
| 1106 | level = zip->level & 0xF; | |
| 1107 | if (level) { | |
| 1108 | done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH); | |
| 1109 | if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) { | |
| 1110 | // Cannot flush compressed buffer | |
| 1111 | err = ZIP_ETDEFLBUF; | |
| 1112 | goto cleanup; | |
| 1113 | } | |
| 1114 | zip->entry.comp_size = zip->entry.state.m_comp_size; | |
| 1115 | zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs; | |
| 1116 | zip->entry.method = MZ_DEFLATED; | |
| 1117 | } | |
| 1118 | ||
| 1119 | entrylen = (mz_uint16)strlen(zip->entry.name); | |
| 1120 | if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) { | |
| 1121 | // No zip64 support, yet | |
| 1122 | err = ZIP_ENOSUP64; | |
| 1123 | goto cleanup; | |
| 1124 | } | |
| 1125 | ||
| 1126 | #ifndef MINIZ_NO_TIME | |
| 1127 | mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date); | |
| 1128 | #endif | |
| 1129 | ||
| 1130 | if (!mz_zip_writer_create_local_dir_header( | |
| 1131 | pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size, | |
| 1132 | zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, | |
| 1133 | dos_time, dos_date)) { | |
| 1134 | // Cannot create zip entry header | |
| 1135 | err = ZIP_ECRTHDR; | |
| 1136 | goto cleanup; | |
| 1137 | } | |
| 1138 | ||
| 1139 | if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, | |
| 1140 | zip->entry.header, | |
| 1141 | sizeof(zip->entry.header)) != sizeof(zip->entry.header)) { | |
| 1142 | // Cannot write zip entry header | |
| 1143 | err = ZIP_EWRTHDR; | |
| 1144 | goto cleanup; | |
| 1145 | } | |
| 1146 | ||
| 1147 | if (!mz_zip_writer_add_to_central_dir( | |
| 1148 | pzip, zip->entry.name, entrylen, NULL, 0, "", 0, | |
| 1149 | zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, | |
| 1150 | zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset, | |
| 1151 | zip->entry.external_attr)) { | |
| 1152 | // Cannot write to zip central dir | |
| 1153 | err = ZIP_EWRTDIR; | |
| 1154 | goto cleanup; | |
| 1155 | } | |
| 1156 | ||
| 1157 | pzip->m_total_files++; | |
| 1158 | pzip->m_archive_size = zip->entry.offset; | |
| 1159 | ||
| 1160 | cleanup: | |
| 1161 | if (zip) { | |
| 1162 | zip->entry.m_time = 0; | |
| 1163 | CLEANUP(zip->entry.name); | |
| 1164 | } | |
| 1165 | return err; | |
| 1166 | } | |
| 1167 | ||
| 1168 | const char *zip_entry_name(struct zip_t *zip) { | |
| 1169 | if (!zip) { | |
| 1170 | // zip_t handler is not initialized | |
| 1171 | return NULL; | |
| 1172 | } | |
| 1173 | ||
| 1174 | return zip->entry.name; | |
| 1175 | } | |
| 1176 | ||
| 1177 | int zip_entry_index(struct zip_t *zip) { | |
| 1178 | if (!zip) { | |
| 1179 | // zip_t handler is not initialized | |
| 1180 | return ZIP_ENOINIT; | |
| 1181 | } | |
| 1182 | ||
| 1183 | return zip->entry.index; | |
| 1184 | } | |
| 1185 | ||
| 1186 | int zip_entry_isdir(struct zip_t *zip) { | |
| 1187 | if (!zip) { | |
| 1188 | // zip_t handler is not initialized | |
| 1189 | return ZIP_ENOINIT; | |
| 1190 | } | |
| 1191 | ||
| 1192 | if (zip->entry.index < 0) { | |
| 1193 | // zip entry is not opened | |
| 1194 | return ZIP_EINVIDX; | |
| 1195 | } | |
| 1196 | ||
| 1197 | return (int)mz_zip_reader_is_file_a_directory(&zip->archive, | |
| 1198 | (mz_uint)zip->entry.index); | |
| 1199 | } | |
| 1200 | ||
| 1201 | unsigned long long zip_entry_size(struct zip_t *zip) { | |
| 1202 | return zip ? zip->entry.uncomp_size : 0; | |
| 1203 | } | |
| 1204 | ||
| 1205 | unsigned int zip_entry_crc32(struct zip_t *zip) { | |
| 1206 | return zip ? zip->entry.uncomp_crc32 : 0; | |
| 1207 | } | |
| 1208 | ||
| 1209 | int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { | |
| 1210 | mz_uint level; | |
| 1211 | mz_zip_archive *pzip = NULL; | |
| 1212 | tdefl_status status; | |
| 1213 | ||
| 1214 | if (!zip) { | |
| 1215 | // zip_t handler is not initialized | |
| 1216 | return ZIP_ENOINIT; | |
| 1217 | } | |
| 1218 | ||
| 1219 | pzip = &(zip->archive); | |
| 1220 | if (buf && bufsize > 0) { | |
| 1221 | zip->entry.uncomp_size += bufsize; | |
| 1222 | zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32( | |
| 1223 | zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize); | |
| 1224 | ||
| 1225 | level = zip->level & 0xF; | |
| 1226 | if (!level) { | |
| 1227 | if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf, | |
| 1228 | bufsize) != bufsize)) { | |
| 1229 | // Cannot write buffer | |
| 1230 | return ZIP_EWRTENT; | |
| 1231 | } | |
| 1232 | zip->entry.offset += bufsize; | |
| 1233 | zip->entry.comp_size += bufsize; | |
| 1234 | } else { | |
| 1235 | status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize, | |
| 1236 | TDEFL_NO_FLUSH); | |
| 1237 | if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) { | |
| 1238 | // Cannot compress buffer | |
| 1239 | return ZIP_ETDEFLBUF; | |
| 1240 | } | |
| 1241 | } | |
| 1242 | } | |
| 1243 | ||
| 1244 | return 0; | |
| 1245 | } | |
| 1246 | ||
| 1247 | int zip_entry_fwrite(struct zip_t *zip, const char *filename) { | |
| 1248 | int err = 0; | |
| 1249 | size_t n = 0; | |
| 1250 | FILE *stream = NULL; | |
| 1251 | mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE]; | |
| 1252 | struct MZ_FILE_STAT_STRUCT file_stat; | |
| 1253 | ||
| 1254 | if (!zip) { | |
| 1255 | // zip_t handler is not initialized | |
| 1256 | return ZIP_ENOINIT; | |
| 1257 | } | |
| 1258 | ||
| 1259 | memset(buf, 0, MZ_ZIP_MAX_IO_BUF_SIZE); | |
| 1260 | memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT)); | |
| 1261 | if (MZ_FILE_STAT(filename, &file_stat) != 0) { | |
| 1262 | // problem getting information - check errno | |
| 1263 | return ZIP_ENOENT; | |
| 1264 | } | |
| 1265 | ||
| 1266 | if ((file_stat.st_mode & 0200) == 0) { | |
| 1267 | // MS-DOS read-only attribute | |
| 1268 | zip->entry.external_attr |= 0x01; | |
| 1269 | } | |
| 1270 | zip->entry.external_attr |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16); | |
| 1271 | zip->entry.m_time = file_stat.st_mtime; | |
| 1272 | ||
| 1273 | #if defined(_MSC_VER) | |
| 1274 | if (fopen_s(&stream, filename, "rb")) | |
| 1275 | #else | |
| 1276 | if (!(stream = fopen(filename, "rb"))) | |
| 1277 | #endif | |
| 1278 | { | |
| 1279 | // Cannot open filename | |
| 1280 | return ZIP_EOPNFILE; | |
| 1281 | } | |
| 1282 | ||
| 1283 | while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) > | |
| 1284 | 0) { | |
| 1285 | if (zip_entry_write(zip, buf, n) < 0) { | |
| 1286 | err = ZIP_EWRTENT; | |
| 1287 | break; | |
| 1288 | } | |
| 1289 | } | |
| 1290 | fclose(stream); | |
| 1291 | ||
| 1292 | return err; | |
| 1293 | } | |
| 1294 | ||
| 1295 | ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { | |
| 1296 | mz_zip_archive *pzip = NULL; | |
| 1297 | mz_uint idx; | |
| 1298 | size_t size = 0; | |
| 1299 | ||
| 1300 | if (!zip) { | |
| 1301 | // zip_t handler is not initialized | |
| 1302 | return (ssize_t)ZIP_ENOINIT; | |
| 1303 | } | |
| 1304 | ||
| 1305 | pzip = &(zip->archive); | |
| 1306 | if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { | |
| 1307 | // the entry is not found or we do not have read access | |
| 1308 | return (ssize_t)ZIP_ENOENT; | |
| 1309 | } | |
| 1310 | ||
| 1311 | idx = (mz_uint)zip->entry.index; | |
| 1312 | if (mz_zip_reader_is_file_a_directory(pzip, idx)) { | |
| 1313 | // the entry is a directory | |
| 1314 | return (ssize_t)ZIP_EINVENTTYPE; | |
| 1315 | } | |
| 1316 | ||
| 1317 | *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0); | |
| 1318 | if (*buf && bufsize) { | |
| 1319 | *bufsize = size; | |
| 1320 | } | |
| 1321 | return (ssize_t)size; | |
| 1322 | } | |
| 1323 | ||
| 1324 | ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) { | |
| 1325 | mz_zip_archive *pzip = NULL; | |
| 1326 | ||
| 1327 | if (!zip) { | |
| 1328 | // zip_t handler is not initialized | |
| 1329 | return (ssize_t)ZIP_ENOINIT; | |
| 1330 | } | |
| 1331 | ||
| 1332 | pzip = &(zip->archive); | |
| 1333 | if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { | |
| 1334 | // the entry is not found or we do not have read access | |
| 1335 | return (ssize_t)ZIP_ENOENT; | |
| 1336 | } | |
| 1337 | ||
| 1338 | if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index, | |
| 1339 | buf, bufsize, 0, NULL, 0)) { | |
| 1340 | return (ssize_t)ZIP_EMEMNOALLOC; | |
| 1341 | } | |
| 1342 | ||
| 1343 | return (ssize_t)zip->entry.uncomp_size; | |
| 1344 | } | |
| 1345 | ||
| 1346 | int zip_entry_fread(struct zip_t *zip, const char *filename) { | |
| 1347 | mz_zip_archive *pzip = NULL; | |
| 1348 | mz_uint idx; | |
| 1349 | mz_uint32 xattr = 0; | |
| 1350 | mz_zip_archive_file_stat info; | |
| 1351 | ||
| 1352 | if (!zip) { | |
| 1353 | // zip_t handler is not initialized | |
| 1354 | return ZIP_ENOINIT; | |
| 1355 | } | |
| 1356 | ||
| 1357 | memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); | |
| 1358 | pzip = &(zip->archive); | |
| 1359 | if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { | |
| 1360 | // the entry is not found or we do not have read access | |
| 1361 | return ZIP_ENOENT; | |
| 1362 | } | |
| 1363 | ||
| 1364 | idx = (mz_uint)zip->entry.index; | |
| 1365 | if (mz_zip_reader_is_file_a_directory(pzip, idx)) { | |
| 1366 | // the entry is a directory | |
| 1367 | return ZIP_EINVENTTYPE; | |
| 1368 | } | |
| 1369 | ||
| 1370 | if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) { | |
| 1371 | return ZIP_ENOFILE; | |
| 1372 | } | |
| 1373 | ||
| 1374 | #if defined(_MSC_VER) | |
| 1375 | (void)xattr; // unused | |
| 1376 | #else | |
| 1377 | if (!mz_zip_reader_file_stat(pzip, idx, &info)) { | |
| 1378 | // Cannot get information about zip archive; | |
| 1379 | return ZIP_ENOFILE; | |
| 1380 | } | |
| 1381 | ||
| 1382 | xattr = (info.m_external_attr >> 16) & 0xFFFF; | |
| 1383 | if (xattr > 0) { | |
| 1384 | if (chmod(filename, (mode_t)xattr) < 0) { | |
| 1385 | return ZIP_ENOPERM; | |
| 1386 | } | |
| 1387 | } | |
| 1388 | #endif | |
| 1389 | ||
| 1390 | return 0; | |
| 1391 | } | |
| 1392 | ||
| 1393 | int zip_entry_extract(struct zip_t *zip, | |
| 1394 | size_t (*on_extract)(void *arg, unsigned long long offset, | |
| 1395 | const void *buf, size_t bufsize), | |
| 1396 | void *arg) { | |
| 1397 | mz_zip_archive *pzip = NULL; | |
| 1398 | mz_uint idx; | |
| 1399 | ||
| 1400 | if (!zip) { | |
| 1401 | // zip_t handler is not initialized | |
| 1402 | return ZIP_ENOINIT; | |
| 1403 | } | |
| 1404 | ||
| 1405 | pzip = &(zip->archive); | |
| 1406 | if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { | |
| 1407 | // the entry is not found or we do not have read access | |
| 1408 | return ZIP_ENOENT; | |
| 1409 | } | |
| 1410 | ||
| 1411 | idx = (mz_uint)zip->entry.index; | |
| 1412 | return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0)) | |
| 1413 | ? 0 | |
| 1414 | : ZIP_EINVIDX; | |
| 1415 | } | |
| 1416 | ||
| 1417 | int zip_entries_total(struct zip_t *zip) { | |
| 1418 | if (!zip) { | |
| 1419 | // zip_t handler is not initialized | |
| 1420 | return ZIP_ENOINIT; | |
| 1421 | } | |
| 1422 | ||
| 1423 | return (int)zip->archive.m_total_files; | |
| 1424 | } | |
| 1425 | ||
| 1426 | int zip_entries_delete(struct zip_t *zip, char *const entries[], | |
| 1427 | const size_t len) { | |
| 1428 | int n = 0; | |
| 1429 | int err = 0; | |
| 1430 | struct zip_entry_mark_t *entry_mark = NULL; | |
| 1431 | ||
| 1432 | if (zip == NULL || (entries == NULL && len != 0)) { | |
| 1433 | return ZIP_ENOINIT; | |
| 1434 | } | |
| 1435 | ||
| 1436 | if (entries == NULL && len == 0) { | |
| 1437 | return 0; | |
| 1438 | } | |
| 1439 | ||
| 1440 | n = zip_entries_total(zip); | |
| 1441 | ||
| 1442 | entry_mark = | |
| 1443 | (struct zip_entry_mark_t *)calloc(n, sizeof(struct zip_entry_mark_t)); | |
| 1444 | if (!entry_mark) { | |
| 1445 | return ZIP_EOOMEM; | |
| 1446 | } | |
| 1447 | ||
| 1448 | zip->archive.m_zip_mode = MZ_ZIP_MODE_READING; | |
| 1449 | ||
| 1450 | err = zip_entry_set(zip, entry_mark, n, entries, len); | |
| 1451 | if (err < 0) { | |
| 1452 | CLEANUP(entry_mark); | |
| 1453 | return err; | |
| 1454 | } | |
| 1455 | ||
| 1456 | err = zip_entries_delete_mark(zip, entry_mark, n); | |
| 1457 | CLEANUP(entry_mark); | |
| 1458 | return err; | |
| 1459 | } | |
| 1460 | ||
| 1461 | int zip_stream_extract(const char *stream, size_t size, const char *dir, | |
| 1462 | int (*on_extract)(const char *filename, void *arg), | |
| 1463 | void *arg) { | |
| 1464 | mz_zip_archive zip_archive; | |
| 1465 | if (!stream || !dir) { | |
| 1466 | // Cannot parse zip archive stream | |
| 1467 | return ZIP_ENOINIT; | |
| 1468 | } | |
| 1469 | if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { | |
| 1470 | // Cannot memset zip archive | |
| 1471 | return ZIP_EMEMSET; | |
| 1472 | } | |
| 1473 | if (!mz_zip_reader_init_mem(&zip_archive, stream, size, 0)) { | |
| 1474 | // Cannot initialize zip_archive reader | |
| 1475 | return ZIP_ENOINIT; | |
| 1476 | } | |
| 1477 | ||
| 1478 | return zip_archive_extract(&zip_archive, dir, on_extract, arg); | |
| 1479 | } | |
| 1480 | ||
| 1481 | struct zip_t *zip_stream_open(const char *stream, size_t size, int level, | |
| 1482 | char mode) { | |
| 1483 | struct zip_t *zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); | |
| 1484 | if (!zip) { | |
| 1485 | return NULL; | |
| 1486 | } | |
| 1487 | ||
| 1488 | if (level < 0) { | |
| 1489 | level = MZ_DEFAULT_LEVEL; | |
| 1490 | } | |
| 1491 | if ((level & 0xF) > MZ_UBER_COMPRESSION) { | |
| 1492 | // Wrong compression level | |
| 1493 | goto cleanup; | |
| 1494 | } | |
| 1495 | zip->level = (mz_uint)level; | |
| 1496 | ||
| 1497 | if ((stream != NULL) && (size > 0) && (mode == 'r')) { | |
| 1498 | if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) { | |
| 1499 | goto cleanup; | |
| 1500 | } | |
| 1501 | } else if ((stream == NULL) && (size == 0) && (mode == 'w')) { | |
| 1502 | // Create a new archive. | |
| 1503 | if (!mz_zip_writer_init_heap(&(zip->archive), 0, 1024)) { | |
| 1504 | // Cannot initialize zip_archive writer | |
| 1505 | goto cleanup; | |
| 1506 | } | |
| 1507 | } else { | |
| 1508 | goto cleanup; | |
| 1509 | } | |
| 1510 | return zip; | |
| 1511 | ||
| 1512 | cleanup: | |
| 1513 | CLEANUP(zip); | |
| 1514 | return NULL; | |
| 1515 | } | |
| 1516 | ||
| 1517 | ssize_t zip_stream_copy(struct zip_t *zip, void **buf, size_t *bufsize) { | |
| 1518 | if (!zip) { | |
| 1519 | return (ssize_t)ZIP_ENOINIT; | |
| 1520 | } | |
| 1521 | ||
| 1522 | zip_archive_finalize(&(zip->archive)); | |
| 1523 | ||
| 1524 | if (bufsize != NULL) { | |
| 1525 | *bufsize = (size_t)zip->archive.m_archive_size; | |
| 1526 | } | |
| 1527 | *buf = calloc(sizeof(unsigned char), zip->archive.m_archive_size); | |
| 1528 | memcpy(*buf, zip->archive.m_pState->m_pMem, zip->archive.m_archive_size); | |
| 1529 | ||
| 1530 | return (ssize_t)zip->archive.m_archive_size; | |
| 1531 | } | |
| 1532 | ||
| 1533 | void zip_stream_close(struct zip_t *zip) { | |
| 1534 | if (zip) { | |
| 1535 | mz_zip_writer_end(&(zip->archive)); | |
| 1536 | mz_zip_reader_end(&(zip->archive)); | |
| 1537 | CLEANUP(zip); | |
| 1538 | } | |
| 1539 | } | |
| 1540 | ||
| 1541 | int zip_create(const char *zipname, const char *filenames[], size_t len) { | |
| 1542 | int err = 0; | |
| 1543 | size_t i; | |
| 1544 | mz_zip_archive zip_archive; | |
| 1545 | struct MZ_FILE_STAT_STRUCT file_stat; | |
| 1546 | mz_uint32 ext_attributes = 0; | |
| 1547 | ||
| 1548 | if (!zipname || strlen(zipname) < 1) { | |
| 1549 | // zip_t archive name is empty or NULL | |
| 1550 | return ZIP_EINVZIPNAME; | |
| 1551 | } | |
| 1552 | ||
| 1553 | // Create a new archive. | |
| 1554 | if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { | |
| 1555 | // Cannot memset zip archive | |
| 1556 | return ZIP_EMEMSET; | |
| 1557 | } | |
| 1558 | ||
| 1559 | if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) { | |
| 1560 | // Cannot initialize zip_archive writer | |
| 1561 | return ZIP_ENOINIT; | |
| 1562 | } | |
| 1563 | ||
| 1564 | if (!memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT))) { | |
| 1565 | return ZIP_EMEMSET; | |
| 1566 | } | |
| 1567 | ||
| 1568 | for (i = 0; i < len; ++i) { | |
| 1569 | const char *name = filenames[i]; | |
| 1570 | if (!name) { | |
| 1571 | err = ZIP_EINVENTNAME; | |
| 1572 | break; | |
| 1573 | } | |
| 1574 | ||
| 1575 | if (MZ_FILE_STAT(name, &file_stat) != 0) { | |
| 1576 | // problem getting information - check errno | |
| 1577 | err = ZIP_ENOFILE; | |
| 1578 | break; | |
| 1579 | } | |
| 1580 | ||
| 1581 | if ((file_stat.st_mode & 0200) == 0) { | |
| 1582 | // MS-DOS read-only attribute | |
| 1583 | ext_attributes |= 0x01; | |
| 1584 | } | |
| 1585 | ext_attributes |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16); | |
| 1586 | ||
| 1587 | if (!mz_zip_writer_add_file(&zip_archive, zip_basename(name), name, "", 0, | |
| 1588 | ZIP_DEFAULT_COMPRESSION_LEVEL, | |
| 1589 | ext_attributes)) { | |
| 1590 | // Cannot add file to zip_archive | |
| 1591 | err = ZIP_ENOFILE; | |
| 1592 | break; | |
| 1593 | } | |
| 1594 | } | |
| 1595 | ||
| 1596 | mz_zip_writer_finalize_archive(&zip_archive); | |
| 1597 | mz_zip_writer_end(&zip_archive); | |
| 1598 | return err; | |
| 1599 | } | |
| 1600 | ||
| 1601 | int zip_extract(const char *zipname, const char *dir, | |
| 1602 | int (*on_extract)(const char *filename, void *arg), void *arg) { | |
| 1603 | mz_zip_archive zip_archive; | |
| 1604 | ||
| 1605 | if (!zipname || !dir) { | |
| 1606 | // Cannot parse zip archive name | |
| 1607 | return ZIP_EINVZIPNAME; | |
| 1608 | } | |
| 1609 | ||
| 1610 | if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { | |
| 1611 | // Cannot memset zip archive | |
| 1612 | return ZIP_EMEMSET; | |
| 1613 | } | |
| 1614 | ||
| 1615 | // Now try to open the archive. | |
| 1616 | if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) { | |
| 1617 | // Cannot initialize zip_archive reader | |
| 1618 | return ZIP_ENOINIT; | |
| 1619 | } | |
| 1620 | ||
| 1621 | return zip_archive_extract(&zip_archive, dir, on_extract, arg); | |
| 1622 | } |
test/standalone/issue_9812/vendor/kuba-zip/zip.h created+433| ... | ... | @@ -0,0 +1,433 @@ |
| 1 | /* | |
| 2 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 3 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 4 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
| 5 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 6 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 7 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 8 | * OTHER DEALINGS IN THE SOFTWARE. | |
| 9 | */ | |
| 10 | ||
| 11 | #pragma once | |
| 12 | #ifndef ZIP_H | |
| 13 | #define ZIP_H | |
| 14 | ||
| 15 | #include <string.h> | |
| 16 | #include <sys/types.h> | |
| 17 | ||
| 18 | #ifndef ZIP_SHARED | |
| 19 | # define ZIP_EXPORT | |
| 20 | #else | |
| 21 | # ifdef _WIN32 | |
| 22 | # ifdef ZIP_BUILD_SHARED | |
| 23 | # define ZIP_EXPORT __declspec(dllexport) | |
| 24 | # else | |
| 25 | # define ZIP_EXPORT __declspec(dllimport) | |
| 26 | # endif | |
| 27 | # else | |
| 28 | # define ZIP_EXPORT __attribute__ ((visibility ("default"))) | |
| 29 | # endif | |
| 30 | #endif | |
| 31 | ||
| 32 | #ifdef __cplusplus | |
| 33 | extern "C" { | |
| 34 | #endif | |
| 35 | ||
| 36 | #if !defined(_POSIX_C_SOURCE) && defined(_MSC_VER) | |
| 37 | // 64-bit Windows is the only mainstream platform | |
| 38 | // where sizeof(long) != sizeof(void*) | |
| 39 | #ifdef _WIN64 | |
| 40 | typedef long long ssize_t; /* byte count or error */ | |
| 41 | #else | |
| 42 | typedef long ssize_t; /* byte count or error */ | |
| 43 | #endif | |
| 44 | #endif | |
| 45 | ||
| 46 | #ifndef MAX_PATH | |
| 47 | #define MAX_PATH 32767 /* # chars in a path name including NULL */ | |
| 48 | #endif | |
| 49 | ||
| 50 | /** | |
| 51 | * @mainpage | |
| 52 | * | |
| 53 | * Documenation for @ref zip. | |
| 54 | */ | |
| 55 | ||
| 56 | /** | |
| 57 | * @addtogroup zip | |
| 58 | * @{ | |
| 59 | */ | |
| 60 | ||
| 61 | /** | |
| 62 | * Default zip compression level. | |
| 63 | */ | |
| 64 | #define ZIP_DEFAULT_COMPRESSION_LEVEL 6 | |
| 65 | ||
| 66 | /** | |
| 67 | * Error codes | |
| 68 | */ | |
| 69 | #define ZIP_ENOINIT -1 // not initialized | |
| 70 | #define ZIP_EINVENTNAME -2 // invalid entry name | |
| 71 | #define ZIP_ENOENT -3 // entry not found | |
| 72 | #define ZIP_EINVMODE -4 // invalid zip mode | |
| 73 | #define ZIP_EINVLVL -5 // invalid compression level | |
| 74 | #define ZIP_ENOSUP64 -6 // no zip 64 support | |
| 75 | #define ZIP_EMEMSET -7 // memset error | |
| 76 | #define ZIP_EWRTENT -8 // cannot write data to entry | |
| 77 | #define ZIP_ETDEFLINIT -9 // cannot initialize tdefl compressor | |
| 78 | #define ZIP_EINVIDX -10 // invalid index | |
| 79 | #define ZIP_ENOHDR -11 // header not found | |
| 80 | #define ZIP_ETDEFLBUF -12 // cannot flush tdefl buffer | |
| 81 | #define ZIP_ECRTHDR -13 // cannot create entry header | |
| 82 | #define ZIP_EWRTHDR -14 // cannot write entry header | |
| 83 | #define ZIP_EWRTDIR -15 // cannot write to central dir | |
| 84 | #define ZIP_EOPNFILE -16 // cannot open file | |
| 85 | #define ZIP_EINVENTTYPE -17 // invalid entry type | |
| 86 | #define ZIP_EMEMNOALLOC -18 // extracting data using no memory allocation | |
| 87 | #define ZIP_ENOFILE -19 // file not found | |
| 88 | #define ZIP_ENOPERM -20 // no permission | |
| 89 | #define ZIP_EOOMEM -21 // out of memory | |
| 90 | #define ZIP_EINVZIPNAME -22 // invalid zip archive name | |
| 91 | #define ZIP_EMKDIR -23 // make dir error | |
| 92 | #define ZIP_ESYMLINK -24 // symlink error | |
| 93 | #define ZIP_ECLSZIP -25 // close archive error | |
| 94 | #define ZIP_ECAPSIZE -26 // capacity size too small | |
| 95 | #define ZIP_EFSEEK -27 // fseek error | |
| 96 | #define ZIP_EFREAD -28 // fread error | |
| 97 | #define ZIP_EFWRITE -29 // fwrite error | |
| 98 | ||
| 99 | /** | |
| 100 | * Looks up the error message string coresponding to an error number. | |
| 101 | * @param errnum error number | |
| 102 | * @return error message string coresponding to errnum or NULL if error is not | |
| 103 | * found. | |
| 104 | */ | |
| 105 | extern ZIP_EXPORT const char *zip_strerror(int errnum); | |
| 106 | ||
| 107 | /** | |
| 108 | * @struct zip_t | |
| 109 | * | |
| 110 | * This data structure is used throughout the library to represent zip archive - | |
| 111 | * forward declaration. | |
| 112 | */ | |
| 113 | struct zip_t; | |
| 114 | ||
| 115 | /** | |
| 116 | * Opens zip archive with compression level using the given mode. | |
| 117 | * | |
| 118 | * @param zipname zip archive file name. | |
| 119 | * @param level compression level (0-9 are the standard zlib-style levels). | |
| 120 | * @param mode file access mode. | |
| 121 | * - 'r': opens a file for reading/extracting (the file must exists). | |
| 122 | * - 'w': creates an empty file for writing. | |
| 123 | * - 'a': appends to an existing archive. | |
| 124 | * | |
| 125 | * @return the zip archive handler or NULL on error | |
| 126 | */ | |
| 127 | extern ZIP_EXPORT struct zip_t *zip_open(const char *zipname, int level, | |
| 128 | char mode); | |
| 129 | ||
| 130 | /** | |
| 131 | * Closes the zip archive, releases resources - always finalize. | |
| 132 | * | |
| 133 | * @param zip zip archive handler. | |
| 134 | */ | |
| 135 | extern ZIP_EXPORT void zip_close(struct zip_t *zip); | |
| 136 | ||
| 137 | /** | |
| 138 | * Determines if the archive has a zip64 end of central directory headers. | |
| 139 | * | |
| 140 | * @param zip zip archive handler. | |
| 141 | * | |
| 142 | * @return the return code - 1 (true), 0 (false), negative number (< 0) on | |
| 143 | * error. | |
| 144 | */ | |
| 145 | extern ZIP_EXPORT int zip_is64(struct zip_t *zip); | |
| 146 | ||
| 147 | /** | |
| 148 | * Opens an entry by name in the zip archive. | |
| 149 | * | |
| 150 | * For zip archive opened in 'w' or 'a' mode the function will append | |
| 151 | * a new entry. In readonly mode the function tries to locate the entry | |
| 152 | * in global dictionary. | |
| 153 | * | |
| 154 | * @param zip zip archive handler. | |
| 155 | * @param entryname an entry name in local dictionary. | |
| 156 | * | |
| 157 | * @return the return code - 0 on success, negative number (< 0) on error. | |
| 158 | */ | |
| 159 | extern ZIP_EXPORT int zip_entry_open(struct zip_t *zip, const char *entryname); | |
| 160 | ||
| 161 | /** | |
| 162 | * Opens a new entry by index in the zip archive. | |
| 163 | * | |
| 164 | * This function is only valid if zip archive was opened in 'r' (readonly) mode. | |
| 165 | * | |
| 166 | * @param zip zip archive handler. | |
| 167 | * @param index index in local dictionary. | |
| 168 | * | |
| 169 | * @return the return code - 0 on success, negative number (< 0) on error. | |
| 170 | */ | |
| 171 | extern ZIP_EXPORT int zip_entry_openbyindex(struct zip_t *zip, int index); | |
| 172 | ||
| 173 | /** | |
| 174 | * Closes a zip entry, flushes buffer and releases resources. | |
| 175 | * | |
| 176 | * @param zip zip archive handler. | |
| 177 | * | |
| 178 | * @return the return code - 0 on success, negative number (< 0) on error. | |
| 179 | */ | |
| 180 | extern ZIP_EXPORT int zip_entry_close(struct zip_t *zip); | |
| 181 | ||
| 182 | /** | |
| 183 | * Returns a local name of the current zip entry. | |
| 184 | * | |
| 185 | * The main difference between user's entry name and local entry name | |
| 186 | * is optional relative path. | |
| 187 | * Following .ZIP File Format Specification - the path stored MUST not contain | |
| 188 | * a drive or device letter, or a leading slash. | |
| 189 | * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' | |
| 190 | * for compatibility with Amiga and UNIX file systems etc. | |
| 191 | * | |
| 192 | * @param zip: zip archive handler. | |
| 193 | * | |
| 194 | * @return the pointer to the current zip entry name, or NULL on error. | |
| 195 | */ | |
| 196 | extern ZIP_EXPORT const char *zip_entry_name(struct zip_t *zip); | |
| 197 | ||
| 198 | /** | |
| 199 | * Returns an index of the current zip entry. | |
| 200 | * | |
| 201 | * @param zip zip archive handler. | |
| 202 | * | |
| 203 | * @return the index on success, negative number (< 0) on error. | |
| 204 | */ | |
| 205 | extern ZIP_EXPORT int zip_entry_index(struct zip_t *zip); | |
| 206 | ||
| 207 | /** | |
| 208 | * Determines if the current zip entry is a directory entry. | |
| 209 | * | |
| 210 | * @param zip zip archive handler. | |
| 211 | * | |
| 212 | * @return the return code - 1 (true), 0 (false), negative number (< 0) on | |
| 213 | * error. | |
| 214 | */ | |
| 215 | extern ZIP_EXPORT int zip_entry_isdir(struct zip_t *zip); | |
| 216 | ||
| 217 | /** | |
| 218 | * Returns an uncompressed size of the current zip entry. | |
| 219 | * | |
| 220 | * @param zip zip archive handler. | |
| 221 | * | |
| 222 | * @return the uncompressed size in bytes. | |
| 223 | */ | |
| 224 | extern ZIP_EXPORT unsigned long long zip_entry_size(struct zip_t *zip); | |
| 225 | ||
| 226 | /** | |
| 227 | * Returns CRC-32 checksum of the current zip entry. | |
| 228 | * | |
| 229 | * @param zip zip archive handler. | |
| 230 | * | |
| 231 | * @return the CRC-32 checksum. | |
| 232 | */ | |
| 233 | extern ZIP_EXPORT unsigned int zip_entry_crc32(struct zip_t *zip); | |
| 234 | ||
| 235 | /** | |
| 236 | * Compresses an input buffer for the current zip entry. | |
| 237 | * | |
| 238 | * @param zip zip archive handler. | |
| 239 | * @param buf input buffer. | |
| 240 | * @param bufsize input buffer size (in bytes). | |
| 241 | * | |
| 242 | * @return the return code - 0 on success, negative number (< 0) on error. | |
| 243 | */ | |
| 244 | extern ZIP_EXPORT int zip_entry_write(struct zip_t *zip, const void *buf, | |
| 245 | size_t bufsize); | |
| 246 | ||
| 247 | /** | |
| 248 | * Compresses a file for the current zip entry. | |
| 249 | * | |
| 250 | * @param zip zip archive handler. | |
| 251 | * @param filename input file. | |
| 252 | * | |
| 253 | * @return the return code - 0 on success, negative number (< 0) on error. | |
| 254 | */ | |
| 255 | extern ZIP_EXPORT int zip_entry_fwrite(struct zip_t *zip, const char *filename); | |
| 256 | ||
| 257 | /** | |
| 258 | * Extracts the current zip entry into output buffer. | |
| 259 | * | |
| 260 | * The function allocates sufficient memory for a output buffer. | |
| 261 | * | |
| 262 | * @param zip zip archive handler. | |
| 263 | * @param buf output buffer. | |
| 264 | * @param bufsize output buffer size (in bytes). | |
| 265 | * | |
| 266 | * @note remember to release memory allocated for a output buffer. | |
| 267 | * for large entries, please take a look at zip_entry_extract function. | |
| 268 | * | |
| 269 | * @return the return code - the number of bytes actually read on success. | |
| 270 | * Otherwise a negative number (< 0) on error. | |
| 271 | */ | |
| 272 | extern ZIP_EXPORT ssize_t zip_entry_read(struct zip_t *zip, void **buf, | |
| 273 | size_t *bufsize); | |
| 274 | ||
| 275 | /** | |
| 276 | * Extracts the current zip entry into a memory buffer using no memory | |
| 277 | * allocation. | |
| 278 | * | |
| 279 | * @param zip zip archive handler. | |
| 280 | * @param buf preallocated output buffer. | |
| 281 | * @param bufsize output buffer size (in bytes). | |
| 282 | * | |
| 283 | * @note ensure supplied output buffer is large enough. | |
| 284 | * zip_entry_size function (returns uncompressed size for the current | |
| 285 | * entry) can be handy to estimate how big buffer is needed. | |
| 286 | * For large entries, please take a look at zip_entry_extract function. | |
| 287 | * | |
| 288 | * @return the return code - the number of bytes actually read on success. | |
| 289 | * Otherwise a negative number (< 0) on error (e.g. bufsize is not large enough). | |
| 290 | */ | |
| 291 | extern ZIP_EXPORT ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, | |
| 292 | size_t bufsize); | |
| 293 | ||
| 294 | /** | |
| 295 | * Extracts the current zip entry into output file. | |
| 296 | * | |
| 297 | * @param zip zip archive handler. | |
| 298 | * @param filename output file. | |
| 299 | * | |
| 300 | * @return the return code - 0 on success, negative number (< 0) on error. | |
| 301 | */ | |
| 302 | extern ZIP_EXPORT int zip_entry_fread(struct zip_t *zip, const char *filename); | |
| 303 | ||
| 304 | /** | |
| 305 | * Extracts the current zip entry using a callback function (on_extract). | |
| 306 | * | |
| 307 | * @param zip zip archive handler. | |
| 308 | * @param on_extract callback function. | |
| 309 | * @param arg opaque pointer (optional argument, which you can pass to the | |
| 310 | * on_extract callback) | |
| 311 | * | |
| 312 | * @return the return code - 0 on success, negative number (< 0) on error. | |
| 313 | */ | |
| 314 | extern ZIP_EXPORT int | |
| 315 | zip_entry_extract(struct zip_t *zip, | |
| 316 | size_t (*on_extract)(void *arg, unsigned long long offset, | |
| 317 | const void *data, size_t size), | |
| 318 | void *arg); | |
| 319 | ||
| 320 | /** | |
| 321 | * Returns the number of all entries (files and directories) in the zip archive. | |
| 322 | * | |
| 323 | * @param zip zip archive handler. | |
| 324 | * | |
| 325 | * @return the return code - the number of entries on success, negative number | |
| 326 | * (< 0) on error. | |
| 327 | */ | |
| 328 | extern ZIP_EXPORT int zip_entries_total(struct zip_t *zip); | |
| 329 | ||
| 330 | /** | |
| 331 | * Deletes zip archive entries. | |
| 332 | * | |
| 333 | * @param zip zip archive handler. | |
| 334 | * @param entries array of zip archive entries to be deleted. | |
| 335 | * @param len the number of entries to be deleted. | |
| 336 | * @return the number of deleted entries, or negative number (< 0) on error. | |
| 337 | */ | |
| 338 | extern ZIP_EXPORT int zip_entries_delete(struct zip_t *zip, | |
| 339 | char *const entries[], size_t len); | |
| 340 | ||
| 341 | /** | |
| 342 | * Extracts a zip archive stream into directory. | |
| 343 | * | |
| 344 | * If on_extract is not NULL, the callback will be called after | |
| 345 | * successfully extracted each zip entry. | |
| 346 | * Returning a negative value from the callback will cause abort and return an | |
| 347 | * error. The last argument (void *arg) is optional, which you can use to pass | |
| 348 | * data to the on_extract callback. | |
| 349 | * | |
| 350 | * @param stream zip archive stream. | |
| 351 | * @param size stream size. | |
| 352 | * @param dir output directory. | |
| 353 | * @param on_extract on extract callback. | |
| 354 | * @param arg opaque pointer. | |
| 355 | * | |
| 356 | * @return the return code - 0 on success, negative number (< 0) on error. | |
| 357 | */ | |
| 358 | extern ZIP_EXPORT int | |
| 359 | zip_stream_extract(const char *stream, size_t size, const char *dir, | |
| 360 | int (*on_extract)(const char *filename, void *arg), | |
| 361 | void *arg); | |
| 362 | ||
| 363 | /** | |
| 364 | * Opens zip archive stream into memory. | |
| 365 | * | |
| 366 | * @param stream zip archive stream. | |
| 367 | * @param size stream size. | |
| 368 | * | |
| 369 | * @return the zip archive handler or NULL on error | |
| 370 | */ | |
| 371 | extern ZIP_EXPORT struct zip_t *zip_stream_open(const char *stream, size_t size, | |
| 372 | int level, char mode); | |
| 373 | ||
| 374 | /** | |
| 375 | * Copy zip archive stream output buffer. | |
| 376 | * | |
| 377 | * @param zip zip archive handler. | |
| 378 | * @param buf output buffer. User should free buf. | |
| 379 | * @param bufsize output buffer size (in bytes). | |
| 380 | * | |
| 381 | * @return copy size | |
| 382 | */ | |
| 383 | extern ZIP_EXPORT ssize_t zip_stream_copy(struct zip_t *zip, void **buf, | |
| 384 | size_t *bufsize); | |
| 385 | ||
| 386 | /** | |
| 387 | * Close zip archive releases resources. | |
| 388 | * | |
| 389 | * @param zip zip archive handler. | |
| 390 | * | |
| 391 | * @return | |
| 392 | */ | |
| 393 | extern ZIP_EXPORT void zip_stream_close(struct zip_t *zip); | |
| 394 | ||
| 395 | /** | |
| 396 | * Creates a new archive and puts files into a single zip archive. | |
| 397 | * | |
| 398 | * @param zipname zip archive file. | |
| 399 | * @param filenames input files. | |
| 400 | * @param len: number of input files. | |
| 401 | * | |
| 402 | * @return the return code - 0 on success, negative number (< 0) on error. | |
| 403 | */ | |
| 404 | extern ZIP_EXPORT int zip_create(const char *zipname, const char *filenames[], | |
| 405 | size_t len); | |
| 406 | ||
| 407 | /** | |
| 408 | * Extracts a zip archive file into directory. | |
| 409 | * | |
| 410 | * If on_extract_entry is not NULL, the callback will be called after | |
| 411 | * successfully extracted each zip entry. | |
| 412 | * Returning a negative value from the callback will cause abort and return an | |
| 413 | * error. The last argument (void *arg) is optional, which you can use to pass | |
| 414 | * data to the on_extract_entry callback. | |
| 415 | * | |
| 416 | * @param zipname zip archive file. | |
| 417 | * @param dir output directory. | |
| 418 | * @param on_extract_entry on extract callback. | |
| 419 | * @param arg opaque pointer. | |
| 420 | * | |
| 421 | * @return the return code - 0 on success, negative number (< 0) on error. | |
| 422 | */ | |
| 423 | extern ZIP_EXPORT int zip_extract(const char *zipname, const char *dir, | |
| 424 | int (*on_extract_entry)(const char *filename, | |
| 425 | void *arg), | |
| 426 | void *arg); | |
| 427 | ||
| 428 | /** @} */ | |
| 429 | #ifdef __cplusplus | |
| 430 | } | |
| 431 | #endif | |
| 432 | ||
| 433 | #endif |
test/tests.zig+1-1| ... | ... | @@ -455,7 +455,7 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St |
| 455 | 455 | const cases = b.allocator.create(TranslateCContext) catch unreachable; |
| 456 | 456 | cases.* = TranslateCContext{ |
| 457 | 457 | .b = b, |
| 458 | .step = b.step("test-translate-c", "Run the C transation tests"), | |
| 458 | .step = b.step("test-translate-c", "Run the C translation tests"), | |
| 459 | 459 | .test_index = 0, |
| 460 | 460 | .test_filter = test_filter, |
| 461 | 461 | }; |
test/translate_c.zig+2-2| ... | ... | @@ -3188,7 +3188,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3188 | 3188 | \\} |
| 3189 | 3189 | }); |
| 3190 | 3190 | |
| 3191 | cases.add("macro comparisions", | |
| 3191 | cases.add("macro comparisons", | |
| 3192 | 3192 | \\#define MIN(a, b) ((b) < (a) ? (b) : (a)) |
| 3193 | 3193 | \\#define MAX(a, b) ((b) > (a) ? (b) : (a)) |
| 3194 | 3194 | , &[_][]const u8{ |
| ... | ... | @@ -3443,7 +3443,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3443 | 3443 | }); |
| 3444 | 3444 | } |
| 3445 | 3445 | |
| 3446 | cases.add("unnamed fields have predictabile names", | |
| 3446 | cases.add("unnamed fields have predictable names", | |
| 3447 | 3447 | \\struct a { |
| 3448 | 3448 | \\ struct {}; |
| 3449 | 3449 | \\}; |
tools/update_clang_options.zig+4| ... | ... | @@ -376,6 +376,10 @@ const known_options = [_]KnownOpt{ |
| 376 | 376 | .name = "mexec-model", |
| 377 | 377 | .ident = "exec_model", |
| 378 | 378 | }, |
| 379 | .{ | |
| 380 | .name = "emit-llvm", | |
| 381 | .ident = "emit_llvm", | |
| 382 | }, | |
| 379 | 383 | }; |
| 380 | 384 | |
| 381 | 385 | const blacklisted_options = [_][]const u8{}; |