authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-30 21:38:04-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-30 21:38:04-07:00
log3eb729b442d6cc69bd9a9d37816e9458190bc30b
treea412281c18914503c1566e79ab6a6f4eea187bf3
parent1f653b7f8e9e358a5bfe2695a11c01da56f3d5ee
parentc4cd592f0e1eeff5a4056796610d97010ae4e38c

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


200 files changed, 34663 insertions(+), 14445 deletions(-)

.builds/freebsd.yml+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1image: freebsd/latest1image: freebsd/latest
2secrets:2secrets:
3 - 51bfddf5-86a6-4e01-8576-358c72a4a0a43 - 51bfddf5-86a6-4e01-8576-358c72a4a0a4
4 - 5cfede76-914e-4071-893e-e5e2e6ae3cea4 - 512ed797-0927-475a-83fd-bc997792860c
5sources:5sources:
6 - https://github.com/ziglang/zig6 - https://github.com/ziglang/zig
7tasks:7tasks:
.builds/netbsd.yml+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1image: netbsd/latest1image: netbsd/latest
2secrets:2secrets:
3 - 51bfddf5-86a6-4e01-8576-358c72a4a0a43 - 51bfddf5-86a6-4e01-8576-358c72a4a0a4
4 - 5cfede76-914e-4071-893e-e5e2e6ae3cea4 - 512ed797-0927-475a-83fd-bc997792860c
5sources:5sources:
6 - https://github.com/ziglang/zig6 - https://github.com/ziglang/zig
7tasks:7tasks:
CMakeLists.txt+16-14
...@@ -537,41 +537,43 @@ set(ZIG_STAGE2_SOURCES...@@ -537,41 +537,43 @@ set(ZIG_STAGE2_SOURCES
537 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"537 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
538 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig"538 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig"
539 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"539 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
540 "${CMAKE_SOURCE_DIR}/src/Air.zig"
541 "${CMAKE_SOURCE_DIR}/src/AstGen.zig"
540 "${CMAKE_SOURCE_DIR}/src/Cache.zig"542 "${CMAKE_SOURCE_DIR}/src/Cache.zig"
541 "${CMAKE_SOURCE_DIR}/src/Compilation.zig"543 "${CMAKE_SOURCE_DIR}/src/Compilation.zig"
542 "${CMAKE_SOURCE_DIR}/src/DepTokenizer.zig"544 "${CMAKE_SOURCE_DIR}/src/DepTokenizer.zig"
545 "${CMAKE_SOURCE_DIR}/src/Liveness.zig"
543 "${CMAKE_SOURCE_DIR}/src/Module.zig"546 "${CMAKE_SOURCE_DIR}/src/Module.zig"
544 "${CMAKE_SOURCE_DIR}/src/Package.zig"547 "${CMAKE_SOURCE_DIR}/src/Package.zig"
545 "${CMAKE_SOURCE_DIR}/src/RangeSet.zig"548 "${CMAKE_SOURCE_DIR}/src/RangeSet.zig"
549 "${CMAKE_SOURCE_DIR}/src/Sema.zig"
546 "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig"550 "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig"
547 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"551 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"
548 "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig"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 "${CMAKE_SOURCE_DIR}/src/clang.zig"558 "${CMAKE_SOURCE_DIR}/src/clang.zig"
551 "${CMAKE_SOURCE_DIR}/src/clang_options.zig"559 "${CMAKE_SOURCE_DIR}/src/clang_options.zig"
552 "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"560 "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"
553 "${CMAKE_SOURCE_DIR}/src/codegen.zig"561 "${CMAKE_SOURCE_DIR}/src/codegen.zig"
554 "${CMAKE_SOURCE_DIR}/src/codegen/aarch64.zig"
555 "${CMAKE_SOURCE_DIR}/src/codegen/arm.zig"
556 "${CMAKE_SOURCE_DIR}/src/codegen/c.zig"562 "${CMAKE_SOURCE_DIR}/src/codegen/c.zig"
557 "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig"563 "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig"
558 "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig"564 "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig"
559 "${CMAKE_SOURCE_DIR}/src/codegen/riscv64.zig"
560 "${CMAKE_SOURCE_DIR}/src/codegen/wasm.zig"565 "${CMAKE_SOURCE_DIR}/src/codegen/wasm.zig"
561 "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig"
562 "${CMAKE_SOURCE_DIR}/src/glibc.zig"566 "${CMAKE_SOURCE_DIR}/src/glibc.zig"
563 "${CMAKE_SOURCE_DIR}/src/introspect.zig"567 "${CMAKE_SOURCE_DIR}/src/introspect.zig"
564 "${CMAKE_SOURCE_DIR}/src/Air.zig"
565 "${CMAKE_SOURCE_DIR}/src/libc_installation.zig"568 "${CMAKE_SOURCE_DIR}/src/libc_installation.zig"
566 "${CMAKE_SOURCE_DIR}/src/libcxx.zig"569 "${CMAKE_SOURCE_DIR}/src/libcxx.zig"
567 "${CMAKE_SOURCE_DIR}/src/libtsan.zig"570 "${CMAKE_SOURCE_DIR}/src/libtsan.zig"
568 "${CMAKE_SOURCE_DIR}/src/libunwind.zig"571 "${CMAKE_SOURCE_DIR}/src/libunwind.zig"
569 "${CMAKE_SOURCE_DIR}/src/link.zig"572 "${CMAKE_SOURCE_DIR}/src/link.zig"
570 "${CMAKE_SOURCE_DIR}/src/link/C.zig"573 "${CMAKE_SOURCE_DIR}/src/link/C.zig"
574 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
571 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"575 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
572 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"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 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"577 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
576 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"578 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
577 "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig"579 "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig"
...@@ -582,20 +584,22 @@ set(ZIG_STAGE2_SOURCES...@@ -582,20 +584,22 @@ set(ZIG_STAGE2_SOURCES
582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"584 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
583 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"585 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
584 "${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"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 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"589 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
590 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
586 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"591 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"
592 "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig"
587 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"593 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"
588 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse/test.zig"594 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse/test.zig"
589 "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig"
590 "${CMAKE_SOURCE_DIR}/src/link/tapi/yaml.zig"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 "${CMAKE_SOURCE_DIR}/src/main.zig"596 "${CMAKE_SOURCE_DIR}/src/main.zig"
595 "${CMAKE_SOURCE_DIR}/src/mingw.zig"597 "${CMAKE_SOURCE_DIR}/src/mingw.zig"
596 "${CMAKE_SOURCE_DIR}/src/musl.zig"598 "${CMAKE_SOURCE_DIR}/src/musl.zig"
599 "${CMAKE_SOURCE_DIR}/src/print_air.zig"
597 "${CMAKE_SOURCE_DIR}/src/print_env.zig"600 "${CMAKE_SOURCE_DIR}/src/print_env.zig"
598 "${CMAKE_SOURCE_DIR}/src/print_targets.zig"601 "${CMAKE_SOURCE_DIR}/src/print_targets.zig"
602 "${CMAKE_SOURCE_DIR}/src/print_zir.zig"
599 "${CMAKE_SOURCE_DIR}/src/stage1.zig"603 "${CMAKE_SOURCE_DIR}/src/stage1.zig"
600 "${CMAKE_SOURCE_DIR}/src/target.zig"604 "${CMAKE_SOURCE_DIR}/src/target.zig"
601 "${CMAKE_SOURCE_DIR}/src/tracy.zig"605 "${CMAKE_SOURCE_DIR}/src/tracy.zig"
...@@ -605,8 +609,6 @@ set(ZIG_STAGE2_SOURCES...@@ -605,8 +609,6 @@ set(ZIG_STAGE2_SOURCES
605 "${CMAKE_SOURCE_DIR}/src/value.zig"609 "${CMAKE_SOURCE_DIR}/src/value.zig"
606 "${CMAKE_SOURCE_DIR}/src/wasi_libc.zig"610 "${CMAKE_SOURCE_DIR}/src/wasi_libc.zig"
607 "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"611 "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"
608 "${CMAKE_SOURCE_DIR}/src/Zir.zig"
609 "${CMAKE_SOURCE_DIR}/src/Sema.zig"
610)612)
611613
612if(MSVC)614if(MSVC)
build.zig+53-26
...@@ -18,6 +18,7 @@ pub fn build(b: *Builder) !void {...@@ -18,6 +18,7 @@ pub fn build(b: *Builder) !void {
18 const mode = b.standardReleaseOptions();18 const mode = b.standardReleaseOptions();
19 const target = b.standardTargetOptions(.{});19 const target = b.standardTargetOptions(.{});
20 const single_threaded = b.option(bool, "single-threaded", "Build artifacts that run in single threaded mode") orelse false;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;
2122
22 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");23 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
23 docgen_exe.single_threaded = single_threaded;24 docgen_exe.single_threaded = single_threaded;
...@@ -125,10 +126,18 @@ pub fn build(b: *Builder) !void {...@@ -125,10 +126,18 @@ pub fn build(b: *Builder) !void {
125 exe.install();126 exe.install();
126 exe.setBuildMode(mode);127 exe.setBuildMode(mode);
127 exe.setTarget(target);128 exe.setTarget(target);
128 toolchain_step.dependOn(&exe.step);129 if (!skip_stage2_tests) {
130 toolchain_step.dependOn(&exe.step);
131 }
129 b.default_step.dependOn(&exe.step);132 b.default_step.dependOn(&exe.step);
130 exe.single_threaded = single_threaded;133 exe.single_threaded = single_threaded;
131134
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 const exe_options = b.addOptions();141 const exe_options = b.addOptions();
133 exe.addOptions("build_options", exe_options);142 exe.addOptions("build_options", exe_options);
134143
...@@ -182,8 +191,8 @@ pub fn build(b: *Builder) !void {...@@ -182,8 +191,8 @@ pub fn build(b: *Builder) !void {
182 b.addSearchPrefix(cfg.cmake_prefix_path);191 b.addSearchPrefix(cfg.cmake_prefix_path);
183 }192 }
184193
185 try addCmakeCfgOptionsToExe(b, cfg, exe);194 try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx);
186 try addCmakeCfgOptionsToExe(b, cfg, test_stage2);195 try addCmakeCfgOptionsToExe(b, cfg, test_stage2, use_zig_libcxx);
187 } else {196 } else {
188 // Here we are -Denable-llvm but no cmake integration.197 // Here we are -Denable-llvm but no cmake integration.
189 try addStaticLlvmOptionsToExe(exe);198 try addStaticLlvmOptionsToExe(exe);
...@@ -260,12 +269,24 @@ pub fn build(b: *Builder) !void {...@@ -260,12 +269,24 @@ pub fn build(b: *Builder) !void {
260 b.allocator,269 b.allocator,
261 &[_][]const u8{ tracy_path, "TracyClient.cpp" },270 &[_][]const u8{ tracy_path, "TracyClient.cpp" },
262 ) catch unreachable;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 exe.addIncludeDir(tracy_path);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 if (!enable_llvm) {281 if (!enable_llvm) {
266 exe.linkSystemLibraryName("c++");282 exe.linkSystemLibraryName("c++");
267 }283 }
268 exe.linkLibC();284 exe.linkLibC();
285
286 if (target.isWindows()) {
287 exe.linkSystemLibrary("dbghelp");
288 exe.linkSystemLibrary("ws2_32");
289 }
269 }290 }
270291
271 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");292 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
...@@ -434,6 +455,7 @@ fn addCmakeCfgOptionsToExe(...@@ -434,6 +455,7 @@ fn addCmakeCfgOptionsToExe(
434 b: *Builder,455 b: *Builder,
435 cfg: CMakeConfig,456 cfg: CMakeConfig,
436 exe: *std.build.LibExeObjStep,457 exe: *std.build.LibExeObjStep,
458 use_zig_libcxx: bool,
437) !void {459) !void {
438 exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{460 exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{
439 cfg.cmake_binary_dir,461 cfg.cmake_binary_dir,
...@@ -446,28 +468,32 @@ fn addCmakeCfgOptionsToExe(...@@ -446,28 +468,32 @@ fn addCmakeCfgOptionsToExe(
446 addCMakeLibraryList(exe, cfg.lld_libraries);468 addCMakeLibraryList(exe, cfg.lld_libraries);
447 addCMakeLibraryList(exe, cfg.llvm_libraries);469 addCMakeLibraryList(exe, cfg.llvm_libraries);
448470
449 const need_cpp_includes = true;471 if (use_zig_libcxx) {
450472 exe.linkLibCpp();
451 // System -lc++ must be used because in this code path we are attempting to link473 } else {
452 // against system-provided LLVM, Clang, LLD.474 const need_cpp_includes = true;
453 if (exe.target.getOsTag() == .linux) {475
454 // First we try to static link against gcc libstdc++. If that doesn't work,476 // System -lc++ must be used because in this code path we are attempting to link
455 // we fall back to -lc++ and cross our fingers.477 // against system-provided LLVM, Clang, LLD.
456 addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {478 if (exe.target.getOsTag() == .linux) {
457 error.RequiredLibraryNotFound => {479 // First we try to static link against gcc libstdc++. If that doesn't work,
458 exe.linkSystemLibrary("c++");480 // we fall back to -lc++ and cross our fingers.
459 },481 addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
460 else => |e| return e,482 error.RequiredLibraryNotFound => {
461 };483 exe.linkSystemLibrary("c++");
462 exe.linkSystemLibrary("unwind");484 },
463 } else if (exe.target.isFreeBSD()) {485 else => |e| return e,
464 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);486 };
465 exe.linkSystemLibrary("pthread");487 exe.linkSystemLibrary("unwind");
466 } else if (exe.target.getOsTag() == .openbsd) {488 } else if (exe.target.isFreeBSD()) {
467 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);489 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
468 try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);490 exe.linkSystemLibrary("pthread");
469 } else if (exe.target.isDarwin()) {491 } else if (exe.target.getOsTag() == .openbsd) {
470 exe.linkSystemLibrary("c++");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 }
472498
473 if (cfg.dia_guids_lib.len != 0) {499 if (cfg.dia_guids_lib.len != 0) {
...@@ -504,6 +530,7 @@ fn addStaticLlvmOptionsToExe(...@@ -504,6 +530,7 @@ fn addStaticLlvmOptionsToExe(
504 if (exe.target.getOs().tag == .windows) {530 if (exe.target.getOs().tag == .windows) {
505 exe.linkSystemLibrary("version");531 exe.linkSystemLibrary("version");
506 exe.linkSystemLibrary("uuid");532 exe.linkSystemLibrary("uuid");
533 exe.linkSystemLibrary("ole32");
507 }534 }
508}535}
509536
ci/azure/linux_script+17-4
...@@ -20,7 +20,7 @@ cd $HOME...@@ -20,7 +20,7 @@ cd $HOME
20wget -nv "https://ziglang.org/deps/$CACHE_BASENAME.tar.xz"20wget -nv "https://ziglang.org/deps/$CACHE_BASENAME.tar.xz"
21tar xf "$CACHE_BASENAME.tar.xz"21tar xf "$CACHE_BASENAME.tar.xz"
2222
23QEMUBASE="qemu-linux-x86_64-5.2.0.1"23QEMUBASE="qemu-linux-x86_64-6.1.0.1"
24wget -nv "https://ziglang.org/deps/$QEMUBASE.tar.xz"24wget -nv "https://ziglang.org/deps/$QEMUBASE.tar.xz"
25tar xf "$QEMUBASE.tar.xz"25tar xf "$QEMUBASE.tar.xz"
26export PATH="$(pwd)/$QEMUBASE/bin:$PATH"26export PATH="$(pwd)/$QEMUBASE/bin:$PATH"
...@@ -71,9 +71,22 @@ make $JOBS install...@@ -71,9 +71,22 @@ make $JOBS install
7171
72release/bin/zig test ../test/behavior.zig -fno-stage1 -fLLVM -I ../test72release/bin/zig test ../test/behavior.zig -fno-stage1 -fLLVM -I ../test
7373
74release/bin/zig build test-toolchain -Denable-qemu -Denable-wasmtime74release/bin/zig build test-behavior -Denable-qemu -Denable-wasmtime
75release/bin/zig build test-std -Denable-qemu -Denable-wasmtime75release/bin/zig build test-compiler-rt -Denable-qemu -Denable-wasmtime
76release/bin/zig build docs -Denable-qemu -Denable-wasmtime76release/bin/zig build test-std -Denable-qemu -Denable-wasmtime
77release/bin/zig build test-minilibc -Denable-qemu -Denable-wasmtime
78release/bin/zig build test-compare-output -Denable-qemu -Denable-wasmtime
79release/bin/zig build test-standalone -Denable-qemu -Denable-wasmtime
80release/bin/zig build test-stack-traces -Denable-qemu -Denable-wasmtime
81release/bin/zig build test-cli -Denable-qemu -Denable-wasmtime
82release/bin/zig build test-asm-link -Denable-qemu -Denable-wasmtime
83release/bin/zig build test-runtime-safety -Denable-qemu -Denable-wasmtime
84release/bin/zig build test-translate-c -Denable-qemu -Denable-wasmtime
85release/bin/zig build test-run-translated-c -Denable-qemu -Denable-wasmtime
86release/bin/zig build docs -Denable-qemu -Denable-wasmtime
87release/bin/zig build # test building self-hosted without LLVM
88release/bin/zig build test-fmt -Denable-qemu -Denable-wasmtime
89release/bin/zig build test-stage2 -Denable-qemu -Denable-wasmtime
7790
78# Look for HTML errors.91# Look for HTML errors.
79tidy -qe ../zig-cache/langref.html92tidy -qe ../zig-cache/langref.html
ci/azure/pipelines.yml+1-2
...@@ -38,9 +38,8 @@ jobs:...@@ -38,9 +38,8 @@ jobs:
38 timeoutInMinutes: 36038 timeoutInMinutes: 360
39 steps:39 steps:
40 - powershell: |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 .\sfx.exe -y -o\42 .\sfx.exe -y -o\
43 del sfx.exe
44 displayName: Download/Extract/Install MSYS243 displayName: Download/Extract/Install MSYS2
45 - script: |44 - script: |
46 @REM install updated filesystem package first without dependency checking45 @REM install updated filesystem package first without dependency checking
ci/azure/windows_msvc_script.bat+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1@echo on1@echo on
2SET "SRCROOT=%cd%"2SET "SRCROOT=%cd%"
3SET "PREVPATH=%PATH%"3SET "PREVPATH=%PATH%"
4SET "PREVMSYSEM=%MSYSTEM%"4SET "PREVMSYSTEM=%MSYSTEM%"
55
6set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem"6set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem"
7SET "MSYSTEM=MINGW64"7SET "MSYSTEM=MINGW64"
doc/docgen.zig+10-3
...@@ -901,6 +901,7 @@ fn tokenizeAndPrintRaw(...@@ -901,6 +901,7 @@ fn tokenizeAndPrintRaw(
901 switch (token.tag) {901 switch (token.tag) {
902 .eof => break,902 .eof => break,
903903
904 .keyword_addrspace,
904 .keyword_align,905 .keyword_align,
905 .keyword_and,906 .keyword_and,
906 .keyword_asm,907 .keyword_asm,
...@@ -1057,15 +1058,21 @@ fn tokenizeAndPrintRaw(...@@ -1057,15 +1058,21 @@ fn tokenizeAndPrintRaw(
1057 .plus_equal,1058 .plus_equal,
1058 .plus_percent,1059 .plus_percent,
1059 .plus_percent_equal,1060 .plus_percent_equal,
1061 .plus_pipe,
1062 .plus_pipe_equal,
1060 .minus,1063 .minus,
1061 .minus_equal,1064 .minus_equal,
1062 .minus_percent,1065 .minus_percent,
1063 .minus_percent_equal,1066 .minus_percent_equal,
1067 .minus_pipe,
1068 .minus_pipe_equal,
1064 .asterisk,1069 .asterisk,
1065 .asterisk_equal,1070 .asterisk_equal,
1066 .asterisk_asterisk,1071 .asterisk_asterisk,
1067 .asterisk_percent,1072 .asterisk_percent,
1068 .asterisk_percent_equal,1073 .asterisk_percent_equal,
1074 .asterisk_pipe,
1075 .asterisk_pipe_equal,
1069 .arrow,1076 .arrow,
1070 .colon,1077 .colon,
1071 .slash,1078 .slash,
...@@ -1078,6 +1085,8 @@ fn tokenizeAndPrintRaw(...@@ -1078,6 +1085,8 @@ fn tokenizeAndPrintRaw(
1078 .angle_bracket_left_equal,1085 .angle_bracket_left_equal,
1079 .angle_bracket_angle_bracket_left,1086 .angle_bracket_angle_bracket_left,
1080 .angle_bracket_angle_bracket_left_equal,1087 .angle_bracket_angle_bracket_left_equal,
1088 .angle_bracket_angle_bracket_left_pipe,
1089 .angle_bracket_angle_bracket_left_pipe_equal,
1081 .angle_bracket_right,1090 .angle_bracket_right,
1082 .angle_bracket_right_equal,1091 .angle_bracket_right_equal,
1083 .angle_bracket_angle_bracket_right,1092 .angle_bracket_angle_bracket_right,
...@@ -1222,9 +1231,7 @@ fn genHtml(...@@ -1222,9 +1231,7 @@ fn genHtml(
12221231
1223 try printSourceBlock(allocator, tokenizer, out, syntax_block);1232 try printSourceBlock(allocator, tokenizer, out, syntax_block);
12241233
1225 // TODO: remove code.just_check_syntax after updating code samples1234 if (!do_code_tests) {
1226 // that have stopped working due to a change in the compiler.
1227 if (!do_code_tests or code.just_check_syntax) {
1228 continue;1235 continue;
1229 }1236 }
12301237
doc/langref.html.in+268-184
...@@ -1244,8 +1244,9 @@ fn divide(a: i32, b: i32) i32 {...@@ -1244,8 +1244,9 @@ fn divide(a: i32, b: i32) i32 {
1244 </p>1244 </p>
1245 <p>1245 <p>
1246 Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on1246 Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on
1247 integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and1247 integer overflow. Alternative operators are provided for wrapping and saturating arithmetic on all targets.
1248 {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.1248 {#syntax#}+%{#endsyntax#} and {#syntax#}-%{#endsyntax#} perform wrapping arithmetic
1249 while {#syntax#}+|{#endsyntax#} and {#syntax#}-|{#endsyntax#} perform saturating arithmetic.
1249 </p>1250 </p>
1250 <p>1251 <p>
1251 Zig supports arbitrary bit-width integers, referenced by using1252 Zig supports arbitrary bit-width integers, referenced by using
...@@ -1395,6 +1396,23 @@ a +%= b{#endsyntax#}</pre></th>...@@ -1395,6 +1396,23 @@ a +%= b{#endsyntax#}</pre></th>
1395 <pre>{#syntax#}@as(u32, std.math.maxInt(u32)) +% 1 == 0{#endsyntax#}</pre>1396 <pre>{#syntax#}@as(u32, std.math.maxInt(u32)) +% 1 == 0{#endsyntax#}</pre>
1396 </td>1397 </td>
1397 </tr>1398 </tr>
1399 <tr>
1400 <th scope="row"><pre>{#syntax#}a +| b
1401a +|= 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 <tr>1416 <tr>
1399 <th scope="row"><pre>{#syntax#}a - b1417 <th scope="row"><pre>{#syntax#}a - b
1400a -= b{#endsyntax#}</pre></th>1418a -= b{#endsyntax#}</pre></th>
...@@ -1434,6 +1452,23 @@ a -%= b{#endsyntax#}</pre></th>...@@ -1434,6 +1452,23 @@ a -%= b{#endsyntax#}</pre></th>
1434 <pre>{#syntax#}@as(u32, 0) -% 1 == std.math.maxInt(u32){#endsyntax#}</pre>1452 <pre>{#syntax#}@as(u32, 0) -% 1 == std.math.maxInt(u32){#endsyntax#}</pre>
1435 </td>1453 </td>
1436 </tr>1454 </tr>
1455 <tr>
1456 <th scope="row"><pre>{#syntax#}a -| b
1457a -|= 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 <tr>1472 <tr>
1438 <th scope="row"><pre>{#syntax#}-a{#endsyntax#}</pre></th>1473 <th scope="row"><pre>{#syntax#}-a{#endsyntax#}</pre></th>
1439 <td>1474 <td>
...@@ -1508,6 +1543,23 @@ a *%= b{#endsyntax#}</pre></th>...@@ -1508,6 +1543,23 @@ a *%= b{#endsyntax#}</pre></th>
1508 <pre>{#syntax#}@as(u8, 200) *% 2 == 144{#endsyntax#}</pre>1543 <pre>{#syntax#}@as(u8, 200) *% 2 == 144{#endsyntax#}</pre>
1509 </td>1544 </td>
1510 </tr>1545 </tr>
1546 <tr>
1547 <th scope="row"><pre>{#syntax#}a *| b
1548a *|= 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 <tr>1563 <tr>
1512 <th scope="row"><pre>{#syntax#}a / b1564 <th scope="row"><pre>{#syntax#}a / b
1513a /= b{#endsyntax#}</pre></th>1565a /= b{#endsyntax#}</pre></th>
...@@ -1577,6 +1629,24 @@ a <<= b{#endsyntax#}</pre></th>...@@ -1577,6 +1629,24 @@ a <<= b{#endsyntax#}</pre></th>
1577 <pre>{#syntax#}1 << 8 == 256{#endsyntax#}</pre>1629 <pre>{#syntax#}1 << 8 == 256{#endsyntax#}</pre>
1578 </td>1630 </td>
1579 </tr>1631 </tr>
1632 <tr>
1633 <th scope="row"><pre>{#syntax#}a <<| b
1634a <<|= 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 <tr>1650 <tr>
1581 <th scope="row"><pre>{#syntax#}a >> b1651 <th scope="row"><pre>{#syntax#}a >> b
1582a >>= b{#endsyntax#}</pre></th>1652a >>= b{#endsyntax#}</pre></th>
...@@ -1968,14 +2038,14 @@ const B = error{Two};...@@ -1968,14 +2038,14 @@ const B = error{Two};
1968a!b2038a!b
1969x{}2039x{}
1970!x -x -%x ~x &x ?x2040!x -x -%x ~x &x ?x
1971* / % ** *% ||2041* / % ** *% *| ||
1972+ - ++ +% -%2042+ - ++ +% -% +| -|
1973<< >>2043<< >> <<|
1974& ^ | orelse catch2044& ^ | orelse catch
1975== != < > <= >=2045== != < > <= >=
1976and2046and
1977or2047or
1978= *= /= %= += -= <<= >>= &= ^= |={#endsyntax#}</pre>2048= *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |={#endsyntax#}</pre>
1979 {#header_close#}2049 {#header_close#}
1980 {#header_close#}2050 {#header_close#}
1981 {#header_open|Arrays#}2051 {#header_open|Arrays#}
...@@ -2125,7 +2195,7 @@ fn dump(args: anytype) !void {...@@ -2125,7 +2195,7 @@ fn dump(args: anytype) !void {
21252195
2126 {#header_open|Multidimensional Arrays#}2196 {#header_open|Multidimensional Arrays#}
2127 <p>2197 <p>
2128 Mutlidimensional arrays can be created by nesting arrays:2198 Multidimensional arrays can be created by nesting arrays:
2129 </p>2199 </p>
2130 {#code_begin|test|multidimensional#}2200 {#code_begin|test|multidimensional#}
2131const std = @import("std");2201const std = @import("std");
...@@ -2898,7 +2968,7 @@ fn bar(x: *const u3) u3 {...@@ -2898,7 +2968,7 @@ fn bar(x: *const u3) u3 {
2898}2968}
2899 {#code_end#}2969 {#code_end#}
2900 <p>2970 <p>
2901 In this case, the function {#syntax#}bar{#endsyntax#} cannot be called becuse the pointer2971 In this case, the function {#syntax#}bar{#endsyntax#} cannot be called because the pointer
2902 to the non-ABI-aligned field mentions the bit offset, but the function expects an ABI-aligned pointer.2972 to the non-ABI-aligned field mentions the bit offset, but the function expects an ABI-aligned pointer.
2903 </p>2973 </p>
2904 <p>2974 <p>
...@@ -4771,6 +4841,8 @@ test "parse u64" {...@@ -4771,6 +4841,8 @@ test "parse u64" {
4771 {#header_open|catch#}4841 {#header_open|catch#}
4772 <p>If you want to provide a default value, you can use the {#syntax#}catch{#endsyntax#} binary operator:</p>4842 <p>If you want to provide a default value, you can use the {#syntax#}catch{#endsyntax#} binary operator:</p>
4773 {#code_begin|syntax#}4843 {#code_begin|syntax#}
4844const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
4845
4774fn doAThing(str: []u8) void {4846fn doAThing(str: []u8) void {
4775 const number = parseU64(str, 10) catch 13;4847 const number = parseU64(str, 10) catch 13;
4776 _ = number; // ...4848 _ = number; // ...
...@@ -4786,6 +4858,8 @@ fn doAThing(str: []u8) void {...@@ -4786,6 +4858,8 @@ fn doAThing(str: []u8) void {
4786 <p>Let's say you wanted to return the error if you got one, otherwise continue with the4858 <p>Let's say you wanted to return the error if you got one, otherwise continue with the
4787 function logic:</p>4859 function logic:</p>
4788 {#code_begin|syntax#}4860 {#code_begin|syntax#}
4861const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
4862
4789fn doAThing(str: []u8) !void {4863fn doAThing(str: []u8) !void {
4790 const number = parseU64(str, 10) catch |err| return err;4864 const number = parseU64(str, 10) catch |err| return err;
4791 _ = number; // ...4865 _ = number; // ...
...@@ -4795,6 +4869,8 @@ fn doAThing(str: []u8) !void {...@@ -4795,6 +4869,8 @@ fn doAThing(str: []u8) !void {
4795 There is a shortcut for this. The {#syntax#}try{#endsyntax#} expression:4869 There is a shortcut for this. The {#syntax#}try{#endsyntax#} expression:
4796 </p>4870 </p>
4797 {#code_begin|syntax#}4871 {#code_begin|syntax#}
4872const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
4873
4798fn doAThing(str: []u8) !void {4874fn doAThing(str: []u8) !void {
4799 const number = try parseU64(str, 10);4875 const number = try parseU64(str, 10);
4800 _ = number; // ...4876 _ = number; // ...
...@@ -4810,7 +4886,7 @@ fn doAThing(str: []u8) !void {...@@ -4810,7 +4886,7 @@ fn doAThing(str: []u8) !void {
4810 Maybe you know with complete certainty that an expression will never be an error.4886 Maybe you know with complete certainty that an expression will never be an error.
4811 In this case you can do this:4887 In this case you can do this:
4812 </p>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 <p>4890 <p>
4815 Here we know for sure that "1234" will parse successfully. So we put the4891 Here we know for sure that "1234" will parse successfully. So we put the
4816 {#syntax#}unreachable{#endsyntax#} value on the right hand side. {#syntax#}unreachable{#endsyntax#} generates4892 {#syntax#}unreachable{#endsyntax#} value on the right hand side. {#syntax#}unreachable{#endsyntax#} generates
...@@ -4822,7 +4898,7 @@ fn doAThing(str: []u8) !void {...@@ -4822,7 +4898,7 @@ fn doAThing(str: []u8) !void {
4822 Finally, you may want to take a different action for every situation. For that, we combine4898 Finally, you may want to take a different action for every situation. For that, we combine
4823 the {#link|if#} and {#link|switch#} expression:4899 the {#link|if#} and {#link|switch#} expression:
4824 </p>4900 </p>
4825 {#code_begin|syntax#}4901 {#syntax_block|zig|handle_all_error_scenarios.zig#}
4826fn doAThing(str: []u8) void {4902fn doAThing(str: []u8) void {
4827 if (parseU64(str, 10)) |number| {4903 if (parseU64(str, 10)) |number| {
4828 doSomethingWithNumber(number);4904 doSomethingWithNumber(number);
...@@ -4834,7 +4910,7 @@ fn doAThing(str: []u8) void {...@@ -4834,7 +4910,7 @@ fn doAThing(str: []u8) void {
4834 error.InvalidChar => unreachable,4910 error.InvalidChar => unreachable,
4835 }4911 }
4836}4912}
4837 {#code_end#}4913 {#end_syntax_block#}
4838 {#header_open|errdefer#}4914 {#header_open|errdefer#}
4839 <p>4915 <p>
4840 The other component to error handling is defer statements.4916 The other component to error handling is defer statements.
...@@ -4845,7 +4921,7 @@ fn doAThing(str: []u8) void {...@@ -4845,7 +4921,7 @@ fn doAThing(str: []u8) void {
4845 <p>4921 <p>
4846 Example:4922 Example:
4847 </p>4923 </p>
4848 {#code_begin|syntax#}4924 {#syntax_block|zig|errdefer_example.zig#}
4849fn createFoo(param: i32) !Foo {4925fn createFoo(param: i32) !Foo {
4850 const foo = try tryToAllocateFoo();4926 const foo = try tryToAllocateFoo();
4851 // now we have allocated foo. we need to free it if the function fails.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,7 +4939,7 @@ fn createFoo(param: i32) !Foo {
4863 // but the defer will run!4939 // but the defer will run!
4864 return foo;4940 return foo;
4865}4941}
4866 {#code_end#}4942 {#end_syntax_block#}
4867 <p>4943 <p>
4868 The neat thing about this is that you get robust error handling without4944 The neat thing about this is that you get robust error handling without
4869 the verbosity and cognitive overhead of trying to make sure every exit path4945 the verbosity and cognitive overhead of trying to make sure every exit path
...@@ -4955,7 +5031,7 @@ test "merge error sets" {...@@ -4955,7 +5031,7 @@ test "merge error sets" {
4955 {#header_open|Inferred Error Sets#}5031 {#header_open|Inferred Error Sets#}
4956 <p>5032 <p>
4957 Because many functions in Zig return a possible error, Zig supports inferring the error set.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 </p>5035 </p>
4960{#code_begin|test|inferred_error_sets#}5036{#code_begin|test|inferred_error_sets#}
4961// With an inferred error set5037// With an inferred error set
...@@ -5132,12 +5208,12 @@ fn bang2() void {...@@ -5132,12 +5208,12 @@ fn bang2() void {
5132 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#}.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 This is to initialize this struct in the stack memory:5209 This is to initialize this struct in the stack memory:
5134 </p>5210 </p>
5135 {#code_begin|syntax#}5211 {#syntax_block|zig|stack_trace_struct.zig#}
5136pub const StackTrace = struct {5212pub const StackTrace = struct {
5137 index: usize,5213 index: usize,
5138 instruction_addresses: [N]usize,5214 instruction_addresses: [N]usize,
5139};5215};
5140 {#code_end#}5216 {#end_syntax_block#}
5141 <p>5217 <p>
5142 Here, N is the maximum function call depth as determined by call graph analysis. Recursion is ignored and counts for 2.5218 Here, N is the maximum function call depth as determined by call graph analysis. Recursion is ignored and counts for 2.
5143 </p>5219 </p>
...@@ -5150,13 +5226,13 @@ pub const StackTrace = struct {...@@ -5150,13 +5226,13 @@ pub const StackTrace = struct {
5150 <p>5226 <p>
5151 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: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 </p>5228 </p>
5153 {#code_begin|syntax#}5229 {#syntax_block|zig|zig_return_error_fn.zig#}
5154// marked as "no-inline" in LLVM IR5230// marked as "no-inline" in LLVM IR
5155fn __zig_return_error(stack_trace: *StackTrace) void {5231fn __zig_return_error(stack_trace: *StackTrace) void {
5156 stack_trace.instruction_addresses[stack_trace.index] = @returnAddress();5232 stack_trace.instruction_addresses[stack_trace.index] = @returnAddress();
5157 stack_trace.index = (stack_trace.index + 1) % N;5233 stack_trace.index = (stack_trace.index + 1) % N;
5158}5234}
5159 {#code_end#}5235 {#end_syntax_block#}
5160 <p>5236 <p>
5161 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.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 </p>5238 </p>
...@@ -5206,16 +5282,16 @@ const optional_int: ?i32 = 5678;...@@ -5206,16 +5282,16 @@ const optional_int: ?i32 = 5678;
5206 Task: call malloc, if the result is null, return null.5282 Task: call malloc, if the result is null, return null.
5207 </p>5283 </p>
5208 <p>C code</p>5284 <p>C code</p>
5209 <pre><code class="cpp">// malloc prototype included for reference5285 {#syntax_block|c|call_malloc_in_c.c#}// malloc prototype included for reference
5210void *malloc(size_t size);5286void *malloc(size_t size);
52115287
5212struct Foo *do_a_thing(void) {5288struct Foo *do_a_thing(void) {
5213 char *ptr = malloc(1234);5289 char *ptr = malloc(1234);
5214 if (!ptr) return NULL;5290 if (!ptr) return NULL;
5215 // ...5291 // ...
5216}</code></pre>5292}{#end_syntax_block#}
5217 <p>Zig code</p>5293 <p>Zig code</p>
5218 {#code_begin|syntax#}5294 {#syntax_block|zig|call_malloc_from_zig.zig#}
5219// malloc prototype included for reference5295// malloc prototype included for reference
5220extern fn malloc(size: size_t) ?*u8;5296extern fn malloc(size: size_t) ?*u8;
52215297
...@@ -5223,7 +5299,7 @@ fn doAThing() ?*Foo {...@@ -5223,7 +5299,7 @@ fn doAThing() ?*Foo {
5223 const ptr = malloc(1234) orelse return null;5299 const ptr = malloc(1234) orelse return null;
5224 _ = ptr; // ...5300 _ = ptr; // ...
5225}5301}
5226 {#code_end#}5302 {#end_syntax_block#}
5227 <p>5303 <p>
5228 Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr"5304 Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr"
5229 is {#syntax#}*u8{#endsyntax#} <em>not</em> {#syntax#}?*u8{#endsyntax#}. The {#syntax#}orelse{#endsyntax#} keyword5305 is {#syntax#}*u8{#endsyntax#} <em>not</em> {#syntax#}?*u8{#endsyntax#}. The {#syntax#}orelse{#endsyntax#} keyword
...@@ -5233,7 +5309,7 @@ fn doAThing() ?*Foo {...@@ -5233,7 +5309,7 @@ fn doAThing() ?*Foo {
5233 <p>5309 <p>
5234 The other form of checking against NULL you might see looks like this:5310 The other form of checking against NULL you might see looks like this:
5235 </p>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 // do some stuff5313 // do some stuff
52385314
5239 if (foo) {5315 if (foo) {
...@@ -5241,11 +5317,14 @@ fn doAThing() ?*Foo {...@@ -5241,11 +5317,14 @@ fn doAThing() ?*Foo {
5241 }5317 }
52425318
5243 // do some stuff5319 // do some stuff
5244}</code></pre>5320}{#end_syntax_block#}
5245 <p>5321 <p>
5246 In Zig you can accomplish the same thing:5322 In Zig you can accomplish the same thing:
5247 </p>5323 </p>
5248 {#code_begin|syntax#}5324 {#code_begin|syntax|checking_null_in_zig#}
5325const Foo = struct{};
5326fn doSomethingWithFoo(foo: *Foo) void { _ = foo; }
5327
5249fn doAThing(optional_foo: ?*Foo) void {5328fn doAThing(optional_foo: ?*Foo) void {
5250 // do some stuff5329 // do some stuff
52515330
...@@ -5540,7 +5619,7 @@ test "coerce to optionals" {...@@ -5540,7 +5619,7 @@ test "coerce to optionals" {
5540}5619}
5541 {#code_end#}5620 {#code_end#}
5542 <p>It works nested inside the {#link|Error Union Type#}, too:</p>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#}
5544const std = @import("std");5623const std = @import("std");
5545const expect = std.testing.expect;5624const expect = std.testing.expect;
55465625
...@@ -6111,7 +6190,7 @@ test "perform fn" {...@@ -6111,7 +6190,7 @@ test "perform fn" {
6111 different code. In this example, the function {#syntax#}performFn{#endsyntax#} is generated three different times,6190 different code. In this example, the function {#syntax#}performFn{#endsyntax#} is generated three different times,
6112 for the different values of {#syntax#}prefix_char{#endsyntax#} provided:6191 for the different values of {#syntax#}prefix_char{#endsyntax#} provided:
6113 </p>6192 </p>
6114 {#code_begin|syntax#}6193 {#syntax_block|zig|performFn_1#}
6115// From the line:6194// From the line:
6116// expect(performFn('t', 1) == 6);6195// expect(performFn('t', 1) == 6);
6117fn performFn(start_value: i32) i32 {6196fn performFn(start_value: i32) i32 {
...@@ -6120,8 +6199,8 @@ fn performFn(start_value: i32) i32 {...@@ -6120,8 +6199,8 @@ fn performFn(start_value: i32) i32 {
6120 result = three(result);6199 result = three(result);
6121 return result;6200 return result;
6122}6201}
6123 {#code_end#}6202 {#end_syntax_block#}
6124 {#code_begin|syntax#}6203 {#syntax_block|zig|performFn_2#}
6125// From the line:6204// From the line:
6126// expect(performFn('o', 0) == 1);6205// expect(performFn('o', 0) == 1);
6127fn performFn(start_value: i32) i32 {6206fn performFn(start_value: i32) i32 {
...@@ -6129,15 +6208,15 @@ fn performFn(start_value: i32) i32 {...@@ -6129,15 +6208,15 @@ fn performFn(start_value: i32) i32 {
6129 result = one(result);6208 result = one(result);
6130 return result;6209 return result;
6131}6210}
6132 {#code_end#}6211 {#end_syntax_block#}
6133 {#code_begin|syntax#}6212 {#syntax_block|zig|performFn_3#}
6134// From the line:6213// From the line:
6135// expect(performFn('w', 99) == 99);6214// expect(performFn('w', 99) == 99);
6136fn performFn(start_value: i32) i32 {6215fn performFn(start_value: i32) i32 {
6137 var result: i32 = start_value;6216 var result: i32 = start_value;
6138 return result;6217 return result;
6139}6218}
6140 {#code_end#}6219 {#end_syntax_block#}
6141 <p>6220 <p>
6142 Note that this happens even in a debug build; in a release build these generated functions still6221 Note that this happens even in a debug build; in a release build these generated functions still
6143 pass through rigorous LLVM optimizations. The important thing to note, however, is not that this6222 pass through rigorous LLVM optimizations. The important thing to note, however, is not that this
...@@ -6367,11 +6446,11 @@ const Node = struct {...@@ -6367,11 +6446,11 @@ const Node = struct {
6367 it works fine.6446 it works fine.
6368 </p>6447 </p>
6369 {#header_close#}6448 {#header_close#}
6370 {#header_open|Case Study: printf in Zig#}6449 {#header_open|Case Study: print in Zig#}
6371 <p>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 </p>6452 </p>
6374 {#code_begin|exe|printf#}6453 {#code_begin|exe|print#}
6375const print = @import("std").debug.print;6454const print = @import("std").debug.print;
63766455
6377const a_number: i32 = 1234;6456const a_number: i32 = 1234;
...@@ -6386,67 +6465,84 @@ pub fn main() void {...@@ -6386,67 +6465,84 @@ pub fn main() void {
6386 Let's crack open the implementation of this and see how it works:6465 Let's crack open the implementation of this and see how it works:
6387 </p>6466 </p>
63886467
6389 {#code_begin|syntax#}6468 {#code_begin|syntax|poc_print_fn#}
6390/// Calls print and then flushes the buffer.6469const Writer = struct {
6391pub fn printf(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void {6470 /// Calls print and then flushes the buffer.
6392 const State = enum {6471 pub fn print(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void {
6393 start,6472 const State = enum {
6394 open_brace,6473 start,
6395 close_brace,6474 open_brace,
6396 };6475 close_brace,
63976476 };
6398 comptime var start_index: usize = 0;
6399 comptime var state = State.start;
6400 comptime var next_arg: usize = 0;
64016477
6402 inline for (format) |c, i| {6478 comptime var start_index: usize = 0;
6403 switch (state) {6479 comptime var state = State.start;
6404 State.start => switch (c) {6480 comptime var next_arg: usize = 0;
6405 '{' => {6481
6406 if (start_index < i) try self.write(format[start_index..i]);6482 inline for (format) |c, i| {
6407 state = State.open_brace;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 '}' => {6495 State.open_brace => switch (c) {
6410 if (start_index < i) try self.write(format[start_index..i]);6496 '{' => {
6411 state = State.close_brace;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 => {},6511 State.close_brace => switch (c) {
6414 },6512 '}' => {
6415 State.open_brace => switch (c) {6513 state = State.start;
6416 '{' => {6514 start_index = i;
6417 state = State.start;6515 },
6418 start_index = i;6516 else => @compileError("Single '}' encountered in format string"),
6419 },
6420 '}' => {
6421 try self.printValue(args[next_arg]);
6422 next_arg += 1;
6423 state = State.start;
6424 start_index = i + 1;
6425 },6517 },
6426 else => @compileError("Unknown format character: " ++ c),6518 }
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 },
6435 }6519 }
6436 }6520 comptime {
6437 comptime {6521 if (args.len != next_arg) {
6438 if (args.len != next_arg) {6522 @compileError("Unused arguments");
6439 @compileError("Unused arguments");6523 }
6524 if (state != State.start) {
6525 @compileError("Incomplete format string: " ++ format);
6526 }
6440 }6527 }
6441 if (state != State.Start) {6528 if (start_index < format.len) {
6442 @compileError("Incomplete format string: " ++ format);6529 try self.write(format[start_index..format.len]);
6443 }6530 }
6531 try self.flush();
6444 }6532 }
6445 if (start_index < format.len) {6533
6446 try self.write(format[start_index..format.len]);6534 fn write(self: *Writer, value: []const u8) !void {
6535 _ = self;
6536 _ = value;
6447 }6537 }
6448 try self.flush();6538 pub fn printValue(self: *Writer, value: anytype) !void {
6449}6539 _ = self;
6540 _ = value;
6541 }
6542 fn flush(self: *Writer) !void {
6543 _ = self;
6544 }
6545};
6450 {#code_end#}6546 {#code_end#}
6451 <p>6547 <p>
6452 This is a proof of concept implementation; the actual function in the standard library has more6548 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,8 +6555,8 @@ pub fn printf(self: *Writer, comptime format: []const u8, args: anytype) anyerro
6459 When this function is analyzed from our example code above, Zig partially evaluates the function6555 When this function is analyzed from our example code above, Zig partially evaluates the function
6460 and emits a function that actually looks like this:6556 and emits a function that actually looks like this:
6461 </p>6557 </p>
6462 {#code_begin|syntax#}6558 {#syntax_block|zig|Emitted print Function#}
6463pub fn printf(self: *Writer, arg0: i32, arg1: []const u8) !void {6559pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
6464 try self.write("here is a string: '");6560 try self.write("here is a string: '");
6465 try self.printValue(arg0);6561 try self.printValue(arg0);
6466 try self.write("' here is a number: ");6562 try self.write("' here is a number: ");
...@@ -6468,28 +6564,46 @@ pub fn printf(self: *Writer, arg0: i32, arg1: []const u8) !void {...@@ -6468,28 +6564,46 @@ pub fn printf(self: *Writer, arg0: i32, arg1: []const u8) !void {
6468 try self.write("\n");6564 try self.write("\n");
6469 try self.flush();6565 try self.flush();
6470}6566}
6471 {#code_end#}6567 {#end_syntax_block#}
6472 <p>6568 <p>
6473 {#syntax#}printValue{#endsyntax#} is a function that takes a parameter of any type, and does different things depending6569 {#syntax#}printValue{#endsyntax#} is a function that takes a parameter of any type, and does different things depending
6474 on the type:6570 on the type:
6475 </p>6571 </p>
6476 {#code_begin|syntax#}6572 {#code_begin|syntax|poc_printValue_fn#}
6477pub fn printValue(self: *Writer, value: anytype) !void {6573 const Writer = struct {
6478 switch (@typeInfo(@TypeOf(value))) {6574 pub fn printValue(self: *Writer, value: anytype) !void {
6479 .Int => {6575 switch (@typeInfo(@TypeOf(value))) {
6480 return self.printInt(T, value);6576 .Int => {
6481 },6577 return self.writeInt(value);
6482 .Float => {6578 },
6483 return self.printFloat(T, value);6579 .Float => {
6484 },6580 return self.writeFloat(value);
6485 else => {6581 },
6486 @compileError("Unable to print type '" ++ @typeName(T) ++ "'");6582 .Pointer => {
6487 },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 {#code_end#}6604 {#code_end#}
6491 <p>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 </p>6607 </p>
6494 {#code_begin|test_err|Unused argument in 'here is a string: '{s}' here is a number: {}#}6608 {#code_begin|test_err|Unused argument in 'here is a string: '{s}' here is a number: {}#}
6495const print = @import("std").debug.print;6609const print = @import("std").debug.print;
...@@ -6497,7 +6611,7 @@ const print = @import("std").debug.print;...@@ -6497,7 +6611,7 @@ const print = @import("std").debug.print;
6497const a_number: i32 = 1234;6611const a_number: i32 = 1234;
6498const a_string = "foobar";6612const a_string = "foobar";
64996613
6500test "printf too many arguments" {6614test "print too many arguments" {
6501 print("here is a string: '{s}' here is a number: {}\n", .{6615 print("here is a string: '{s}' here is a number: {}\n", .{
6502 a_string,6616 a_string,
6503 a_number,6617 a_number,
...@@ -6512,7 +6626,7 @@ test "printf too many arguments" {...@@ -6512,7 +6626,7 @@ test "printf too many arguments" {
6512 Zig doesn't care whether the format argument is a string literal,6626 Zig doesn't care whether the format argument is a string literal,
6513 only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:6627 only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:
6514 </p>6628 </p>
6515 {#code_begin|exe|printf#}6629 {#code_begin|exe|print#}
6516const print = @import("std").debug.print;6630const print = @import("std").debug.print;
65176631
6518const a_number: i32 = 1234;6632const a_number: i32 = 1234;
...@@ -7118,16 +7232,6 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {...@@ -7118,16 +7232,6 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
7118 If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.7232 If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
7119 </p>7233 </p>
7120 {#header_close#}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 {#header_open|@alignCast#}7235 {#header_open|@alignCast#}
7132 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre>7236 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre>
7133 <p>7237 <p>
...@@ -7216,7 +7320,9 @@ fn func(y: *i32) void {...@@ -7216,7 +7320,9 @@ fn func(y: *i32) void {
7216 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,7320 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
7217 an integer or an enum.7321 an integer or an enum.
7218 </p>7322 </p>
7323 {#see_also|@atomicStore|@atomicRmw|@fence|@cmpxchgWeak|@cmpxchgStrong#}
7219 {#header_close#}7324 {#header_close#}
7325
7220 {#header_open|@atomicRmw#}7326 {#header_open|@atomicRmw#}
7221 <pre>{#syntax#}@atomicRmw(comptime T: type, ptr: *T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre>7327 <pre>{#syntax#}@atomicRmw(comptime T: type, ptr: *T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre>
7222 <p>7328 <p>
...@@ -7242,7 +7348,9 @@ fn func(y: *i32) void {...@@ -7242,7 +7348,9 @@ fn func(y: *i32) void {
7242 <li>{#syntax#}.Max{#endsyntax#} - stores the operand if it is larger. Supports integers and floats.</li>7348 <li>{#syntax#}.Max{#endsyntax#} - stores the operand if it is larger. Supports integers and floats.</li>
7243 <li>{#syntax#}.Min{#endsyntax#} - stores the operand if it is smaller. Supports integers and floats.</li>7349 <li>{#syntax#}.Min{#endsyntax#} - stores the operand if it is smaller. Supports integers and floats.</li>
7244 </ul>7350 </ul>
7351 {#see_also|@atomicStore|@atomicLoad|@fence|@cmpxchgWeak|@cmpxchgStrong#}
7245 {#header_close#}7352 {#header_close#}
7353
7246 {#header_open|@atomicStore#}7354 {#header_open|@atomicStore#}
7247 <pre>{#syntax#}@atomicStore(comptime T: type, ptr: *T, value: T, comptime ordering: builtin.AtomicOrder) void{#endsyntax#}</pre>7355 <pre>{#syntax#}@atomicStore(comptime T: type, ptr: *T, value: T, comptime ordering: builtin.AtomicOrder) void{#endsyntax#}</pre>
7248 <p>7356 <p>
...@@ -7252,6 +7360,7 @@ fn func(y: *i32) void {...@@ -7252,6 +7360,7 @@ fn func(y: *i32) void {
7252 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,7360 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
7253 an integer or an enum.7361 an integer or an enum.
7254 </p>7362 </p>
7363 {#see_also|@atomicLoad|@atomicRmw|@fence|@cmpxchgWeak|@cmpxchgStrong#}
7255 {#header_close#}7364 {#header_close#}
72567365
7257 {#header_open|@bitCast#}7366 {#header_open|@bitCast#}
...@@ -7396,9 +7505,11 @@ fn add(a: i32, b: i32) i32 {...@@ -7396,9 +7505,11 @@ fn add(a: i32, b: i32) i32 {
7396 {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The7505 {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The
7397 {#syntax#}CallOptions{#endsyntax#} struct is reproduced here:7506 {#syntax#}CallOptions{#endsyntax#} struct is reproduced here:
7398 </p>7507 </p>
7399 {#code_begin|syntax#}7508 {#syntax_block|zig|builtin.CallOptions struct#}
7400pub const CallOptions = struct {7509pub const CallOptions = struct {
7401 modifier: Modifier = .auto,7510 modifier: Modifier = .auto,
7511
7512 /// Only valid when `Modifier` is `Modifier.async_kw`.
7402 stack: ?[]align(std.Target.stack_align) u8 = null,7513 stack: ?[]align(std.Target.stack_align) u8 = null,
74037514
7404 pub const Modifier = enum {7515 pub const Modifier = enum {
...@@ -7435,7 +7546,7 @@ pub const CallOptions = struct {...@@ -7435,7 +7546,7 @@ pub const CallOptions = struct {
7435 compile_time,7546 compile_time,
7436 };7547 };
7437};7548};
7438 {#code_end#}7549 {#end_syntax_block#}
7439 {#header_close#}7550 {#header_close#}
74407551
7441 {#header_open|@cDefine#}7552 {#header_open|@cDefine#}
...@@ -7540,15 +7651,16 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v...@@ -7540,15 +7651,16 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v
7540 an integer or an enum.7651 an integer or an enum.
7541 </p>7652 </p>
7542 <p>{#syntax#}@typeInfo(@TypeOf(ptr)).Pointer.alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p>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 {#header_close#}7655 {#header_close#}
7656
7545 {#header_open|@cmpxchgWeak#}7657 {#header_open|@cmpxchgWeak#}
7546 <pre>{#syntax#}@cmpxchgWeak(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T{#endsyntax#}</pre>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 <p>7659 <p>
7548 This function performs a weak atomic compare exchange operation. It's the equivalent of this code,7660 This function performs a weak atomic compare exchange operation. It's the equivalent of this code,
7549 except atomic:7661 except atomic:
7550 </p>7662 </p>
7551 {#code_begin|syntax#}7663 {#syntax_block|zig|cmpxchgWeakButNotAtomic#}
7552fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T {7664fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T {
7553 const old_value = ptr.*;7665 const old_value = ptr.*;
7554 if (old_value == expected_value and usuallyTrueButSometimesFalse()) {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,7 +7670,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
7558 return old_value;7670 return old_value;
7559 }7671 }
7560}7672}
7561 {#code_end#}7673 {#end_syntax_block#}
7562 <p>7674 <p>
7563 If you are using cmpxchg in a loop, the sporadic failure will be no problem, and {#syntax#}cmpxchgWeak{#endsyntax#}7675 If you are using cmpxchg in a loop, the sporadic failure will be no problem, and {#syntax#}cmpxchgWeak{#endsyntax#}
7564 is the better choice, because it can be implemented more efficiently in machine instructions.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,7 +7681,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
7569 an integer or an enum.7681 an integer or an enum.
7570 </p>7682 </p>
7571 <p>{#syntax#}@typeInfo(@TypeOf(ptr)).Pointer.alignment{#endsyntax#} must be {#syntax#}>= @sizeOf(T).{#endsyntax#}</p>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 {#header_close#}7685 {#header_close#}
75747686
7575 {#header_open|@compileError#}7687 {#header_open|@compileError#}
...@@ -7617,7 +7729,7 @@ test "main" {...@@ -7617,7 +7729,7 @@ test "main" {
7617}7729}
7618 {#code_end#}7730 {#code_end#}
7619 <p>7731 <p>
7620 will ouput:7732 will output:
7621 </p>7733 </p>
7622 <p>7734 <p>
7623 If all {#syntax#}@compileLog{#endsyntax#} calls are removed or7735 If all {#syntax#}@compileLog{#endsyntax#} calls are removed or
...@@ -7734,7 +7846,7 @@ test "main" {...@@ -7734,7 +7846,7 @@ test "main" {
7734 the tag value is used as the enumeration value.7846 the tag value is used as the enumeration value.
7735 </p>7847 </p>
7736 <p>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 known at {#link|comptime#}.7850 known at {#link|comptime#}.
7739 </p>7851 </p>
7740 {#see_also|@intToEnum#}7852 {#see_also|@intToEnum#}
...@@ -7849,7 +7961,7 @@ export fn @"A function name that is a complete sentence."() void {}...@@ -7849,7 +7961,7 @@ export fn @"A function name that is a complete sentence."() void {}
7849 <p>7961 <p>
7850 {#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.7962 {#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.
7851 </p>7963 </p>
7852 {#see_also|Compile Variables#}7964 {#see_also|@atomicStore|@atomicLoad|@atomicRmw|@cmpxchgWeak|@cmpxchgStrong#}
7853 {#header_close#}7965 {#header_close#}
78547966
7855 {#header_open|@field#}7967 {#header_open|@field#}
...@@ -8241,22 +8353,6 @@ test "@wasmMemoryGrow" {...@@ -8241,22 +8353,6 @@ test "@wasmMemoryGrow" {
8241 </p>8353 </p>
8242 {#header_close#}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 {#header_open|@panic#}8356 {#header_open|@panic#}
8261 <pre>{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}</pre>8357 <pre>{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}</pre>
8262 <p>8358 <p>
...@@ -8474,14 +8570,16 @@ test "@setRuntimeSafety" {...@@ -8474,14 +8570,16 @@ test "@setRuntimeSafety" {
8474 {#header_open|@shlExact#}8570 {#header_open|@shlExact#}
8475 <pre>{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>8571 <pre>{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>
8476 <p>8572 <p>
8477 Performs the left shift operation ({#syntax#}<<{#endsyntax#}). Caller guarantees8573 Performs the left shift operation ({#syntax#}<<{#endsyntax#}).
8478 that the shift will not shift any 1 bits out.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 </p>8577 </p>
8480 <p>8578 <p>
8481 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.8579 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
8482 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.8580 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
8483 </p>8581 </p>
8484 {#see_also|@shrExact|@shlWithOverflow|@shlWithSaturation#}8582 {#see_also|@shrExact|@shlWithOverflow#}
8485 {#header_close#}8583 {#header_close#}
84868584
8487 {#header_open|@shlWithOverflow#}8585 {#header_open|@shlWithOverflow#}
...@@ -8495,24 +8593,9 @@ test "@setRuntimeSafety" {...@@ -8495,24 +8593,9 @@ test "@setRuntimeSafety" {
8495 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.8593 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
8496 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.8594 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
8497 </p>8595 </p>
8498 {#see_also|@shlExact|@shrExact|@shlWithSaturation#}8596 {#see_also|@shlExact|@shrExact#}
8499 {#header_close#}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 {#header_open|@shrExact#}8599 {#header_open|@shrExact#}
8517 <pre>{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>8600 <pre>{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>
8518 <p>8601 <p>
...@@ -8523,7 +8606,7 @@ test "@setRuntimeSafety" {...@@ -8523,7 +8606,7 @@ test "@setRuntimeSafety" {
8523 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.8606 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
8524 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.8607 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
8525 </p>8608 </p>
8526 {#see_also|@shlExact|@shlWithOverflow|@shlWithSaturation#}8609 {#see_also|@shlExact|@shlWithOverflow#}
8527 {#header_close#}8610 {#header_close#}
85288611
8529 {#header_open|@shuffle#}8612 {#header_open|@shuffle#}
...@@ -8684,7 +8767,7 @@ fn doTheTest() !void {...@@ -8684,7 +8767,7 @@ fn doTheTest() !void {
8684 {#header_open|@sin#}8767 {#header_open|@sin#}
8685 <pre>{#syntax#}@sin(value: anytype) @TypeOf(value){#endsyntax#}</pre>8768 <pre>{#syntax#}@sin(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8686 <p>8769 <p>
8687 Sine trigometric function on a floating point number. Uses a dedicated hardware instruction8770 Sine trigonometric function on a floating point number. Uses a dedicated hardware instruction
8688 when available.8771 when available.
8689 </p>8772 </p>
8690 <p>8773 <p>
...@@ -8695,7 +8778,7 @@ fn doTheTest() !void {...@@ -8695,7 +8778,7 @@ fn doTheTest() !void {
8695 {#header_open|@cos#}8778 {#header_open|@cos#}
8696 <pre>{#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}</pre>8779 <pre>{#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8697 <p>8780 <p>
8698 Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction8781 Cosine trigonometric function on a floating point number. Uses a dedicated hardware instruction
8699 when available.8782 when available.
8700 </p>8783 </p>
8701 <p>8784 <p>
...@@ -8783,7 +8866,7 @@ fn doTheTest() !void {...@@ -8783,7 +8866,7 @@ fn doTheTest() !void {
8783 {#header_open|@ceil#}8866 {#header_open|@ceil#}
8784 <pre>{#syntax#}@ceil(value: anytype) @TypeOf(value){#endsyntax#}</pre>8867 <pre>{#syntax#}@ceil(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8785 <p>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 Uses a dedicated hardware instruction when available.8870 Uses a dedicated hardware instruction when available.
8788 </p>8871 </p>
8789 <p>8872 <p>
...@@ -8823,17 +8906,6 @@ fn doTheTest() !void {...@@ -8823,17 +8906,6 @@ fn doTheTest() !void {
8823 </p>8906 </p>
8824 {#header_close#}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 {#header_open|@tagName#}8909 {#header_open|@tagName#}
8838 <pre>{#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}</pre>8910 <pre>{#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}</pre>
8839 <p>8911 <p>
...@@ -10153,7 +10225,7 @@ pub fn main() void {...@@ -10153,7 +10225,7 @@ pub fn main() void {
10153 This expression is evaluated at compile-time and is used to control10225 This expression is evaluated at compile-time and is used to control
10154 preprocessor directives and include multiple <code class="file">.h</code> files:10226 preprocessor directives and include multiple <code class="file">.h</code> files:
10155 </p>10227 </p>
10156 {#code_begin|syntax#}10228 {#syntax_block|zig|@cImport Expression#}
10157const builtin = @import("builtin");10229const builtin = @import("builtin");
1015810230
10159const c = @cImport({10231const c = @cImport({
...@@ -10167,7 +10239,7 @@ const c = @cImport({...@@ -10167,7 +10239,7 @@ const c = @cImport({
10167 }10239 }
10168 @cInclude("soundio.h");10240 @cInclude("soundio.h");
10169});10241});
10170 {#code_end#}10242 {#end_syntax_block#}
10171 {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#}10243 {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#}
10172 {#header_close#}10244 {#header_close#}
1017310245
...@@ -10273,7 +10345,7 @@ pub fn main() void {...@@ -10273,7 +10345,7 @@ pub fn main() void {
10273 <p>10345 <p>
10274 Some C constructs cannot be translated to Zig - for example, <em>goto</em>,10346 Some C constructs cannot be translated to Zig - for example, <em>goto</em>,
10275 structs with bitfields, and token-pasting macros. Zig employs <em>demotion</em> to allow translation10347 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 </p>10349 </p>
10278 <p>10350 <p>
10279 Demotion comes in three varieties - {#link|opaque#}, <em>extern</em>, and10351 Demotion comes in three varieties - {#link|opaque#}, <em>extern</em>, and
...@@ -10283,13 +10355,13 @@ pub fn main() void {...@@ -10283,13 +10355,13 @@ pub fn main() void {
10283 Functions that contain opaque types or code constructs that cannot be translated will be demoted10355 Functions that contain opaque types or code constructs that cannot be translated will be demoted
10284 to {#syntax#}extern{#endsyntax#} declarations.10356 to {#syntax#}extern{#endsyntax#} declarations.
1028510357
10286 Thus, non-translateable types can still be used as pointers, and non-translateable functions10358 Thus, non-translatable types can still be used as pointers, and non-translatable functions
10287 can be called so long as the linker is aware of the compiled function.10359 can be called so long as the linker is aware of the compiled function.
10288 </p>10360 </p>
10289 <p>10361 <p>
10290 {#syntax#}@compileError{#endsyntax#} is used when top-level definitions (global variables,10362 {#syntax#}@compileError{#endsyntax#} is used when top-level definitions (global variables,
10291 function prototypes, macros) cannot be translated or demoted. Since Zig uses lazy analysis for10363 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 unless10364 top-level declarations, untranslatable entities will not cause a compile error in your code unless
10293 you actually use them.10365 you actually use them.
10294 </p>10366 </p>
10295 {#see_also|opaque|extern|@compileError#}10367 {#see_also|opaque|extern|@compileError#}
...@@ -10301,7 +10373,7 @@ pub fn main() void {...@@ -10301,7 +10373,7 @@ pub fn main() void {
10301 can be translated to Zig. Macros that cannot be translated will be be demoted to10373 can be translated to Zig. Macros that cannot be translated will be be demoted to
10302 {#syntax#}@compileError{#endsyntax#}. Note that C code which <em>uses</em> macros will be10374 {#syntax#}@compileError{#endsyntax#}. Note that C code which <em>uses</em> macros will be
10303 translated without any additional issues (since Zig operates on the pre-processed source10375 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 to10376 with macros expanded). It is merely the macros themselves which may not be translatable to
10305 Zig.10377 Zig.
10306 </p>10378 </p>
10307 <p>Consider the following example:</p>10379 <p>Consider the following example:</p>
...@@ -10321,7 +10393,7 @@ pub export fn foo() c_int {...@@ -10321,7 +10393,7 @@ pub export fn foo() c_int {
10321}10393}
10322pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected token .Equal"); // macro.c:1:910394pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected token .Equal"); // macro.c:1:9
10323 {#code_end#}10395 {#code_end#}
10324 <p>Note that {#syntax#}foo{#endsyntax#} was translated correctly despite using a non-translateable10396 <p>Note that {#syntax#}foo{#endsyntax#} was translated correctly despite using a non-translatable
10325 macro. {#syntax#}MAKELOCAL{#endsyntax#} was demoted to {#syntax#}@compileError{#endsyntax#} since10397 macro. {#syntax#}MAKELOCAL{#endsyntax#} was demoted to {#syntax#}@compileError{#endsyntax#} since
10326 it cannot be expressed as a Zig function; this simply means that you cannot directly use10398 it cannot be expressed as a Zig function; this simply means that you cannot directly use
10327 {#syntax#}MAKELOCAL{#endsyntax#} from Zig.10399 {#syntax#}MAKELOCAL{#endsyntax#} from Zig.
...@@ -10491,8 +10563,8 @@ const typedArray = new Uint8Array(source);...@@ -10491,8 +10563,8 @@ const typedArray = new Uint8Array(source);
1049110563
10492WebAssembly.instantiate(typedArray, {10564WebAssembly.instantiate(typedArray, {
10493 env: {10565 env: {
10494 print: (result) =&gt; { console.log(`The result is ${result}`); }10566 print: (result) => { console.log(`The result is ${result}`); }
10495 }}).then(result =&gt; {10567 }}).then(result => {
10496 const add = result.instance.exports.add;10568 const add = result.instance.exports.add;
10497 add(1, 2);10569 add(1, 2);
10498});{#end_syntax_block#}10570});{#end_syntax_block#}
...@@ -11787,6 +11859,7 @@ AssignOp...@@ -11787,6 +11859,7 @@ AssignOp
11787 / PLUSEQUAL11859 / PLUSEQUAL
11788 / MINUSEQUAL11860 / MINUSEQUAL
11789 / LARROW2EQUAL11861 / LARROW2EQUAL
11862 / LARROW2PIPEEQUAL
11790 / RARROW2EQUAL11863 / RARROW2EQUAL
11791 / AMPERSANDEQUAL11864 / AMPERSANDEQUAL
11792 / CARETEQUAL11865 / CARETEQUAL
...@@ -11821,6 +11894,8 @@ AdditionOp...@@ -11821,6 +11894,8 @@ AdditionOp
11821 / PLUS211894 / PLUS2
11822 / PLUSPERCENT11895 / PLUSPERCENT
11823 / MINUSPERCENT11896 / MINUSPERCENT
11897 / PLUSPIPE
11898 / MINUSPIPE
1182411899
11825MultiplyOp11900MultiplyOp
11826 &lt;- PIPE211901 &lt;- PIPE2
...@@ -11829,6 +11904,7 @@ MultiplyOp...@@ -11829,6 +11904,7 @@ MultiplyOp
11829 / PERCENT11904 / PERCENT
11830 / ASTERISK211905 / ASTERISK2
11831 / ASTERISKPERCENT11906 / ASTERISKPERCENT
11907 / ASTERISKPIPE
1183211908
11833PrefixOp11909PrefixOp
11834 &lt;- EXCLAMATIONMARK11910 &lt;- EXCLAMATIONMARK
...@@ -11992,6 +12068,8 @@ ASTERISK2 &lt;- '**' skip...@@ -11992,6 +12068,8 @@ ASTERISK2 &lt;- '**' skip
11992ASTERISKEQUAL &lt;- '*=' skip12068ASTERISKEQUAL &lt;- '*=' skip
11993ASTERISKPERCENT &lt;- '*%' ![=] skip12069ASTERISKPERCENT &lt;- '*%' ![=] skip
11994ASTERISKPERCENTEQUAL &lt;- '*%=' skip12070ASTERISKPERCENTEQUAL &lt;- '*%=' skip
12071ASTERISKPIPE &lt;- '*|' ![=] skip
12072ASTERISKPIPEEQUAL &lt;- '*|=' skip
11995CARET &lt;- '^' ![=] skip12073CARET &lt;- '^' ![=] skip
11996CARETEQUAL &lt;- '^=' skip12074CARETEQUAL &lt;- '^=' skip
11997COLON &lt;- ':' skip12075COLON &lt;- ':' skip
...@@ -12008,6 +12086,8 @@ EXCLAMATIONMARK &lt;- '!' ![=] skip...@@ -12008,6 +12086,8 @@ EXCLAMATIONMARK &lt;- '!' ![=] skip
12008EXCLAMATIONMARKEQUAL &lt;- '!=' skip12086EXCLAMATIONMARKEQUAL &lt;- '!=' skip
12009LARROW &lt;- '&lt;' ![&lt;=] skip12087LARROW &lt;- '&lt;' ![&lt;=] skip
12010LARROW2 &lt;- '&lt;&lt;' ![=] skip12088LARROW2 &lt;- '&lt;&lt;' ![=] skip
12089LARROW2PIPE &lt;- '&lt;&lt;|' ![=] skip
12090LARROW2PIPEEQUAL &lt;- '&lt;&lt;|=' ![=] skip
12011LARROW2EQUAL &lt;- '&lt;&lt;=' skip12091LARROW2EQUAL &lt;- '&lt;&lt;=' skip
12012LARROWEQUAL &lt;- '&lt;=' skip12092LARROWEQUAL &lt;- '&lt;=' skip
12013LBRACE &lt;- '{' skip12093LBRACE &lt;- '{' skip
...@@ -12017,6 +12097,8 @@ MINUS &lt;- '-' ![%=&gt;] skip...@@ -12017,6 +12097,8 @@ MINUS &lt;- '-' ![%=&gt;] skip
12017MINUSEQUAL &lt;- '-=' skip12097MINUSEQUAL &lt;- '-=' skip
12018MINUSPERCENT &lt;- '-%' ![=] skip12098MINUSPERCENT &lt;- '-%' ![=] skip
12019MINUSPERCENTEQUAL &lt;- '-%=' skip12099MINUSPERCENTEQUAL &lt;- '-%=' skip
12100MINUSPIPE &lt;- '-|' ![=] skip
12101MINUSPIPEEQUAL &lt;- '-|=' skip
12020MINUSRARROW &lt;- '-&gt;' skip12102MINUSRARROW &lt;- '-&gt;' skip
12021PERCENT &lt;- '%' ![=] skip12103PERCENT &lt;- '%' ![=] skip
12022PERCENTEQUAL &lt;- '%=' skip12104PERCENTEQUAL &lt;- '%=' skip
...@@ -12028,6 +12110,8 @@ PLUS2 &lt;- '++' skip...@@ -12028,6 +12110,8 @@ PLUS2 &lt;- '++' skip
12028PLUSEQUAL &lt;- '+=' skip12110PLUSEQUAL &lt;- '+=' skip
12029PLUSPERCENT &lt;- '+%' ![=] skip12111PLUSPERCENT &lt;- '+%' ![=] skip
12030PLUSPERCENTEQUAL &lt;- '+%=' skip12112PLUSPERCENTEQUAL &lt;- '+%=' skip
12113PLUSPIPE &lt;- '+|' ![=] skip
12114PLUSPIPEEQUAL &lt;- '+|=' skip
12031LETTERC &lt;- 'c' skip12115LETTERC &lt;- 'c' skip
12032QUESTIONMARK &lt;- '?' skip12116QUESTIONMARK &lt;- '?' skip
12033RARROW &lt;- '&gt;' ![&gt;=] skip12117RARROW &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 */
11int __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
37int __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,6 +41,7 @@ pub const max_name_len = switch (target.os.tag) {
41 .netbsd => 31,41 .netbsd => 31,
42 .freebsd => 15,42 .freebsd => 15,
43 .openbsd => 31,43 .openbsd => 31,
44 .solaris => 31,
44 else => 0,45 else => 0,
45};46};
4647
...@@ -112,7 +113,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {...@@ -112,7 +113,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
112 else => |e| return os.unexpectedErrno(e),113 else => |e| return os.unexpectedErrno(e),
113 }114 }
114 },115 },
115 .netbsd => if (use_pthreads) {116 .netbsd, .solaris => if (use_pthreads) {
116 const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr, null);117 const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr, null);
117 switch (err) {118 switch (err) {
118 .SUCCESS => return,119 .SUCCESS => return,
...@@ -202,7 +203,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co...@@ -202,7 +203,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
202 else => |e| return os.unexpectedErrno(e),203 else => |e| return os.unexpectedErrno(e),
203 }204 }
204 },205 },
205 .netbsd => if (use_pthreads) {206 .netbsd, .solaris => if (use_pthreads) {
206 const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);207 const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);
207 switch (err) {208 switch (err) {
208 .SUCCESS => return std.mem.sliceTo(buffer, 0),209 .SUCCESS => return std.mem.sliceTo(buffer, 0),
...@@ -565,6 +566,16 @@ const PosixThreadImpl = struct {...@@ -565,6 +566,16 @@ const PosixThreadImpl = struct {
565 };566 };
566 return @intCast(usize, count);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 .haiku => {579 .haiku => {
569 var count: u32 = undefined;580 var count: u32 = undefined;
570 var system_info: os.system_info = undefined;581 var system_info: os.system_info = undefined;
lib/std/Thread/Condition.zig+1-1
...@@ -54,7 +54,7 @@ pub const WindowsCondition = struct {...@@ -54,7 +54,7 @@ pub const WindowsCondition = struct {
54 pub fn wait(cond: *WindowsCondition, mutex: *Mutex) void {54 pub fn wait(cond: *WindowsCondition, mutex: *Mutex) void {
55 const rc = windows.kernel32.SleepConditionVariableSRW(55 const rc = windows.kernel32.SleepConditionVariableSRW(
56 &cond.cond,56 &cond.cond,
57 &mutex.srwlock,57 &mutex.impl.srwlock,
58 windows.INFINITE,58 windows.INFINITE,
59 @as(windows.ULONG, 0),59 @as(windows.ULONG, 0),
60 );60 );
lib/std/Thread/Mutex.zig+97-80
...@@ -33,17 +33,29 @@ const testing = std.testing;...@@ -33,17 +33,29 @@ const testing = std.testing;
33const StaticResetEvent = std.thread.StaticResetEvent;33const StaticResetEvent = std.thread.StaticResetEvent;
3434
35/// Try to acquire the mutex without blocking. Returns `null` if the mutex is35/// Try to acquire the mutex without blocking. Returns `null` if the mutex is
36/// unavailable. Otherwise returns `Held`. Call `release` on `Held`.36/// unavailable. Otherwise returns `Held`. Call `release` on `Held`, or use
37pub fn tryAcquire(m: *Mutex) ?Impl.Held {37/// releaseDirect().
38pub fn tryAcquire(m: *Mutex) ?Held {
38 return m.impl.tryAcquire();39 return m.impl.tryAcquire();
39}40}
4041
41/// Acquire the mutex. Deadlocks if the mutex is already42/// Acquire the mutex. Deadlocks if the mutex is already
42/// held by the calling thread.43/// held by the calling thread.
43pub fn acquire(m: *Mutex) Impl.Held {44pub fn acquire(m: *Mutex) Held {
44 return m.impl.acquire();45 return m.impl.acquire();
45}46}
4647
48/// Release the mutex. Prefer Held.release() if available.
49pub 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.
57pub const Held = Impl.Held;
58
47const Impl = if (builtin.single_threaded)59const Impl = if (builtin.single_threaded)
48 Dummy60 Dummy
49else if (builtin.os.tag == .windows)61else if (builtin.os.tag == .windows)
...@@ -53,6 +65,32 @@ else if (std.Thread.use_pthreads)...@@ -53,6 +65,32 @@ else if (std.Thread.use_pthreads)
53else65else
54 AtomicMutex;66 AtomicMutex;
5567
68fn 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
56pub const AtomicMutex = struct {94pub const AtomicMutex = struct {
57 state: State = .unlocked,95 state: State = .unlocked,
5896
...@@ -62,39 +100,32 @@ pub const AtomicMutex = struct {...@@ -62,39 +100,32 @@ pub const AtomicMutex = struct {
62 waiting,100 waiting,
63 };101 };
64102
65 pub const Held = struct {103 pub usingnamespace HeldInterface(@This());
66 mutex: *AtomicMutex,
67104
68 pub fn release(held: Held) void {105 fn tryAcquireDirect(m: *AtomicMutex) bool {
69 switch (@atomicRmw(State, &held.mutex.state, .Xchg, .unlocked, .Release)) {106 return @cmpxchgStrong(
70 .unlocked => unreachable,
71 .locked => {},
72 .waiting => held.mutex.unlockSlow(),
73 }
74 }
75 };
76
77 pub fn tryAcquire(m: *AtomicMutex) ?Held {
78 if (@cmpxchgStrong(
79 State,107 State,
80 &m.state,108 &m.state,
81 .unlocked,109 .unlocked,
82 .locked,110 .locked,
83 .Acquire,111 .Acquire,
84 .Monotonic,112 .Monotonic,
85 ) == null) {113 ) == null;
86 return Held{ .mutex = m };
87 } else {
88 return null;
89 }
90 }114 }
91115
92 pub fn acquire(m: *AtomicMutex) Held {116 fn acquireDirect(m: *AtomicMutex) void {
93 switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) {117 switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) {
94 .unlocked => {},118 .unlocked => {},
95 else => |s| m.lockSlow(s),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 }
99130
100 fn lockSlow(m: *AtomicMutex, current_state: State) void {131 fn lockSlow(m: *AtomicMutex, current_state: State) void {
...@@ -171,36 +202,20 @@ pub const AtomicMutex = struct {...@@ -171,36 +202,20 @@ pub const AtomicMutex = struct {
171pub const PthreadMutex = struct {202pub const PthreadMutex = struct {
172 pthread_mutex: std.c.pthread_mutex_t = .{},203 pthread_mutex: std.c.pthread_mutex_t = .{},
173204
174 pub const Held = struct {205 pub usingnamespace HeldInterface(@This());
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 };
187206
188 /// Try to acquire the mutex without blocking. Returns null if207 /// Try to acquire the mutex without blocking. Returns true if
189 /// the mutex is unavailable. Otherwise returns Held. Call208 /// the mutex is unavailable. Otherwise returns false. Call
190 /// release on Held.209 /// release when done.
191 pub fn tryAcquire(m: *PthreadMutex) ?Held {210 fn tryAcquireDirect(m: *PthreadMutex) bool {
192 if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS) {211 return std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS;
193 return Held{ .mutex = m };
194 } else {
195 return null;
196 }
197 }212 }
198213
199 /// Acquire the mutex. Will deadlock if the mutex is already214 /// Acquire the mutex. Will deadlock if the mutex is already
200 /// held by the calling thread.215 /// held by the calling thread.
201 pub fn acquire(m: *PthreadMutex) Held {216 fn acquireDirect(m: *PthreadMutex) void {
202 switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) {217 switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) {
203 .SUCCESS => return Held{ .mutex = m },218 .SUCCESS => {},
204 .INVAL => unreachable,219 .INVAL => unreachable,
205 .BUSY => unreachable,220 .BUSY => unreachable,
206 .AGAIN => unreachable,221 .AGAIN => unreachable,
...@@ -209,6 +224,16 @@ pub const PthreadMutex = struct {...@@ -209,6 +224,16 @@ pub const PthreadMutex = struct {
209 else => unreachable,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};
213238
214/// This has the sematics as `Mutex`, however it does not actually do any239/// This has the sematics as `Mutex`, however it does not actually do any
...@@ -216,58 +241,50 @@ pub const PthreadMutex = struct {...@@ -216,58 +241,50 @@ pub const PthreadMutex = struct {
216pub const Dummy = struct {241pub const Dummy = struct {
217 lock: @TypeOf(lock_init) = lock_init,242 lock: @TypeOf(lock_init) = lock_init,
218243
219 const lock_init = if (std.debug.runtime_safety) false else {};244 pub usingnamespace HeldInterface(@This());
220
221 pub const Held = struct {
222 mutex: *Dummy,
223245
224 pub fn release(held: Held) void {246 const lock_init = if (std.debug.runtime_safety) false else {};
225 if (std.debug.runtime_safety) {
226 held.mutex.lock = false;
227 }
228 }
229 };
230247
231 /// Try to acquire the mutex without blocking. Returns null if248 /// Try to acquire the mutex without blocking. Returns false if
232 /// the mutex is unavailable. Otherwise returns Held. Call249 /// the mutex is unavailable. Otherwise returns true.
233 /// release on Held.250 fn tryAcquireDirect(m: *Dummy) bool {
234 pub fn tryAcquire(m: *Dummy) ?Held {
235 if (std.debug.runtime_safety) {251 if (std.debug.runtime_safety) {
236 if (m.lock) return null;252 if (m.lock) return false;
237 m.lock = true;253 m.lock = true;
238 }254 }
239 return Held{ .mutex = m };255 return true;
240 }256 }
241257
242 /// Acquire the mutex. Will deadlock if the mutex is already258 /// Acquire the mutex. Will deadlock if the mutex is already
243 /// held by the calling thread.259 /// held by the calling thread.
244 pub fn acquire(m: *Dummy) Held {260 fn acquireDirect(m: *Dummy) void {
245 return m.tryAcquire() orelse @panic("deadlock detected");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};
248272
249const WindowsMutex = struct {273const WindowsMutex = struct {
250 srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT,274 srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT,
251275
252 pub const Held = struct {276 pub usingnamespace HeldInterface(@This());
253 mutex: *WindowsMutex,
254277
255 pub fn release(held: Held) void {278 fn tryAcquireDirect(m: *WindowsMutex) bool {
256 windows.kernel32.ReleaseSRWLockExclusive(&held.mutex.srwlock);279 return windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE;
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 }
266 }280 }
267281
268 pub fn acquire(m: *WindowsMutex) Held {282 fn acquireDirect(m: *WindowsMutex) void {
269 windows.kernel32.AcquireSRWLockExclusive(&m.srwlock);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};
273290
lib/std/array_hash_map.zig+10-10
...@@ -90,7 +90,7 @@ pub fn ArrayHashMap(...@@ -90,7 +90,7 @@ pub fn ArrayHashMap(
90 /// Modifying the key is allowed only if it does not change the hash.90 /// Modifying the key is allowed only if it does not change the hash.
91 /// Modifying the value is allowed.91 /// Modifying the value is allowed.
92 /// Entry pointers become invalid whenever this ArrayHashMap is modified,92 /// Entry pointers become invalid whenever this ArrayHashMap is modified,
93 /// unless `ensureCapacity` was previously used.93 /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
94 pub const Entry = Unmanaged.Entry;94 pub const Entry = Unmanaged.Entry;
9595
96 /// A KV pair which has been copied out of the backing store96 /// A KV pair which has been copied out of the backing store
...@@ -110,7 +110,7 @@ pub fn ArrayHashMap(...@@ -110,7 +110,7 @@ pub fn ArrayHashMap(
110 /// Modifying the key is allowed only if it does not change the hash.110 /// Modifying the key is allowed only if it does not change the hash.
111 /// Modifying the value is allowed.111 /// Modifying the value is allowed.
112 /// Entry pointers become invalid whenever this ArrayHashMap is modified,112 /// Entry pointers become invalid whenever this ArrayHashMap is modified,
113 /// unless `ensureCapacity` was previously used.113 /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
114 pub const GetOrPutResult = Unmanaged.GetOrPutResult;114 pub const GetOrPutResult = Unmanaged.GetOrPutResult;
115115
116 /// An Iterator over Entry pointers.116 /// An Iterator over Entry pointers.
...@@ -478,7 +478,7 @@ pub fn ArrayHashMapUnmanaged(...@@ -478,7 +478,7 @@ pub fn ArrayHashMapUnmanaged(
478 /// Modifying the key is allowed only if it does not change the hash.478 /// Modifying the key is allowed only if it does not change the hash.
479 /// Modifying the value is allowed.479 /// Modifying the value is allowed.
480 /// Entry pointers become invalid whenever this ArrayHashMap is modified,480 /// Entry pointers become invalid whenever this ArrayHashMap is modified,
481 /// unless `ensureCapacity` was previously used.481 /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
482 pub const Entry = struct {482 pub const Entry = struct {
483 key_ptr: *K,483 key_ptr: *K,
484 value_ptr: *V,484 value_ptr: *V,
...@@ -509,7 +509,7 @@ pub fn ArrayHashMapUnmanaged(...@@ -509,7 +509,7 @@ pub fn ArrayHashMapUnmanaged(
509 /// Modifying the key is allowed only if it does not change the hash.509 /// Modifying the key is allowed only if it does not change the hash.
510 /// Modifying the value is allowed.510 /// Modifying the value is allowed.
511 /// Entry pointers become invalid whenever this ArrayHashMap is modified,511 /// Entry pointers become invalid whenever this ArrayHashMap is modified,
512 /// unless `ensureCapacity` was previously used.512 /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
513 pub const GetOrPutResult = struct {513 pub const GetOrPutResult = struct {
514 key_ptr: *K,514 key_ptr: *K,
515 value_ptr: *V,515 value_ptr: *V,
...@@ -759,20 +759,20 @@ pub fn ArrayHashMapUnmanaged(...@@ -759,20 +759,20 @@ pub fn ArrayHashMapUnmanaged(
759 }759 }
760 pub fn ensureTotalCapacityContext(self: *Self, allocator: *Allocator, new_capacity: usize, ctx: Context) !void {760 pub fn ensureTotalCapacityContext(self: *Self, allocator: *Allocator, new_capacity: usize, ctx: Context) !void {
761 if (new_capacity <= linear_scan_max) {761 if (new_capacity <= linear_scan_max) {
762 try self.entries.ensureCapacity(allocator, new_capacity);762 try self.entries.ensureTotalCapacity(allocator, new_capacity);
763 return;763 return;
764 }764 }
765765
766 if (self.index_header) |header| {766 if (self.index_header) |header| {
767 if (new_capacity <= header.capacity()) {767 if (new_capacity <= header.capacity()) {
768 try self.entries.ensureCapacity(allocator, new_capacity);768 try self.entries.ensureTotalCapacity(allocator, new_capacity);
769 return;769 return;
770 }770 }
771 }771 }
772772
773 const new_bit_index = try IndexHeader.findBitIndex(new_capacity);773 const new_bit_index = try IndexHeader.findBitIndex(new_capacity);
774 const new_header = try IndexHeader.alloc(allocator, new_bit_index);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);
776776
777 if (self.index_header) |old_header| old_header.free(allocator);777 if (self.index_header) |old_header| old_header.free(allocator);
778 self.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, new_header);778 self.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, new_header);
...@@ -1441,7 +1441,7 @@ pub fn ArrayHashMapUnmanaged(...@@ -1441,7 +1441,7 @@ pub fn ArrayHashMapUnmanaged(
1441 unreachable;1441 unreachable;
1442 }1442 }
14431443
1444 /// Must ensureCapacity before calling this.1444 /// Must `ensureTotalCapacity`/`ensureUnusedCapacity` before calling this.
1445 fn getOrPutInternal(self: *Self, key: anytype, ctx: anytype, header: *IndexHeader, comptime I: type) GetOrPutResult {1445 fn getOrPutInternal(self: *Self, key: anytype, ctx: anytype, header: *IndexHeader, comptime I: type) GetOrPutResult {
1446 const slice = self.entries.slice();1446 const slice = self.entries.slice();
1447 const hashes_array = if (store_hash) slice.items(.hash) else {};1447 const hashes_array = if (store_hash) slice.items(.hash) else {};
...@@ -1485,7 +1485,7 @@ pub fn ArrayHashMapUnmanaged(...@@ -1485,7 +1485,7 @@ pub fn ArrayHashMapUnmanaged(
1485 }1485 }
14861486
1487 // This pointer survives the following append because we call1487 // This pointer survives the following append because we call
1488 // entries.ensureCapacity before getOrPutInternal.1488 // entries.ensureTotalCapacity before getOrPutInternal.
1489 const hash_match = if (store_hash) h == hashes_array[slot_data.entry_index] else true;1489 const hash_match = if (store_hash) h == hashes_array[slot_data.entry_index] else true;
1490 if (hash_match and checkedEql(ctx, key, keys_array[slot_data.entry_index])) {1490 if (hash_match and checkedEql(ctx, key, keys_array[slot_data.entry_index])) {
1491 return .{1491 return .{
...@@ -1946,7 +1946,7 @@ test "iterator hash map" {...@@ -1946,7 +1946,7 @@ test "iterator hash map" {
1946 var reset_map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);1946 var reset_map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
1947 defer reset_map.deinit();1947 defer reset_map.deinit();
19481948
1949 // test ensureCapacity with a 0 parameter1949 // test ensureTotalCapacity with a 0 parameter
1950 try reset_map.ensureTotalCapacity(0);1950 try reset_map.ensureTotalCapacity(0);
19511951
1952 try reset_map.putNoClobber(0, 11);1952 try reset_map.putNoClobber(0, 11);
lib/std/build.zig+7-6
...@@ -684,7 +684,11 @@ pub const Builder = struct {...@@ -684,7 +684,11 @@ pub const Builder = struct {
684 );684 );
685 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");685 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
686686
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";
688692
689 var diags: CrossTarget.ParseOptions.Diagnostics = .{};693 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
690 const selected_target = CrossTarget.parse(.{694 const selected_target = CrossTarget.parse(.{
...@@ -2432,11 +2436,8 @@ pub const LibExeObjStep = struct {...@@ -2432,11 +2436,8 @@ pub const LibExeObjStep = struct {
24322436
2433 if (populated_cpu_features.eql(cross.cpu.features)) {2437 if (populated_cpu_features.eql(cross.cpu.features)) {
2434 // The CPU name alone is sufficient.2438 // The CPU name alone is sufficient.
2435 // If it is the baseline CPU, no command line args are required.2439 try zig_args.append("-mcpu");
2436 if (cross.cpu.model != std.Target.Cpu.baseline(cross.cpu.arch).model) {2440 try zig_args.append(cross.cpu.model.name);
2437 try zig_args.append("-mcpu");
2438 try zig_args.append(cross.cpu.model.name);
2439 }
2440 } else {2441 } else {
2441 var mcpu_buffer = std.ArrayList(u8).init(builder.allocator);2442 var mcpu_buffer = std.ArrayList(u8).init(builder.allocator);
24422443
lib/std/builtin.zig+11-1
...@@ -166,6 +166,15 @@ pub const CallingConvention = enum {...@@ -166,6 +166,15 @@ pub const CallingConvention = enum {
166 SysV,166 SysV,
167};167};
168168
169/// This data structure is used by the Zig language code generation and
170/// therefore must be kept in sync with the compiler implementation.
171pub const AddressSpace = enum {
172 generic,
173 gs,
174 fs,
175 ss,
176};
177
169/// This data structure is used by the Zig language code generation and178/// This data structure is used by the Zig language code generation and
170/// therefore must be kept in sync with the compiler implementation.179/// therefore must be kept in sync with the compiler implementation.
171pub const SourceLocation = struct {180pub const SourceLocation = struct {
...@@ -226,6 +235,7 @@ pub const TypeInfo = union(enum) {...@@ -226,6 +235,7 @@ pub const TypeInfo = union(enum) {
226 is_const: bool,235 is_const: bool,
227 is_volatile: bool,236 is_volatile: bool,
228 alignment: comptime_int,237 alignment: comptime_int,
238 address_space: AddressSpace,
229 child: type,239 child: type,
230 is_allowzero: bool,240 is_allowzero: bool,
231241
...@@ -700,7 +710,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn...@@ -700,7 +710,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
700 },710 },
701 else => {711 else => {
702 const first_trace_addr = @returnAddress();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,6 +252,33 @@ pub extern "c" fn kevent(
252 timeout: ?*const c.timespec,252 timeout: ?*const c.timespec,
253) c_int;253) c_int;
254254
255pub extern "c" fn port_create() c.port_t;
256pub 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;
263pub extern "c" fn port_dissociate(port: c.port_t, source: u32, object: usize) c_int;
264pub extern "c" fn port_send(port: c.port_t, events: u32, user_var: ?*c_void) c_int;
265pub 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;
272pub extern "c" fn port_get(port: c.port_t, event: *c.port_event, timeout: ?*c.timespec) c_int;
273pub 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;
280pub extern "c" fn port_alert(port: c.port_t, flags: u32, events: u32, user_var: ?*c_void) c_int;
281
255pub extern "c" fn getaddrinfo(282pub extern "c" fn getaddrinfo(
256 noalias node: ?[*:0]const u8,283 noalias node: ?[*:0]const u8,
257 noalias service: ?[*:0]const u8,284 noalias service: ?[*:0]const u8,
lib/std/c/darwin.zig+1
...@@ -291,6 +291,7 @@ pub const sockaddr = extern struct {...@@ -291,6 +291,7 @@ pub const sockaddr = extern struct {
291 family: sa_family_t,291 family: sa_family_t,
292 data: [14]u8,292 data: [14]u8,
293293
294 pub const SS_MAXSIZE = 128;
294 pub const storage = std.x.os.Socket.Address.Native.Storage;295 pub const storage = std.x.os.Socket.Address.Native.Storage;
295 pub const in = extern struct {296 pub const in = extern struct {
296 len: u8 = @sizeOf(in),297 len: u8 = @sizeOf(in),
lib/std/c/dragonfly.zig+1
...@@ -465,6 +465,7 @@ pub const sockaddr = extern struct {...@@ -465,6 +465,7 @@ pub const sockaddr = extern struct {
465 family: u8,465 family: u8,
466 data: [14]u8,466 data: [14]u8,
467467
468 pub const SS_MAXSIZE = 128;
468 pub const storage = std.x.os.Socket.Address.Native.Storage;469 pub const storage = std.x.os.Socket.Address.Native.Storage;
469470
470 pub const in = extern struct {471 pub const in = extern struct {
lib/std/c/freebsd.zig+1
...@@ -323,6 +323,7 @@ pub const sockaddr = extern struct {...@@ -323,6 +323,7 @@ pub const sockaddr = extern struct {
323 /// actually longer; address value323 /// actually longer; address value
324 data: [14]u8,324 data: [14]u8,
325325
326 pub const SS_MAXSIZE = 128;
326 pub const storage = std.x.os.Socket.Address.Native.Storage;327 pub const storage = std.x.os.Socket.Address.Native.Storage;
327328
328 pub const in = extern struct {329 pub const in = extern struct {
lib/std/c/haiku.zig+1
...@@ -339,6 +339,7 @@ pub const sockaddr = extern struct {...@@ -339,6 +339,7 @@ pub const sockaddr = extern struct {
339 /// actually longer; address value339 /// actually longer; address value
340 data: [14]u8,340 data: [14]u8,
341341
342 pub const SS_MAXSIZE = 128;
342 pub const storage = std.x.os.Socket.Address.Native.Storage;343 pub const storage = std.x.os.Socket.Address.Native.Storage;
343344
344 pub const in = extern struct {345 pub const in = extern struct {
lib/std/c/netbsd.zig+1
...@@ -476,6 +476,7 @@ pub const sockaddr = extern struct {...@@ -476,6 +476,7 @@ pub const sockaddr = extern struct {
476 /// actually longer; address value476 /// actually longer; address value
477 data: [14]u8,477 data: [14]u8,
478478
479 pub const SS_MAXSIZE = 128;
479 pub const storage = std.x.os.Socket.Address.Native.Storage;480 pub const storage = std.x.os.Socket.Address.Native.Storage;
480481
481 pub const in = extern struct {482 pub const in = extern struct {
lib/std/c/openbsd.zig+1
...@@ -279,6 +279,7 @@ pub const sockaddr = extern struct {...@@ -279,6 +279,7 @@ pub const sockaddr = extern struct {
279 /// actually longer; address value279 /// actually longer; address value
280 data: [14]u8,280 data: [14]u8,
281281
282 pub const SS_MAXSIZE = 256;
282 pub const storage = std.x.os.Socket.Address.Native.Storage;283 pub const storage = std.x.os.Socket.Address.Native.Storage;
283284
284 pub const in = extern struct {285 pub const in = extern struct {
lib/std/c/solaris.zig+1909-11
...@@ -1,15 +1,1913 @@...@@ -1,15 +1,1913 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const maxInt = std.math.maxInt;
4const iovec = std.os.iovec;
5const iovec_const = std.os.iovec_const;
6const timezone = std.c.timezone;
7
8extern "c" fn ___errno() *c_int;
9pub const _errno = ___errno;
10
11pub const dl_iterate_phdr_callback = fn (info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int;
12pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;
13
14pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
15pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
16pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
17pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void;
18pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
19pub extern "c" fn sysconf(sc: c_int) i64;
20pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) c_int;
21pub extern "c" fn madvise(address: [*]u8, len: usize, advise: u32) c_int;
22
1pub const pthread_mutex_t = extern struct {23pub const pthread_mutex_t = extern struct {
2 __pthread_mutex_flag1: u16 = 0,24 flag1: u16 = 0,
3 __pthread_mutex_flag2: u8 = 0,25 flag2: u8 = 0,
4 __pthread_mutex_ceiling: u8 = 0,26 ceiling: u8 = 0,
5 __pthread_mutex_type: u16 = 0,27 @"type": u16 = 0,
6 __pthread_mutex_magic: u16 = 0x4d58,28 magic: u16 = 0x4d58,
7 __pthread_mutex_lock: u64 = 0,29 lock: u64 = 0,
8 __pthread_mutex_data: u64 = 0,30 data: u64 = 0,
9};31};
10pub const pthread_cond_t = extern struct {32pub const pthread_cond_t = extern struct {
11 __pthread_cond_flag: u32 = 0,33 flag: [4]u8 = [_]u8{0} ** 4,
12 __pthread_cond_type: u16 = 0,34 @"type": u16 = 0,
13 __pthread_cond_magic: u16 = 0x4356,35 magic: u16 = 0x4356,
14 __pthread_cond_data: u64 = 0,36 data: u64 = 0,
15};37};
38pub 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};
46pub const pthread_attr_t = extern struct {
47 mutexattr: ?*c_void = null,
48};
49pub const pthread_key_t = c_int;
50
51pub 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
59pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8, arg: ?*c_void) E;
60pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) E;
61
62pub const blkcnt_t = i64;
63pub const blksize_t = i32;
64pub const clock_t = i64;
65pub const dev_t = i32;
66pub const fd_t = c_int;
67pub const gid_t = u32;
68pub const ino_t = u64;
69pub const mode_t = u32;
70pub const nlink_t = u32;
71pub const off_t = i64;
72pub const pid_t = i32;
73pub const socklen_t = u32;
74pub const time_t = i64;
75pub const suseconds_t = i64;
76pub const uid_t = u32;
77pub const major_t = u32;
78pub const minor_t = u32;
79pub const port_t = c_int;
80pub const nfds_t = usize;
81pub const id_t = i32;
82pub const taskid_t = id_t;
83pub const projid_t = id_t;
84pub const poolid_t = id_t;
85pub const zoneid_t = id_t;
86pub const ctid_t = id_t;
87
88pub 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
99pub 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
118pub 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
129pub 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
138pub 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
149pub 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
180pub const EAI_MAX = 14;
181
182pub 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
199pub 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
216pub 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.
223pub 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
252pub const timespec = extern struct {
253 tv_sec: i64,
254 tv_nsec: isize,
255};
256
257pub const timeval = extern struct {
258 /// seconds
259 tv_sec: time_t,
260 /// microseconds
261 tv_usec: suseconds_t,
262};
263
264pub const MAXNAMLEN = 511;
265
266pub 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
281pub 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
298pub 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
333pub const SOMAXCONN = 128;
334
335pub const SCM = struct {
336 pub const UCRED = 0x1012;
337 pub const RIGHTS = 0x1010;
338 pub const TIMESTAMP = SO.TIMESTAMP;
339};
340
341pub 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
381pub 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
388pub 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
427pub const in_port_t = u16;
428pub const sa_family_t = u16;
429
430pub 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
463pub 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
479pub 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
492pub const PATH_MAX = 1024;
493pub const IOV_MAX = 1024;
494
495pub const STDIN_FILENO = 0;
496pub const STDOUT_FILENO = 1;
497pub const STDERR_FILENO = 2;
498
499pub 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
506pub 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
516pub 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
537pub 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
560pub 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
595pub 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
605pub const F_OK = 0; // test for existence of file
606pub const X_OK = 1; // test for execute or search permission
607pub const W_OK = 2; // test for write permission
608pub const R_OK = 4; // test for read permission
609
610pub 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
716pub 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
742pub 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
749pub const FD_CLOEXEC = 1;
750
751pub 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
759pub const tcflag_t = c_uint;
760pub const cc_t = u8;
761pub const speed_t = c_uint;
762
763pub const NCCS = 19;
764
765pub 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
773fn tioc(t: u16, num: u8) u16 {
774 return (t << 8) | num;
775}
776
777pub 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
863pub const winsize = extern struct {
864 ws_row: u16,
865 ws_col: u16,
866 ws_xpixel: u16,
867 ws_ypixel: u16,
868};
869
870const NSIG = 75;
871
872pub 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.
948pub 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
963pub const sigval_t = extern union {
964 int: c_int,
965 ptr: ?*c_void,
966};
967
968pub 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
1017comptime {
1018 std.debug.assert(@sizeOf(siginfo_t) == 256);
1019 std.debug.assert(@alignOf(siginfo_t) == @sizeOf(usize));
1020}
1021
1022pub const sigset_t = extern struct {
1023 __bits: [SIG.WORDS]u32,
1024};
1025
1026pub const empty_sigset = sigset_t{ .__bits = [_]u32{0} ** SIG.WORDS };
1027
1028pub 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
1051pub const mcontext_t = extern struct {
1052 gregs: [28]u64,
1053 fpregs: fpregset_t,
1054};
1055
1056pub const REG = struct {
1057 pub const RBP = 10;
1058 pub const RIP = 17;
1059 pub const RSP = 20;
1060};
1061
1062pub 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
1072pub const GETCONTEXT = 0;
1073pub const SETCONTEXT = 1;
1074pub const GETUSTACK = 2;
1075pub const SETUSTACK = 3;
1076
1077pub 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
1344pub const MINSIGSTKSZ = 2048;
1345pub const SIGSTKSZ = 8192;
1346
1347pub const SS_ONSTACK = 0x1;
1348pub const SS_DISABLE = 0x2;
1349
1350pub const stack_t = extern struct {
1351 sp: [*]u8,
1352 size: isize,
1353 flags: i32,
1354};
1355
1356pub 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
1424pub 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
1441pub 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
1450pub const HOST_NAME_MAX = 255;
1451
1452pub 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
1511pub 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
1524pub 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
1537pub const rlim_t = u64;
1538
1539pub 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
1546pub const rlimit = extern struct {
1547 /// Soft limit
1548 cur: rlim_t,
1549 /// Hard limit
1550 max: rlim_t,
1551};
1552
1553pub const RUSAGE_SELF = 0;
1554pub const RUSAGE_CHILDREN = -1;
1555pub const RUSAGE_THREAD = 1;
1556
1557pub 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
1576pub const SHUT = struct {
1577 pub const RD = 0;
1578 pub const WR = 1;
1579 pub const RDWR = 2;
1580};
1581
1582pub 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).
1589pub 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.
1612pub 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.
1664pub 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.
1673pub const _SC = struct {
1674 pub const NPROCESSORS_ONLN = 15;
1675};
1676
1677pub 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
1720pub const SFD = struct {
1721 pub const CLOEXEC = 0o2000000;
1722 pub const NONBLOCK = 0o4000;
1723};
1724
1725pub 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
1745pub 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
1755pub const PORT_ALERT = struct {
1756 pub const SET = 0x01;
1757 pub const UPDATE = 0x02;
1758};
1759
1760/// User watchable file events.
1761pub 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
1780pub 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
1791pub const port_notify = extern struct {
1792 /// Bind request(s) to port.
1793 port: u32,
1794 /// User defined variable.
1795 user: ?*void,
1796};
1797
1798pub 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.
1811pub const SIOCGLIFINDEX = IOWR('i', 133, lifreq);
1812pub const SIOCGIFINDEX = SIOCGLIFINDEX;
1813pub const MAX_HDW_LEN = 64;
1814pub const IFNAMESIZE = 32;
1815
1816pub 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
1827pub 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.
1835pub 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
1884pub const ifreq = lifreq;
1885
1886const 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
1893fn 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
1899pub fn IO(io_type: u8, nr: u8) i32 {
1900 return ioImpl(.none, io_type, nr, void);
1901}
1902
1903pub fn IOR(io_type: u8, nr: u8, comptime IOT: type) i32 {
1904 return ioImpl(.write, io_type, nr, IOT);
1905}
1906
1907pub fn IOW(io_type: u8, nr: u8, comptime IOT: type) i32 {
1908 return ioImpl(.read, io_type, nr, IOT);
1909}
1910
1911pub 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,7 +195,7 @@ pub const ChildProcess = struct {
195 };195 };
196196
197 var dead_fds: usize = 0;197 var dead_fds: usize = 0;
198 // We ask for ensureCapacity with this much extra space. This has more of an198 // We ask for ensureTotalCapacity with this much extra space. This has more of an
199 // effect on small reads because once the reads start to get larger the amount199 // effect on small reads because once the reads start to get larger the amount
200 // of space an ArrayList will allocate grows exponentially.200 // of space an ArrayList will allocate grows exponentially.
201 const bump_amt = 512;201 const bump_amt = 512;
...@@ -215,7 +215,7 @@ pub const ChildProcess = struct {...@@ -215,7 +215,7 @@ pub const ChildProcess = struct {
215 if (poll_fds[0].revents & os.POLL.IN != 0) {215 if (poll_fds[0].revents & os.POLL.IN != 0) {
216 // stdout is ready.216 // stdout is ready.
217 const new_capacity = std.math.min(stdout.items.len + bump_amt, max_output_bytes);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 const buf = stdout.unusedCapacitySlice();219 const buf = stdout.unusedCapacitySlice();
220 if (buf.len == 0) return error.StdoutStreamTooLong;220 if (buf.len == 0) return error.StdoutStreamTooLong;
221 const nread = try os.read(poll_fds[0].fd, buf);221 const nread = try os.read(poll_fds[0].fd, buf);
...@@ -230,7 +230,7 @@ pub const ChildProcess = struct {...@@ -230,7 +230,7 @@ pub const ChildProcess = struct {
230 if (poll_fds[1].revents & os.POLL.IN != 0) {230 if (poll_fds[1].revents & os.POLL.IN != 0) {
231 // stderr is ready.231 // stderr is ready.
232 const new_capacity = std.math.min(stderr.items.len + bump_amt, max_output_bytes);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 const buf = stderr.unusedCapacitySlice();234 const buf = stderr.unusedCapacitySlice();
235 if (buf.len == 0) return error.StderrStreamTooLong;235 if (buf.len == 0) return error.StderrStreamTooLong;
236 const nread = try os.read(poll_fds[1].fd, buf);236 const nread = try os.read(poll_fds[1].fd, buf);
...@@ -276,7 +276,8 @@ pub const ChildProcess = struct {...@@ -276,7 +276,8 @@ pub const ChildProcess = struct {
276276
277 // Windows Async IO requires an initial call to ReadFile before waiting on the handle277 // Windows Async IO requires an initial call to ReadFile before waiting on the handle
278 for ([_]u1{ 0, 1 }) |i| {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 const buf = outs[i].unusedCapacitySlice();281 const buf = outs[i].unusedCapacitySlice();
281 _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]);282 _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]);
282 wait_objects[wait_object_count] = handles[i];283 wait_objects[wait_object_count] = handles[i];
...@@ -318,7 +319,7 @@ pub const ChildProcess = struct {...@@ -318,7 +319,7 @@ pub const ChildProcess = struct {
318319
319 outs[i].items.len += read_bytes;320 outs[i].items.len += read_bytes;
320 const new_capacity = std.math.min(outs[i].items.len + bump_amt, max_output_bytes);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 const buf = outs[i].unusedCapacitySlice();323 const buf = outs[i].unusedCapacitySlice();
323 if (buf.len == 0) return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong;324 if (buf.len == 0) return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong;
324 _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]);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,7 +277,7 @@ pub const Coff = struct {
277 if (self.sections.items.len == self.coff_header.number_of_sections)277 if (self.sections.items.len == self.coff_header.number_of_sections)
278 return;278 return;
279279
280 try self.sections.ensureCapacity(self.coff_header.number_of_sections);280 try self.sections.ensureTotalCapacity(self.coff_header.number_of_sections);
281281
282 const in = self.in_file.reader();282 const in = self.in_file.reader();
283283
lib/std/compress/deflate.zig+26-6
...@@ -58,8 +58,10 @@ const Huffman = struct {...@@ -58,8 +58,10 @@ const Huffman = struct {
58 }58 }
5959
60 // All zero.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 return;63 return;
64 }
6365
64 var left: isize = 1;66 var left: isize = 1;
65 for (self.count[1..]) |val| {67 for (self.count[1..]) |val| {
...@@ -280,7 +282,7 @@ pub fn InflateStream(comptime ReaderType: type) type {...@@ -280,7 +282,7 @@ pub fn InflateStream(comptime ReaderType: type) type {
280 return self.bits & mask;282 return self.bits & mask;
281 }283 }
282 fn readBits(self: *Self, bits: usize) !u32 {284 fn readBits(self: *Self, bits: usize) !u32 {
283 const val = self.peekBits(bits);285 const val = try self.peekBits(bits);
284 self.discardBits(bits);286 self.discardBits(bits);
285 return val;287 return val;
286 }288 }
...@@ -487,6 +489,8 @@ pub fn InflateStream(comptime ReaderType: type) type {...@@ -487,6 +489,8 @@ pub fn InflateStream(comptime ReaderType: type) type {
487 // We can't read PREFIX_LUT_BITS as we don't want to read past the489 // We can't read PREFIX_LUT_BITS as we don't want to read past the
488 // deflate stream end, use an incremental approach instead.490 // deflate stream end, use an incremental approach instead.
489 var code_len = h.min_code_len;491 var code_len = h.min_code_len;
492 if (code_len == 0)
493 return error.OutOfCodes;
490 while (true) {494 while (true) {
491 _ = try self.peekBits(code_len);495 _ = try self.peekBits(code_len);
492 // Small optimization win, use as many bits as possible in the496 // Small optimization win, use as many bits as possible in the
...@@ -658,11 +662,27 @@ test "lengths overflow" {...@@ -658,11 +662,27 @@ test "lengths overflow" {
658 // f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6662 // f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6
659 // 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11663 // 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11
660 const stream = [_]u8{ 0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110 };664 const stream = [_]u8{ 0b11101101, 0b00011101, 0b00100100, 0b11101001, 0b11111111, 0b11111111, 0b00111001, 0b00001110 };
665 try std.testing.expectError(error.InvalidLength, testInflate(stream[0..]));
666}
667
668test "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}
661675
662 const reader = std.io.fixedBufferStream(&stream).reader();676test "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
682fn testInflate(data: []const u8) !void {
663 var window: [0x8000]u8 = undefined;683 var window: [0x8000]u8 = undefined;
684 const reader = std.io.fixedBufferStream(data).reader();
664 var inflate = inflateStream(reader, &window);685 var inflate = inflateStream(reader, &window);
665686 var inflated = try inflate.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize));
666 var buf: [1]u8 = undefined;687 defer std.testing.allocator.free(inflated);
667 try std.testing.expectError(error.InvalidLength, inflate.read(&buf));
668}688}
lib/std/crypto/benchmark.zig+1-1
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1// zig run benchmark.zig --release-fast --zig-lib-dir ..1// zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig
22
3const std = @import("../std.zig");3const std = @import("../std.zig");
4const builtin = std.builtin;4const builtin = std.builtin;
lib/std/crypto/blake3.zig+4-4
...@@ -398,10 +398,10 @@ pub const Blake3 = struct {...@@ -398,10 +398,10 @@ pub const Blake3 = struct {
398 return Blake3.init_internal(context_key_words, DERIVE_KEY_MATERIAL);398 return Blake3.init_internal(context_key_words, DERIVE_KEY_MATERIAL);
399 }399 }
400400
401 pub fn hash(in: []const u8, out: []u8, options: Options) void {401 pub fn hash(b: []const u8, out: []u8, options: Options) void {
402 var hasher = Blake3.init(options);402 var d = Blake3.init(options);
403 hasher.update(in);403 d.update(b);
404 hasher.final(out);404 d.final(out);
405 }405 }
406406
407 fn pushCv(self: *Blake3, cv: [8]u32) void {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,9 +228,32 @@ pub fn assert(ok: bool) void {
228228
229pub fn panic(comptime format: []const u8, args: anytype) noreturn {229pub fn panic(comptime format: []const u8, args: anytype) noreturn {
230 @setCold(true);230 @setCold(true);
231 // TODO: remove conditional once wasi / LLVM defines __builtin_return_address231
232 const first_trace_addr = if (native_os == .wasi) null else @returnAddress();232 panicExtra(null, format, args);
233 panicExtra(null, first_trace_addr, format, args);233}
234
235/// `panicExtra` is useful when you want to print out an `@errorReturnTrace`
236/// and also print out some values.
237pub 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}
235258
236/// Non-zero whenever the program triggered a panic.259/// Non-zero whenever the program triggered a panic.
...@@ -244,7 +267,9 @@ var panic_mutex = std.Thread.Mutex{};...@@ -244,7 +267,9 @@ var panic_mutex = std.Thread.Mutex{};
244/// This is used to catch and handle panics triggered by the panic handler.267/// This is used to catch and handle panics triggered by the panic handler.
245threadlocal var panic_stage: usize = 0;268threadlocal var panic_stage: usize = 0;
246269
247pub 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)
272pub fn panicImpl(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, msg: []const u8) noreturn {
248 @setCold(true);273 @setCold(true);
249274
250 if (enable_segfault_handler) {275 if (enable_segfault_handler) {
...@@ -271,7 +296,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -271,7 +296,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
271 const current_thread_id = std.Thread.getCurrentId();296 const current_thread_id = std.Thread.getCurrentId();
272 stderr.print("thread {} panic: ", .{current_thread_id}) catch os.abort();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 if (trace) |t| {300 if (trace) |t| {
276 dumpStackTrace(t.*);301 dumpStackTrace(t.*);
277 }302 }
...@@ -654,6 +679,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {...@@ -654,6 +679,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
654 .openbsd,679 .openbsd,
655 .macos,680 .macos,
656 .windows,681 .windows,
682 .solaris,
657 => return DebugInfo.init(allocator),683 => return DebugInfo.init(allocator),
658 else => return error.UnsupportedDebugInfo,684 else => return error.UnsupportedDebugInfo,
659 }685 }
...@@ -1420,7 +1446,7 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -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 base_address: usize,1450 base_address: usize,
1425 dwarf: DW.DwarfInfo,1451 dwarf: DW.DwarfInfo,
1426 mapped_memory: []const u8,1452 mapped_memory: []const u8,
...@@ -1468,7 +1494,7 @@ fn getDebugInfoAllocator() *mem.Allocator {...@@ -1468,7 +1494,7 @@ fn getDebugInfoAllocator() *mem.Allocator {
14681494
1469/// Whether or not the current target can print useful debug information when a segfault occurs.1495/// Whether or not the current target can print useful debug information when a segfault occurs.
1470pub const have_segfault_handling_support = switch (native_os) {1496pub const have_segfault_handling_support = switch (native_os) {
1471 .linux, .netbsd => true,1497 .linux, .netbsd, .solaris => true,
1472 .windows => true,1498 .windows => true,
1473 .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"),1499 .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"),
1474 else => false,1500 else => false,
...@@ -1535,6 +1561,7 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v...@@ -1535,6 +1561,7 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v
1535 .freebsd => @ptrToInt(info.addr),1561 .freebsd => @ptrToInt(info.addr),
1536 .netbsd => @ptrToInt(info.info.reason.fault.addr),1562 .netbsd => @ptrToInt(info.info.reason.fault.addr),
1537 .openbsd => @ptrToInt(info.data.fault.addr),1563 .openbsd => @ptrToInt(info.data.fault.addr),
1564 .solaris => @ptrToInt(info.reason.fault.addr),
1538 else => unreachable,1565 else => unreachable,
1539 };1566 };
15401567
...@@ -1559,13 +1586,13 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v...@@ -1559,13 +1586,13 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v
1559 .x86_64 => {1586 .x86_64 => {
1560 const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));1587 const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
1561 const ip = switch (native_os) {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 .freebsd => @intCast(usize, ctx.mcontext.rip),1590 .freebsd => @intCast(usize, ctx.mcontext.rip),
1564 .openbsd => @intCast(usize, ctx.sc_rip),1591 .openbsd => @intCast(usize, ctx.sc_rip),
1565 else => unreachable,1592 else => unreachable,
1566 };1593 };
1567 const bp = switch (native_os) {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 .openbsd => @intCast(usize, ctx.sc_rbp),1596 .openbsd => @intCast(usize, ctx.sc_rbp),
1570 .freebsd => @intCast(usize, ctx.mcontext.rbp),1597 .freebsd => @intCast(usize, ctx.mcontext.rbp),
1571 else => unreachable,1598 else => unreachable,
...@@ -1624,9 +1651,14 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: u...@@ -1624,9 +1651,14 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: u
1624 os.abort();1651 os.abort();
1625 } else {1652 } else {
1626 switch (msg) {1653 switch (msg) {
1627 0 => panicExtra(null, exception_address, format.?, .{}),1654 0 => panicImpl(null, exception_address, format.?),
1628 1 => panicExtra(null, exception_address, "Segmentation fault at address 0x{x}", .{info.ExceptionRecord.ExceptionInformation[1]}),1655 1 => {
1629 2 => panicExtra(null, exception_address, "Illegal Instruction", .{}),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 else => unreachable,1662 else => unreachable,
1631 }1663 }
1632 }1664 }
lib/std/dynamic_library.zig+1-1
...@@ -14,7 +14,7 @@ const max = std.math.max;...@@ -14,7 +14,7 @@ const max = std.math.max;
14pub const DynLib = switch (builtin.os.tag) {14pub const DynLib = switch (builtin.os.tag) {
15 .linux => if (builtin.link_libc) DlDynlib else ElfDynLib,15 .linux => if (builtin.link_libc) DlDynlib else ElfDynLib,
16 .windows => WindowsDynLib,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 else => void,18 else => void,
19};19};
2020
lib/std/elf.zig+97
...@@ -1591,3 +1591,100 @@ pub const PF_MASKOS = 0x0ff00000;...@@ -1591,3 +1591,100 @@ pub const PF_MASKOS = 0x0ff00000;
15911591
1592/// Bits for processor-specific semantics.1592/// Bits for processor-specific semantics.
1593pub const PF_MASKPROC = 0xf0000000;1593pub const PF_MASKPROC = 0xf0000000;
1594
1595// Special section indexes used in Elf{32,64}_Sym.
1596pub const SHN_UNDEF = 0;
1597pub const SHN_LORESERVE = 0xff00;
1598pub const SHN_LOPROC = 0xff00;
1599pub const SHN_HIPROC = 0xff1f;
1600pub const SHN_LIVEPATCH = 0xff20;
1601pub const SHN_ABS = 0xfff1;
1602pub const SHN_COMMON = 0xfff2;
1603pub const SHN_HIRESERVE = 0xffff;
1604
1605/// AMD x86-64 relocations.
1606/// No reloc
1607pub const R_X86_64_NONE = 0;
1608/// Direct 64 bit
1609pub const R_X86_64_64 = 1;
1610/// PC relative 32 bit signed
1611pub const R_X86_64_PC32 = 2;
1612/// 32 bit GOT entry
1613pub const R_X86_64_GOT32 = 3;
1614/// 32 bit PLT address
1615pub const R_X86_64_PLT32 = 4;
1616/// Copy symbol at runtime
1617pub const R_X86_64_COPY = 5;
1618/// Create GOT entry
1619pub const R_X86_64_GLOB_DAT = 6;
1620/// Create PLT entry
1621pub const R_X86_64_JUMP_SLOT = 7;
1622/// Adjust by program base
1623pub const R_X86_64_RELATIVE = 8;
1624/// 32 bit signed PC relative offset to GOT
1625pub const R_X86_64_GOTPCREL = 9;
1626/// Direct 32 bit zero extended
1627pub const R_X86_64_32 = 10;
1628/// Direct 32 bit sign extended
1629pub const R_X86_64_32S = 11;
1630/// Direct 16 bit zero extended
1631pub const R_X86_64_16 = 12;
1632/// 16 bit sign extended pc relative
1633pub const R_X86_64_PC16 = 13;
1634/// Direct 8 bit sign extended
1635pub const R_X86_64_8 = 14;
1636/// 8 bit sign extended pc relative
1637pub const R_X86_64_PC8 = 15;
1638/// ID of module containing symbol
1639pub const R_X86_64_DTPMOD64 = 16;
1640/// Offset in module's TLS block
1641pub const R_X86_64_DTPOFF64 = 17;
1642/// Offset in initial TLS block
1643pub const R_X86_64_TPOFF64 = 18;
1644/// 32 bit signed PC relative offset to two GOT entries for GD symbol
1645pub const R_X86_64_TLSGD = 19;
1646/// 32 bit signed PC relative offset to two GOT entries for LD symbol
1647pub const R_X86_64_TLSLD = 20;
1648/// Offset in TLS block
1649pub const R_X86_64_DTPOFF32 = 21;
1650/// 32 bit signed PC relative offset to GOT entry for IE symbol
1651pub const R_X86_64_GOTTPOFF = 22;
1652/// Offset in initial TLS block
1653pub const R_X86_64_TPOFF32 = 23;
1654/// PC relative 64 bit
1655pub const R_X86_64_PC64 = 24;
1656/// 64 bit offset to GOT
1657pub const R_X86_64_GOTOFF64 = 25;
1658/// 32 bit signed pc relative offset to GOT
1659pub const R_X86_64_GOTPC32 = 26;
1660/// 64 bit GOT entry offset
1661pub const R_X86_64_GOT64 = 27;
1662/// 64 bit PC relative offset to GOT entry
1663pub const R_X86_64_GOTPCREL64 = 28;
1664/// 64 bit PC relative offset to GOT
1665pub const R_X86_64_GOTPC64 = 29;
1666/// Like GOT64, says PLT entry needed
1667pub const R_X86_64_GOTPLT64 = 30;
1668/// 64-bit GOT relative offset to PLT entry
1669pub const R_X86_64_PLTOFF64 = 31;
1670/// Size of symbol plus 32-bit addend
1671pub const R_X86_64_SIZE32 = 32;
1672/// Size of symbol plus 64-bit addend
1673pub const R_X86_64_SIZE64 = 33;
1674/// GOT offset for TLS descriptor
1675pub const R_X86_64_GOTPC32_TLSDESC = 34;
1676/// Marker for call through TLS descriptor
1677pub const R_X86_64_TLSDESC_CALL = 35;
1678/// TLS descriptor
1679pub const R_X86_64_TLSDESC = 36;
1680/// Adjust indirectly by program base
1681pub const R_X86_64_IRELATIVE = 37;
1682/// 64-bit adjust by program base
1683pub 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
1687pub const R_X86_64_GOTPCRELX = 41;
1688/// Load from 32 bit signed PC relative offset to GOT entry with REX prefix, relaxable
1689pub const R_X86_64_REX_GOTPCRELX = 42;
1690pub const R_X86_64_NUM = 43;
lib/std/fifo.zig+6-3
...@@ -119,8 +119,11 @@ pub fn LinearFifo(...@@ -119,8 +119,11 @@ pub fn LinearFifo(
119 }119 }
120 }120 }
121121
122 /// Deprecated: call `ensureUnusedCapacity` or `ensureTotalCapacity`.
123 pub const ensureCapacity = ensureTotalCapacity;
124
122 /// Ensure that the buffer can fit at least `size` items125 /// 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 if (self.buf.len >= size) return;127 if (self.buf.len >= size) return;
125 if (buffer_type == .Dynamic) {128 if (buffer_type == .Dynamic) {
126 self.realign();129 self.realign();
...@@ -135,7 +138,7 @@ pub fn LinearFifo(...@@ -135,7 +138,7 @@ pub fn LinearFifo(
135 pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void {138 pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void {
136 if (self.writableLength() >= size) return;139 if (self.writableLength() >= size) return;
137140
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 }
140143
141 /// Returns number of items currently in fifo144 /// Returns number of items currently in fifo
...@@ -471,7 +474,7 @@ test "LinearFifo(u8, .Dynamic)" {...@@ -471,7 +474,7 @@ test "LinearFifo(u8, .Dynamic)" {
471 }474 }
472475
473 {476 {
474 try fifo.ensureCapacity(1);477 try fifo.ensureTotalCapacity(1);
475 var in_fbs = std.io.fixedBufferStream("pump test");478 var in_fbs = std.io.fixedBufferStream("pump test");
476 var out_buf: [50]u8 = undefined;479 var out_buf: [50]u8 = undefined;
477 var out_fbs = std.io.fixedBufferStream(&out_buf);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,7 +35,7 @@ pub const Watch = @import("fs/watch.zig").Watch;
35/// fit into a UTF-8 encoded array of this length.35/// fit into a UTF-8 encoded array of this length.
36/// The byte count includes room for a null sentinel byte.36/// The byte count includes room for a null sentinel byte.
37pub const MAX_PATH_BYTES = switch (builtin.os.tag) {37pub 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 // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.39 // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
40 // If it would require 4 UTF-8 bytes, then there would be a surrogate40 // If it would require 4 UTF-8 bytes, then there would be a surrogate
41 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.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,10 +298,10 @@ pub const Dir = struct {
298 pub const Kind = File.Kind;298 pub const Kind = File.Kind;
299 };299 };
300300
301 const IteratorError = error{AccessDenied} || os.UnexpectedError;301 const IteratorError = error{ AccessDenied, SystemResources } || os.UnexpectedError;
302302
303 pub const Iterator = switch (builtin.os.tag) {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 dir: Dir,305 dir: Dir,
306 seek: i64,306 seek: i64,
307 buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)),307 buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)),
...@@ -318,6 +318,7 @@ pub const Dir = struct {...@@ -318,6 +318,7 @@ pub const Dir = struct {
318 switch (builtin.os.tag) {318 switch (builtin.os.tag) {
319 .macos, .ios => return self.nextDarwin(),319 .macos, .ios => return self.nextDarwin(),
320 .freebsd, .netbsd, .dragonfly, .openbsd => return self.nextBsd(),320 .freebsd, .netbsd, .dragonfly, .openbsd => return self.nextBsd(),
321 .solaris => return self.nextSolaris(),
321 else => @compileError("unimplemented"),322 else => @compileError("unimplemented"),
322 }323 }
323 }324 }
...@@ -372,6 +373,60 @@ pub const Dir = struct {...@@ -372,6 +373,60 @@ pub const Dir = struct {
372 }373 }
373 }374 }
374375
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 fn nextBsd(self: *Self) !?Entry {430 fn nextBsd(self: *Self) !?Entry {
376 start_over: while (true) {431 start_over: while (true) {
377 if (self.index >= self.end_index) {432 if (self.index >= self.end_index) {
...@@ -704,6 +759,7 @@ pub const Dir = struct {...@@ -704,6 +759,7 @@ pub const Dir = struct {
704 .netbsd,759 .netbsd,
705 .dragonfly,760 .dragonfly,
706 .openbsd,761 .openbsd,
762 .solaris,
707 => return Iterator{763 => return Iterator{
708 .dir = self,764 .dir = self,
709 .seek = 0,765 .seek = 0,
...@@ -1556,7 +1612,7 @@ pub const Dir = struct {...@@ -1556,7 +1612,7 @@ pub const Dir = struct {
1556 error.AccessDenied => |e| switch (builtin.os.tag) {1612 error.AccessDenied => |e| switch (builtin.os.tag) {
1557 // non-Linux POSIX systems return EPERM when trying to delete a directory, so1613 // non-Linux POSIX systems return EPERM when trying to delete a directory, so
1558 // we need to handle that case specifically and translate the error1614 // 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 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them)1616 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them)
1561 const fstat = os.fstatatZ(self.fd, sub_path_c, os.AT.SYMLINK_NOFOLLOW) catch return e;1617 const fstat = os.fstatatZ(self.fd, sub_path_c, os.AT.SYMLINK_NOFOLLOW) catch return e;
1562 const is_dir = fstat.mode & os.S.IFMT == os.S.IFDIR;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,6 +2497,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
2441 }2497 }
2442 switch (builtin.os.tag) {2498 switch (builtin.os.tag) {
2443 .linux => return os.readlinkZ("/proc/self/exe", out_buffer),2499 .linux => return os.readlinkZ("/proc/self/exe", out_buffer),
2500 .solaris => return os.readlinkZ("/proc/self/path/a.out", out_buffer),
2444 .freebsd, .dragonfly => {2501 .freebsd, .dragonfly => {
2445 var mib = [4]c_int{ os.CTL.KERN, os.KERN.PROC, os.KERN.PROC_PATHNAME, -1 };2502 var mib = [4]c_int{ os.CTL.KERN, os.KERN.PROC, os.KERN.PROC_PATHNAME, -1 };
2446 var out_len: usize = out_buffer.len;2503 var out_len: usize = out_buffer.len;
lib/std/fs/file.zig+34-18
...@@ -41,6 +41,8 @@ pub const File = struct {...@@ -41,6 +41,8 @@ pub const File = struct {
41 File,41 File,
42 UnixDomainSocket,42 UnixDomainSocket,
43 Whiteout,43 Whiteout,
44 Door,
45 EventPort,
44 Unknown,46 Unknown,
45 };47 };
4648
...@@ -320,28 +322,40 @@ pub const File = struct {...@@ -320,28 +322,40 @@ pub const File = struct {
320 const atime = st.atime();322 const atime = st.atime();
321 const mtime = st.mtime();323 const mtime = st.mtime();
322 const ctime = st.ctime();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 return Stat{354 return Stat{
324 .inode = st.ino,355 .inode = st.ino,
325 .size = @bitCast(u64, st.size),356 .size = @bitCast(u64, st.size),
326 .mode = st.mode,357 .mode = st.mode,
327 .kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) {358 .kind = kind,
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 },
345 .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,359 .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
346 .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,360 .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
347 .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,361 .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
...@@ -852,6 +866,7 @@ pub const File = struct {...@@ -852,6 +866,7 @@ pub const File = struct {
852866
853 pub const LockError = error{867 pub const LockError = error{
854 SystemResources,868 SystemResources,
869 FileLocksNotSupported,
855 } || os.UnexpectedError;870 } || os.UnexpectedError;
856871
857 /// Blocks when an incompatible lock is held by another process.872 /// Blocks when an incompatible lock is held by another process.
...@@ -914,6 +929,7 @@ pub const File = struct {...@@ -914,6 +929,7 @@ pub const File = struct {
914 return os.flock(file.handle, os.LOCK.UN) catch |err| switch (err) {929 return os.flock(file.handle, os.LOCK.UN) catch |err| switch (err) {
915 error.WouldBlock => unreachable, // unlocking can't block930 error.WouldBlock => unreachable, // unlocking can't block
916 error.SystemResources => unreachable, // We are deallocating resources.931 error.SystemResources => unreachable, // We are deallocating resources.
932 error.FileLocksNotSupported => unreachable, // We already got the lock.
917 error.Unexpected => unreachable, // Resource deallocation must succeed.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,7 +44,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
44 };44 };
45 return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname });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 const home_dir = os.getenv("HOME") orelse {48 const home_dir = os.getenv("HOME") orelse {
49 // TODO look in /etc/passwd49 // TODO look in /etc/passwd
50 return error.AppDataDirUnavailable;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,7 +188,7 @@ fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
188188
189test "Dir.realpath smoke test" {189test "Dir.realpath smoke test" {
190 switch (builtin.os.tag) {190 switch (builtin.os.tag) {
191 .linux, .windows, .macos, .ios, .watchos, .tvos => {},191 .linux, .windows, .macos, .ios, .watchos, .tvos, .solaris => {},
192 else => return error.SkipZigTest,192 else => return error.SkipZigTest,
193 }193 }
194194
lib/std/hash/benchmark.zig+1-1
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1// zig run benchmark.zig --release-fast --zig-lib-dir ..1// zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig
22
3const builtin = std.builtin;3const builtin = std.builtin;
4const std = @import("std");4const std = @import("std");
lib/std/hash_map.zig+9-9
...@@ -1568,11 +1568,11 @@ test "std.hash_map basic usage" {...@@ -1568,11 +1568,11 @@ test "std.hash_map basic usage" {
1568 try expectEqual(total, sum);1568 try expectEqual(total, sum);
1569}1569}
15701570
1571test "std.hash_map ensureCapacity" {1571test "std.hash_map ensureTotalCapacity" {
1572 var map = AutoHashMap(i32, i32).init(std.testing.allocator);1572 var map = AutoHashMap(i32, i32).init(std.testing.allocator);
1573 defer map.deinit();1573 defer map.deinit();
15741574
1575 try map.ensureCapacity(20);1575 try map.ensureTotalCapacity(20);
1576 const initial_capacity = map.capacity();1576 const initial_capacity = map.capacity();
1577 try testing.expect(initial_capacity >= 20);1577 try testing.expect(initial_capacity >= 20);
1578 var i: i32 = 0;1578 var i: i32 = 0;
...@@ -1583,13 +1583,13 @@ test "std.hash_map ensureCapacity" {...@@ -1583,13 +1583,13 @@ test "std.hash_map ensureCapacity" {
1583 try testing.expect(initial_capacity == map.capacity());1583 try testing.expect(initial_capacity == map.capacity());
1584}1584}
15851585
1586test "std.hash_map ensureCapacity with tombstones" {1586test "std.hash_map ensureUnusedCapacity with tombstones" {
1587 var map = AutoHashMap(i32, i32).init(std.testing.allocator);1587 var map = AutoHashMap(i32, i32).init(std.testing.allocator);
1588 defer map.deinit();1588 defer map.deinit();
15891589
1590 var i: i32 = 0;1590 var i: i32 = 0;
1591 while (i < 100) : (i += 1) {1591 while (i < 100) : (i += 1) {
1592 try map.ensureCapacity(@intCast(u32, map.count() + 1));1592 try map.ensureUnusedCapacity(1);
1593 map.putAssumeCapacity(i, i);1593 map.putAssumeCapacity(i, i);
1594 // Remove to create tombstones that still count as load in the hashmap.1594 // Remove to create tombstones that still count as load in the hashmap.
1595 _ = map.remove(i);1595 _ = map.remove(i);
...@@ -1669,7 +1669,7 @@ test "std.hash_map clone" {...@@ -1669,7 +1669,7 @@ test "std.hash_map clone" {
1669 try expectEqual(b.get(3).?, 3);1669 try expectEqual(b.get(3).?, 3);
1670}1670}
16711671
1672test "std.hash_map ensureCapacity with existing elements" {1672test "std.hash_map ensureTotalCapacity with existing elements" {
1673 var map = AutoHashMap(u32, u32).init(std.testing.allocator);1673 var map = AutoHashMap(u32, u32).init(std.testing.allocator);
1674 defer map.deinit();1674 defer map.deinit();
16751675
...@@ -1677,16 +1677,16 @@ test "std.hash_map ensureCapacity with existing elements" {...@@ -1677,16 +1677,16 @@ test "std.hash_map ensureCapacity with existing elements" {
1677 try expectEqual(map.count(), 1);1677 try expectEqual(map.count(), 1);
1678 try expectEqual(map.capacity(), @TypeOf(map).Unmanaged.minimal_capacity);1678 try expectEqual(map.capacity(), @TypeOf(map).Unmanaged.minimal_capacity);
16791679
1680 try map.ensureCapacity(65);1680 try map.ensureTotalCapacity(65);
1681 try expectEqual(map.count(), 1);1681 try expectEqual(map.count(), 1);
1682 try expectEqual(map.capacity(), 128);1682 try expectEqual(map.capacity(), 128);
1683}1683}
16841684
1685test "std.hash_map ensureCapacity satisfies max load factor" {1685test "std.hash_map ensureTotalCapacity satisfies max load factor" {
1686 var map = AutoHashMap(u32, u32).init(std.testing.allocator);1686 var map = AutoHashMap(u32, u32).init(std.testing.allocator);
1687 defer map.deinit();1687 defer map.deinit();
16881688
1689 try map.ensureCapacity(127);1689 try map.ensureTotalCapacity(127);
1690 try expectEqual(map.capacity(), 256);1690 try expectEqual(map.capacity(), 256);
1691}1691}
16921692
...@@ -1870,7 +1870,7 @@ test "std.hash_map putAssumeCapacity" {...@@ -1870,7 +1870,7 @@ test "std.hash_map putAssumeCapacity" {
1870 var map = AutoHashMap(u32, u32).init(std.testing.allocator);1870 var map = AutoHashMap(u32, u32).init(std.testing.allocator);
1871 defer map.deinit();1871 defer map.deinit();
18721872
1873 try map.ensureCapacity(20);1873 try map.ensureTotalCapacity(20);
1874 var i: u32 = 0;1874 var i: u32 = 0;
1875 while (i < 20) : (i += 1) {1875 while (i < 20) : (i += 1) {
1876 map.putAssumeCapacityNoClobber(i, i);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,10 +746,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
746746
747 const new_aligned_size = math.max(len, ptr_align);747 const new_aligned_size = math.max(len, ptr_align);
748 if (new_aligned_size > largest_bucket_object_size) {748 if (new_aligned_size > largest_bucket_object_size) {
749 try self.large_allocations.ensureCapacity(749 try self.large_allocations.ensureUnusedCapacity(self.backing_allocator, 1);
750 self.backing_allocator,
751 self.large_allocations.count() + 1,
752 );
753750
754 const slice = try self.backing_allocator.allocFn(self.backing_allocator, len, ptr_align, len_align, ret_addr);751 const slice = try self.backing_allocator.allocFn(self.backing_allocator, len, ptr_align, len_align, ret_addr);
755752
lib/std/io/reader.zig+2-2
...@@ -61,7 +61,7 @@ pub fn Reader(...@@ -61,7 +61,7 @@ pub fn Reader(
61 array_list: *std.ArrayListAligned(u8, alignment),61 array_list: *std.ArrayListAligned(u8, alignment),
62 max_append_size: usize,62 max_append_size: usize,
63 ) !void {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 const original_len = array_list.items.len;65 const original_len = array_list.items.len;
66 var start_index: usize = original_len;66 var start_index: usize = original_len;
67 while (true) {67 while (true) {
...@@ -81,7 +81,7 @@ pub fn Reader(...@@ -81,7 +81,7 @@ pub fn Reader(
81 }81 }
8282
83 // This will trigger ArrayList to expand superlinearly at whatever its growth rate is.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 }
8787
lib/std/json.zig+1-1
...@@ -1838,7 +1838,7 @@ fn parseInternal(...@@ -1838,7 +1838,7 @@ fn parseInternal(
1838 else => {},1838 else => {},
1839 }1839 }
18401840
1841 try arraylist.ensureCapacity(arraylist.items.len + 1);1841 try arraylist.ensureUnusedCapacity(1);
1842 const v = try parseInternal(ptrInfo.child, tok, tokens, options);1842 const v = try parseInternal(ptrInfo.child, tok, tokens, options);
1843 arraylist.appendAssumeCapacity(v);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,6 +76,14 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {
76 const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift));76 const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift));
77 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;77 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;
78 if (remaining_bits != -1) return error.Overflow;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 }
8088
81 value |= temp;89 value |= temp;
...@@ -215,6 +223,8 @@ test "deserialize signed LEB128" {...@@ -215,6 +223,8 @@ test "deserialize signed LEB128" {
215 try testing.expectError(error.Overflow, test_read_ileb128(i32, "\x80\x80\x80\x80\x40"));223 try testing.expectError(error.Overflow, test_read_ileb128(i32, "\x80\x80\x80\x80\x40"));
216 try testing.expectError(error.Overflow, test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40"));224 try testing.expectError(error.Overflow, test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40"));
217 try testing.expectError(error.Overflow, test_read_ileb128(i8, "\xff\x7e"));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"));
218228
219 // Decode SLEB128229 // Decode SLEB128
220 try testing.expect((try test_read_ileb128(i64, "\x00")) == 0);230 try testing.expect((try test_read_ileb128(i64, "\x00")) == 0);
...@@ -233,8 +243,8 @@ test "deserialize signed LEB128" {...@@ -233,8 +243,8 @@ test "deserialize signed LEB128" {
233 try testing.expect((try test_read_ileb128(i8, "\xff\x7f")) == -1);243 try testing.expect((try test_read_ileb128(i8, "\xff\x7f")) == -1);
234 try testing.expect((try test_read_ileb128(i16, "\xff\xff\x7f")) == -1);244 try testing.expect((try test_read_ileb128(i16, "\xff\xff\x7f")) == -1);
235 try testing.expect((try test_read_ileb128(i32, "\xff\xff\xff\xff\x7f")) == -1);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);246 try testing.expect((try test_read_ileb128(i32, "\x80\x80\x80\x80\x78")) == -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)));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 try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x40")) == -0x4000000000000000);248 try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x40")) == -0x4000000000000000);
239 try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7f")) == -0x8000000000000000);249 try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7f")) == -0x8000000000000000);
240250
lib/std/math/big/int.zig+330-36
...@@ -552,47 +552,78 @@ pub const Mutable = struct {...@@ -552,47 +552,78 @@ pub const Mutable = struct {
552 r.positive = a.positive;552 r.positive = a.positive;
553 }553 }
554554
555 /// r = a | b555 /// r = a | b under 2s complement semantics.
556 /// r may alias with a or b.556 /// r may alias with a or b.
557 ///557 ///
558 /// a and b are zero-extended to the longer of a or b.558 /// a and b are zero-extended to the longer of a or b.
559 ///559 ///
560 /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`.560 /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`.
561 pub fn bitOr(r: *Mutable, a: Const, b: Const) void {561 pub fn bitOr(r: *Mutable, a: Const, b: Const) void {
562 if (a.limbs.len > b.limbs.len) {562 // Trivial cases, llsignedor does not support zero.
563 llor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);563 if (a.eqZero()) {
564 r.len = a.limbs.len;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 } else {574 } else {
566 llor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);575 r.positive = llsignedor(r.limbs, b.limbs, b.positive, a.limbs, a.positive);
567 r.len = b.limbs.len;576 r.normalize(if (a.positive) b.limbs.len else a.limbs.len);
568 }577 }
569 }578 }
570579
571 /// r = a & b580 /// r = a & b under 2s complement semantics.
572 /// r may alias with a or b.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 pub fn bitAnd(r: *Mutable, a: Const, b: Const) void {586 pub fn bitAnd(r: *Mutable, a: Const, b: Const) void {
576 if (a.limbs.len > b.limbs.len) {587 // Trivial cases, llsignedand does not support zero.
577 lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);588 if (a.eqZero()) {
578 r.normalize(b.limbs.len);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 } else {599 } else {
580 lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);600 r.positive = llsignedand(r.limbs, b.limbs, b.positive, a.limbs, a.positive);
581 r.normalize(a.limbs.len);601 r.normalize(if (a.positive or b.positive) a.limbs.len else b.limbs.len + 1);
582 }602 }
583 }603 }
584604
585 /// r = a ^ b605 /// r = a ^ b under 2s complement semantics.
586 /// r may alias with a or b.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 pub fn bitXor(r: *Mutable, a: Const, b: Const) void {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 if (a.limbs.len > b.limbs.len) {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]);622 r.positive = llsignedxor(r.limbs, a.limbs, a.positive, b.limbs, b.positive);
592 r.normalize(a.limbs.len);623 r.normalize(a.limbs.len + @boolToInt(a.positive != b.positive));
593 } else {624 } else {
594 llxor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);625 r.positive = llsignedxor(r.limbs, b.limbs, b.positive, a.limbs, a.positive);
595 r.normalize(b.limbs.len);626 r.normalize(b.limbs.len + @boolToInt(a.positive != b.positive));
596 }627 }
597 }628 }
598629
...@@ -1834,7 +1865,11 @@ pub const Managed = struct {...@@ -1834,7 +1865,11 @@ pub const Managed = struct {
18341865
1835 /// r = a & b1866 /// r = a & b
1836 pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void {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 var m = r.toMutable();1873 var m = r.toMutable();
1839 m.bitAnd(a.toConst(), b.toConst());1874 m.bitAnd(a.toConst(), b.toConst());
1840 r.setMetadata(m.positive, m.len);1875 r.setMetadata(m.positive, m.len);
...@@ -1842,7 +1877,9 @@ pub const Managed = struct {...@@ -1842,7 +1877,9 @@ pub const Managed = struct {
18421877
1843 /// r = a ^ b1878 /// r = a ^ b
1844 pub fn bitXor(r: *Managed, a: Managed, b: Managed) !void {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 var m = r.toMutable();1883 var m = r.toMutable();
1847 m.bitXor(a.toConst(), b.toConst());1884 m.bitXor(a.toConst(), b.toConst());
1848 r.setMetadata(m.positive, m.len);1885 r.setMetadata(m.positive, m.len);
...@@ -2221,42 +2258,299 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void {...@@ -2221,42 +2258,299 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
2221 }2258 }
2222}2259}
22232260
2224fn 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.
2267fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool {
2225 @setRuntimeSafety(debug_safety);2268 @setRuntimeSafety(debug_safety);
2226 assert(r.len >= a.len);2269 assert(r.len >= a.len);
2227 assert(a.len >= b.len);2270 assert(a.len >= b.len);
22282271
2229 var i: usize = 0;2272 if (a_positive and b_positive) {
2230 while (i < b.len) : (i += 1) {2273 // Trivial case, result is positive.
2231 r[i] = a[i] | b[i];2274 var i: usize = 0;
2232 }2275 while (i < b.len) : (i += 1) {
2233 while (i < a.len) : (i += 1) {2276 r[i] = a[i] | b[i];
2234 r[i] = a[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}
22372388
2238fn 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.
2395fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_positive: bool) bool {
2239 @setRuntimeSafety(debug_safety);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 assert(a.len >= b.len);2399 assert(a.len >= b.len);
22422400
2243 var i: usize = 0;2401 if (a_positive and b_positive) {
2244 while (i < b.len) : (i += 1) {2402 // Trivial case, result is positive.
2245 r[i] = a[i] & b[i];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}
22482493
2249fn 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.
2500fn 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 assert(r.len >= a.len);2503 assert(r.len >= a.len);
2251 assert(a.len >= b.len);2504 assert(a.len >= b.len);
22522505
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 var i: usize = 0;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 while (i < b.len) : (i += 1) {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 while (i < a.len) : (i += 1) {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}
22612555
2262/// r MUST NOT alias x.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,6 +5,7 @@ const Managed = std.math.big.int.Managed;
5const Mutable = std.math.big.int.Mutable;5const Mutable = std.math.big.int.Mutable;
6const Limb = std.math.big.Limb;6const Limb = std.math.big.Limb;
7const DoubleLimb = std.math.big.DoubleLimb;7const DoubleLimb = std.math.big.DoubleLimb;
8const SignedDoubleLimb = std.math.big.SignedDoubleLimb;
8const maxInt = std.math.maxInt;9const maxInt = std.math.maxInt;
9const minInt = std.math.minInt;10const minInt = std.math.minInt;
1011
...@@ -1364,6 +1365,83 @@ test "big.int bitwise and multi-limb" {...@@ -1364,6 +1365,83 @@ test "big.int bitwise and multi-limb" {
1364 try testing.expect((try a.to(u128)) == 0);1365 try testing.expect((try a.to(u128)) == 0);
1365}1366}
13661367
1368test "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
1379test "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
1390test "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
1401test "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
1412test "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
1423test "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
1434test "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
1367test "big.int bitwise xor simple" {1445test "big.int bitwise xor simple" {
1368 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);1446 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1369 defer a.deinit();1447 defer a.deinit();
...@@ -1386,6 +1464,72 @@ test "big.int bitwise xor multi-limb" {...@@ -1386,6 +1464,72 @@ test "big.int bitwise xor multi-limb" {
1386 try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb));1464 try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb));
1387}1465}
13881466
1467test "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
1478test "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
1489test "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
1500test "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
1511test "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
1522test "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
1389test "big.int bitwise or simple" {1533test "big.int bitwise or simple" {
1390 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);1534 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1391 defer a.deinit();1535 defer a.deinit();
...@@ -1409,6 +1553,72 @@ test "big.int bitwise or multi-limb" {...@@ -1409,6 +1553,72 @@ test "big.int bitwise or multi-limb" {
1409 try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));1553 try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));
1410}1554}
14111555
1556test "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
1567test "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
1578test "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
1589test "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
1600test "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
1611test "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
1412test "big.int var args" {1622test "big.int var args" {
1413 var a = try Managed.initSet(testing.allocator, 5);1623 var a = try Managed.initSet(testing.allocator, 5);
1414 defer a.deinit();1624 defer a.deinit();
lib/std/mem.zig+42
...@@ -147,6 +147,46 @@ test "mem.Allocator basics" {...@@ -147,6 +147,46 @@ test "mem.Allocator basics" {
147 try testing.expectError(error.OutOfMemory, failAllocator.allocSentinel(u8, 1, 0));147 try testing.expectError(error.OutOfMemory, failAllocator.allocSentinel(u8, 1, 0));
148}148}
149149
150test "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/// Copy all of source into dest at position 0.190/// Copy all of source into dest at position 0.
151/// dest.len must be >= source.len.191/// dest.len must be >= source.len.
152/// If the slices overlap, dest.ptr must be <= src.ptr.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,6 +2512,7 @@ fn CopyPtrAttrs(comptime source: type, comptime size: std.builtin.TypeInfo.Point
2472 .is_volatile = info.is_volatile,2512 .is_volatile = info.is_volatile,
2473 .is_allowzero = info.is_allowzero,2513 .is_allowzero = info.is_allowzero,
2474 .alignment = info.alignment,2514 .alignment = info.alignment,
2515 .address_space = info.address_space,
2475 .child = child,2516 .child = child,
2476 .sentinel = null,2517 .sentinel = null,
2477 },2518 },
...@@ -2960,6 +3001,7 @@ fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: u29) typ...@@ -2960,6 +3001,7 @@ fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: u29) typ
2960 .is_volatile = info.is_volatile,3001 .is_volatile = info.is_volatile,
2961 .is_allowzero = info.is_allowzero,3002 .is_allowzero = info.is_allowzero,
2962 .alignment = new_alignment,3003 .alignment = new_alignment,
3004 .address_space = info.address_space,
2963 .child = info.child,3005 .child = info.child,
2964 .sentinel = null,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,7 +313,7 @@ pub fn resize(self: *Allocator, old_mem: anytype, new_n: usize) Error!@TypeOf(ol
313 const new_byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;313 const new_byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;
314 const rc = try self.resizeFn(self, old_byte_slice, Slice.alignment, new_byte_count, 0, @returnAddress());314 const rc = try self.resizeFn(self, old_byte_slice, Slice.alignment, new_byte_count, 0, @returnAddress());
315 assert(rc == new_byte_count);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 return mem.bytesAsSlice(T, new_byte_slice);317 return mem.bytesAsSlice(T, new_byte_slice);
318}318}
319319
lib/std/meta.zig+3
...@@ -235,6 +235,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {...@@ -235,6 +235,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
235 .is_const = info.is_const,235 .is_const = info.is_const,
236 .is_volatile = info.is_volatile,236 .is_volatile = info.is_volatile,
237 .alignment = info.alignment,237 .alignment = info.alignment,
238 .address_space = info.address_space,
238 .child = @Type(.{239 .child = @Type(.{
239 .Array = .{240 .Array = .{
240 .len = array_info.len,241 .len = array_info.len,
...@@ -254,6 +255,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {...@@ -254,6 +255,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
254 .is_const = info.is_const,255 .is_const = info.is_const,
255 .is_volatile = info.is_volatile,256 .is_volatile = info.is_volatile,
256 .alignment = info.alignment,257 .alignment = info.alignment,
258 .address_space = info.address_space,
257 .child = info.child,259 .child = info.child,
258 .is_allowzero = info.is_allowzero,260 .is_allowzero = info.is_allowzero,
259 .sentinel = sentinel_val,261 .sentinel = sentinel_val,
...@@ -271,6 +273,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {...@@ -271,6 +273,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
271 .is_const = ptr_info.is_const,273 .is_const = ptr_info.is_const,
272 .is_volatile = ptr_info.is_volatile,274 .is_volatile = ptr_info.is_volatile,
273 .alignment = ptr_info.alignment,275 .alignment = ptr_info.alignment,
276 .address_space = ptr_info.address_space,
274 .child = ptr_info.child,277 .child = ptr_info.child,
275 .is_allowzero = ptr_info.is_allowzero,278 .is_allowzero = ptr_info.is_allowzero,
276 .sentinel = sentinel_val,279 .sentinel = sentinel_val,
lib/std/multi_array_list.zig+2-2
...@@ -189,7 +189,7 @@ pub fn MultiArrayList(comptime S: type) type {...@@ -189,7 +189,7 @@ pub fn MultiArrayList(comptime S: type) type {
189 /// sets the given index to the specified element. May reallocate189 /// sets the given index to the specified element. May reallocate
190 /// and invalidate iterators.190 /// and invalidate iterators.
191 pub fn insert(self: *Self, gpa: *Allocator, index: usize, elem: S) void {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 self.insertAssumeCapacity(index, elem);193 self.insertAssumeCapacity(index, elem);
194 }194 }
195195
...@@ -376,7 +376,7 @@ pub fn MultiArrayList(comptime S: type) type {...@@ -376,7 +376,7 @@ pub fn MultiArrayList(comptime S: type) type {
376 pub fn clone(self: Self, gpa: *Allocator) !Self {376 pub fn clone(self: Self, gpa: *Allocator) !Self {
377 var result = Self{};377 var result = Self{};
378 errdefer result.deinit(gpa);378 errdefer result.deinit(gpa);
379 try result.ensureCapacity(gpa, self.len);379 try result.ensureTotalCapacity(gpa, self.len);
380 result.len = self.len;380 result.len = self.len;
381 const self_slice = self.slice();381 const self_slice = self.slice();
382 const result_slice = result.slice();382 const result_slice = result.slice();
lib/std/net.zig+1-1
...@@ -157,7 +157,7 @@ pub const Address = extern union {...@@ -157,7 +157,7 @@ pub const Address = extern union {
157 unreachable;157 unreachable;
158 }158 }
159159
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 else => unreachable,162 else => unreachable,
163 }163 }
lib/std/net/test.zig+13
...@@ -90,6 +90,19 @@ test "parse and render IPv4 addresses" {...@@ -90,6 +90,19 @@ test "parse and render IPv4 addresses" {
90 try testing.expectError(error.NonCanonical, net.Address.parseIp4("127.01.0.1", 0));90 try testing.expectError(error.NonCanonical, net.Address.parseIp4("127.01.0.1", 0));
91}91}
9292
93test "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
93test "resolve DNS" {106test "resolve DNS" {
94 if (builtin.os.tag == .wasi) return error.SkipZigTest;107 if (builtin.os.tag == .wasi) return error.SkipZigTest;
95108
lib/std/os.zig+24
...@@ -31,6 +31,7 @@ pub const freebsd = std.c;...@@ -31,6 +31,7 @@ pub const freebsd = std.c;
31pub const haiku = std.c;31pub const haiku = std.c;
32pub const netbsd = std.c;32pub const netbsd = std.c;
33pub const openbsd = std.c;33pub const openbsd = std.c;
34pub const solaris = std.c;
34pub const linux = @import("os/linux.zig");35pub const linux = @import("os/linux.zig");
35pub const uefi = @import("os/uefi.zig");36pub const uefi = @import("os/uefi.zig");
36pub const wasi = @import("os/wasi.zig");37pub const wasi = @import("os/wasi.zig");
...@@ -64,8 +65,10 @@ else switch (builtin.os.tag) {...@@ -64,8 +65,10 @@ else switch (builtin.os.tag) {
64};65};
6566
66pub const AF = system.AF;67pub const AF = system.AF;
68pub const AF_SUN = system.AF_SUN;
67pub const ARCH = system.ARCH;69pub const ARCH = system.ARCH;
68pub const AT = system.AT;70pub const AT = system.AT;
71pub const AT_SUN = system.AT_SUN;
69pub const CLOCK = system.CLOCK;72pub const CLOCK = system.CLOCK;
70pub const CPU_COUNT = system.CPU_COUNT;73pub const CPU_COUNT = system.CPU_COUNT;
71pub const CTL = system.CTL;74pub const CTL = system.CTL;
...@@ -101,6 +104,7 @@ pub const RR = system.RR;...@@ -101,6 +104,7 @@ pub const RR = system.RR;
101pub const S = system.S;104pub const S = system.S;
102pub const SA = system.SA;105pub const SA = system.SA;
103pub const SC = system.SC;106pub const SC = system.SC;
107pub const _SC = system._SC;
104pub const SEEK = system.SEEK;108pub const SEEK = system.SEEK;
105pub const SHUT = system.SHUT;109pub const SHUT = system.SHUT;
106pub const SIG = system.SIG;110pub const SIG = system.SIG;
...@@ -143,6 +147,10 @@ pub const off_t = system.off_t;...@@ -143,6 +147,10 @@ pub const off_t = system.off_t;
143pub const oflags_t = system.oflags_t;147pub const oflags_t = system.oflags_t;
144pub const pid_t = system.pid_t;148pub const pid_t = system.pid_t;
145pub const pollfd = system.pollfd;149pub const pollfd = system.pollfd;
150pub const port_t = system.port_t;
151pub const port_event = system.port_event;
152pub const port_notify = system.port_notify;
153pub const file_obj = system.file_obj;
146pub const rights_t = system.rights_t;154pub const rights_t = system.rights_t;
147pub const rlim_t = system.rlim_t;155pub const rlim_t = system.rlim_t;
148pub const rlimit = system.rlimit;156pub const rlimit = system.rlimit;
...@@ -2038,6 +2046,7 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr...@@ -2038,6 +2046,7 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr
2038 .NOTDIR => return error.NotDir,2046 .NOTDIR => return error.NotDir,
2039 .NOMEM => return error.SystemResources,2047 .NOMEM => return error.SystemResources,
2040 .ROFS => return error.ReadOnlyFileSystem,2048 .ROFS => return error.ReadOnlyFileSystem,
2049 .EXIST => return error.DirNotEmpty,
2041 .NOTEMPTY => return error.DirNotEmpty,2050 .NOTEMPTY => return error.DirNotEmpty,
20422051
2043 .INVAL => unreachable, // invalid flags, or pathname has . as last component2052 .INVAL => unreachable, // invalid flags, or pathname has . as last component
...@@ -4492,8 +4501,12 @@ pub const FlockError = error{...@@ -4492,8 +4501,12 @@ pub const FlockError = error{
44924501
4493 /// The kernel ran out of memory for allocating file locks4502 /// The kernel ran out of memory for allocating file locks
4494 SystemResources,4503 SystemResources,
4504
4505 /// The underlying filesystem does not support file locks
4506 FileLocksNotSupported,
4495} || UnexpectedError;4507} || UnexpectedError;
44964508
4509/// Depending on the operating system `flock` may or may not interact with `fcntl` locks made by other processes.
4497pub fn flock(fd: fd_t, operation: i32) FlockError!void {4510pub fn flock(fd: fd_t, operation: i32) FlockError!void {
4498 while (true) {4511 while (true) {
4499 const rc = system.flock(fd, operation);4512 const rc = system.flock(fd, operation);
...@@ -4504,6 +4517,7 @@ pub fn flock(fd: fd_t, operation: i32) FlockError!void {...@@ -4504,6 +4517,7 @@ pub fn flock(fd: fd_t, operation: i32) FlockError!void {
4504 .INVAL => unreachable, // invalid parameters4517 .INVAL => unreachable, // invalid parameters
4505 .NOLCK => return error.SystemResources,4518 .NOLCK => return error.SystemResources,
4506 .AGAIN => return error.WouldBlock, // TODO: integrate with async instead of just returning an error4519 .AGAIN => return error.WouldBlock, // TODO: integrate with async instead of just returning an error
4520 .OPNOTSUPP => return error.FileLocksNotSupported,
4507 else => |err| return unexpectedErrno(err),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,6 +4681,16 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4667 };4681 };
4668 return target;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 else => @compileError("querying for canonical path of a handle is unsupported on this host"),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,6 +89,7 @@ pub const user_desc = arch_bits.user_desc;
89pub const tls = @import("linux/tls.zig");89pub const tls = @import("linux/tls.zig");
90pub const pie = @import("linux/start_pie.zig");90pub const pie = @import("linux/start_pie.zig");
91pub const BPF = @import("linux/bpf.zig");91pub const BPF = @import("linux/bpf.zig");
92pub const IOCTL = @import("linux/ioctl.zig");
9293
93pub const MAP = struct {94pub const MAP = struct {
94 pub usingnamespace arch_bits.MAP;95 pub usingnamespace arch_bits.MAP;
...@@ -2585,18 +2586,18 @@ pub const T = struct {...@@ -2585,18 +2586,18 @@ pub const T = struct {
2585 pub const IOCGSID = 0x5429;2586 pub const IOCGSID = 0x5429;
2586 pub const IOCGRS485 = 0x542E;2587 pub const IOCGRS485 = 0x542E;
2587 pub const IOCSRS485 = 0x542F;2588 pub const IOCSRS485 = 0x542F;
2588 pub const IOCGPTN = 0x80045430;2589 pub const IOCGPTN = IOCTL.IOR('T', 0x30, c_uint);
2589 pub const IOCSPTLCK = 0x40045431;2590 pub const IOCSPTLCK = IOCTL.IOW('T', 0x31, c_int);
2590 pub const IOCGDEV = 0x80045432;2591 pub const IOCGDEV = IOCTL.IOR('T', 0x32, c_uint);
2591 pub const CGETX = 0x5432;2592 pub const CGETX = 0x5432;
2592 pub const CSETX = 0x5433;2593 pub const CSETX = 0x5433;
2593 pub const CSETXF = 0x5434;2594 pub const CSETXF = 0x5434;
2594 pub const CSETXW = 0x5435;2595 pub const CSETXW = 0x5435;
2595 pub const IOCSIG = 0x40045436;2596 pub const IOCSIG = IOCTL.IOW('T', 0x36, c_int);
2596 pub const IOCVHANGUP = 0x5437;2597 pub const IOCVHANGUP = 0x5437;
2597 pub const IOCGPKT = 0x80045438;2598 pub const IOCGPKT = IOCTL.IOR('T', 0x38, c_int);
2598 pub const IOCGPTLCK = 0x80045439;2599 pub const IOCGPTLCK = IOCTL.IOR('T', 0x39, c_int);
2599 pub const IOCGEXCL = 0x80045440;2600 pub const IOCGEXCL = IOCTL.IOR('T', 0x40, c_int);
2600};2601};
26012602
2602pub const EPOLL = struct {2603pub const EPOLL = struct {
...@@ -2923,6 +2924,7 @@ pub const sockaddr = extern struct {...@@ -2923,6 +2924,7 @@ pub const sockaddr = extern struct {
2923 family: sa_family_t,2924 family: sa_family_t,
2924 data: [14]u8,2925 data: [14]u8,
29252926
2927 pub const SS_MAXSIZE = 128;
2926 pub const storage = std.x.os.Socket.Address.Native.Storage;2928 pub const storage = std.x.os.Socket.Address.Native.Storage;
29272929
2928 /// IPv4 socket address2930 /// IPv4 socket address
lib/std/os/linux/io_uring.zig+128
...@@ -404,6 +404,25 @@ pub const IO_Uring = struct {...@@ -404,6 +404,25 @@ pub const IO_Uring = struct {
404 return sqe;404 return sqe;
405 }405 }
406406
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 /// Queues (but does not submit) an SQE to perform a `pwritev()`.426 /// Queues (but does not submit) an SQE to perform a `pwritev()`.
408 /// Returns a pointer to the SQE so that you can further modify the SQE for advanced use cases.427 /// Returns a pointer to the SQE so that you can further modify the SQE for advanced use cases.
409 /// For example, if you want to do a `pwritev2()` then set `rw_flags` on the returned SQE.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,6 +440,25 @@ pub const IO_Uring = struct {
421 return sqe;440 return sqe;
422 }441 }
423442
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 /// Queues (but does not submit) an SQE to perform an `accept4(2)` on a socket.462 /// Queues (but does not submit) an SQE to perform an `accept4(2)` on a socket.
425 /// Returns a pointer to the SQE.463 /// Returns a pointer to the SQE.
426 pub fn accept(464 pub fn accept(
...@@ -674,6 +712,29 @@ pub const IO_Uring = struct {...@@ -674,6 +712,29 @@ pub const IO_Uring = struct {
674 try handle_registration_result(res);712 try handle_registration_result(res);
675 }713 }
676714
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 fn handle_registration_result(res: usize) !void {738 fn handle_registration_result(res: usize) !void {
678 switch (linux.getErrno(res)) {739 switch (linux.getErrno(res)) {
679 .SUCCESS => {},740 .SUCCESS => {},
...@@ -905,6 +966,16 @@ pub fn io_uring_prep_writev(...@@ -905,6 +966,16 @@ pub fn io_uring_prep_writev(
905 io_uring_prep_rw(.WRITEV, sqe, fd, @ptrToInt(iovecs.ptr), iovecs.len, offset);966 io_uring_prep_rw(.WRITEV, sqe, fd, @ptrToInt(iovecs.ptr), iovecs.len, offset);
906}967}
907968
969pub 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
974pub 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
908pub fn io_uring_prep_accept(979pub fn io_uring_prep_accept(
909 sqe: *io_uring_sqe,980 sqe: *io_uring_sqe,
910 fd: os.fd_t,981 fd: os.fd_t,
...@@ -1282,6 +1353,63 @@ test "write/read" {...@@ -1282,6 +1353,63 @@ test "write/read" {
1282 try testing.expectEqualSlices(u8, buffer_write[0..], buffer_read[0..]);1353 try testing.expectEqualSlices(u8, buffer_write[0..], buffer_read[0..]);
1283}1354}
12841355
1356test "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
1285test "openat" {1413test "openat" {
1286 if (builtin.os.tag != .linux) return error.SkipZigTest;1414 if (builtin.os.tag != .linux) return error.SkipZigTest;
12871415
lib/std/os/linux/ioctl.zig created+56
...@@ -0,0 +1,56 @@
1const std = @import("../../std.zig");
2
3const 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
19const Direction = std.meta.Int(.unsigned, bits.dir);
20
21pub const Request = packed struct {
22 nr: u8,
23 io_type: u8,
24 size: std.meta.Int(.unsigned, bits.size),
25 dir: Direction,
26};
27
28fn 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
38pub fn IO(io_type: u8, nr: u8) u32 {
39 return io_impl(bits.none, io_type, nr, void);
40}
41
42pub fn IOR(io_type: u8, nr: u8, comptime T: type) u32 {
43 return io_impl(bits.read, io_type, nr, T);
44}
45
46pub fn IOW(io_type: u8, nr: u8, comptime T: type) u32 {
47 return io_impl(bits.write, io_type, nr, T);
48}
49
50pub 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
54comptime {
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,6 +674,10 @@ pub const msghdr_const = extern struct {
674pub const off_t = i64;674pub const off_t = i64;
675pub const ino_t = u64;675pub const ino_t = u64;
676pub const mode_t = u32;676pub const mode_t = u32;
677pub const dev_t = usize;
678pub const nlink_t = u32;
679pub const blksize_t = isize;
680pub const blkcnt_t = isize;
677681
678// The `stat64` definition used by the kernel.682// The `stat64` definition used by the kernel.
679pub const Stat = extern struct {683pub 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,7 +188,10 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
188}188}
189189
190test "link with relative paths" {190test "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 var cwd = fs.cwd();195 var cwd = fs.cwd();
193196
194 cwd.deleteFile("example.txt") catch {};197 cwd.deleteFile("example.txt") catch {};
...@@ -222,7 +225,10 @@ test "link with relative paths" {...@@ -222,7 +225,10 @@ test "link with relative paths" {
222}225}
223226
224test "linkat with different directories" {227test "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 var cwd = fs.cwd();232 var cwd = fs.cwd();
227 var tmp = tmpDir(.{});233 var tmp = tmpDir(.{});
228234
...@@ -634,8 +640,10 @@ test "fcntl" {...@@ -634,8 +640,10 @@ test "fcntl" {
634}640}
635641
636test "signalfd" {642test "signalfd" {
637 if (native_os != .linux)643 switch (native_os) {
638 return error.SkipZigTest;644 .linux, .solaris => {},
645 else => return error.SkipZigTest,
646 }
639 _ = std.os.signalfd;647 _ = std.os.signalfd;
640}648}
641649
...@@ -658,8 +666,10 @@ test "sync" {...@@ -658,8 +666,10 @@ test "sync" {
658}666}
659667
660test "fsync" {668test "fsync" {
661 if (native_os != .linux and native_os != .windows)669 switch (native_os) {
662 return error.SkipZigTest;670 .linux, .windows, .solaris => {},
671 else => return error.SkipZigTest,
672 }
663673
664 var tmp = tmpDir(.{});674 var tmp = tmpDir(.{});
665 defer tmp.cleanup();675 defer tmp.cleanup();
...@@ -754,7 +764,10 @@ test "sigaction" {...@@ -754,7 +764,10 @@ test "sigaction" {
754}764}
755765
756test "dup & dup2" {766test "dup & dup2" {
757 if (native_os != .linux) return error.SkipZigTest;767 switch (native_os) {
768 .linux, .solaris => {},
769 else => return error.SkipZigTest,
770 }
758771
759 var tmp = tmpDir(.{});772 var tmp = tmpDir(.{});
760 defer tmp.cleanup();773 defer tmp.cleanup();
lib/std/os/windows/kernel32.zig+4
...@@ -152,6 +152,8 @@ pub extern "kernel32" fn GetCommandLineW() callconv(WINAPI) LPWSTR;...@@ -152,6 +152,8 @@ pub extern "kernel32" fn GetCommandLineW() callconv(WINAPI) LPWSTR;
152152
153pub extern "kernel32" fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) callconv(WINAPI) BOOL;153pub extern "kernel32" fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) callconv(WINAPI) BOOL;
154154
155pub extern "kernel32" fn GetConsoleOutputCP() callconv(WINAPI) UINT;
156
155pub extern "kernel32" fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) callconv(WINAPI) BOOL;157pub extern "kernel32" fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) callconv(WINAPI) BOOL;
156pub extern "kernel32" fn FillConsoleOutputCharacterA(hConsoleOutput: HANDLE, cCharacter: CHAR, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfCharsWritten: *DWORD) callconv(WINAPI) BOOL;158pub extern "kernel32" fn FillConsoleOutputCharacterA(hConsoleOutput: HANDLE, cCharacter: CHAR, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfCharsWritten: *DWORD) callconv(WINAPI) BOOL;
157pub extern "kernel32" fn FillConsoleOutputCharacterW(hConsoleOutput: HANDLE, cCharacter: WCHAR, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfCharsWritten: *DWORD) callconv(WINAPI) BOOL;159pub 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,6 +288,8 @@ pub extern "kernel32" fn SetConsoleCtrlHandler(
286 Add: BOOL,288 Add: BOOL,
287) callconv(WINAPI) BOOL;289) callconv(WINAPI) BOOL;
288290
291pub extern "kernel32" fn SetConsoleOutputCP(wCodePageID: UINT) callconv(WINAPI) BOOL;
292
289pub extern "kernel32" fn SetFileCompletionNotificationModes(293pub extern "kernel32" fn SetFileCompletionNotificationModes(
290 FileHandle: HANDLE,294 FileHandle: HANDLE,
291 Flags: UCHAR,295 Flags: UCHAR,
lib/std/os/windows/ws2_32.zig+1
...@@ -1105,6 +1105,7 @@ pub const sockaddr = extern struct {...@@ -1105,6 +1105,7 @@ pub const sockaddr = extern struct {
1105 family: ADDRESS_FAMILY,1105 family: ADDRESS_FAMILY,
1106 data: [14]u8,1106 data: [14]u8,
11071107
1108 pub const SS_MAXSIZE = 128;
1108 pub const storage = std.x.os.Socket.Address.Native.Storage;1109 pub const storage = std.x.os.Socket.Address.Native.Storage;
11091110
1110 /// IPv4 socket address1111 /// IPv4 socket address
lib/std/priority_dequeue.zig+14-5
...@@ -43,13 +43,13 @@ pub fn PriorityDequeue(comptime T: type) type {...@@ -43,13 +43,13 @@ pub fn PriorityDequeue(comptime T: type) type {
4343
44 /// Insert a new element, maintaining priority.44 /// Insert a new element, maintaining priority.
45 pub fn add(self: *Self, elem: T) !void {45 pub fn add(self: *Self, elem: T) !void {
46 try ensureCapacity(self, self.len + 1);46 try self.ensureUnusedCapacity(1);
47 addUnchecked(self, elem);47 addUnchecked(self, elem);
48 }48 }
4949
50 /// Add each element in `items` to the dequeue.50 /// Add each element in `items` to the dequeue.
51 pub fn addSlice(self: *Self, items: []const T) !void {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 for (items) |e| {53 for (items) |e| {
54 self.addUnchecked(e);54 self.addUnchecked(e);
55 }55 }
...@@ -359,7 +359,11 @@ pub fn PriorityDequeue(comptime T: type) type {...@@ -359,7 +359,11 @@ pub fn PriorityDequeue(comptime T: type) type {
359 return queue;359 return queue;
360 }360 }
361361
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 var better_capacity = self.capacity();367 var better_capacity = self.capacity();
364 if (better_capacity >= new_capacity) return;368 if (better_capacity >= new_capacity) return;
365 while (true) {369 while (true) {
...@@ -369,6 +373,11 @@ pub fn PriorityDequeue(comptime T: type) type {...@@ -369,6 +373,11 @@ pub fn PriorityDequeue(comptime T: type) type {
369 self.items = try self.allocator.realloc(self.items, better_capacity);373 self.items = try self.allocator.realloc(self.items, better_capacity);
370 }374 }
371375
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 /// Reduce allocated capacity to `new_len`.381 /// Reduce allocated capacity to `new_len`.
373 pub fn shrinkAndFree(self: *Self, new_len: usize) void {382 pub fn shrinkAndFree(self: *Self, new_len: usize) void {
374 assert(new_len <= self.items.len);383 assert(new_len <= self.items.len);
...@@ -824,7 +833,7 @@ test "std.PriorityDequeue: shrinkAndFree" {...@@ -824,7 +833,7 @@ test "std.PriorityDequeue: shrinkAndFree" {
824 var queue = PDQ.init(testing.allocator, lessThanComparison);833 var queue = PDQ.init(testing.allocator, lessThanComparison);
825 defer queue.deinit();834 defer queue.deinit();
826835
827 try queue.ensureCapacity(4);836 try queue.ensureTotalCapacity(4);
828 try expect(queue.capacity() >= 4);837 try expect(queue.capacity() >= 4);
829838
830 try queue.add(1);839 try queue.add(1);
...@@ -940,7 +949,7 @@ fn fuzzTestMinMax(rng: *std.rand.Random, queue_size: usize) !void {...@@ -940,7 +949,7 @@ fn fuzzTestMinMax(rng: *std.rand.Random, queue_size: usize) !void {
940949
941fn generateRandomSlice(allocator: *std.mem.Allocator, rng: *std.rand.Random, size: usize) ![]u32 {950fn generateRandomSlice(allocator: *std.mem.Allocator, rng: *std.rand.Random, size: usize) ![]u32 {
942 var array = std.ArrayList(u32).init(allocator);951 var array = std.ArrayList(u32).init(allocator);
943 try array.ensureCapacity(size);952 try array.ensureTotalCapacity(size);
944953
945 var i: usize = 0;954 var i: usize = 0;
946 while (i < size) : (i += 1) {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,7 +42,7 @@ pub fn PriorityQueue(comptime T: type) type {
4242
43 /// Insert a new element, maintaining priority.43 /// Insert a new element, maintaining priority.
44 pub fn add(self: *Self, elem: T) !void {44 pub fn add(self: *Self, elem: T) !void {
45 try ensureCapacity(self, self.len + 1);45 try self.ensureUnusedCapacity(1);
46 addUnchecked(self, elem);46 addUnchecked(self, elem);
47 }47 }
4848
...@@ -69,7 +69,7 @@ pub fn PriorityQueue(comptime T: type) type {...@@ -69,7 +69,7 @@ pub fn PriorityQueue(comptime T: type) type {
6969
70 /// Add each element in `items` to the queue.70 /// Add each element in `items` to the queue.
71 pub fn addSlice(self: *Self, items: []const T) !void {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 for (items) |e| {73 for (items) |e| {
74 self.addUnchecked(e);74 self.addUnchecked(e);
75 }75 }
...@@ -175,7 +175,11 @@ pub fn PriorityQueue(comptime T: type) type {...@@ -175,7 +175,11 @@ pub fn PriorityQueue(comptime T: type) type {
175 return queue;175 return queue;
176 }176 }
177177
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 var better_capacity = self.capacity();183 var better_capacity = self.capacity();
180 if (better_capacity >= new_capacity) return;184 if (better_capacity >= new_capacity) return;
181 while (true) {185 while (true) {
...@@ -185,6 +189,11 @@ pub fn PriorityQueue(comptime T: type) type {...@@ -185,6 +189,11 @@ pub fn PriorityQueue(comptime T: type) type {
185 self.items = try self.allocator.realloc(self.items, better_capacity);189 self.items = try self.allocator.realloc(self.items, better_capacity);
186 }190 }
187191
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 /// Reduce allocated capacity to `new_len`.197 /// Reduce allocated capacity to `new_len`.
189 pub fn shrinkAndFree(self: *Self, new_len: usize) void {198 pub fn shrinkAndFree(self: *Self, new_len: usize) void {
190 assert(new_len <= self.items.len);199 assert(new_len <= self.items.len);
...@@ -483,7 +492,7 @@ test "std.PriorityQueue: shrinkAndFree" {...@@ -483,7 +492,7 @@ test "std.PriorityQueue: shrinkAndFree" {
483 var queue = PQ.init(testing.allocator, lessThan);492 var queue = PQ.init(testing.allocator, lessThan);
484 defer queue.deinit();493 defer queue.deinit();
485494
486 try queue.ensureCapacity(4);495 try queue.ensureTotalCapacity(4);
487 try expect(queue.capacity() >= 4);496 try expect(queue.capacity() >= 4);
488497
489 try queue.add(1);498 try queue.add(1);
lib/std/process.zig+2-1
...@@ -625,7 +625,7 @@ pub const UserInfo = struct {...@@ -625,7 +625,7 @@ pub const UserInfo = struct {
625/// POSIX function which gets a uid from username.625/// POSIX function which gets a uid from username.
626pub fn getUserInfo(name: []const u8) !UserInfo {626pub fn getUserInfo(name: []const u8) !UserInfo {
627 return switch (builtin.os.tag) {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 else => @compileError("Unsupported OS"),629 else => @compileError("Unsupported OS"),
630 };630 };
631}631}
...@@ -753,6 +753,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]...@@ -753,6 +753,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]
753 .netbsd,753 .netbsd,
754 .dragonfly,754 .dragonfly,
755 .openbsd,755 .openbsd,
756 .solaris,
756 => {757 => {
757 var paths = List.init(allocator);758 var paths = List.init(allocator);
758 errdefer {759 errdefer {
lib/std/special/c.zig+43-1174
...@@ -1,177 +1,36 @@...@@ -1,177 +1,36 @@
1// This is Zig's multi-target implementation of libc.1//! This is Zig's multi-target implementation of libc.
2// When builtin.link_libc is true, we need to export all the functions and2//! When builtin.link_libc is true, we need to export all the functions and
3// provide an entire C API.3//! provide an entire C API.
4// Otherwise, only the functions which LLVM generates calls to need to be generated,4//! Otherwise, only the functions which LLVM generates calls to need to be generated,
5// such as memcpy, memset, and some math functions.5//! such as memcpy, memset, and some math functions.
66
7const std = @import("std");7const std = @import("std");
8const builtin = std.builtin;8const builtin = @import("builtin");
9const maxInt = std.math.maxInt;9const native_os = builtin.os.tag;
10const isNan = std.math.isNan;
11const native_arch = std.Target.current.cpu.arch;
12const native_abi = std.Target.current.abi;
13const native_os = std.Target.current.os.tag;
1410
15const is_wasm = switch (native_arch) {
16 .wasm32, .wasm64 => true,
17 else => false,
18};
19const is_msvc = switch (native_abi) {
20 .msvc => true,
21 else => false,
22};
23const is_freestanding = switch (native_os) {
24 .freestanding => true,
25 else => false,
26};
27comptime {11comptime {
28 if (is_freestanding and is_wasm and builtin.link_libc) {12 // When the self-hosted compiler is further along, all the logic from c_stage1.zig will
29 @export(wasm_start, .{ .name = "_start", .linkage = .Strong });13 // be migrated to this file and then c_stage1.zig will be deleted. Until then we have a
30 }14 // simpler implementation of c.zig that only uses features already implemented in self-hosted.
31 if (builtin.link_libc) {15 if (builtin.zig_is_stage2) {
32 @export(strcmp, .{ .name = "strcmp", .linkage = .Strong });16 @export(memset, .{ .name = "memset", .linkage = .Strong });
33 @export(strncmp, .{ .name = "strncmp", .linkage = .Strong });17 @export(memcpy, .{ .name = "memcpy", .linkage = .Strong });
34 @export(strerror, .{ .name = "strerror", .linkage = .Strong });18 } else {
35 @export(strlen, .{ .name = "strlen", .linkage = .Strong });19 _ = @import("c_stage1.zig");
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
45var _fltused: c_int = 1;
46
47extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
48fn wasm_start() callconv(.C) void {
49 _ = main(0, undefined);
50}
51
52fn 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
62test "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
70fn 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
82test "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
90fn 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
103test "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
113fn 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
126test "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
136fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int {
137 return std.cstr.cmp(s1, s2);
138}
139
140fn strlen(s: [*:0]const u8) callconv(.C) usize {
141 return std.mem.len(s);
142}
143
144fn 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;
153 }20 }
154 return @as(c_int, l[0]) - @as(c_int, r[0]);
155}
156
157fn strerror(errnum: c_int) callconv(.C) [*:0]const u8 {
158 _ = errnum;
159 return "TODO strerror implementation";
160}
161
162test "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}
16822
169// Avoid dragging in the runtime safety mechanisms into this .o file,23// Avoid dragging in the runtime safety mechanisms into this .o file,
170// unless we're trying to test this file.24// unless we're trying to test this file.
171pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {25pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn {
26 @setCold(true);
172 _ = error_return_trace;27 _ = error_return_trace;
28 if (builtin.zig_is_stage2) {
29 while (true) {
30 @breakpoint();
31 }
32 }
173 if (builtin.is_test) {33 if (builtin.is_test) {
174 @setCold(true);
175 std.debug.panic("{s}", .{msg});34 std.debug.panic("{s}", .{msg});
176 }35 }
177 if (native_os != .freestanding and native_os != .other) {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,1028 +39,38 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn
180 while (true) {}39 while (true) {}
181}40}
18241
183export fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8 {42fn memset(dest: ?[*]u8, c: u8, len: 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
193export 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
199export 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
209export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 {
210 @setRuntimeSafety(false);43 @setRuntimeSafety(false);
21144
212 if (@ptrToInt(dest) < @ptrToInt(src)) {45 if (len != 0) {
213 var index: usize = 0;46 var d = dest.?;
214 while (index != n) : (index += 1) {47 var n = len;
215 dest.?[index] = src.?[index];48 while (true) {
216 }49 d.* = c;
217 } else {50 n -= 1;
218 var index = n;51 if (n == 0) break;
219 while (index != 0) {52 d += 1;
220 index -= 1;
221 dest.?[index] = src.?[index];
222 }53 }
223 }54 }
22455
225 return dest;56 return dest;
226}57}
22758
228export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) c_int {59fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, len: usize) callconv(.C) ?[*]u8 {
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
242test "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
253export 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
266test "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
277comptime {
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.
286fn 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
652const math = std.math;
653
654export fn fmodf(x: f32, y: f32) f32 {
655 return generic_fmod(f32, x, y);
656}
657export 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
663export fn floorf(x: f32) f32 {
664 return math.floor(x);
665}
666
667export fn ceilf(x: f32) f32 {
668 return math.ceil(x);
669}
670
671export fn floor(x: f64) f64 {
672 return math.floor(x);
673}
674
675export fn ceil(x: f64) f64 {
676 return math.ceil(x);
677}
678
679export fn fma(a: f64, b: f64, c: f64) f64 {
680 return math.fma(f64, a, b, c);
681}
682
683export fn fmaf(a: f32, b: f32, c: f32) f32 {
684 return math.fma(f32, a, b, c);
685}
686
687export fn sin(a: f64) f64 {
688 return math.sin(a);
689}
690
691export fn sinf(a: f32) f32 {
692 return math.sin(a);
693}
694
695export fn cos(a: f64) f64 {
696 return math.cos(a);
697}
698
699export fn cosf(a: f32) f32 {
700 return math.cos(a);
701}
702
703export 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
708export 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
713export fn exp(a: f64) f64 {
714 return math.exp(a);
715}
716
717export fn expf(a: f32) f32 {
718 return math.exp(a);
719}
720
721export fn exp2(a: f64) f64 {
722 return math.exp2(a);
723}
724
725export fn exp2f(a: f32) f32 {
726 return math.exp2(a);
727}
728
729export fn log(a: f64) f64 {
730 return math.ln(a);
731}
732
733export fn logf(a: f32) f32 {
734 return math.ln(a);
735}
736
737export fn log2(a: f64) f64 {
738 return math.log2(a);
739}
740
741export fn log2f(a: f32) f32 {
742 return math.log2(a);
743}
744
745export fn log10(a: f64) f64 {
746 return math.log10(a);
747}
748
749export fn log10f(a: f32) f32 {
750 return math.log10(a);
751}
752
753export fn fabs(a: f64) f64 {
754 return math.fabs(a);
755}
756
757export fn fabsf(a: f32) f32 {
758 return math.fabs(a);
759}
760
761export fn trunc(a: f64) f64 {
762 return math.trunc(a);
763}
764
765export fn truncf(a: f32) f32 {
766 return math.trunc(a);
767}
768
769export fn round(a: f64) f64 {
770 return math.round(a);
771}
772
773export fn roundf(a: f32) f32 {
774 return math.round(a);
775}
776
777fn generic_fmod(comptime T: type, x: T, y: T) T {
778 @setRuntimeSafety(false);60 @setRuntimeSafety(false);
77961
780 const bits = @typeInfo(T).Float.bits;62 if (len != 0) {
781 const uint = std.meta.Int(.unsigned, bits);63 var d = dest.?;
782 const log2uint = math.Log2Int(uint);64 var s = src.?;
783 const digits = if (T == f32) 23 else 52;65 var n = len;
784 const exp_bits = if (T == f32) 9 else 12;66 while (true) {
785 const bits_minus_1 = bits - 1;67 d.* = s.*;
786 const mask = if (T == f32) 0xff else 0x7ff;68 n -= 1;
787 var ux = @bitCast(uint, x);69 if (n == 0) break;
788 var uy = @bitCast(uint, y);70 d += 1;
789 var ex = @intCast(i32, (ux >> digits) & mask);71 s += 1;
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
863test "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
884fn 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
892export fn fminf(x: f32, y: f32) callconv(.C) f32 {
893 return generic_fmin(f32, x, y);
894}
895
896export fn fmin(x: f64, y: f64) callconv(.C) f64 {
897 return generic_fmin(f64, x, y);
898}
899
900test "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
913fn 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
921export fn fmaxf(x: f32, y: f32) callconv(.C) f32 {
922 return generic_fmax(f32, x, y);
923}
924
925export fn fmax(x: f64, y: f64) callconv(.C) f64 {
926 return generic_fmax(f64, x, y);
927}
928
929test "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.
945export 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;
981 }72 }
982 m -= @intCast(i32, i) - 1;
983 ix0 |= ix1 >> @intCast(u5, 32 - i);
984 ix1 <<= @intCast(u5, i);
985 }73 }
98674
987 // unbias exponent75 return dest;
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
1075test "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
1096test "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
1104export 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
1180test "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
1201test "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))));
1207}76}
lib/std/special/c_stage1.zig created+1187
...@@ -0,0 +1,1187 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const maxInt = std.math.maxInt;
4const isNan = std.math.isNan;
5const native_arch = builtin.cpu.arch;
6const native_abi = builtin.abi;
7const native_os = builtin.os.tag;
8
9const is_wasm = switch (native_arch) {
10 .wasm32, .wasm64 => true,
11 else => false,
12};
13const is_msvc = switch (native_abi) {
14 .msvc => true,
15 else => false,
16};
17const is_freestanding = switch (native_os) {
18 .freestanding => true,
19 else => false,
20};
21comptime {
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
39var _fltused: c_int = 1;
40
41extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
42fn wasm_start() callconv(.C) void {
43 _ = main(0, undefined);
44}
45
46fn 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
56test "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
64fn 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
76test "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
84fn 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
97test "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
107fn 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
120test "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
130fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int {
131 return std.cstr.cmp(s1, s2);
132}
133
134fn strlen(s: [*:0]const u8) callconv(.C) usize {
135 return std.mem.len(s);
136}
137
138fn 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
151fn strerror(errnum: c_int) callconv(.C) [*:0]const u8 {
152 _ = errnum;
153 return "TODO strerror implementation";
154}
155
156test "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
163export 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
173export 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
179export 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
189export 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
208export 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
222test "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
233export 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
246test "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
257comptime {
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.
266fn 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
632const math = std.math;
633
634export fn fmodf(x: f32, y: f32) f32 {
635 return generic_fmod(f32, x, y);
636}
637export 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
643export fn floorf(x: f32) f32 {
644 return math.floor(x);
645}
646
647export fn ceilf(x: f32) f32 {
648 return math.ceil(x);
649}
650
651export fn floor(x: f64) f64 {
652 return math.floor(x);
653}
654
655export fn ceil(x: f64) f64 {
656 return math.ceil(x);
657}
658
659export fn fma(a: f64, b: f64, c: f64) f64 {
660 return math.fma(f64, a, b, c);
661}
662
663export fn fmaf(a: f32, b: f32, c: f32) f32 {
664 return math.fma(f32, a, b, c);
665}
666
667export fn sin(a: f64) f64 {
668 return math.sin(a);
669}
670
671export fn sinf(a: f32) f32 {
672 return math.sin(a);
673}
674
675export fn cos(a: f64) f64 {
676 return math.cos(a);
677}
678
679export fn cosf(a: f32) f32 {
680 return math.cos(a);
681}
682
683export 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
688export 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
693export fn exp(a: f64) f64 {
694 return math.exp(a);
695}
696
697export fn expf(a: f32) f32 {
698 return math.exp(a);
699}
700
701export fn exp2(a: f64) f64 {
702 return math.exp2(a);
703}
704
705export fn exp2f(a: f32) f32 {
706 return math.exp2(a);
707}
708
709export fn log(a: f64) f64 {
710 return math.ln(a);
711}
712
713export fn logf(a: f32) f32 {
714 return math.ln(a);
715}
716
717export fn log2(a: f64) f64 {
718 return math.log2(a);
719}
720
721export fn log2f(a: f32) f32 {
722 return math.log2(a);
723}
724
725export fn log10(a: f64) f64 {
726 return math.log10(a);
727}
728
729export fn log10f(a: f32) f32 {
730 return math.log10(a);
731}
732
733export fn fabs(a: f64) f64 {
734 return math.fabs(a);
735}
736
737export fn fabsf(a: f32) f32 {
738 return math.fabs(a);
739}
740
741export fn trunc(a: f64) f64 {
742 return math.trunc(a);
743}
744
745export fn truncf(a: f32) f32 {
746 return math.trunc(a);
747}
748
749export fn round(a: f64) f64 {
750 return math.round(a);
751}
752
753export fn roundf(a: f32) f32 {
754 return math.round(a);
755}
756
757fn 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
843test "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
864fn 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
872export fn fminf(x: f32, y: f32) callconv(.C) f32 {
873 return generic_fmin(f32, x, y);
874}
875
876export fn fmin(x: f64, y: f64) callconv(.C) f64 {
877 return generic_fmin(f64, x, y);
878}
879
880test "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
893fn 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
901export fn fmaxf(x: f32, y: f32) callconv(.C) f32 {
902 return generic_fmax(f32, x, y);
903}
904
905export fn fmax(x: f64, y: f64) callconv(.C) f64 {
906 return generic_fmax(f64, x, y);
907}
908
909test "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.
925export 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
1055test "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
1076test "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
1084export 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
1160test "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
1181test "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,171 +1,24 @@
1const std = @import("std");1const std = @import("std");
2const builtin = std.builtin;2const builtin = @import("builtin");
3const is_test = builtin.is_test;3const is_test = builtin.is_test;
4const os_tag = std.Target.current.os.tag;4const os_tag = std.Target.current.os.tag;
5const arch = std.Target.current.cpu.arch;5const arch = builtin.stage2_arch;
6const abi = std.Target.current.abi;6const abi = std.Target.current.abi;
77
8const is_gnu = abi.isGnu();8const is_gnu = abi.isGnu();
9const is_mingw = os_tag == .windows and is_gnu;9const is_mingw = os_tag == .windows and is_gnu;
1010
11comptime {11const linkage = if (is_test)
12 const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak;12 std.builtin.GlobalLinkage.Internal
13 const strong_linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong;13else
1414 std.builtin.GlobalLinkage.Weak;
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 }
2815
29 // __clear_cache manages its own logic about whether to be exported or not.16const strong_linkage = if (is_test)
30 _ = @import("compiler_rt/clear_cache.zig").clear_cache;17 std.builtin.GlobalLinkage.Internal
3118else
32 const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2;19 std.builtin.GlobalLinkage.Strong;
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 });
16820
21comptime {
169 const __extenddftf2 = @import("compiler_rt/extendXfYf2.zig").__extenddftf2;22 const __extenddftf2 = @import("compiler_rt/extendXfYf2.zig").__extenddftf2;
170 @export(__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage });23 @export(__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage });
171 const __extendsftf2 = @import("compiler_rt/extendXfYf2.zig").__extendsftf2;24 const __extendsftf2 = @import("compiler_rt/extendXfYf2.zig").__extendsftf2;
...@@ -175,446 +28,611 @@ comptime {...@@ -175,446 +28,611 @@ comptime {
175 const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2;28 const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2;
176 @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage });29 @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage });
17730
178 const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2;31 if (!builtin.zig_is_stage2) {
179 @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage });32 switch (arch) {
180 const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2;33 .i386,
181 @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage });34 .x86_64,
182 const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2;35 => {
183 @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage });36 const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;
184 const __trunctfdf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfdf2;37 @export(zig_probe_stack, .{
185 @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage });38 .name = "__zig_probe_stack",
186 const __trunctfsf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfsf2;39 .linkage = linkage,
187 @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage });40 });
18841 },
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 }
28342
284 if ((arch.isARM() or arch.isThumb()) and !is_test) {43 else => {},
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 });
329 }44 }
33045
331 const __aeabi_f2d = @import("compiler_rt/extendXfYf2.zig").__aeabi_f2d;46 // __clear_cache manages its own logic about whether to be exported or not.
332 @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage });47 _ = @import("compiler_rt/clear_cache.zig").clear_cache;
333 const __aeabi_i2d = @import("compiler_rt/floatsiXf.zig").__aeabi_i2d;48
334 @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage });49 const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2;
335 const __aeabi_l2d = @import("compiler_rt/floatdidf.zig").__aeabi_l2d;50 @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage });
336 @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage });51 const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2;
337 const __aeabi_l2f = @import("compiler_rt/floatXisf.zig").__aeabi_l2f;52 @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage });
338 @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage });53 const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2;
339 const __aeabi_ui2d = @import("compiler_rt/floatunsidf.zig").__aeabi_ui2d;54 @export(__letf2, .{ .name = "__letf2", .linkage = linkage });
340 @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage });55
341 const __aeabi_ul2d = @import("compiler_rt/floatundidf.zig").__aeabi_ul2d;56 const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2;
342 @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage });57 @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage });
343 const __aeabi_ui2f = @import("compiler_rt/floatunsisf.zig").__aeabi_ui2f;58 const __gedf2 = @import("compiler_rt/compareXf2.zig").__gedf2;
344 @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage });59 @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage });
345 const __aeabi_ul2f = @import("compiler_rt/floatundisf.zig").__aeabi_ul2f;60 const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2;
346 @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage });61 @export(__getf2, .{ .name = "__getf2", .linkage = linkage });
34762
348 const __aeabi_fneg = @import("compiler_rt/negXf2.zig").__aeabi_fneg;63 if (!is_test) {
349 @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage });64 @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage });
350 const __aeabi_dneg = @import("compiler_rt/negXf2.zig").__aeabi_dneg;65 @export(__ledf2, .{ .name = "__cmpdf2", .linkage = linkage });
351 @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage });66 @export(__letf2, .{ .name = "__cmptf2", .linkage = linkage });
35267
353 const __aeabi_fmul = @import("compiler_rt/mulXf3.zig").__aeabi_fmul;68 const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2;
354 @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage });69 @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage });
355 const __aeabi_dmul = @import("compiler_rt/mulXf3.zig").__aeabi_dmul;70 const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2;
356 @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage });71 @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage });
35772 @export(__letf2, .{ .name = "__eqtf2", .linkage = linkage });
358 const __aeabi_d2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2h;73
359 @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage });74 const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2;
36075 @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage });
361 const __aeabi_f2ulz = @import("compiler_rt/fixunssfdi.zig").__aeabi_f2ulz;76 const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2;
362 @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage });77 @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage });
363 const __aeabi_d2ulz = @import("compiler_rt/fixunsdfdi.zig").__aeabi_d2ulz;78 @export(__letf2, .{ .name = "__lttf2", .linkage = linkage });
364 @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage });79
36580 const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2;
366 const __aeabi_f2lz = @import("compiler_rt/fixsfdi.zig").__aeabi_f2lz;81 @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage });
367 @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage });82 const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2;
368 const __aeabi_d2lz = @import("compiler_rt/fixdfdi.zig").__aeabi_d2lz;83 @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage });
369 @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage });84 @export(__letf2, .{ .name = "__netf2", .linkage = linkage });
37085
371 const __aeabi_d2uiz = @import("compiler_rt/fixunsdfsi.zig").__aeabi_d2uiz;86 const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2;
372 @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage });87 @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage });
37388 const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2;
374 const __aeabi_h2f = @import("compiler_rt/extendXfYf2.zig").__aeabi_h2f;89 @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage });
375 @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage });90 @export(__getf2, .{ .name = "__gttf2", .linkage = linkage });
376 const __aeabi_f2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_f2h;91
377 @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage });92 @export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{
37893 .name = "__gnu_h2f_ieee",
379 const __aeabi_i2f = @import("compiler_rt/floatsiXf.zig").__aeabi_i2f;94 .linkage = linkage,
380 @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage });95 });
381 const __aeabi_d2f = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2f;96 @export(@import("compiler_rt/truncXfYf2.zig").__truncsfhf2, .{
382 @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage });97 .name = "__gnu_f2h_ieee",
38398 .linkage = linkage,
384 const __aeabi_fadd = @import("compiler_rt/addXf3.zig").__aeabi_fadd;99 });
385 @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage });100 }
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 }
439101
440 if (arch == .i386 and abi == .msvc) {102 const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;
441 // Don't let LLVM apply the stdcall name mangling on those MSVC builtins103 @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage });
442 const _alldiv = @import("compiler_rt/aulldiv.zig")._alldiv;104 const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2;
443 @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage });105 @export(__unorddf2, .{ .name = "__unorddf2", .linkage = linkage });
444 const _aulldiv = @import("compiler_rt/aulldiv.zig")._aulldiv;106 const __unordtf2 = @import("compiler_rt/compareXf2.zig").__unordtf2;
445 @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage });107 @export(__unordtf2, .{ .name = "__unordtf2", .linkage = linkage });
446 const _allrem = @import("compiler_rt/aullrem.zig")._allrem;108
447 @export(_allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage });109 const __addsf3 = @import("compiler_rt/addXf3.zig").__addsf3;
448 const _aullrem = @import("compiler_rt/aullrem.zig")._aullrem;110 @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage });
449 @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });111 const __adddf3 = @import("compiler_rt/addXf3.zig").__adddf3;
450 }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 });
451189
452 if (arch.isSPARC()) {190 const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2;
453 // SPARC systems use a different naming scheme191 @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage });
454 const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add;192 const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2;
455 @export(_Qp_add, .{ .name = "_Qp_add", .linkage = linkage });193 @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage });
456 const _Qp_div = @import("compiler_rt/sparc.zig")._Qp_div;194 const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2;
457 @export(_Qp_div, .{ .name = "_Qp_div", .linkage = linkage });195 @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage });
458 const _Qp_mul = @import("compiler_rt/sparc.zig")._Qp_mul;196 const __trunctfdf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfdf2;
459 @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage });197 @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage });
460 const _Qp_sub = @import("compiler_rt/sparc.zig")._Qp_sub;198 const __trunctfsf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfsf2;
461 @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage });199 @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage });
462200
463 const _Qp_cmp = @import("compiler_rt/sparc.zig")._Qp_cmp;201 const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2;
464 @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage });202 @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage });
465 const _Qp_feq = @import("compiler_rt/sparc.zig")._Qp_feq;203
466 @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage });204 const __extendsfdf2 = @import("compiler_rt/extendXfYf2.zig").__extendsfdf2;
467 const _Qp_fne = @import("compiler_rt/sparc.zig")._Qp_fne;205 @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage });
468 @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage });206
469 const _Qp_flt = @import("compiler_rt/sparc.zig")._Qp_flt;207 const __fixunssfsi = @import("compiler_rt/fixunssfsi.zig").__fixunssfsi;
470 @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage });208 @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage });
471 const _Qp_fle = @import("compiler_rt/sparc.zig")._Qp_fle;209 const __fixunssfdi = @import("compiler_rt/fixunssfdi.zig").__fixunssfdi;
472 @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage });210 @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage });
473 const _Qp_fgt = @import("compiler_rt/sparc.zig")._Qp_fgt;211 const __fixunssfti = @import("compiler_rt/fixunssfti.zig").__fixunssfti;
474 @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage });212 @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage });
475 const _Qp_fge = @import("compiler_rt/sparc.zig")._Qp_fge;213
476 @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage });214 const __fixunsdfsi = @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi;
477215 @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage });
478 const _Qp_itoq = @import("compiler_rt/sparc.zig")._Qp_itoq;216 const __fixunsdfdi = @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi;
479 @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage });217 @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage });
480 const _Qp_uitoq = @import("compiler_rt/sparc.zig")._Qp_uitoq;218 const __fixunsdfti = @import("compiler_rt/fixunsdfti.zig").__fixunsdfti;
481 @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage });219 @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage });
482 const _Qp_xtoq = @import("compiler_rt/sparc.zig")._Qp_xtoq;220
483 @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage });221 const __fixunstfsi = @import("compiler_rt/fixunstfsi.zig").__fixunstfsi;
484 const _Qp_uxtoq = @import("compiler_rt/sparc.zig")._Qp_uxtoq;222 @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage });
485 @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage });223 const __fixunstfdi = @import("compiler_rt/fixunstfdi.zig").__fixunstfdi;
486 const _Qp_stoq = @import("compiler_rt/sparc.zig")._Qp_stoq;224 @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage });
487 @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage });225 const __fixunstfti = @import("compiler_rt/fixunstfti.zig").__fixunstfti;
488 const _Qp_dtoq = @import("compiler_rt/sparc.zig")._Qp_dtoq;226 @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage });
489 @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage });227
490 const _Qp_qtoi = @import("compiler_rt/sparc.zig")._Qp_qtoi;228 const __fixdfdi = @import("compiler_rt/fixdfdi.zig").__fixdfdi;
491 @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage });229 @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage });
492 const _Qp_qtoui = @import("compiler_rt/sparc.zig")._Qp_qtoui;230 const __fixdfsi = @import("compiler_rt/fixdfsi.zig").__fixdfsi;
493 @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage });231 @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage });
494 const _Qp_qtox = @import("compiler_rt/sparc.zig")._Qp_qtox;232 const __fixdfti = @import("compiler_rt/fixdfti.zig").__fixdfti;
495 @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage });233 @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage });
496 const _Qp_qtoux = @import("compiler_rt/sparc.zig")._Qp_qtoux;234 const __fixsfdi = @import("compiler_rt/fixsfdi.zig").__fixsfdi;
497 @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage });235 @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage });
498 const _Qp_qtos = @import("compiler_rt/sparc.zig")._Qp_qtos;236 const __fixsfsi = @import("compiler_rt/fixsfsi.zig").__fixsfsi;
499 @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage });237 @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage });
500 const _Qp_qtod = @import("compiler_rt/sparc.zig")._Qp_qtod;238 const __fixsfti = @import("compiler_rt/fixsfti.zig").__fixsfti;
501 @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });239 @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage });
502 }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 }
503295
504 if ((arch == .powerpc or arch.isPPC64()) and !is_test) {296 if ((arch.isARM() or arch.isThumb()) and !is_test) {
505 @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage });297 const __aeabi_unwind_cpp_pr0 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0;
506 @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage });298 @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage });
507 @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage });299 const __aeabi_unwind_cpp_pr1 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1;
508 @export(__divtf3, .{ .name = "__divkf3", .linkage = linkage });300 @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage });
509 @export(__extendsftf2, .{ .name = "__extendsfkf2", .linkage = linkage });301 const __aeabi_unwind_cpp_pr2 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr2;
510 @export(__extenddftf2, .{ .name = "__extenddfkf2", .linkage = linkage });302 @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage });
511 @export(__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = linkage });303
512 @export(__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = linkage });304 @export(__muldi3, .{ .name = "__aeabi_lmul", .linkage = linkage });
513 @export(__fixtfdi, .{ .name = "__fixkfdi", .linkage = linkage });305
514 @export(__fixtfsi, .{ .name = "__fixkfsi", .linkage = linkage });306 const __aeabi_ldivmod = @import("compiler_rt/arm.zig").__aeabi_ldivmod;
515 @export(__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = linkage });307 @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage });
516 @export(__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = linkage });308 const __aeabi_uldivmod = @import("compiler_rt/arm.zig").__aeabi_uldivmod;
517 @export(__floatsitf, .{ .name = "__floatsikf", .linkage = linkage });309 @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage });
518 @export(__floatditf, .{ .name = "__floatdikf", .linkage = linkage });310
519 @export(__floatunditf, .{ .name = "__floatundikf", .linkage = linkage });311 @export(__divsi3, .{ .name = "__aeabi_idiv", .linkage = linkage });
520 @export(__floatunsitf, .{ .name = "__floatunsikf", .linkage = linkage });312 const __aeabi_idivmod = @import("compiler_rt/arm.zig").__aeabi_idivmod;
521313 @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage });
522 @export(__letf2, .{ .name = "__eqkf2", .linkage = linkage });314 @export(__udivsi3, .{ .name = "__aeabi_uidiv", .linkage = linkage });
523 @export(__letf2, .{ .name = "__nekf2", .linkage = linkage });315 const __aeabi_uidivmod = @import("compiler_rt/arm.zig").__aeabi_uidivmod;
524 @export(__getf2, .{ .name = "__gekf2", .linkage = linkage });316 @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage });
525 @export(__letf2, .{ .name = "__ltkf2", .linkage = linkage });317
526 @export(__letf2, .{ .name = "__lekf2", .linkage = linkage });318 const __aeabi_memcpy = @import("compiler_rt/arm.zig").__aeabi_memcpy;
527 @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage });319 @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage });
528 @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage });320 @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy4", .linkage = linkage });
529 }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 }
530451
531 if (builtin.os.tag == .windows) {452 if (arch == .i386 and abi == .msvc) {
532 // Default stack-probe functions emitted by LLVM453 // Don't let LLVM apply the stdcall name mangling on those MSVC builtins
533 if (is_mingw) {454 const _alldiv = @import("compiler_rt/aulldiv.zig")._alldiv;
534 const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk;455 @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage });
535 @export(_chkstk, .{ .name = "_alloca", .linkage = strong_linkage });456 const _aulldiv = @import("compiler_rt/aulldiv.zig")._aulldiv;
536 const ___chkstk_ms = @import("compiler_rt/stack_probe.zig").___chkstk_ms;457 @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage });
537 @export(___chkstk_ms, .{ .name = "___chkstk_ms", .linkage = strong_linkage });458 const _allrem = @import("compiler_rt/aullrem.zig")._allrem;
538 } else if (!builtin.link_libc) {459 @export(_allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage });
539 // This symbols are otherwise exported by MSVCRT.lib460 const _aullrem = @import("compiler_rt/aullrem.zig")._aullrem;
540 const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk;461 @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });
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 });
544 }462 }
545463
546 switch (arch) {464 if (arch.isSPARC()) {
547 .i386 => {465 // SPARC systems use a different naming scheme
548 const __divti3 = @import("compiler_rt/divti3.zig").__divti3;466 const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add;
549 @export(__divti3, .{ .name = "__divti3", .linkage = linkage });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 const __modti3 = @import("compiler_rt/modti3.zig").__modti3;596 const __modti3 = @import("compiler_rt/modti3.zig").__modti3;
551 @export(__modti3, .{ .name = "__modti3", .linkage = linkage });597 @export(__modti3, .{ .name = "__modti3", .linkage = linkage });
552 const __multi3 = @import("compiler_rt/multi3.zig").__multi3;598 const __udivti3_windows = @import("compiler_rt/udivti3.zig").__udivti3;
553 @export(__multi3, .{ .name = "__multi3", .linkage = linkage });599 @export(__udivti3_windows, .{ .name = "__udivti3", .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 });
558 const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3;600 const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3;
559 @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage });601 @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage });
560 },602 }
561 .x86_64 => {603 } else {
562 // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI604 const __divti3 = @import("compiler_rt/divti3.zig").__divti3;
563 // that LLVM expects compiler-rt to have.605 @export(__divti3, .{ .name = "__divti3", .linkage = linkage });
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 });
584 const __modti3 = @import("compiler_rt/modti3.zig").__modti3;606 const __modti3 = @import("compiler_rt/modti3.zig").__modti3;
585 @export(__modti3, .{ .name = "__modti3", .linkage = linkage });607 @export(__modti3, .{ .name = "__modti3", .linkage = linkage });
586 const __udivti3_windows = @import("compiler_rt/udivti3.zig").__udivti3;608 const __multi3 = @import("compiler_rt/multi3.zig").__multi3;
587 @export(__udivti3_windows, .{ .name = "__udivti3", .linkage = linkage });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 const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3;614 const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3;
589 @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage });615 @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage });
590 }616 }
591 } else {617 const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4;
592 const __divti3 = @import("compiler_rt/divti3.zig").__divti3;618 @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage });
593 @export(__divti3, .{ .name = "__divti3", .linkage = linkage });619 const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4;
594 const __modti3 = @import("compiler_rt/modti3.zig").__modti3;620 @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage });
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 });
609621
610 _ = @import("compiler_rt/atomics.zig");622 _ = @import("compiler_rt/atomics.zig");
623 }
611}624}
612625
613// Avoid dragging in the runtime safety mechanisms into this .o file,626// Avoid dragging in the runtime safety mechanisms into this .o file,
614// unless we're trying to test this file.627// unless we're trying to test this file.
615pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {628pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn {
616 _ = error_return_trace;629 _ = error_return_trace;
617 @setCold(true);630 @setCold(true);
631 if (builtin.zig_is_stage2) {
632 while (true) {
633 @breakpoint();
634 }
635 }
618 if (is_test) {636 if (is_test) {
619 std.debug.panic("{s}", .{msg});637 std.debug.panic("{s}", .{msg});
620 } else {638 } else {
lib/std/special/compiler_rt/extendXfYf2.zig+6-6
...@@ -3,23 +3,23 @@ const builtin = @import("builtin");...@@ -3,23 +3,23 @@ const builtin = @import("builtin");
3const is_test = builtin.is_test;3const is_test = builtin.is_test;
44
5pub fn __extendsfdf2(a: f32) callconv(.C) f64 {5pub 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}
88
9pub fn __extenddftf2(a: f64) callconv(.C) f128 {9pub 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}
1212
13pub fn __extendsftf2(a: f32) callconv(.C) f128 {13pub 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}
1616
17pub fn __extendhfsf2(a: u16) callconv(.C) f32 {17pub 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}
2020
21pub fn __extendhftf2(a: u16) callconv(.C) f128 {21pub 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}
2424
25pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 {25pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 {
...@@ -34,7 +34,7 @@ pub fn __aeabi_f2d(arg: f32) callconv(.AAPCS) f64 {...@@ -34,7 +34,7 @@ pub fn __aeabi_f2d(arg: f32) callconv(.AAPCS) f64 {
3434
35const CHAR_BIT = 8;35const CHAR_BIT = 8;
3636
37fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits)) dst_t {37inline fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits)) dst_t {
38 @setRuntimeSafety(builtin.is_test);38 @setRuntimeSafety(builtin.is_test);
3939
40 const src_rep_t = std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits);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,19 +53,6 @@ pub fn zig_probe_stack() callconv(.Naked) void {
53 },53 },
54 else => {},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 }
6956
70 unreachable;57 unreachable;
71}58}
...@@ -118,6 +105,21 @@ fn win_probe_stack_only() void {...@@ -118,6 +105,21 @@ fn win_probe_stack_only() void {
118 },105 },
119 else => {},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 }
121123
122 unreachable;124 unreachable;
123}125}
...@@ -199,7 +201,9 @@ pub fn _chkstk() callconv(.Naked) void {...@@ -199,7 +201,9 @@ pub fn _chkstk() callconv(.Naked) void {
199}201}
200pub fn __chkstk() callconv(.Naked) void {202pub fn __chkstk() callconv(.Naked) void {
201 @setRuntimeSafety(false);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 .i386 => @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{}),207 .i386 => @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{}),
204 .x86_64 => @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{}),208 .x86_64 => @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{}),
205 else => unreachable,209 else => unreachable,
lib/std/special/test_runner.zig+16-2
...@@ -13,6 +13,12 @@ fn processArgs() void {...@@ -13,6 +13,12 @@ fn processArgs() void {
13 const args = std.process.argsAlloc(&args_allocator.allocator) catch {13 const args = std.process.argsAlloc(&args_allocator.allocator) catch {
14 @panic("Too many bytes passed over the CLI to the test runner");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 std.testing.zig_exe_path = args[1];22 std.testing.zig_exe_path = args[1];
17}23}
1824
...@@ -56,7 +62,7 @@ pub fn main() void {...@@ -56,7 +62,7 @@ pub fn main() void {
56 .evented => blk: {62 .evented => blk: {
57 if (async_frame_buffer.len < size) {63 if (async_frame_buffer.len < size) {
58 std.heap.page_allocator.free(async_frame_buffer);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 const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func);67 const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func);
62 break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{});68 break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{});
...@@ -123,8 +129,16 @@ pub fn log(...@@ -123,8 +129,16 @@ pub fn log(
123}129}
124130
125pub fn main2() anyerror!void {131pub fn main2() anyerror!void {
132 var bad = false;
126 // Simpler main(), exercising fewer language features, so that stage2 can handle it.133 // Simpler main(), exercising fewer language features, so that stage2 can handle it.
127 for (builtin.test_functions) |test_fn| {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,6 +28,8 @@ comptime {
28 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {28 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
29 @export(main2, .{ .name = "main" });29 @export(main2, .{ .name = "main" });
30 }30 }
31 } else if (builtin.os.tag == .windows) {
32 @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
31 } else {33 } else {
32 if (!@hasDecl(root, "_start")) {34 if (!@hasDecl(root, "_start")) {
33 @export(_start2, .{ .name = "_start" });35 @export(_start2, .{ .name = "_start" });
...@@ -87,6 +89,16 @@ fn main2() callconv(.C) c_int {...@@ -87,6 +89,16 @@ fn main2() callconv(.C) c_int {
87}89}
8890
89fn _start2() callconv(.Naked) noreturn {91fn _start2() callconv(.Naked) noreturn {
92 callMain2();
93}
94
95fn callMain2() noreturn {
96 @setAlignStack(16);
97 root.main();
98 exit2(0);
99}
100
101fn wWinMainCRTStartup2() callconv(.C) noreturn {
90 root.main();102 root.main();
91 exit2(0);103 exit2(0);
92}104}
...@@ -143,11 +155,16 @@ fn exit2(code: usize) noreturn {...@@ -143,11 +155,16 @@ fn exit2(code: usize) noreturn {
143 },155 },
144 else => @compileError("TODO"),156 else => @compileError("TODO"),
145 },157 },
158 .windows => {
159 ExitProcess(@truncate(u32, code));
160 },
146 else => @compileError("TODO"),161 else => @compileError("TODO"),
147 }162 }
148 unreachable;163 unreachable;
149}164}
150165
166extern "kernel32" fn ExitProcess(exit_code: u32) callconv(.C) noreturn;
167
151////////////////////////////////////////////////////////////////////////////////168////////////////////////////////////////////////////////////////////////////////
152169
153fn _DllMainCRTStartup(170fn _DllMainCRTStartup(
lib/std/target.zig+9-3
...@@ -235,7 +235,6 @@ pub const Target = struct {...@@ -235,7 +235,6 @@ pub const Target = struct {
235 .fuchsia,235 .fuchsia,
236 .kfreebsd,236 .kfreebsd,
237 .lv2,237 .lv2,
238 .solaris,
239 .zos,238 .zos,
240 .haiku,239 .haiku,
241 .minix,240 .minix,
...@@ -310,6 +309,12 @@ pub const Target = struct {...@@ -310,6 +309,12 @@ pub const Target = struct {
310 .max = .{ .major = 6, .minor = 0 },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 },
313318
314 .linux => return .{319 .linux => return .{
315 .linux = .{320 .linux = .{
...@@ -353,6 +358,7 @@ pub const Target = struct {...@@ -353,6 +358,7 @@ pub const Target = struct {
353 .netbsd,358 .netbsd,
354 .openbsd,359 .openbsd,
355 .dragonfly,360 .dragonfly,
361 .solaris,
356 => return TaggedVersionRange{ .semver = self.version_range.semver },362 => return TaggedVersionRange{ .semver = self.version_range.semver },
357363
358 else => return .none,364 else => return .none,
...@@ -385,6 +391,7 @@ pub const Target = struct {...@@ -385,6 +391,7 @@ pub const Target = struct {
385 .dragonfly,391 .dragonfly,
386 .openbsd,392 .openbsd,
387 .haiku,393 .haiku,
394 .solaris,
388 => true,395 => true,
389396
390 .linux,397 .linux,
...@@ -395,7 +402,6 @@ pub const Target = struct {...@@ -395,7 +402,6 @@ pub const Target = struct {
395 .fuchsia,402 .fuchsia,
396 .kfreebsd,403 .kfreebsd,
397 .lv2,404 .lv2,
398 .solaris,
399 .zos,405 .zos,
400 .minix,406 .minix,
401 .rtems,407 .rtems,
...@@ -1523,6 +1529,7 @@ pub const Target = struct {...@@ -1523,6 +1529,7 @@ pub const Target = struct {
1523 .netbsd => return copy(&result, "/libexec/ld.elf_so"),1529 .netbsd => return copy(&result, "/libexec/ld.elf_so"),
1524 .openbsd => return copy(&result, "/usr/libexec/ld.so"),1530 .openbsd => return copy(&result, "/usr/libexec/ld.so"),
1525 .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"),1531 .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"),
1532 .solaris => return copy(&result, "/lib/64/ld.so.1"),
1526 .linux => switch (self.cpu.arch) {1533 .linux => switch (self.cpu.arch) {
1527 .i386,1534 .i386,
1528 .sparc,1535 .sparc,
...@@ -1642,7 +1649,6 @@ pub const Target = struct {...@@ -1642,7 +1649,6 @@ pub const Target = struct {
1642 .fuchsia,1649 .fuchsia,
1643 .kfreebsd,1650 .kfreebsd,
1644 .lv2,1651 .lv2,
1645 .solaris,
1646 .zos,1652 .zos,
1647 .minix,1653 .minix,
1648 .rtems,1654 .rtems,
lib/std/unicode.zig+22-4
...@@ -553,8 +553,9 @@ fn testDecode(bytes: []const u8) !u21 {...@@ -553,8 +553,9 @@ fn testDecode(bytes: []const u8) !u21 {
553/// Caller must free returned memory.553/// Caller must free returned memory.
554pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {554pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
555 var result = std.ArrayList(u8).init(allocator);555 var result = std.ArrayList(u8).init(allocator);
556 errdefer result.deinit();
556 // optimistically guess that it will all be ascii.557 // optimistically guess that it will all be ascii.
557 try result.ensureCapacity(utf16le.len);558 try result.ensureTotalCapacity(utf16le.len);
558 var out_index: usize = 0;559 var out_index: usize = 0;
559 var it = Utf16LeIterator.init(utf16le);560 var it = Utf16LeIterator.init(utf16le);
560 while (try it.nextCodepoint()) |codepoint| {561 while (try it.nextCodepoint()) |codepoint| {
...@@ -569,9 +570,10 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8...@@ -569,9 +570,10 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8
569570
570/// Caller must free returned memory.571/// Caller must free returned memory.
571pub fn utf16leToUtf8AllocZ(allocator: *mem.Allocator, utf16le: []const u16) ![:0]u8 {572pub 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 // optimistically guess that it will all be ascii.575 // optimistically guess that it will all be ascii.
574 try result.ensureCapacity(utf16le.len);576 try result.ensureTotalCapacity(utf16le.len);
575 var out_index: usize = 0;577 var out_index: usize = 0;
576 var it = Utf16LeIterator.init(utf16le);578 var it = Utf16LeIterator.init(utf16le);
577 while (try it.nextCodepoint()) |codepoint| {579 while (try it.nextCodepoint()) |codepoint| {
...@@ -653,12 +655,20 @@ test "utf16leToUtf8" {...@@ -653,12 +655,20 @@ test "utf16leToUtf8" {
653 defer std.testing.allocator.free(utf8);655 defer std.testing.allocator.free(utf8);
654 try testing.expect(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));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}
657666
658pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![:0]u16 {667pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![:0]u16 {
659 var result = std.ArrayList(u16).init(allocator);668 var result = std.ArrayList(u16).init(allocator);
669 errdefer result.deinit();
660 // optimistically guess that it will not require surrogate pairs670 // optimistically guess that it will not require surrogate pairs
661 try result.ensureCapacity(utf8.len + 1);671 try result.ensureTotalCapacity(utf8.len + 1);
662672
663 const view = try Utf8View.init(utf8);673 const view = try Utf8View.init(utf8);
664 var it = view.iterator();674 var it = view.iterator();
...@@ -718,6 +728,10 @@ test "utf8ToUtf16Le" {...@@ -718,6 +728,10 @@ test "utf8ToUtf16Le" {
718 try testing.expectEqual(@as(usize, 2), length);728 try testing.expectEqual(@as(usize, 2), length);
719 try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16le[0..]));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}
722736
723test "utf8ToUtf16LeWithNull" {737test "utf8ToUtf16LeWithNull" {
...@@ -733,6 +747,10 @@ test "utf8ToUtf16LeWithNull" {...@@ -733,6 +747,10 @@ test "utf8ToUtf16LeWithNull" {
733 try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16[0..]));747 try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16[0..]));
734 try testing.expect(utf16[2] == 0);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}
737755
738/// Converts a UTF-8 string literal into a UTF-16LE string literal.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,7 +27,7 @@ pub fn resolveScopeId(name: []const u8) !u32 {
27 return rc;27 return rc;
28 }28 }
2929
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 defer os.closeSocket(fd);31 defer os.closeSocket(fd);
3232
33 var f: os.ifreq = undefined;33 var f: os.ifreq = undefined;
...@@ -566,21 +566,17 @@ test "ipv6: parse & format" {...@@ -566,21 +566,17 @@ test "ipv6: parse & format" {
566566
567test "ipv6: parse & format addresses with scope ids" {567test "ipv6: parse & format addresses with scope ids" {
568 if (!have_ifnamesize) return error.SkipZigTest;568 if (!have_ifnamesize) return error.SkipZigTest;
569569 const iface = if (native_os.tag == .linux)
570 const inputs = [_][]const u8{570 "lo"
571 "FF01::FB%lo",571 else
572 };572 "lo0";
573573 const input = "FF01::FB%" ++ iface;
574 const outputs = [_][]const u8{574 const output = "ff01::fb%1";
575 "ff01::fb%1",575
576 const parsed = IPv6.parse(input) catch |err| switch (err) {
577 error.InterfaceNotFound => return,
578 else => return err,
576 };579 };
577580
578 for (inputs) |input, i| {581 try testing.expectFmt(output, "{}", .{parsed});
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 }
586}582}
lib/std/x/os/socket.zig+1-1
...@@ -37,7 +37,7 @@ pub const Socket = struct {...@@ -37,7 +37,7 @@ pub const Socket = struct {
3737
38 /// POSIX `sockaddr.storage`. The expected size and alignment is specified in IETF RFC 2553.38 /// POSIX `sockaddr.storage`. The expected size and alignment is specified in IETF RFC 2553.
39 pub const Storage = extern struct {39 pub const Storage = extern struct {
40 pub const expected_size = 128;40 pub const expected_size = os.sockaddr.SS_MAXSIZE;
41 pub const expected_alignment = 8;41 pub const expected_alignment = 8;
4242
43 pub const padding_size = expected_size -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,6 +262,9 @@ pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {
262 token_tags[parse_error.token].symbol(),262 token_tags[parse_error.token].symbol(),
263 });263 });
264 },264 },
265 .extra_addrspace_qualifier => {
266 return stream.writeAll("extra addrspace qualifier");
267 },
265 .extra_align_qualifier => {268 .extra_align_qualifier => {
266 return stream.writeAll("extra align qualifier");269 return stream.writeAll("extra align qualifier");
267 },270 },
...@@ -392,14 +395,18 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -392,14 +395,18 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
392 .assign_mod,395 .assign_mod,
393 .assign_add,396 .assign_add,
394 .assign_sub,397 .assign_sub,
395 .assign_bit_shift_left,398 .assign_shl,
396 .assign_bit_shift_right,399 .assign_shl_sat,
400 .assign_shr,
397 .assign_bit_and,401 .assign_bit_and,
398 .assign_bit_xor,402 .assign_bit_xor,
399 .assign_bit_or,403 .assign_bit_or,
400 .assign_mul_wrap,404 .assign_mul_wrap,
401 .assign_add_wrap,405 .assign_add_wrap,
402 .assign_sub_wrap,406 .assign_sub_wrap,
407 .assign_mul_sat,
408 .assign_add_sat,
409 .assign_sub_sat,
403 .assign,410 .assign,
404 .merge_error_sets,411 .merge_error_sets,
405 .mul,412 .mul,
...@@ -407,13 +414,17 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -407,13 +414,17 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
407 .mod,414 .mod,
408 .array_mult,415 .array_mult,
409 .mul_wrap,416 .mul_wrap,
417 .mul_sat,
410 .add,418 .add,
411 .sub,419 .sub,
412 .array_cat,420 .array_cat,
413 .add_wrap,421 .add_wrap,
414 .sub_wrap,422 .sub_wrap,
415 .bit_shift_left,423 .add_sat,
416 .bit_shift_right,424 .sub_sat,
425 .shl,
426 .shl_sat,
427 .shr,
417 .bit_and,428 .bit_and,
418 .bit_xor,429 .bit_xor,
419 .bit_or,430 .bit_or,
...@@ -648,14 +659,18 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -648,14 +659,18 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
648 .assign_mod,659 .assign_mod,
649 .assign_add,660 .assign_add,
650 .assign_sub,661 .assign_sub,
651 .assign_bit_shift_left,662 .assign_shl,
652 .assign_bit_shift_right,663 .assign_shl_sat,
664 .assign_shr,
653 .assign_bit_and,665 .assign_bit_and,
654 .assign_bit_xor,666 .assign_bit_xor,
655 .assign_bit_or,667 .assign_bit_or,
656 .assign_mul_wrap,668 .assign_mul_wrap,
657 .assign_add_wrap,669 .assign_add_wrap,
658 .assign_sub_wrap,670 .assign_sub_wrap,
671 .assign_mul_sat,
672 .assign_add_sat,
673 .assign_sub_sat,
659 .assign,674 .assign,
660 .merge_error_sets,675 .merge_error_sets,
661 .mul,676 .mul,
...@@ -663,13 +678,17 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -663,13 +678,17 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
663 .mod,678 .mod,
664 .array_mult,679 .array_mult,
665 .mul_wrap,680 .mul_wrap,
681 .mul_sat,
666 .add,682 .add,
667 .sub,683 .sub,
668 .array_cat,684 .array_cat,
669 .add_wrap,685 .add_wrap,
670 .sub_wrap,686 .sub_wrap,
671 .bit_shift_left,687 .add_sat,
672 .bit_shift_right,688 .sub_sat,
689 .shl,
690 .shl_sat,
691 .shr,
673 .bit_and,692 .bit_and,
674 .bit_xor,693 .bit_xor,
675 .bit_or,694 .bit_or,
...@@ -1021,7 +1040,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -1021,7 +1040,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
1021 },1040 },
1022 .fn_proto_one => {1041 .fn_proto_one => {
1023 const extra = tree.extraData(datas[n].lhs, Node.FnProtoOne);1042 const extra = tree.extraData(datas[n].lhs, Node.FnProtoOne);
1024 // linksection, callconv, align can appear in any order, so we1043 // addrspace, linksection, callconv, align can appear in any order, so we
1025 // find the last one here.1044 // find the last one here.
1026 var max_node: Node.Index = datas[n].rhs;1045 var max_node: Node.Index = datas[n].rhs;
1027 var max_start = token_starts[main_tokens[max_node]];1046 var max_start = token_starts[main_tokens[max_node]];
...@@ -1034,6 +1053,14 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -1034,6 +1053,14 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
1034 max_offset = 1; // for the rparen1053 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 if (extra.section_expr != 0) {1064 if (extra.section_expr != 0) {
1038 const start = token_starts[main_tokens[extra.section_expr]];1065 const start = token_starts[main_tokens[extra.section_expr]];
1039 if (start > max_start) {1066 if (start > max_start) {
...@@ -1055,7 +1082,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -1055,7 +1082,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
1055 },1082 },
1056 .fn_proto => {1083 .fn_proto => {
1057 const extra = tree.extraData(datas[n].lhs, Node.FnProto);1084 const extra = tree.extraData(datas[n].lhs, Node.FnProto);
1058 // linksection, callconv, align can appear in any order, so we1085 // addrspace, linksection, callconv, align can appear in any order, so we
1059 // find the last one here.1086 // find the last one here.
1060 var max_node: Node.Index = datas[n].rhs;1087 var max_node: Node.Index = datas[n].rhs;
1061 var max_start = token_starts[main_tokens[max_node]];1088 var max_start = token_starts[main_tokens[max_node]];
...@@ -1068,6 +1095,14 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -1068,6 +1095,14 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
1068 max_offset = 1; // for the rparen1095 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 if (extra.section_expr != 0) {1106 if (extra.section_expr != 0) {
1072 const start = token_starts[main_tokens[extra.section_expr]];1107 const start = token_starts[main_tokens[extra.section_expr]];
1073 if (start > max_start) {1108 if (start > max_start) {
...@@ -1138,6 +1173,7 @@ pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl {...@@ -1138,6 +1173,7 @@ pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
1138 return tree.fullVarDecl(.{1173 return tree.fullVarDecl(.{
1139 .type_node = extra.type_node,1174 .type_node = extra.type_node,
1140 .align_node = extra.align_node,1175 .align_node = extra.align_node,
1176 .addrspace_node = extra.addrspace_node,
1141 .section_node = extra.section_node,1177 .section_node = extra.section_node,
1142 .init_node = data.rhs,1178 .init_node = data.rhs,
1143 .mut_token = tree.nodes.items(.main_token)[node],1179 .mut_token = tree.nodes.items(.main_token)[node],
...@@ -1151,6 +1187,7 @@ pub fn localVarDecl(tree: Tree, node: Node.Index) full.VarDecl {...@@ -1151,6 +1187,7 @@ pub fn localVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
1151 return tree.fullVarDecl(.{1187 return tree.fullVarDecl(.{
1152 .type_node = extra.type_node,1188 .type_node = extra.type_node,
1153 .align_node = extra.align_node,1189 .align_node = extra.align_node,
1190 .addrspace_node = 0,
1154 .section_node = 0,1191 .section_node = 0,
1155 .init_node = data.rhs,1192 .init_node = data.rhs,
1156 .mut_token = tree.nodes.items(.main_token)[node],1193 .mut_token = tree.nodes.items(.main_token)[node],
...@@ -1163,6 +1200,7 @@ pub fn simpleVarDecl(tree: Tree, node: Node.Index) full.VarDecl {...@@ -1163,6 +1200,7 @@ pub fn simpleVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
1163 return tree.fullVarDecl(.{1200 return tree.fullVarDecl(.{
1164 .type_node = data.lhs,1201 .type_node = data.lhs,
1165 .align_node = 0,1202 .align_node = 0,
1203 .addrspace_node = 0,
1166 .section_node = 0,1204 .section_node = 0,
1167 .init_node = data.rhs,1205 .init_node = data.rhs,
1168 .mut_token = tree.nodes.items(.main_token)[node],1206 .mut_token = tree.nodes.items(.main_token)[node],
...@@ -1175,6 +1213,7 @@ pub fn alignedVarDecl(tree: Tree, node: Node.Index) full.VarDecl {...@@ -1175,6 +1213,7 @@ pub fn alignedVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
1175 return tree.fullVarDecl(.{1213 return tree.fullVarDecl(.{
1176 .type_node = 0,1214 .type_node = 0,
1177 .align_node = data.lhs,1215 .align_node = data.lhs,
1216 .addrspace_node = 0,
1178 .section_node = 0,1217 .section_node = 0,
1179 .init_node = data.rhs,1218 .init_node = data.rhs,
1180 .mut_token = tree.nodes.items(.main_token)[node],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,6 +1288,7 @@ pub fn fnProtoSimple(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.
1249 .return_type = data.rhs,1288 .return_type = data.rhs,
1250 .params = params,1289 .params = params,
1251 .align_expr = 0,1290 .align_expr = 0,
1291 .addrspace_expr = 0,
1252 .section_expr = 0,1292 .section_expr = 0,
1253 .callconv_expr = 0,1293 .callconv_expr = 0,
1254 });1294 });
...@@ -1265,6 +1305,7 @@ pub fn fnProtoMulti(tree: Tree, node: Node.Index) full.FnProto {...@@ -1265,6 +1305,7 @@ pub fn fnProtoMulti(tree: Tree, node: Node.Index) full.FnProto {
1265 .return_type = data.rhs,1305 .return_type = data.rhs,
1266 .params = params,1306 .params = params,
1267 .align_expr = 0,1307 .align_expr = 0,
1308 .addrspace_expr = 0,
1268 .section_expr = 0,1309 .section_expr = 0,
1269 .callconv_expr = 0,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,6 +1323,7 @@ pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnP
1282 .return_type = data.rhs,1323 .return_type = data.rhs,
1283 .params = params,1324 .params = params,
1284 .align_expr = extra.align_expr,1325 .align_expr = extra.align_expr,
1326 .addrspace_expr = extra.addrspace_expr,
1285 .section_expr = extra.section_expr,1327 .section_expr = extra.section_expr,
1286 .callconv_expr = extra.callconv_expr,1328 .callconv_expr = extra.callconv_expr,
1287 });1329 });
...@@ -1298,6 +1340,7 @@ pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto {...@@ -1298,6 +1340,7 @@ pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto {
1298 .return_type = data.rhs,1340 .return_type = data.rhs,
1299 .params = params,1341 .params = params,
1300 .align_expr = extra.align_expr,1342 .align_expr = extra.align_expr,
1343 .addrspace_expr = extra.addrspace_expr,
1301 .section_expr = extra.section_expr,1344 .section_expr = extra.section_expr,
1302 .callconv_expr = extra.callconv_expr,1345 .callconv_expr = extra.callconv_expr,
1303 });1346 });
...@@ -1453,6 +1496,7 @@ pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType {...@@ -1453,6 +1496,7 @@ pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType {
1453 return tree.fullPtrType(.{1496 return tree.fullPtrType(.{
1454 .main_token = tree.nodes.items(.main_token)[node],1497 .main_token = tree.nodes.items(.main_token)[node],
1455 .align_node = data.lhs,1498 .align_node = data.lhs,
1499 .addrspace_node = 0,
1456 .sentinel = 0,1500 .sentinel = 0,
1457 .bit_range_start = 0,1501 .bit_range_start = 0,
1458 .bit_range_end = 0,1502 .bit_range_end = 0,
...@@ -1466,6 +1510,7 @@ pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType {...@@ -1466,6 +1510,7 @@ pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType {
1466 return tree.fullPtrType(.{1510 return tree.fullPtrType(.{
1467 .main_token = tree.nodes.items(.main_token)[node],1511 .main_token = tree.nodes.items(.main_token)[node],
1468 .align_node = 0,1512 .align_node = 0,
1513 .addrspace_node = 0,
1469 .sentinel = data.lhs,1514 .sentinel = data.lhs,
1470 .bit_range_start = 0,1515 .bit_range_start = 0,
1471 .bit_range_end = 0,1516 .bit_range_end = 0,
...@@ -1480,6 +1525,7 @@ pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType {...@@ -1480,6 +1525,7 @@ pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType {
1480 return tree.fullPtrType(.{1525 return tree.fullPtrType(.{
1481 .main_token = tree.nodes.items(.main_token)[node],1526 .main_token = tree.nodes.items(.main_token)[node],
1482 .align_node = extra.align_node,1527 .align_node = extra.align_node,
1528 .addrspace_node = extra.addrspace_node,
1483 .sentinel = extra.sentinel,1529 .sentinel = extra.sentinel,
1484 .bit_range_start = 0,1530 .bit_range_start = 0,
1485 .bit_range_end = 0,1531 .bit_range_end = 0,
...@@ -1494,6 +1540,7 @@ pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType {...@@ -1494,6 +1540,7 @@ pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType {
1494 return tree.fullPtrType(.{1540 return tree.fullPtrType(.{
1495 .main_token = tree.nodes.items(.main_token)[node],1541 .main_token = tree.nodes.items(.main_token)[node],
1496 .align_node = extra.align_node,1542 .align_node = extra.align_node,
1543 .addrspace_node = extra.addrspace_node,
1497 .sentinel = extra.sentinel,1544 .sentinel = extra.sentinel,
1498 .bit_range_start = extra.bit_range_start,1545 .bit_range_start = extra.bit_range_start,
1499 .bit_range_end = extra.bit_range_end,1546 .bit_range_end = extra.bit_range_end,
...@@ -2063,6 +2110,7 @@ pub const full = struct {...@@ -2063,6 +2110,7 @@ pub const full = struct {
2063 mut_token: TokenIndex,2110 mut_token: TokenIndex,
2064 type_node: Node.Index,2111 type_node: Node.Index,
2065 align_node: Node.Index,2112 align_node: Node.Index,
2113 addrspace_node: Node.Index,
2066 section_node: Node.Index,2114 section_node: Node.Index,
2067 init_node: Node.Index,2115 init_node: Node.Index,
2068 };2116 };
...@@ -2130,6 +2178,7 @@ pub const full = struct {...@@ -2130,6 +2178,7 @@ pub const full = struct {
2130 return_type: Node.Index,2178 return_type: Node.Index,
2131 params: []const Node.Index,2179 params: []const Node.Index,
2132 align_expr: Node.Index,2180 align_expr: Node.Index,
2181 addrspace_expr: Node.Index,
2133 section_expr: Node.Index,2182 section_expr: Node.Index,
2134 callconv_expr: Node.Index,2183 callconv_expr: Node.Index,
2135 };2184 };
...@@ -2288,6 +2337,7 @@ pub const full = struct {...@@ -2288,6 +2337,7 @@ pub const full = struct {
2288 pub const Components = struct {2337 pub const Components = struct {
2289 main_token: TokenIndex,2338 main_token: TokenIndex,
2290 align_node: Node.Index,2339 align_node: Node.Index,
2340 addrspace_node: Node.Index,
2291 sentinel: Node.Index,2341 sentinel: Node.Index,
2292 bit_range_start: Node.Index,2342 bit_range_start: Node.Index,
2293 bit_range_end: Node.Index,2343 bit_range_end: Node.Index,
...@@ -2397,6 +2447,7 @@ pub const Error = struct {...@@ -2397,6 +2447,7 @@ pub const Error = struct {
2397 expected_var_decl_or_fn,2447 expected_var_decl_or_fn,
2398 expected_loop_payload,2448 expected_loop_payload,
2399 expected_container,2449 expected_container,
2450 extra_addrspace_qualifier,
2400 extra_align_qualifier,2451 extra_align_qualifier,
2401 extra_allowzero_qualifier,2452 extra_allowzero_qualifier,
2402 extra_const_qualifier,2453 extra_const_qualifier,
...@@ -2489,9 +2540,11 @@ pub const Node = struct {...@@ -2489,9 +2540,11 @@ pub const Node = struct {
2489 /// `lhs -= rhs`. main_token is op.2540 /// `lhs -= rhs`. main_token is op.
2490 assign_sub,2541 assign_sub,
2491 /// `lhs <<= rhs`. main_token is op.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 /// `lhs >>= rhs`. main_token is op.2546 /// `lhs >>= rhs`. main_token is op.
2494 assign_bit_shift_right,2547 assign_shr,
2495 /// `lhs &= rhs`. main_token is op.2548 /// `lhs &= rhs`. main_token is op.
2496 assign_bit_and,2549 assign_bit_and,
2497 /// `lhs ^= rhs`. main_token is op.2550 /// `lhs ^= rhs`. main_token is op.
...@@ -2504,6 +2557,12 @@ pub const Node = struct {...@@ -2504,6 +2557,12 @@ pub const Node = struct {
2504 assign_add_wrap,2557 assign_add_wrap,
2505 /// `lhs -%= rhs`. main_token is op.2558 /// `lhs -%= rhs`. main_token is op.
2506 assign_sub_wrap,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 /// `lhs = rhs`. main_token is op.2566 /// `lhs = rhs`. main_token is op.
2508 assign,2567 assign,
2509 /// `lhs || rhs`. main_token is the `||`.2568 /// `lhs || rhs`. main_token is the `||`.
...@@ -2518,6 +2577,8 @@ pub const Node = struct {...@@ -2518,6 +2577,8 @@ pub const Node = struct {
2518 array_mult,2577 array_mult,
2519 /// `lhs *% rhs`. main_token is the `*%`.2578 /// `lhs *% rhs`. main_token is the `*%`.
2520 mul_wrap,2579 mul_wrap,
2580 /// `lhs *| rhs`. main_token is the `*|`.
2581 mul_sat,
2521 /// `lhs + rhs`. main_token is the `+`.2582 /// `lhs + rhs`. main_token is the `+`.
2522 add,2583 add,
2523 /// `lhs - rhs`. main_token is the `-`.2584 /// `lhs - rhs`. main_token is the `-`.
...@@ -2528,10 +2589,16 @@ pub const Node = struct {...@@ -2528,10 +2589,16 @@ pub const Node = struct {
2528 add_wrap,2589 add_wrap,
2529 /// `lhs -% rhs`. main_token is the `-%`.2590 /// `lhs -% rhs`. main_token is the `-%`.
2530 sub_wrap,2591 sub_wrap,
2592 /// `lhs +| rhs`. main_token is the `+|`.
2593 add_sat,
2594 /// `lhs -| rhs`. main_token is the `-|`.
2595 sub_sat,
2531 /// `lhs << rhs`. main_token is the `<<`.2596 /// `lhs << rhs`. main_token is the `<<`.
2532 bit_shift_left,2597 shl,
2598 /// `lhs <<| rhs`. main_token is the `<<|`.
2599 shl_sat,
2533 /// `lhs >> rhs`. main_token is the `>>`.2600 /// `lhs >> rhs`. main_token is the `>>`.
2534 bit_shift_right,2601 shr,
2535 /// `lhs & rhs`. main_token is the `&`.2602 /// `lhs & rhs`. main_token is the `&`.
2536 bit_and,2603 bit_and,
2537 /// `lhs ^ rhs`. main_token is the `^`.2604 /// `lhs ^ rhs`. main_token is the `^`.
...@@ -2723,13 +2790,13 @@ pub const Node = struct {...@@ -2723,13 +2790,13 @@ pub const Node = struct {
2723 /// main_token is the `fn` keyword.2790 /// main_token is the `fn` keyword.
2724 /// extern function declarations use this tag.2791 /// extern function declarations use this tag.
2725 fn_proto_multi,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 /// zero or one parameters.2794 /// zero or one parameters.
2728 /// anytype and ... parameters are omitted from the AST tree.2795 /// anytype and ... parameters are omitted from the AST tree.
2729 /// main_token is the `fn` keyword.2796 /// main_token is the `fn` keyword.
2730 /// extern function declarations use this tag.2797 /// extern function declarations use this tag.
2731 fn_proto_one,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 /// anytype and ... parameters are omitted from the AST tree.2800 /// anytype and ... parameters are omitted from the AST tree.
2734 /// main_token is the `fn` keyword.2801 /// main_token is the `fn` keyword.
2735 /// extern function declarations use this tag.2802 /// extern function declarations use this tag.
...@@ -2893,11 +2960,13 @@ pub const Node = struct {...@@ -2893,11 +2960,13 @@ pub const Node = struct {
2893 pub const PtrType = struct {2960 pub const PtrType = struct {
2894 sentinel: Index,2961 sentinel: Index,
2895 align_node: Index,2962 align_node: Index,
2963 addrspace_node: Index,
2896 };2964 };
28972965
2898 pub const PtrTypeBitRange = struct {2966 pub const PtrTypeBitRange = struct {
2899 sentinel: Index,2967 sentinel: Index,
2900 align_node: Index,2968 align_node: Index,
2969 addrspace_node: Index,
2901 bit_range_start: Index,2970 bit_range_start: Index,
2902 bit_range_end: Index,2971 bit_range_end: Index,
2903 };2972 };
...@@ -2920,8 +2989,13 @@ pub const Node = struct {...@@ -2920,8 +2989,13 @@ pub const Node = struct {
2920 };2989 };
29212990
2922 pub const GlobalVarDecl = struct {2991 pub const GlobalVarDecl = struct {
2992 /// Populated if there is an explicit type ascription.
2923 type_node: Index,2993 type_node: Index,
2994 /// Populated if align(A) is present.
2924 align_node: Index,2995 align_node: Index,
2996 /// Populated if addrspace(A) is present.
2997 addrspace_node: Index,
2998 /// Populated if linksection(A) is present.
2925 section_node: Index,2999 section_node: Index,
2926 };3000 };
29273001
...@@ -2953,6 +3027,8 @@ pub const Node = struct {...@@ -2953,6 +3027,8 @@ pub const Node = struct {
2953 param: Index,3027 param: Index,
2954 /// Populated if align(A) is present.3028 /// Populated if align(A) is present.
2955 align_expr: Index,3029 align_expr: Index,
3030 /// Populated if addrspace(A) is present.
3031 addrspace_expr: Index,
2956 /// Populated if linksection(A) is present.3032 /// Populated if linksection(A) is present.
2957 section_expr: Index,3033 section_expr: Index,
2958 /// Populated if callconv(A) is present.3034 /// Populated if callconv(A) is present.
...@@ -2964,6 +3040,8 @@ pub const Node = struct {...@@ -2964,6 +3040,8 @@ pub const Node = struct {
2964 params_end: Index,3040 params_end: Index,
2965 /// Populated if align(A) is present.3041 /// Populated if align(A) is present.
2966 align_expr: Index,3042 align_expr: Index,
3043 /// Populated if addrspace(A) is present.
3044 addrspace_expr: Index,
2967 /// Populated if linksection(A) is present.3045 /// Populated if linksection(A) is present.
2968 section_expr: Index,3046 section_expr: Index,
2969 /// Populated if callconv(A) is present.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,6 +325,7 @@ pub fn FlexibleArrayType(comptime SelfType: type, ElementType: type) type {
325 .is_const = ptr.is_const,325 .is_const = ptr.is_const,
326 .is_volatile = ptr.is_volatile,326 .is_volatile = ptr.is_volatile,
327 .alignment = @alignOf(ElementType),327 .alignment = @alignOf(ElementType),
328 .address_space = .generic,
328 .child = ElementType,329 .child = ElementType,
329 .is_allowzero = true,330 .is_allowzero = true,
330 .sentinel = null,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,7 +17,7 @@ pub fn parse(gpa: *Allocator, source: [:0]const u8) Allocator.Error!Ast {
1717
18 // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count.18 // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count.
19 const estimated_token_count = source.len / 8;19 const estimated_token_count = source.len / 8;
20 try tokens.ensureCapacity(gpa, estimated_token_count);20 try tokens.ensureTotalCapacity(gpa, estimated_token_count);
2121
22 var tokenizer = std.zig.Tokenizer.init(source);22 var tokenizer = std.zig.Tokenizer.init(source);
23 while (true) {23 while (true) {
...@@ -48,7 +48,7 @@ pub fn parse(gpa: *Allocator, source: [:0]const u8) Allocator.Error!Ast {...@@ -48,7 +48,7 @@ pub fn parse(gpa: *Allocator, source: [:0]const u8) Allocator.Error!Ast {
48 // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes.48 // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes.
49 // Make sure at least 1 so we can use appendAssumeCapacity on the root node below.49 // Make sure at least 1 so we can use appendAssumeCapacity on the root node below.
50 const estimated_node_count = (tokens.len + 2) / 2;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);
5252
53 // Root node must be index 0.53 // Root node must be index 0.
54 // Root <- skip ContainerMembers eof54 // Root <- skip ContainerMembers eof
...@@ -138,7 +138,7 @@ const Parser = struct {...@@ -138,7 +138,7 @@ const Parser = struct {
138138
139 fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {139 fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {
140 const fields = std.meta.fields(@TypeOf(extra));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 const result = @intCast(u32, p.extra_data.items.len);142 const result = @intCast(u32, p.extra_data.items.len);
143 inline for (fields) |field| {143 inline for (fields) |field| {
144 comptime assert(field.field_type == Node.Index);144 comptime assert(field.field_type == Node.Index);
...@@ -629,7 +629,7 @@ const Parser = struct {...@@ -629,7 +629,7 @@ const Parser = struct {
629 };629 };
630 }630 }
631631
632 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr632 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
633 fn parseFnProto(p: *Parser) !Node.Index {633 fn parseFnProto(p: *Parser) !Node.Index {
634 const fn_token = p.eatToken(.keyword_fn) orelse return null_node;634 const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
635635
...@@ -639,6 +639,7 @@ const Parser = struct {...@@ -639,6 +639,7 @@ const Parser = struct {
639 _ = p.eatToken(.identifier);639 _ = p.eatToken(.identifier);
640 const params = try p.parseParamDeclList();640 const params = try p.parseParamDeclList();
641 const align_expr = try p.parseByteAlign();641 const align_expr = try p.parseByteAlign();
642 const addrspace_expr = try p.parseAddrSpace();
642 const section_expr = try p.parseLinkSection();643 const section_expr = try p.parseLinkSection();
643 const callconv_expr = try p.parseCallconv();644 const callconv_expr = try p.parseCallconv();
644 _ = p.eatToken(.bang);645 _ = p.eatToken(.bang);
...@@ -650,7 +651,7 @@ const Parser = struct {...@@ -650,7 +651,7 @@ const Parser = struct {
650 try p.warn(.expected_return_type);651 try p.warn(.expected_return_type);
651 }652 }
652653
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 switch (params) {655 switch (params) {
655 .zero_or_one => |param| return p.setNode(fn_proto_index, .{656 .zero_or_one => |param| return p.setNode(fn_proto_index, .{
656 .tag = .fn_proto_simple,657 .tag = .fn_proto_simple,
...@@ -683,6 +684,7 @@ const Parser = struct {...@@ -683,6 +684,7 @@ const Parser = struct {
683 .lhs = try p.addExtra(Node.FnProtoOne{684 .lhs = try p.addExtra(Node.FnProtoOne{
684 .param = param,685 .param = param,
685 .align_expr = align_expr,686 .align_expr = align_expr,
687 .addrspace_expr = addrspace_expr,
686 .section_expr = section_expr,688 .section_expr = section_expr,
687 .callconv_expr = callconv_expr,689 .callconv_expr = callconv_expr,
688 }),690 }),
...@@ -698,6 +700,7 @@ const Parser = struct {...@@ -698,6 +700,7 @@ const Parser = struct {
698 .params_start = span.start,700 .params_start = span.start,
699 .params_end = span.end,701 .params_end = span.end,
700 .align_expr = align_expr,702 .align_expr = align_expr,
703 .addrspace_expr = addrspace_expr,
701 .section_expr = section_expr,704 .section_expr = section_expr,
702 .callconv_expr = callconv_expr,705 .callconv_expr = callconv_expr,
703 }),706 }),
...@@ -708,7 +711,7 @@ const Parser = struct {...@@ -708,7 +711,7 @@ const Parser = struct {
708 }711 }
709 }712 }
710713
711 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON714 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON
712 fn parseVarDecl(p: *Parser) !Node.Index {715 fn parseVarDecl(p: *Parser) !Node.Index {
713 const mut_token = p.eatToken(.keyword_const) orelse716 const mut_token = p.eatToken(.keyword_const) orelse
714 p.eatToken(.keyword_var) orelse717 p.eatToken(.keyword_var) orelse
...@@ -717,9 +720,10 @@ const Parser = struct {...@@ -717,9 +720,10 @@ const Parser = struct {
717 _ = try p.expectToken(.identifier);720 _ = try p.expectToken(.identifier);
718 const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr();721 const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr();
719 const align_node = try p.parseByteAlign();722 const align_node = try p.parseByteAlign();
723 const addrspace_node = try p.parseAddrSpace();
720 const section_node = try p.parseLinkSection();724 const section_node = try p.parseLinkSection();
721 const init_node: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr();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 if (align_node == 0) {727 if (align_node == 0) {
724 return p.addNode(.{728 return p.addNode(.{
725 .tag = .simple_var_decl,729 .tag = .simple_var_decl,
...@@ -759,6 +763,7 @@ const Parser = struct {...@@ -759,6 +763,7 @@ const Parser = struct {
759 .lhs = try p.addExtra(Node.GlobalVarDecl{763 .lhs = try p.addExtra(Node.GlobalVarDecl{
760 .type_node = type_node,764 .type_node = type_node,
761 .align_node = align_node,765 .align_node = align_node,
766 .addrspace_node = addrspace_node,
762 .section_node = section_node,767 .section_node = section_node,
763 }),768 }),
764 .rhs = init_node,769 .rhs = init_node,
...@@ -1263,14 +1268,18 @@ const Parser = struct {...@@ -1263,14 +1268,18 @@ const Parser = struct {
1263 .percent_equal => .assign_mod,1268 .percent_equal => .assign_mod,
1264 .plus_equal => .assign_add,1269 .plus_equal => .assign_add,
1265 .minus_equal => .assign_sub,1270 .minus_equal => .assign_sub,
1266 .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left,1271 .angle_bracket_angle_bracket_left_equal => .assign_shl,
1267 .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right,1272 .angle_bracket_angle_bracket_left_pipe_equal => .assign_shl_sat,
1273 .angle_bracket_angle_bracket_right_equal => .assign_shr,
1268 .ampersand_equal => .assign_bit_and,1274 .ampersand_equal => .assign_bit_and,
1269 .caret_equal => .assign_bit_xor,1275 .caret_equal => .assign_bit_xor,
1270 .pipe_equal => .assign_bit_or,1276 .pipe_equal => .assign_bit_or,
1271 .asterisk_percent_equal => .assign_mul_wrap,1277 .asterisk_percent_equal => .assign_mul_wrap,
1272 .plus_percent_equal => .assign_add_wrap,1278 .plus_percent_equal => .assign_add_wrap,
1273 .minus_percent_equal => .assign_sub_wrap,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 .equal => .assign,1283 .equal => .assign,
1275 else => return expr,1284 else => return expr,
1276 };1285 };
...@@ -1337,14 +1346,17 @@ const Parser = struct {...@@ -1337,14 +1346,17 @@ const Parser = struct {
1337 .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" },1346 .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" },
1338 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },1347 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },
13391348
1340 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left },1349 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .shl },
1341 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right },1350 .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .shl_sat },
1351 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .shr },
13421352
1343 .plus = .{ .prec = 60, .tag = .add },1353 .plus = .{ .prec = 60, .tag = .add },
1344 .minus = .{ .prec = 60, .tag = .sub },1354 .minus = .{ .prec = 60, .tag = .sub },
1345 .plus_plus = .{ .prec = 60, .tag = .array_cat },1355 .plus_plus = .{ .prec = 60, .tag = .array_cat },
1346 .plus_percent = .{ .prec = 60, .tag = .add_wrap },1356 .plus_percent = .{ .prec = 60, .tag = .add_wrap },
1347 .minus_percent = .{ .prec = 60, .tag = .sub_wrap },1357 .minus_percent = .{ .prec = 60, .tag = .sub_wrap },
1358 .plus_pipe = .{ .prec = 60, .tag = .add_sat },
1359 .minus_pipe = .{ .prec = 60, .tag = .sub_sat },
13481360
1349 .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets },1361 .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets },
1350 .asterisk = .{ .prec = 70, .tag = .mul },1362 .asterisk = .{ .prec = 70, .tag = .mul },
...@@ -1352,6 +1364,7 @@ const Parser = struct {...@@ -1352,6 +1364,7 @@ const Parser = struct {
1352 .percent = .{ .prec = 70, .tag = .mod },1364 .percent = .{ .prec = 70, .tag = .mod },
1353 .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult },1365 .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult },
1354 .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap },1366 .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap },
1367 .asterisk_pipe = .{ .prec = 70, .tag = .mul_sat },
1355 });1368 });
13561369
1357 fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index {1370 fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index {
...@@ -1440,8 +1453,8 @@ const Parser = struct {...@@ -1440,8 +1453,8 @@ const Parser = struct {
1440 /// PrefixTypeOp1453 /// PrefixTypeOp
1441 /// <- QUESTIONMARK1454 /// <- QUESTIONMARK
1442 /// / KEYWORD_anyframe MINUSRARROW1455 /// / KEYWORD_anyframe MINUSRARROW
1443 /// / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*1456 /// / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
1444 /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / 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 /// / ArrayTypeStart1458 /// / ArrayTypeStart
1446 /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET1459 /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET
1447 /// PtrTypeStart1460 /// PtrTypeStart
...@@ -1474,29 +1487,43 @@ const Parser = struct {...@@ -1474,29 +1487,43 @@ const Parser = struct {
1474 const asterisk = p.nextToken();1487 const asterisk = p.nextToken();
1475 const mods = try p.parsePtrModifiers();1488 const mods = try p.parsePtrModifiers();
1476 const elem_type = try p.expectTypeExpr();1489 const elem_type = try p.expectTypeExpr();
1477 if (mods.bit_range_start == 0) {1490 if (mods.bit_range_start != 0) {
1478 return p.addNode(.{1491 return p.addNode(.{
1479 .tag = .ptr_type_aligned,1492 .tag = .ptr_type_bit_range,
1480 .main_token = asterisk,1493 .main_token = asterisk,
1481 .data = .{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 .rhs = elem_type,1502 .rhs = elem_type,
1484 },1503 },
1485 });1504 });
1486 } else {1505 } else if (mods.addrspace_node != 0) {
1487 return p.addNode(.{1506 return p.addNode(.{
1488 .tag = .ptr_type_bit_range,1507 .tag = .ptr_type,
1489 .main_token = asterisk,1508 .main_token = asterisk,
1490 .data = .{1509 .data = .{
1491 .lhs = try p.addExtra(Node.PtrTypeBitRange{1510 .lhs = try p.addExtra(Node.PtrType{
1492 .sentinel = 0,1511 .sentinel = 0,
1493 .align_node = mods.align_node,1512 .align_node = mods.align_node,
1494 .bit_range_start = mods.bit_range_start,1513 .addrspace_node = mods.addrspace_node,
1495 .bit_range_end = mods.bit_range_end,
1496 }),1514 }),
1497 .rhs = elem_type,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 .asterisk_asterisk => {1529 .asterisk_asterisk => {
...@@ -1504,29 +1531,43 @@ const Parser = struct {...@@ -1504,29 +1531,43 @@ const Parser = struct {
1504 const mods = try p.parsePtrModifiers();1531 const mods = try p.parsePtrModifiers();
1505 const elem_type = try p.expectTypeExpr();1532 const elem_type = try p.expectTypeExpr();
1506 const inner: Node.Index = inner: {1533 const inner: Node.Index = inner: {
1507 if (mods.bit_range_start == 0) {1534 if (mods.bit_range_start != 0) {
1508 break :inner try p.addNode(.{1535 break :inner try p.addNode(.{
1509 .tag = .ptr_type_aligned,1536 .tag = .ptr_type_bit_range,
1510 .main_token = asterisk,1537 .main_token = asterisk,
1511 .data = .{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 .rhs = elem_type,1546 .rhs = elem_type,
1514 },1547 },
1515 });1548 });
1516 } else {1549 } else if (mods.addrspace_node != 0) {
1517 break :inner try p.addNode(.{1550 break :inner try p.addNode(.{
1518 .tag = .ptr_type_bit_range,1551 .tag = .ptr_type,
1519 .main_token = asterisk,1552 .main_token = asterisk,
1520 .data = .{1553 .data = .{
1521 .lhs = try p.addExtra(Node.PtrTypeBitRange{1554 .lhs = try p.addExtra(Node.PtrType{
1522 .sentinel = 0,1555 .sentinel = 0,
1523 .align_node = mods.align_node,1556 .align_node = mods.align_node,
1524 .bit_range_start = mods.bit_range_start,1557 .addrspace_node = mods.addrspace_node,
1525 .bit_range_end = mods.bit_range_end,
1526 }),1558 }),
1527 .rhs = elem_type,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 return p.addNode(.{1573 return p.addNode(.{
...@@ -1543,24 +1584,19 @@ const Parser = struct {...@@ -1543,24 +1584,19 @@ const Parser = struct {
1543 _ = p.nextToken();1584 _ = p.nextToken();
1544 const asterisk = p.nextToken();1585 const asterisk = p.nextToken();
1545 var sentinel: Node.Index = 0;1586 var sentinel: Node.Index = 0;
1546 prefix: {1587 if (p.eatToken(.identifier)) |ident| {
1547 if (p.eatToken(.identifier)) |ident| {1588 const ident_slice = p.source[p.token_starts[ident]..p.token_starts[ident + 1]];
1548 const token_slice = p.source[p.token_starts[ident]..][0..2];1589 if (!std.mem.eql(u8, std.mem.trimRight(u8, ident_slice, &std.ascii.spaces), "c")) {
1549 if (!std.mem.eql(u8, token_slice, "c]")) {1590 p.tok_i -= 1;
1550 p.tok_i -= 1;
1551 } else {
1552 break :prefix;
1553 }
1554 }
1555 if (p.eatToken(.colon)) |_| {
1556 sentinel = try p.expectExpr();
1557 }1591 }
1592 } else if (p.eatToken(.colon)) |_| {
1593 sentinel = try p.expectExpr();
1558 }1594 }
1559 _ = try p.expectToken(.r_bracket);1595 _ = try p.expectToken(.r_bracket);
1560 const mods = try p.parsePtrModifiers();1596 const mods = try p.parsePtrModifiers();
1561 const elem_type = try p.expectTypeExpr();1597 const elem_type = try p.expectTypeExpr();
1562 if (mods.bit_range_start == 0) {1598 if (mods.bit_range_start == 0) {
1563 if (sentinel == 0) {1599 if (sentinel == 0 and mods.addrspace_node == 0) {
1564 return p.addNode(.{1600 return p.addNode(.{
1565 .tag = .ptr_type_aligned,1601 .tag = .ptr_type_aligned,
1566 .main_token = asterisk,1602 .main_token = asterisk,
...@@ -1569,7 +1605,7 @@ const Parser = struct {...@@ -1569,7 +1605,7 @@ const Parser = struct {
1569 .rhs = elem_type,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 return p.addNode(.{1609 return p.addNode(.{
1574 .tag = .ptr_type_sentinel,1610 .tag = .ptr_type_sentinel,
1575 .main_token = asterisk,1611 .main_token = asterisk,
...@@ -1586,6 +1622,7 @@ const Parser = struct {...@@ -1586,6 +1622,7 @@ const Parser = struct {
1586 .lhs = try p.addExtra(Node.PtrType{1622 .lhs = try p.addExtra(Node.PtrType{
1587 .sentinel = sentinel,1623 .sentinel = sentinel,
1588 .align_node = mods.align_node,1624 .align_node = mods.align_node,
1625 .addrspace_node = mods.addrspace_node,
1589 }),1626 }),
1590 .rhs = elem_type,1627 .rhs = elem_type,
1591 },1628 },
...@@ -1599,6 +1636,7 @@ const Parser = struct {...@@ -1599,6 +1636,7 @@ const Parser = struct {
1599 .lhs = try p.addExtra(Node.PtrTypeBitRange{1636 .lhs = try p.addExtra(Node.PtrTypeBitRange{
1600 .sentinel = sentinel,1637 .sentinel = sentinel,
1601 .align_node = mods.align_node,1638 .align_node = mods.align_node,
1639 .addrspace_node = mods.addrspace_node,
1602 .bit_range_start = mods.bit_range_start,1640 .bit_range_start = mods.bit_range_start,
1603 .bit_range_end = mods.bit_range_end,1641 .bit_range_end = mods.bit_range_end,
1604 }),1642 }),
...@@ -1624,7 +1662,7 @@ const Parser = struct {...@@ -1624,7 +1662,7 @@ const Parser = struct {
1624 .token = p.nodes.items(.main_token)[mods.bit_range_start],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 return p.addNode(.{1666 return p.addNode(.{
1629 .tag = .ptr_type_aligned,1667 .tag = .ptr_type_aligned,
1630 .main_token = lbracket,1668 .main_token = lbracket,
...@@ -1633,7 +1671,7 @@ const Parser = struct {...@@ -1633,7 +1671,7 @@ const Parser = struct {
1633 .rhs = elem_type,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 return p.addNode(.{1675 return p.addNode(.{
1638 .tag = .ptr_type_sentinel,1676 .tag = .ptr_type_sentinel,
1639 .main_token = lbracket,1677 .main_token = lbracket,
...@@ -1650,6 +1688,7 @@ const Parser = struct {...@@ -1650,6 +1688,7 @@ const Parser = struct {
1650 .lhs = try p.addExtra(Node.PtrType{1688 .lhs = try p.addExtra(Node.PtrType{
1651 .sentinel = sentinel,1689 .sentinel = sentinel,
1652 .align_node = mods.align_node,1690 .align_node = mods.align_node,
1691 .addrspace_node = mods.addrspace_node,
1653 }),1692 }),
1654 .rhs = elem_type,1693 .rhs = elem_type,
1655 },1694 },
...@@ -1661,6 +1700,7 @@ const Parser = struct {...@@ -1661,6 +1700,7 @@ const Parser = struct {
1661 .keyword_const,1700 .keyword_const,
1662 .keyword_volatile,1701 .keyword_volatile,
1663 .keyword_allowzero,1702 .keyword_allowzero,
1703 .keyword_addrspace,
1664 => return p.fail(.ptr_mod_on_array_child_type),1704 => return p.fail(.ptr_mod_on_array_child_type),
1665 else => {},1705 else => {},
1666 }1706 }
...@@ -2879,6 +2919,15 @@ const Parser = struct {...@@ -2879,6 +2919,15 @@ const Parser = struct {
2879 return expr_node;2919 return expr_node;
2880 }2920 }
28812921
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 /// ParamDecl2931 /// ParamDecl
2883 /// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType2932 /// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
2884 /// / DOT32933 /// / DOT3
...@@ -3011,6 +3060,7 @@ const Parser = struct {...@@ -3011,6 +3060,7 @@ const Parser = struct {
30113060
3012 const PtrModifiers = struct {3061 const PtrModifiers = struct {
3013 align_node: Node.Index,3062 align_node: Node.Index,
3063 addrspace_node: Node.Index,
3014 bit_range_start: Node.Index,3064 bit_range_start: Node.Index,
3015 bit_range_end: Node.Index,3065 bit_range_end: Node.Index,
3016 };3066 };
...@@ -3018,12 +3068,14 @@ const Parser = struct {...@@ -3018,12 +3068,14 @@ const Parser = struct {
3018 fn parsePtrModifiers(p: *Parser) !PtrModifiers {3068 fn parsePtrModifiers(p: *Parser) !PtrModifiers {
3019 var result: PtrModifiers = .{3069 var result: PtrModifiers = .{
3020 .align_node = 0,3070 .align_node = 0,
3071 .addrspace_node = 0,
3021 .bit_range_start = 0,3072 .bit_range_start = 0,
3022 .bit_range_end = 0,3073 .bit_range_end = 0,
3023 };3074 };
3024 var saw_const = false;3075 var saw_const = false;
3025 var saw_volatile = false;3076 var saw_volatile = false;
3026 var saw_allowzero = false;3077 var saw_allowzero = false;
3078 var saw_addrspace = false;
3027 while (true) {3079 while (true) {
3028 switch (p.token_tags[p.tok_i]) {3080 switch (p.token_tags[p.tok_i]) {
3029 .keyword_align => {3081 .keyword_align => {
...@@ -3063,6 +3115,12 @@ const Parser = struct {...@@ -3063,6 +3115,12 @@ const Parser = struct {
3063 p.tok_i += 1;3115 p.tok_i += 1;
3064 saw_allowzero = true;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 else => return result,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,6 +404,10 @@ test "zig fmt: trailing comma in fn parameter list" {
404 \\pub fn f(404 \\pub fn f(
405 \\ a: i32,405 \\ a: i32,
406 \\ b: i32,406 \\ b: i32,
407 \\) addrspace(.generic) i32 {}
408 \\pub fn f(
409 \\ a: i32,
410 \\ b: i32,
407 \\) linksection(".text") i32 {}411 \\) linksection(".text") i32 {}
408 \\pub fn f(412 \\pub fn f(
409 \\ a: i32,413 \\ a: i32,
...@@ -553,8 +557,8 @@ test "zig fmt: sentinel-terminated slice type" {...@@ -553,8 +557,8 @@ test "zig fmt: sentinel-terminated slice type" {
553test "zig fmt: pointer-to-one with modifiers" {557test "zig fmt: pointer-to-one with modifiers" {
554 try testCanonical(558 try testCanonical(
555 \\const x: *u32 = undefined;559 \\const x: *u32 = undefined;
556 \\const y: *allowzero align(8) const volatile u32 = undefined;560 \\const y: *allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
557 \\const z: *allowzero align(8:4:2) 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,8 +566,8 @@ test "zig fmt: pointer-to-one with modifiers" {
562test "zig fmt: pointer-to-many with modifiers" {566test "zig fmt: pointer-to-many with modifiers" {
563 try testCanonical(567 try testCanonical(
564 \\const x: [*]u32 = undefined;568 \\const x: [*]u32 = undefined;
565 \\const y: [*]allowzero align(8) const volatile u32 = undefined;569 \\const y: [*]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
566 \\const z: [*]allowzero align(8:4:2) 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,8 +575,8 @@ test "zig fmt: pointer-to-many with modifiers" {
571test "zig fmt: sentinel pointer with modifiers" {575test "zig fmt: sentinel pointer with modifiers" {
572 try testCanonical(576 try testCanonical(
573 \\const x: [*:42]u32 = undefined;577 \\const x: [*:42]u32 = undefined;
574 \\const y: [*:42]allowzero align(8) const volatile u32 = undefined;578 \\const y: [*:42]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
575 \\const y: [*:42]allowzero align(8:4:2) 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,8 +584,8 @@ test "zig fmt: sentinel pointer with modifiers" {
580test "zig fmt: c pointer with modifiers" {584test "zig fmt: c pointer with modifiers" {
581 try testCanonical(585 try testCanonical(
582 \\const x: [*c]u32 = undefined;586 \\const x: [*c]u32 = undefined;
583 \\const y: [*c]allowzero align(8) const volatile u32 = undefined;587 \\const y: [*c]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
584 \\const z: [*c]allowzero align(8:4:2) 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,7 +593,7 @@ test "zig fmt: c pointer with modifiers" {
589test "zig fmt: slice with modifiers" {593test "zig fmt: slice with modifiers" {
590 try testCanonical(594 try testCanonical(
591 \\const x: []u32 = undefined;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,7 +601,7 @@ test "zig fmt: slice with modifiers" {
597test "zig fmt: sentinel slice with modifiers" {601test "zig fmt: sentinel slice with modifiers" {
598 try testCanonical(602 try testCanonical(
599 \\const x: [:42]u32 = undefined;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,6 +1133,16 @@ test "zig fmt: linksection" {
1129 );1133 );
1130}1134}
11311135
1136test "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
1132test "zig fmt: correctly space struct fields with doc comments" {1146test "zig fmt: correctly space struct fields with doc comments" {
1133 try testTransform(1147 try testTransform(
1134 \\pub const S = struct {1148 \\pub const S = struct {
...@@ -4725,6 +4739,26 @@ test "zig fmt: assignment with inline for and inline while" {...@@ -4725,6 +4739,26 @@ test "zig fmt: assignment with inline for and inline while" {
4725 );4739 );
4726}4740}
47274741
4742test "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
4728test "zig fmt: insert trailing comma if there are comments between switch values" {4762test "zig fmt: insert trailing comma if there are comments between switch values" {
4729 try testTransform(4763 try testTransform(
4730 \\const a = switch (b) {4764 \\const a = switch (b) {
...@@ -5225,6 +5259,14 @@ test "recovery: nonfinal varargs" {...@@ -5225,6 +5259,14 @@ test "recovery: nonfinal varargs" {
5225 });5259 });
5226}5260}
52275261
5262test "recovery: eof in c pointer" {
5263 try testError(
5264 \\const Ptr = [*c
5265 , &[_]Error{
5266 .expected_token,
5267 });
5268}
5269
5228const std = @import("std");5270const std = @import("std");
5229const mem = std.mem;5271const mem = std.mem;
5230const print = std.debug.print;5272const 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,27 +333,33 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
333333
334 .add,334 .add,
335 .add_wrap,335 .add_wrap,
336 .add_sat,
336 .array_cat,337 .array_cat,
337 .array_mult,338 .array_mult,
338 .assign,339 .assign,
339 .assign_bit_and,340 .assign_bit_and,
340 .assign_bit_or,341 .assign_bit_or,
341 .assign_bit_shift_left,342 .assign_shl,
342 .assign_bit_shift_right,343 .assign_shl_sat,
344 .assign_shr,
343 .assign_bit_xor,345 .assign_bit_xor,
344 .assign_div,346 .assign_div,
345 .assign_sub,347 .assign_sub,
346 .assign_sub_wrap,348 .assign_sub_wrap,
349 .assign_sub_sat,
347 .assign_mod,350 .assign_mod,
348 .assign_add,351 .assign_add,
349 .assign_add_wrap,352 .assign_add_wrap,
353 .assign_add_sat,
350 .assign_mul,354 .assign_mul,
351 .assign_mul_wrap,355 .assign_mul_wrap,
356 .assign_mul_sat,
352 .bang_equal,357 .bang_equal,
353 .bit_and,358 .bit_and,
354 .bit_or,359 .bit_or,
355 .bit_shift_left,360 .shl,
356 .bit_shift_right,361 .shl_sat,
362 .shr,
357 .bit_xor,363 .bit_xor,
358 .bool_and,364 .bool_and,
359 .bool_or,365 .bool_or,
...@@ -367,8 +373,10 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -367,8 +373,10 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
367 .mod,373 .mod,
368 .mul,374 .mul,
369 .mul_wrap,375 .mul_wrap,
376 .mul_sat,
370 .sub,377 .sub,
371 .sub_wrap,378 .sub_wrap,
379 .sub_sat,
372 .@"orelse",380 .@"orelse",
373 => {381 => {
374 const infix = datas[node];382 const infix = datas[node];
...@@ -797,6 +805,14 @@ fn renderPtrType(...@@ -797,6 +805,14 @@ fn renderPtrType(
797 }805 }
798 }806 }
799807
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 if (ptr_type.const_token) |const_token| {816 if (ptr_type.const_token) |const_token| {
801 try renderToken(ais, tree, const_token, .space);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,6 +937,7 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe
921937
922 const name_space = if (var_decl.ast.type_node == 0 and938 const name_space = if (var_decl.ast.type_node == 0 and
923 (var_decl.ast.align_node != 0 or939 (var_decl.ast.align_node != 0 or
940 var_decl.ast.addrspace_node != 0 or
924 var_decl.ast.section_node != 0 or941 var_decl.ast.section_node != 0 or
925 var_decl.ast.init_node != 0))942 var_decl.ast.init_node != 0))
926 Space.space943 Space.space
...@@ -930,8 +947,8 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe...@@ -930,8 +947,8 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe
930947
931 if (var_decl.ast.type_node != 0) {948 if (var_decl.ast.type_node != 0) {
932 try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // :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 or950 if (var_decl.ast.align_node != 0 or var_decl.ast.addrspace_node != 0 or
934 var_decl.ast.init_node != 0)951 var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0)
935 {952 {
936 try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space);953 try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space);
937 } else {954 } else {
...@@ -948,6 +965,23 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe...@@ -948,6 +965,23 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe
948 try renderToken(ais, tree, align_kw, Space.none); // align965 try renderToken(ais, tree, align_kw, Space.none); // align
949 try renderToken(ais, tree, lparen, Space.none); // (966 try renderToken(ais, tree, lparen, Space.none); // (
950 try renderExpression(gpa, ais, tree, var_decl.ast.align_node, Space.none);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 if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) {985 if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) {
952 try renderToken(ais, tree, rparen, .space); // )986 try renderToken(ais, tree, rparen, .space); // )
953 } else {987 } else {
...@@ -1267,6 +1301,14 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro...@@ -1267,6 +1301,14 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro
1267 smallest_start = start;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 if (fn_proto.ast.section_expr != 0) {1312 if (fn_proto.ast.section_expr != 0) {
1271 const tok = tree.firstToken(fn_proto.ast.section_expr) - 3;1313 const tok = tree.firstToken(fn_proto.ast.section_expr) - 3;
1272 const start = token_starts[tok];1314 const start = token_starts[tok];
...@@ -1407,6 +1449,16 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro...@@ -1407,6 +1449,16 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro
1407 try renderToken(ais, tree, align_rparen, .space); // )1449 try renderToken(ais, tree, align_rparen, .space); // )
1408 }1450 }
14091451
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 if (fn_proto.ast.section_expr != 0) {1462 if (fn_proto.ast.section_expr != 0) {
1411 const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;1463 const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;
1412 const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;1464 const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;
...@@ -2476,8 +2528,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {...@@ -2476,8 +2528,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
2476 .assign,2528 .assign,
2477 .assign_bit_and,2529 .assign_bit_and,
2478 .assign_bit_or,2530 .assign_bit_or,
2479 .assign_bit_shift_left,2531 .assign_shl,
2480 .assign_bit_shift_right,2532 .assign_shr,
2481 .assign_bit_xor,2533 .assign_bit_xor,
2482 .assign_div,2534 .assign_div,
2483 .assign_sub,2535 .assign_sub,
...@@ -2490,8 +2542,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {...@@ -2490,8 +2542,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
2490 .bang_equal,2542 .bang_equal,
2491 .bit_and,2543 .bit_and,
2492 .bit_or,2544 .bit_or,
2493 .bit_shift_left,2545 .shl,
2494 .bit_shift_right,2546 .shr,
2495 .bit_xor,2547 .bit_xor,
2496 .bool_and,2548 .bool_and,
2497 .bool_or,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,7 +29,7 @@ pub fn parseAppend(buf: *std.ArrayList(u8), bytes: []const u8) error{OutOfMemory
29 const slice = bytes[1..];29 const slice = bytes[1..];
3030
31 const prev_len = buf.items.len;31 const prev_len = buf.items.len;
32 try buf.ensureCapacity(prev_len + slice.len - 1);32 try buf.ensureUnusedCapacity(slice.len - 1);
33 errdefer buf.shrinkRetainingCapacity(prev_len);33 errdefer buf.shrinkRetainingCapacity(prev_len);
3434
35 const State = enum {35 const State = enum {
lib/std/zig/system.zig+23
...@@ -101,6 +101,17 @@ pub const NativePaths = struct {...@@ -101,6 +101,17 @@ pub const NativePaths = struct {
101 return self;101 return self;
102 }102 }
103103
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 if (native_target.os.tag != .windows) {115 if (native_target.os.tag != .windows) {
105 const triple = try native_target.linuxTriple(allocator);116 const triple = try native_target.linuxTriple(allocator);
106 const qual = native_target.cpu.arch.ptrBitWidth();117 const qual = native_target.cpu.arch.ptrBitWidth();
...@@ -243,6 +254,18 @@ pub const NativeTargetInfo = struct {...@@ -243,6 +254,18 @@ pub const NativeTargetInfo = struct {
243 error.InvalidVersion => {},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 .windows => {269 .windows => {
247 const detected_version = windows.detectRuntimeVersion();270 const detected_version = windows.detectRuntimeVersion();
248 os.version_range.windows.min = detected_version;271 os.version_range.windows.min = detected_version;
lib/std/zig/tokenizer.zig+115-1
...@@ -11,6 +11,7 @@ pub const Token = struct {...@@ -11,6 +11,7 @@ pub const Token = struct {
11 };11 };
1212
13 pub const keywords = std.ComptimeStringMap(Tag, .{13 pub const keywords = std.ComptimeStringMap(Tag, .{
14 .{ "addrspace", .keyword_addrspace },
14 .{ "align", .keyword_align },15 .{ "align", .keyword_align },
15 .{ "allowzero", .keyword_allowzero },16 .{ "allowzero", .keyword_allowzero },
16 .{ "and", .keyword_and },17 .{ "and", .keyword_and },
...@@ -102,15 +103,21 @@ pub const Token = struct {...@@ -102,15 +103,21 @@ pub const Token = struct {
102 plus_equal,103 plus_equal,
103 plus_percent,104 plus_percent,
104 plus_percent_equal,105 plus_percent_equal,
106 plus_pipe,
107 plus_pipe_equal,
105 minus,108 minus,
106 minus_equal,109 minus_equal,
107 minus_percent,110 minus_percent,
108 minus_percent_equal,111 minus_percent_equal,
112 minus_pipe,
113 minus_pipe_equal,
109 asterisk,114 asterisk,
110 asterisk_equal,115 asterisk_equal,
111 asterisk_asterisk,116 asterisk_asterisk,
112 asterisk_percent,117 asterisk_percent,
113 asterisk_percent_equal,118 asterisk_percent_equal,
119 asterisk_pipe,
120 asterisk_pipe_equal,
114 arrow,121 arrow,
115 colon,122 colon,
116 slash,123 slash,
...@@ -123,6 +130,8 @@ pub const Token = struct {...@@ -123,6 +130,8 @@ pub const Token = struct {
123 angle_bracket_left_equal,130 angle_bracket_left_equal,
124 angle_bracket_angle_bracket_left,131 angle_bracket_angle_bracket_left,
125 angle_bracket_angle_bracket_left_equal,132 angle_bracket_angle_bracket_left_equal,
133 angle_bracket_angle_bracket_left_pipe,
134 angle_bracket_angle_bracket_left_pipe_equal,
126 angle_bracket_right,135 angle_bracket_right,
127 angle_bracket_right_equal,136 angle_bracket_right_equal,
128 angle_bracket_angle_bracket_right,137 angle_bracket_angle_bracket_right,
...@@ -132,6 +141,7 @@ pub const Token = struct {...@@ -132,6 +141,7 @@ pub const Token = struct {
132 float_literal,141 float_literal,
133 doc_comment,142 doc_comment,
134 container_doc_comment,143 container_doc_comment,
144 keyword_addrspace,
135 keyword_align,145 keyword_align,
136 keyword_allowzero,146 keyword_allowzero,
137 keyword_and,147 keyword_and,
...@@ -225,15 +235,21 @@ pub const Token = struct {...@@ -225,15 +235,21 @@ pub const Token = struct {
225 .plus_equal => "+=",235 .plus_equal => "+=",
226 .plus_percent => "+%",236 .plus_percent => "+%",
227 .plus_percent_equal => "+%=",237 .plus_percent_equal => "+%=",
238 .plus_pipe => "+|",
239 .plus_pipe_equal => "+|=",
228 .minus => "-",240 .minus => "-",
229 .minus_equal => "-=",241 .minus_equal => "-=",
230 .minus_percent => "-%",242 .minus_percent => "-%",
231 .minus_percent_equal => "-%=",243 .minus_percent_equal => "-%=",
244 .minus_pipe => "-|",
245 .minus_pipe_equal => "-|=",
232 .asterisk => "*",246 .asterisk => "*",
233 .asterisk_equal => "*=",247 .asterisk_equal => "*=",
234 .asterisk_asterisk => "**",248 .asterisk_asterisk => "**",
235 .asterisk_percent => "*%",249 .asterisk_percent => "*%",
236 .asterisk_percent_equal => "*%=",250 .asterisk_percent_equal => "*%=",
251 .asterisk_pipe => "*|",
252 .asterisk_pipe_equal => "*|=",
237 .arrow => "->",253 .arrow => "->",
238 .colon => ":",254 .colon => ":",
239 .slash => "/",255 .slash => "/",
...@@ -246,11 +262,14 @@ pub const Token = struct {...@@ -246,11 +262,14 @@ pub const Token = struct {
246 .angle_bracket_left_equal => "<=",262 .angle_bracket_left_equal => "<=",
247 .angle_bracket_angle_bracket_left => "<<",263 .angle_bracket_angle_bracket_left => "<<",
248 .angle_bracket_angle_bracket_left_equal => "<<=",264 .angle_bracket_angle_bracket_left_equal => "<<=",
265 .angle_bracket_angle_bracket_left_pipe => "<<|",
266 .angle_bracket_angle_bracket_left_pipe_equal => "<<|=",
249 .angle_bracket_right => ">",267 .angle_bracket_right => ">",
250 .angle_bracket_right_equal => ">=",268 .angle_bracket_right_equal => ">=",
251 .angle_bracket_angle_bracket_right => ">>",269 .angle_bracket_angle_bracket_right => ">>",
252 .angle_bracket_angle_bracket_right_equal => ">>=",270 .angle_bracket_angle_bracket_right_equal => ">>=",
253 .tilde => "~",271 .tilde => "~",
272 .keyword_addrspace => "addrspace",
254 .keyword_align => "align",273 .keyword_align => "align",
255 .keyword_allowzero => "allowzero",274 .keyword_allowzero => "allowzero",
256 .keyword_and => "and",275 .keyword_and => "and",
...@@ -349,8 +368,10 @@ pub const Tokenizer = struct {...@@ -349,8 +368,10 @@ pub const Tokenizer = struct {
349 pipe,368 pipe,
350 minus,369 minus,
351 minus_percent,370 minus_percent,
371 minus_pipe,
352 asterisk,372 asterisk,
353 asterisk_percent,373 asterisk_percent,
374 asterisk_pipe,
354 slash,375 slash,
355 line_comment_start,376 line_comment_start,
356 line_comment,377 line_comment,
...@@ -379,8 +400,10 @@ pub const Tokenizer = struct {...@@ -379,8 +400,10 @@ pub const Tokenizer = struct {
379 percent,400 percent,
380 plus,401 plus,
381 plus_percent,402 plus_percent,
403 plus_pipe,
382 angle_bracket_left,404 angle_bracket_left,
383 angle_bracket_angle_bracket_left,405 angle_bracket_angle_bracket_left,
406 angle_bracket_angle_bracket_left_pipe,
384 angle_bracket_right,407 angle_bracket_right,
385 angle_bracket_angle_bracket_right,408 angle_bracket_angle_bracket_right,
386 period,409 period,
...@@ -581,6 +604,9 @@ pub const Tokenizer = struct {...@@ -581,6 +604,9 @@ pub const Tokenizer = struct {
581 '%' => {604 '%' => {
582 state = .asterisk_percent;605 state = .asterisk_percent;
583 },606 },
607 '|' => {
608 state = .asterisk_pipe;
609 },
584 else => {610 else => {
585 result.tag = .asterisk;611 result.tag = .asterisk;
586 break;612 break;
...@@ -599,6 +625,18 @@ pub const Tokenizer = struct {...@@ -599,6 +625,18 @@ pub const Tokenizer = struct {
599 },625 },
600 },626 },
601627
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 .percent => switch (c) {640 .percent => switch (c) {
603 '=' => {641 '=' => {
604 result.tag = .percent_equal;642 result.tag = .percent_equal;
...@@ -625,6 +663,9 @@ pub const Tokenizer = struct {...@@ -625,6 +663,9 @@ pub const Tokenizer = struct {
625 '%' => {663 '%' => {
626 state = .plus_percent;664 state = .plus_percent;
627 },665 },
666 '|' => {
667 state = .plus_pipe;
668 },
628 else => {669 else => {
629 result.tag = .plus;670 result.tag = .plus;
630 break;671 break;
...@@ -643,6 +684,18 @@ pub const Tokenizer = struct {...@@ -643,6 +684,18 @@ pub const Tokenizer = struct {
643 },684 },
644 },685 },
645686
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 .caret => switch (c) {699 .caret => switch (c) {
647 '=' => {700 '=' => {
648 result.tag = .caret_equal;701 result.tag = .caret_equal;
...@@ -700,7 +753,7 @@ pub const Tokenizer = struct {...@@ -700,7 +753,7 @@ pub const Tokenizer = struct {
700 },753 },
701754
702 .string_literal_backslash => switch (c) {755 .string_literal_backslash => switch (c) {
703 '\n' => {756 0, '\n' => {
704 result.tag = .invalid;757 result.tag = .invalid;
705 break;758 break;
706 },759 },
...@@ -769,6 +822,10 @@ pub const Tokenizer = struct {...@@ -769,6 +822,10 @@ pub const Tokenizer = struct {
769 },822 },
770823
771 .char_literal_unicode_escape_saw_u => switch (c) {824 .char_literal_unicode_escape_saw_u => switch (c) {
825 0 => {
826 result.tag = .invalid;
827 break;
828 },
772 '{' => {829 '{' => {
773 state = .char_literal_unicode_escape;830 state = .char_literal_unicode_escape;
774 },831 },
...@@ -779,6 +836,10 @@ pub const Tokenizer = struct {...@@ -779,6 +836,10 @@ pub const Tokenizer = struct {
779 },836 },
780837
781 .char_literal_unicode_escape => switch (c) {838 .char_literal_unicode_escape => switch (c) {
839 0 => {
840 result.tag = .invalid;
841 break;
842 },
782 '0'...'9', 'a'...'f', 'A'...'F' => {},843 '0'...'9', 'a'...'f', 'A'...'F' => {},
783 '}' => {844 '}' => {
784 state = .char_literal_end; // too many/few digits handled later845 state = .char_literal_end; // too many/few digits handled later
...@@ -892,6 +953,9 @@ pub const Tokenizer = struct {...@@ -892,6 +953,9 @@ pub const Tokenizer = struct {
892 '%' => {953 '%' => {
893 state = .minus_percent;954 state = .minus_percent;
894 },955 },
956 '|' => {
957 state = .minus_pipe;
958 },
895 else => {959 else => {
896 result.tag = .minus;960 result.tag = .minus;
897 break;961 break;
...@@ -909,6 +973,17 @@ pub const Tokenizer = struct {...@@ -909,6 +973,17 @@ pub const Tokenizer = struct {
909 break;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 },
912987
913 .angle_bracket_left => switch (c) {988 .angle_bracket_left => switch (c) {
914 '<' => {989 '<' => {
...@@ -931,12 +1006,27 @@ pub const Tokenizer = struct {...@@ -931,12 +1006,27 @@ pub const Tokenizer = struct {
931 self.index += 1;1006 self.index += 1;
932 break;1007 break;
933 },1008 },
1009 '|' => {
1010 state = .angle_bracket_angle_bracket_left_pipe;
1011 },
934 else => {1012 else => {
935 result.tag = .angle_bracket_angle_bracket_left;1013 result.tag = .angle_bracket_angle_bracket_left;
936 break;1014 break;
937 },1015 },
938 },1016 },
9391017
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 .angle_bracket_right => switch (c) {1030 .angle_bracket_right => switch (c) {
941 '>' => {1031 '>' => {
942 state = .angle_bracket_angle_bracket_right;1032 state = .angle_bracket_angle_bracket_right;
...@@ -1919,6 +2009,30 @@ test "tokenizer - invalid builtin identifiers" {...@@ -1919,6 +2009,30 @@ test "tokenizer - invalid builtin identifiers" {
1919 try testTokenize("@0()", &.{ .invalid, .integer_literal, .l_paren, .r_paren });2009 try testTokenize("@0()", &.{ .invalid, .integer_literal, .l_paren, .r_paren });
1920}2010}
19212011
2012test "tokenizer - invalid token with unfinished escape right before eof" {
2013 try testTokenize("\"\\", &.{.invalid});
2014 try testTokenize("'\\", &.{.invalid});
2015 try testTokenize("'\\u", &.{.invalid});
2016}
2017
2018test "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
1922fn testTokenize(source: [:0]const u8, expected_tokens: []const Token.Tag) !void {2036fn testTokenize(source: [:0]const u8, expected_tokens: []const Token.Tag) !void {
1923 var tokenizer = Tokenizer.init(source);2037 var tokenizer = Tokenizer.init(source);
1924 for (expected_tokens) |expected_token_id| {2038 for (expected_tokens) |expected_token_id| {
src/Air.zig+150-8
...@@ -44,6 +44,11 @@ pub const Inst = struct {...@@ -44,6 +44,11 @@ pub const Inst = struct {
44 /// is the same as both operands.44 /// is the same as both operands.
45 /// Uses the `bin_op` field.45 /// Uses the `bin_op` field.
46 addwrap,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 /// Float or integer subtraction. For integers, wrapping is undefined behavior.52 /// Float or integer subtraction. For integers, wrapping is undefined behavior.
48 /// Both operands are guaranteed to be the same type, and the result type53 /// Both operands are guaranteed to be the same type, and the result type
49 /// is the same as both operands.54 /// is the same as both operands.
...@@ -54,6 +59,11 @@ pub const Inst = struct {...@@ -54,6 +59,11 @@ pub const Inst = struct {
54 /// is the same as both operands.59 /// is the same as both operands.
55 /// Uses the `bin_op` field.60 /// Uses the `bin_op` field.
56 subwrap,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 /// Float or integer multiplication. For integers, wrapping is undefined behavior.67 /// Float or integer multiplication. For integers, wrapping is undefined behavior.
58 /// Both operands are guaranteed to be the same type, and the result type68 /// Both operands are guaranteed to be the same type, and the result type
59 /// is the same as both operands.69 /// is the same as both operands.
...@@ -64,15 +74,26 @@ pub const Inst = struct {...@@ -64,15 +74,26 @@ pub const Inst = struct {
64 /// is the same as both operands.74 /// is the same as both operands.
65 /// Uses the `bin_op` field.75 /// Uses the `bin_op` field.
66 mulwrap,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 /// Integer or float division. For integers, wrapping is undefined behavior.82 /// Integer or float division. For integers, wrapping is undefined behavior.
68 /// Both operands are guaranteed to be the same type, and the result type83 /// Both operands are guaranteed to be the same type, and the result type
69 /// is the same as both operands.84 /// is the same as both operands.
70 /// Uses the `bin_op` field.85 /// Uses the `bin_op` field.
71 div,86 div,
72 /// Integer or float remainder.87 /// Integer or float remainder division.
73 /// Both operands are guaranteed to be the same type, and the result type is the same as both operands.88 /// Both operands are guaranteed to be the same type, and the result type
89 /// is the same as both operands.
74 /// Uses the `bin_op` field.90 /// Uses the `bin_op` field.
75 rem,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 /// Add an offset to a pointer, returning a new pointer.97 /// Add an offset to a pointer, returning a new pointer.
77 /// The offset is in element type units, not bytes.98 /// The offset is in element type units, not bytes.
78 /// Wrapping is undefined behavior.99 /// Wrapping is undefined behavior.
...@@ -104,6 +125,14 @@ pub const Inst = struct {...@@ -104,6 +125,14 @@ pub const Inst = struct {
104 /// Shift left. `<<`125 /// Shift left. `<<`
105 /// Uses the `bin_op` field.126 /// Uses the `bin_op` field.
106 shl,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 /// Bitwise XOR. `^`136 /// Bitwise XOR. `^`
108 /// Uses the `bin_op` field.137 /// Uses the `bin_op` field.
109 xor,138 xor,
...@@ -131,6 +160,15 @@ pub const Inst = struct {...@@ -131,6 +160,15 @@ pub const Inst = struct {
131 /// Result type is the return type of the function being called.160 /// Result type is the return type of the function being called.
132 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.161 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.
133 call,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 /// `<`. Result type is always bool.172 /// `<`. Result type is always bool.
135 /// Uses the `bin_op` field.173 /// Uses the `bin_op` field.
136 cmp_lt,174 cmp_lt,
...@@ -149,6 +187,7 @@ pub const Inst = struct {...@@ -149,6 +187,7 @@ pub const Inst = struct {
149 /// `!=`. Result type is always bool.187 /// `!=`. Result type is always bool.
150 /// Uses the `bin_op` field.188 /// Uses the `bin_op` field.
151 cmp_neq,189 cmp_neq,
190
152 /// Conditional branch.191 /// Conditional branch.
153 /// Result type is always noreturn; no instructions in a block follow this one.192 /// Result type is always noreturn; no instructions in a block follow this one.
154 /// Uses the `pl_op` field. Operand is the condition. Payload is `CondBr`.193 /// Uses the `pl_op` field. Operand is the condition. Payload is `CondBr`.
...@@ -225,9 +264,12 @@ pub const Inst = struct {...@@ -225,9 +264,12 @@ pub const Inst = struct {
225 /// Indicates the program counter will never get to this instruction.264 /// Indicates the program counter will never get to this instruction.
226 /// Result type is always noreturn; no instructions in a block follow this one.265 /// Result type is always noreturn; no instructions in a block follow this one.
227 unreach,266 unreach,
228 /// Convert from one float type to another.267 /// Convert from a float type to a smaller one.
229 /// Uses the `ty_op` field.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 /// Returns an integer with a different type than the operand. The new type may have273 /// Returns an integer with a different type than the operand. The new type may have
232 /// fewer, the same, or more bits than the operand type. However, the instruction274 /// fewer, the same, or more bits than the operand type. However, the instruction
233 /// guarantees that the same integer value fits in both types.275 /// guarantees that the same integer value fits in both types.
...@@ -265,19 +307,29 @@ pub const Inst = struct {...@@ -265,19 +307,29 @@ pub const Inst = struct {
265 /// wrap from E to E!T307 /// wrap from E to E!T
266 /// Uses the `ty_op` field.308 /// Uses the `ty_op` field.
267 wrap_errunion_err,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 /// Uses the `ty_pl` field, payload is `StructField`.311 /// Uses the `ty_pl` field, payload is `StructField`.
312 /// TODO rename to `agg_field_ptr`.
270 struct_field_ptr,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 /// The field index is the number at the end of the name.315 /// The field index is the number at the end of the name.
273 /// Uses `ty_op` field.316 /// Uses `ty_op` field.
317 /// TODO rename to `agg_field_ptr_index_X`
274 struct_field_ptr_index_0,318 struct_field_ptr_index_0,
275 struct_field_ptr_index_1,319 struct_field_ptr_index_1,
276 struct_field_ptr_index_2,320 struct_field_ptr_index_2,
277 struct_field_ptr_index_3,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 /// Uses the `ty_pl` field, payload is `StructField`.323 /// Uses the `ty_pl` field, payload is `StructField`.
324 /// TODO rename to `agg_field_val`
280 struct_field_val,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 /// Given a slice value, return the length.333 /// Given a slice value, return the length.
282 /// Result type is always usize.334 /// Result type is always usize.
283 /// Uses the `ty_op` field.335 /// Uses the `ty_op` field.
...@@ -309,10 +361,52 @@ pub const Inst = struct {...@@ -309,10 +361,52 @@ pub const Inst = struct {
309 /// Given a pointer to an array, return a slice.361 /// Given a pointer to an array, return a slice.
310 /// Uses the `ty_op` field.362 /// Uses the `ty_op` field.
311 array_to_slice,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 /// Uses the `ty_pl` field with payload `Cmpxchg`.384 /// Uses the `ty_pl` field with payload `Cmpxchg`.
313 cmpxchg_weak,385 cmpxchg_weak,
314 /// Uses the `ty_pl` field with payload `Cmpxchg`.386 /// Uses the `ty_pl` field with payload `Cmpxchg`.
315 cmpxchg_strong,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,
316410
317 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {411 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
318 return switch (op) {412 return switch (op) {
...@@ -380,6 +474,11 @@ pub const Inst = struct {...@@ -380,6 +474,11 @@ pub const Inst = struct {
380 line: u32,474 line: u32,
381 column: u32,475 column: u32,
382 },476 },
477 fence: std.builtin.AtomicOrder,
478 atomic_load: struct {
479 ptr: Ref,
480 order: std.builtin.AtomicOrder,
481 },
383482
384 // Make sure we don't accidentally add a field to make this union483 // Make sure we don't accidentally add a field to make this union
385 // bigger than expected. Note that in Debug builds, Zig is allowed484 // bigger than expected. Note that in Debug builds, Zig is allowed
...@@ -464,6 +563,21 @@ pub const Cmpxchg = struct {...@@ -464,6 +563,21 @@ pub const Cmpxchg = struct {
464 }563 }
465};564};
466565
566pub 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
467pub fn getMainBody(air: Air) []const Air.Inst.Index {581pub fn getMainBody(air: Air) []const Air.Inst.Index {
468 const body_index = air.extra[@enumToInt(ExtraIndex.main_block)];582 const body_index = air.extra[@enumToInt(ExtraIndex.main_block)];
469 const extra = air.extraData(Block, body_index);583 const extra = air.extraData(Block, body_index);
...@@ -485,12 +599,16 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -485,12 +599,16 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
485599
486 .add,600 .add,
487 .addwrap,601 .addwrap,
602 .add_sat,
488 .sub,603 .sub,
489 .subwrap,604 .subwrap,
605 .sub_sat,
490 .mul,606 .mul,
491 .mulwrap,607 .mulwrap,
608 .mul_sat,
492 .div,609 .div,
493 .rem,610 .rem,
611 .mod,
494 .bit_and,612 .bit_and,
495 .bit_or,613 .bit_or,
496 .xor,614 .xor,
...@@ -498,6 +616,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -498,6 +616,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
498 .ptr_sub,616 .ptr_sub,
499 .shr,617 .shr,
500 .shl,618 .shl,
619 .shl_exact,
620 .shl_sat,
501 => return air.typeOf(datas[inst].bin_op.lhs),621 => return air.typeOf(datas[inst].bin_op.lhs),
502622
503 .cmp_lt,623 .cmp_lt,
...@@ -535,7 +655,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -535,7 +655,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
535 .not,655 .not,
536 .bitcast,656 .bitcast,
537 .load,657 .load,
538 .floatcast,658 .fpext,
659 .fptrunc,
539 .intcast,660 .intcast,
540 .trunc,661 .trunc,
541 .optional_payload,662 .optional_payload,
...@@ -553,6 +674,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -553,6 +674,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
553 .struct_field_ptr_index_2,674 .struct_field_ptr_index_2,
554 .struct_field_ptr_index_3,675 .struct_field_ptr_index_3,
555 .array_to_slice,676 .array_to_slice,
677 .float_to_int,
678 .int_to_float,
679 .get_union_tag,
680 .clz,
681 .ctz,
556 => return air.getRefType(datas[inst].ty_op.ty),682 => return air.getRefType(datas[inst].ty_op.ty),
557683
558 .loop,684 .loop,
...@@ -566,6 +692,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -566,6 +692,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
566 .breakpoint,692 .breakpoint,
567 .dbg_stmt,693 .dbg_stmt,
568 .store,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 => return Type.initTag(.void),703 => return Type.initTag(.void),
570704
571 .ptrtoint,705 .ptrtoint,
...@@ -588,6 +722,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -588,6 +722,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
588 const inner_ptr_ty = outer_ptr_ty.elemType();722 const inner_ptr_ty = outer_ptr_ty.elemType();
589 return inner_ptr_ty.elemType();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}
593735
src/AstGen.zig+691-261
...@@ -56,6 +56,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {...@@ -56,6 +56,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
56 u32 => @field(extra, field.name),56 u32 => @field(extra, field.name),
57 Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),57 Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
58 i32 => @bitCast(u32, @field(extra, field.name)),58 i32 => @bitCast(u32, @field(extra, field.name)),
59 Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),
59 else => @compileError("bad field type"),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,7 +125,7 @@ pub fn generate(gpa: *Allocator, tree: Ast) Allocator.Error!Zir {
124 container_decl,125 container_decl,
125 .Auto,126 .Auto,
126 )) |struct_decl_ref| {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 } else |err| switch (err) {129 } else |err| switch (err) {
129 error.OutOfMemory => return error.OutOfMemory,130 error.OutOfMemory => return error.OutOfMemory,
130 error.AnalysisFail => {}, // Handled via compile_errors below.131 error.AnalysisFail => {}, // Handled via compile_errors below.
...@@ -197,7 +198,7 @@ pub const ResultLoc = union(enum) {...@@ -197,7 +198,7 @@ pub const ResultLoc = union(enum) {
197 none_or_ref,198 none_or_ref,
198 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.199 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
199 ty: Zir.Inst.Ref,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 /// so no `as` instruction needs to be emitted.202 /// so no `as` instruction needs to be emitted.
202 coerced_ty: Zir.Inst.Ref,203 coerced_ty: Zir.Inst.Ref,
203 /// The expression must store its result into this typed pointer. The result instruction204 /// 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,29 +317,37 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
316 .assign,317 .assign,
317 .assign_bit_and,318 .assign_bit_and,
318 .assign_bit_or,319 .assign_bit_or,
319 .assign_bit_shift_left,320 .assign_shl,
320 .assign_bit_shift_right,321 .assign_shl_sat,
322 .assign_shr,
321 .assign_bit_xor,323 .assign_bit_xor,
322 .assign_div,324 .assign_div,
323 .assign_sub,325 .assign_sub,
324 .assign_sub_wrap,326 .assign_sub_wrap,
327 .assign_sub_sat,
325 .assign_mod,328 .assign_mod,
326 .assign_add,329 .assign_add,
327 .assign_add_wrap,330 .assign_add_wrap,
331 .assign_add_sat,
328 .assign_mul,332 .assign_mul,
329 .assign_mul_wrap,333 .assign_mul_wrap,
334 .assign_mul_sat,
330 .add,335 .add,
331 .add_wrap,336 .add_wrap,
337 .add_sat,
332 .sub,338 .sub,
333 .sub_wrap,339 .sub_wrap,
340 .sub_sat,
334 .mul,341 .mul,
335 .mul_wrap,342 .mul_wrap,
343 .mul_sat,
336 .div,344 .div,
337 .mod,345 .mod,
338 .bit_and,346 .bit_and,
339 .bit_or,347 .bit_or,
340 .bit_shift_left,348 .shl,
341 .bit_shift_right,349 .shl_sat,
350 .shr,
342 .bit_xor,351 .bit_xor,
343 .bang_equal,352 .bang_equal,
344 .equal_equal,353 .equal_equal,
...@@ -479,7 +488,7 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins...@@ -479,7 +488,7 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
479 return expr(gz, scope, .ref, node);488 return expr(gz, scope, .ref, node);
480}489}
481490
482/// Turn Zig AST into untyped ZIR istructions.491/// Turn Zig AST into untyped ZIR instructions.
483/// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the492/// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
484/// result instruction can be used to inspect whether it is isNoReturn() but that is it,493/// result instruction can be used to inspect whether it is isNoReturn() but that is it,
485/// it must otherwise not be used.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,11 +530,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
521 return rvalue(gz, rl, .void_value, node);530 return rvalue(gz, rl, .void_value, node);
522 },531 },
523532
524 .assign_bit_shift_left => {533 .assign_shl => {
525 try assignShift(gz, scope, node, .shl);534 try assignShift(gz, scope, node, .shl);
526 return rvalue(gz, rl, .void_value, node);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 try assignShift(gz, scope, node, .shr);542 try assignShift(gz, scope, node, .shr);
530 return rvalue(gz, rl, .void_value, node);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,6 +567,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
554 try assignOp(gz, scope, node, .subwrap);567 try assignOp(gz, scope, node, .subwrap);
555 return rvalue(gz, rl, .void_value, node);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 .assign_mod => {574 .assign_mod => {
558 try assignOp(gz, scope, node, .mod_rem);575 try assignOp(gz, scope, node, .mod_rem);
559 return rvalue(gz, rl, .void_value, node);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,6 +583,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
566 try assignOp(gz, scope, node, .addwrap);583 try assignOp(gz, scope, node, .addwrap);
567 return rvalue(gz, rl, .void_value, node);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 .assign_mul => {590 .assign_mul => {
570 try assignOp(gz, scope, node, .mul);591 try assignOp(gz, scope, node, .mul);
571 return rvalue(gz, rl, .void_value, node);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,19 +595,28 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
574 try assignOp(gz, scope, node, .mulwrap);595 try assignOp(gz, scope, node, .mulwrap);
575 return rvalue(gz, rl, .void_value, node);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 },
577602
578 // zig fmt: off603 // zig fmt: off
579 .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),604 .shl => 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),605 .shr => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
581606
582 .add => return simpleBinOp(gz, scope, rl, node, .add),607 .add => return simpleBinOp(gz, scope, rl, node, .add),
583 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),608 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),
609 .add_sat => return simpleBinOp(gz, scope, rl, node, .add_sat),
584 .sub => return simpleBinOp(gz, scope, rl, node, .sub),610 .sub => return simpleBinOp(gz, scope, rl, node, .sub),
585 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),611 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),
612 .sub_sat => return simpleBinOp(gz, scope, rl, node, .sub_sat),
586 .mul => return simpleBinOp(gz, scope, rl, node, .mul),613 .mul => return simpleBinOp(gz, scope, rl, node, .mul),
587 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),614 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),
615 .mul_sat => return simpleBinOp(gz, scope, rl, node, .mul_sat),
588 .div => return simpleBinOp(gz, scope, rl, node, .div),616 .div => return simpleBinOp(gz, scope, rl, node, .div),
589 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),617 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),
618 .shl_sat => return simpleBinOp(gz, scope, rl, node, .shl_sat),
619
590 .bit_and => {620 .bit_and => {
591 const current_ampersand_token = main_tokens[node];621 const current_ampersand_token = main_tokens[node];
592 if (token_tags[current_ampersand_token + 1] == .ampersand) {622 if (token_tags[current_ampersand_token + 1] == .ampersand) {
...@@ -1116,6 +1146,11 @@ fn fnProtoExpr(...@@ -1116,6 +1146,11 @@ fn fnProtoExpr(
1116 const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {1146 const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
1117 break :inst try expr(gz, scope, align_rl, fn_proto.ast.align_expr);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 if (fn_proto.ast.section_expr != 0) {1154 if (fn_proto.ast.section_expr != 0) {
1120 return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{});1155 return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{});
1121 }1156 }
...@@ -1352,7 +1387,14 @@ fn structInitExpr(...@@ -1352,7 +1387,14 @@ fn structInitExpr(
1352 const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) {1387 const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) {
1353 .array_type => tree.arrayType(struct_init.ast.type_expr),1388 .array_type => tree.arrayType(struct_init.ast.type_expr),
1354 .array_type_sentinel => tree.arrayTypeSentinel(struct_init.ast.type_expr),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 const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and1399 const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
1358 // This intentionally does not support `@"_"` syntax.1400 // This intentionally does not support `@"_"` syntax.
...@@ -1414,8 +1456,8 @@ fn structInitExpr(...@@ -1414,8 +1456,8 @@ fn structInitExpr(
1414 const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init);1456 const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init);
1415 return rvalue(gz, rl, result, node);1457 return rvalue(gz, rl, result, node);
1416 },1458 },
1417 .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst),1459 .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst),
1418 .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, node, struct_init, block_gz.rl_ptr),1460 .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr),
1419 }1461 }
1420}1462}
14211463
...@@ -1454,6 +1496,26 @@ fn structInitExprRlNone(...@@ -1454,6 +1496,26 @@ fn structInitExprRlNone(
1454}1496}
14551497
1456fn structInitExprRlPtr(1498fn 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
1518fn structInitExprRlPtrInner(
1457 gz: *GenZir,1519 gz: *GenZir,
1458 scope: *Scope,1520 scope: *Scope,
1459 node: Ast.Node.Index,1521 node: Ast.Node.Index,
...@@ -1467,9 +1529,6 @@ fn structInitExprRlPtr(...@@ -1467,9 +1529,6 @@ fn structInitExprRlPtr(
1467 const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len);1529 const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len);
1468 defer gpa.free(field_ptr_list);1530 defer gpa.free(field_ptr_list);
14691531
1470 if (struct_init.ast.type_expr != 0)
1471 _ = try typeExpr(gz, scope, struct_init.ast.type_expr);
1472
1473 for (struct_init.ast.fields) |field_init, i| {1532 for (struct_init.ast.fields) |field_init, i| {
1474 const name_token = tree.firstToken(field_init) - 2;1533 const name_token = tree.firstToken(field_init) - 2;
1475 const str_index = try astgen.identAsString(name_token);1534 const str_index = try astgen.identAsString(name_token);
...@@ -1484,7 +1543,7 @@ fn structInitExprRlPtr(...@@ -1484,7 +1543,7 @@ fn structInitExprRlPtr(
1484 .body_len = @intCast(u32, field_ptr_list.len),1543 .body_len = @intCast(u32, field_ptr_list.len),
1485 });1544 });
1486 try astgen.extra.appendSlice(gpa, field_ptr_list);1545 try astgen.extra.appendSlice(gpa, field_ptr_list);
1487 return .void_value;1546 return Zir.Inst.Ref.void_value;
1488}1547}
14891548
1490fn structInitExprRlTy(1549fn structInitExprRlTy(
...@@ -1868,8 +1927,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod...@@ -1868,8 +1927,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
18681927
1869 .assign => try assign(gz, scope, statement),1928 .assign => try assign(gz, scope, statement),
18701929
1871 .assign_bit_shift_left => try assignShift(gz, scope, statement, .shl),1930 .assign_shl => try assignShift(gz, scope, statement, .shl),
1872 .assign_bit_shift_right => try assignShift(gz, scope, statement, .shr),1931 .assign_shr => try assignShift(gz, scope, statement, .shr),
18731932
1874 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),1933 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),
1875 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or),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,17 +1964,21 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
1905 // in the above while loop.1964 // in the above while loop.
1906 const zir_tags = gz.astgen.instructions.items(.tag);1965 const zir_tags = gz.astgen.instructions.items(.tag);
1907 switch (zir_tags[inst]) {1966 switch (zir_tags[inst]) {
1908 // For some instructions, swap in a slightly different ZIR tag1967 // For some instructions, modify the zir data
1909 // so we can avoid a separate ensure_result_used instruction.1968 // so we can avoid a separate ensure_result_used instruction.
1910 .call_chkused => unreachable,
1911 .call => {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 break :b true;1975 break :b true;
1914 },1976 },
19151977
1916 // ZIR instructions that might be a type other than `noreturn` or `void`.1978 // ZIR instructions that might be a type other than `noreturn` or `void`.
1917 .add,1979 .add,
1918 .addwrap,1980 .addwrap,
1981 .add_sat,
1919 .param,1982 .param,
1920 .param_comptime,1983 .param_comptime,
1921 .param_anytype,1984 .param_anytype,
...@@ -1947,9 +2010,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -1947,9 +2010,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
1947 .bool_br_and,2010 .bool_br_and,
1948 .bool_br_or,2011 .bool_br_or,
1949 .bool_not,2012 .bool_not,
1950 .call_compile_time,
1951 .call_nosuspend,
1952 .call_async,
1953 .cmp_lt,2013 .cmp_lt,
1954 .cmp_lte,2014 .cmp_lte,
1955 .cmp_eq,2015 .cmp_eq,
...@@ -1967,8 +2027,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -1967,8 +2027,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
1967 .elem_val_node,2027 .elem_val_node,
1968 .field_ptr,2028 .field_ptr,
1969 .field_val,2029 .field_val,
2030 .field_call_bind,
1970 .field_ptr_named,2031 .field_ptr_named,
1971 .field_val_named,2032 .field_val_named,
2033 .field_call_bind_named,
1972 .func,2034 .func,
1973 .func_inferred,2035 .func_inferred,
1974 .int,2036 .int,
...@@ -1983,13 +2045,15 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -1983,13 +2045,15 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
1983 .mod_rem,2045 .mod_rem,
1984 .mul,2046 .mul,
1985 .mulwrap,2047 .mulwrap,
1986 .param_type,2048 .mul_sat,
1987 .ref,2049 .ref,
1988 .shl,2050 .shl,
2051 .shl_sat,
1989 .shr,2052 .shr,
1990 .str,2053 .str,
1991 .sub,2054 .sub,
1992 .subwrap,2055 .subwrap,
2056 .sub_sat,
1993 .negate,2057 .negate,
1994 .negate_wrap,2058 .negate_wrap,
1995 .typeof,2059 .typeof,
...@@ -2049,9 +2113,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2049,9 +2113,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2049 .union_init_ptr,2113 .union_init_ptr,
2050 .field_type,2114 .field_type,
2051 .field_type_ref,2115 .field_type_ref,
2052 .opaque_decl,
2053 .opaque_decl_anon,
2054 .opaque_decl_func,
2055 .error_set_decl,2116 .error_set_decl,
2056 .error_set_decl_anon,2117 .error_set_decl_anon,
2057 .error_set_decl_func,2118 .error_set_decl_func,
...@@ -2118,14 +2179,11 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2118,14 +2179,11 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2118 .select,2179 .select,
2119 .atomic_load,2180 .atomic_load,
2120 .atomic_rmw,2181 .atomic_rmw,
2121 .atomic_store,
2122 .mul_add,2182 .mul_add,
2123 .builtin_call,2183 .builtin_call,
2124 .field_ptr_type,2184 .field_ptr_type,
2125 .field_parent_ptr,2185 .field_parent_ptr,
2126 .maximum,2186 .maximum,
2127 .memcpy,
2128 .memset,
2129 .minimum,2187 .minimum,
2130 .builtin_async_call,2188 .builtin_async_call,
2131 .c_import,2189 .c_import,
...@@ -2134,6 +2192,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2134,6 +2192,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2134 .await_nosuspend,2192 .await_nosuspend,
2135 .ret_err_value_code,2193 .ret_err_value_code,
2136 .extended,2194 .extended,
2195 .closure_get,
2137 => break :b false,2196 => break :b false,
21382197
2139 // ZIR instructions that are always `noreturn`.2198 // ZIR instructions that are always `noreturn`.
...@@ -2162,8 +2221,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2162,8 +2221,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2162 .ensure_result_used,2221 .ensure_result_used,
2163 .ensure_result_non_error,2222 .ensure_result_non_error,
2164 .@"export",2223 .@"export",
2224 .export_value,
2165 .set_eval_branch_quota,2225 .set_eval_branch_quota,
2166 .ensure_err_payload_void,2226 .ensure_err_payload_void,
2227 .atomic_store,
2167 .store,2228 .store,
2168 .store_node,2229 .store_node,
2169 .store_to_block_ptr,2230 .store_to_block_ptr,
...@@ -2175,6 +2236,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2175,6 +2236,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2175 .set_cold,2236 .set_cold,
2176 .set_float_mode,2237 .set_float_mode,
2177 .set_runtime_safety,2238 .set_runtime_safety,
2239 .closure_capture,
2240 .memcpy,
2241 .memset,
2178 => break :b true,2242 => break :b true,
2179 }2243 }
2180 } else switch (maybe_unused_result) {2244 } else switch (maybe_unused_result) {
...@@ -2371,6 +2435,7 @@ fn varDecl(...@@ -2371,6 +2435,7 @@ fn varDecl(
2371 const gpa = astgen.gpa;2435 const gpa = astgen.gpa;
2372 const tree = astgen.tree;2436 const tree = astgen.tree;
2373 const token_tags = tree.tokens.items(.tag);2437 const token_tags = tree.tokens.items(.tag);
2438 const main_tokens = tree.nodes.items(.main_token);
23742439
2375 const name_token = var_decl.ast.mut_token + 1;2440 const name_token = var_decl.ast.mut_token + 1;
2376 const ident_name_raw = tree.tokenSlice(name_token);2441 const ident_name_raw = tree.tokenSlice(name_token);
...@@ -2385,6 +2450,14 @@ fn varDecl(...@@ -2385,6 +2450,14 @@ fn varDecl(
2385 return astgen.failNode(node, "variables must be initialized", .{});2450 return astgen.failNode(node, "variables must be initialized", .{});
2386 }2451 }
23872452
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 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)2461 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
2389 try expr(gz, scope, align_rl, var_decl.ast.align_node)2462 try expr(gz, scope, align_rl, var_decl.ast.align_node)
2390 else2463 else
...@@ -2494,7 +2567,11 @@ fn varDecl(...@@ -2494,7 +2567,11 @@ fn varDecl(
2494 for (init_scope.instructions.items) |src_inst| {2567 for (init_scope.instructions.items) |src_inst| {
2495 if (zir_tags[src_inst] == .store_to_block_ptr) {2568 if (zir_tags[src_inst] == .store_to_block_ptr) {
2496 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) {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 parent_zir.appendAssumeCapacity(src_inst);2577 parent_zir.appendAssumeCapacity(src_inst);
...@@ -2668,6 +2745,24 @@ fn assignShift(...@@ -2668,6 +2745,24 @@ fn assignShift(
2668 _ = try gz.addBin(.store, lhs_ptr, result);2745 _ = try gz.addBin(.store, lhs_ptr, result);
2669}2746}
26702747
2748fn 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
2671fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {2766fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
2672 const astgen = gz.astgen;2767 const astgen = gz.astgen;
2673 const tree = astgen.tree;2768 const tree = astgen.tree;
...@@ -2714,6 +2809,7 @@ fn ptrType(...@@ -2714,6 +2809,7 @@ fn ptrType(
2714 const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);2809 const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
27152810
2716 const simple = ptr_info.ast.align_node == 0 and2811 const simple = ptr_info.ast.align_node == 0 and
2812 ptr_info.ast.addrspace_node == 0 and
2717 ptr_info.ast.sentinel == 0 and2813 ptr_info.ast.sentinel == 0 and
2718 ptr_info.ast.bit_range_start == 0;2814 ptr_info.ast.bit_range_start == 0;
27192815
...@@ -2732,6 +2828,7 @@ fn ptrType(...@@ -2732,6 +2828,7 @@ fn ptrType(
27322828
2733 var sentinel_ref: Zir.Inst.Ref = .none;2829 var sentinel_ref: Zir.Inst.Ref = .none;
2734 var align_ref: Zir.Inst.Ref = .none;2830 var align_ref: Zir.Inst.Ref = .none;
2831 var addrspace_ref: Zir.Inst.Ref = .none;
2735 var bit_start_ref: Zir.Inst.Ref = .none;2832 var bit_start_ref: Zir.Inst.Ref = .none;
2736 var bit_end_ref: Zir.Inst.Ref = .none;2833 var bit_end_ref: Zir.Inst.Ref = .none;
2737 var trailing_count: u32 = 0;2834 var trailing_count: u32 = 0;
...@@ -2744,6 +2841,10 @@ fn ptrType(...@@ -2744,6 +2841,10 @@ fn ptrType(
2744 align_ref = try expr(gz, scope, align_rl, ptr_info.ast.align_node);2841 align_ref = try expr(gz, scope, align_rl, ptr_info.ast.align_node);
2745 trailing_count += 1;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 if (ptr_info.ast.bit_range_start != 0) {2848 if (ptr_info.ast.bit_range_start != 0) {
2748 assert(ptr_info.ast.bit_range_end != 0);2849 assert(ptr_info.ast.bit_range_end != 0);
2749 bit_start_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_start);2850 bit_start_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_start);
...@@ -2764,6 +2865,9 @@ fn ptrType(...@@ -2764,6 +2865,9 @@ fn ptrType(
2764 if (align_ref != .none) {2865 if (align_ref != .none) {
2765 gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref));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 if (bit_start_ref != .none) {2871 if (bit_start_ref != .none) {
2768 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));2872 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
2769 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));2873 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
...@@ -2779,6 +2883,7 @@ fn ptrType(...@@ -2779,6 +2883,7 @@ fn ptrType(
2779 .is_volatile = ptr_info.volatile_token != null,2883 .is_volatile = ptr_info.volatile_token != null,
2780 .has_sentinel = sentinel_ref != .none,2884 .has_sentinel = sentinel_ref != .none,
2781 .has_align = align_ref != .none,2885 .has_align = align_ref != .none,
2886 .has_addrspace = addrspace_ref != .none,
2782 .has_bit_range = bit_start_ref != .none,2887 .has_bit_range = bit_start_ref != .none,
2783 },2888 },
2784 .size = ptr_info.size,2889 .size = ptr_info.size,
...@@ -2847,7 +2952,7 @@ const WipDecls = struct {...@@ -2847,7 +2952,7 @@ const WipDecls = struct {
2847 is_pub: bool,2952 is_pub: bool,
2848 is_export: bool,2953 is_export: bool,
2849 has_align: bool,2954 has_align: bool,
2850 has_section: bool,2955 has_section_or_addrspace: bool,
2851 ) Allocator.Error!void {2956 ) Allocator.Error!void {
2852 if (wip_decls.decl_index % fields_per_u32 == 0 and wip_decls.decl_index != 0) {2957 if (wip_decls.decl_index % fields_per_u32 == 0 and wip_decls.decl_index != 0) {
2853 try wip_decls.bit_bag.append(gpa, wip_decls.cur_bit_bag);2958 try wip_decls.bit_bag.append(gpa, wip_decls.cur_bit_bag);
...@@ -2857,7 +2962,7 @@ const WipDecls = struct {...@@ -2857,7 +2962,7 @@ const WipDecls = struct {
2857 (@as(u32, @boolToInt(is_pub)) << 28) |2962 (@as(u32, @boolToInt(is_pub)) << 28) |
2858 (@as(u32, @boolToInt(is_export)) << 29) |2963 (@as(u32, @boolToInt(is_export)) << 29) |
2859 (@as(u32, @boolToInt(has_align)) << 30) |2964 (@as(u32, @boolToInt(has_align)) << 30) |
2860 (@as(u32, @boolToInt(has_section)) << 31);2965 (@as(u32, @boolToInt(has_section_or_addrspace)) << 31);
2861 wip_decls.decl_index += 1;2966 wip_decls.decl_index += 1;
2862 }2967 }
28632968
...@@ -2922,7 +3027,8 @@ fn fnDecl(...@@ -2922,7 +3027,8 @@ fn fnDecl(
2922 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;3027 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
2923 break :blk token_tags[maybe_inline_token] == .keyword_inline;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);
29263032
2927 var params_scope = &fn_gz.base;3033 var params_scope = &fn_gz.base;
2928 const is_var_args = is_var_args: {3034 const is_var_args = is_var_args: {
...@@ -3011,6 +3117,9 @@ fn fnDecl(...@@ -3011,6 +3117,9 @@ fn fnDecl(
3011 const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {3117 const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
3012 break :inst try expr(&decl_gz, params_scope, align_rl, fn_proto.ast.align_expr);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 const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {3123 const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
3015 break :inst try comptimeExpr(&decl_gz, params_scope, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr);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,7 +3221,7 @@ fn fnDecl(
3112 _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);3221 _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
3113 try decl_gz.setBlockBody(block_inst);3222 try decl_gz.setBlockBody(block_inst);
31143223
3115 try wip_decls.payload.ensureUnusedCapacity(gpa, 9);3224 try wip_decls.payload.ensureUnusedCapacity(gpa, 10);
3116 {3225 {
3117 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));3226 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
3118 const casted = @bitCast([4]u32, contents_hash);3227 const casted = @bitCast([4]u32, contents_hash);
...@@ -3127,8 +3236,10 @@ fn fnDecl(...@@ -3127,8 +3236,10 @@ fn fnDecl(
3127 if (align_inst != .none) {3236 if (align_inst != .none) {
3128 wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));3237 wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));
3129 }3238 }
3130 if (section_inst != .none) {3239
3240 if (has_section_or_addrspace) {
3131 wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst));3241 wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst));
3242 wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst));
3132 }3243 }
3133}3244}
31343245
...@@ -3175,10 +3286,14 @@ fn globalVarDecl(...@@ -3175,10 +3286,14 @@ fn globalVarDecl(
3175 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: {3286 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: {
3176 break :inst try expr(&block_scope, &block_scope.base, align_rl, var_decl.ast.align_node);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 const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: {3292 const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: {
3179 break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .ty = .const_slice_u8_type }, var_decl.ast.section_node);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);
31823297
3183 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {3298 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
3184 if (!is_mutable) {3299 if (!is_mutable) {
...@@ -3256,7 +3371,7 @@ fn globalVarDecl(...@@ -3256,7 +3371,7 @@ fn globalVarDecl(
3256 _ = try block_scope.addBreak(.break_inline, block_inst, var_inst);3371 _ = try block_scope.addBreak(.break_inline, block_inst, var_inst);
3257 try block_scope.setBlockBody(block_inst);3372 try block_scope.setBlockBody(block_inst);
32583373
3259 try wip_decls.payload.ensureUnusedCapacity(gpa, 9);3374 try wip_decls.payload.ensureUnusedCapacity(gpa, 10);
3260 {3375 {
3261 const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));3376 const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
3262 const casted = @bitCast([4]u32, contents_hash);3377 const casted = @bitCast([4]u32, contents_hash);
...@@ -3271,8 +3386,9 @@ fn globalVarDecl(...@@ -3271,8 +3386,9 @@ fn globalVarDecl(
3271 if (align_inst != .none) {3386 if (align_inst != .none) {
3272 wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));3387 wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));
3273 }3388 }
3274 if (section_inst != .none) {3389 if (has_section_or_addrspace) {
3275 wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst));3390 wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst));
3391 wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst));
3276 }3392 }
3277}3393}
32783394
...@@ -3474,8 +3590,9 @@ fn structDeclInner(...@@ -3474,8 +3590,9 @@ fn structDeclInner(
3474 container_decl: Ast.full.ContainerDecl,3590 container_decl: Ast.full.ContainerDecl,
3475 layout: std.builtin.TypeInfo.ContainerLayout,3591 layout: std.builtin.TypeInfo.ContainerLayout,
3476) InnerError!Zir.Inst.Ref {3592) InnerError!Zir.Inst.Ref {
3593 const decl_inst = try gz.reserveInstructionIndex();
3594
3477 if (container_decl.ast.members.len == 0) {3595 if (container_decl.ast.members.len == 0) {
3478 const decl_inst = try gz.reserveInstructionIndex();
3479 try gz.setStruct(decl_inst, .{3596 try gz.setStruct(decl_inst, .{
3480 .src_node = node,3597 .src_node = node,
3481 .layout = layout,3598 .layout = layout,
...@@ -3493,11 +3610,19 @@ fn structDeclInner(...@@ -3493,11 +3610,19 @@ fn structDeclInner(
3493 const node_tags = tree.nodes.items(.tag);3610 const node_tags = tree.nodes.items(.tag);
3494 const node_datas = tree.nodes.items(.data);3611 const node_datas = tree.nodes.items(.data);
34953612
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 // The struct_decl instruction introduces a scope in which the decls of the struct3621 // The struct_decl instruction introduces a scope in which the decls of the struct
3497 // are in scope, so that field types, alignments, and default value expressions3622 // are in scope, so that field types, alignments, and default value expressions
3498 // can refer to decls within the struct itself.3623 // can refer to decls within the struct itself.
3499 var block_scope: GenZir = .{3624 var block_scope: GenZir = .{
3500 .parent = scope,3625 .parent = &namespace.base,
3501 .decl_node_index = node,3626 .decl_node_index = node,
3502 .decl_line = gz.calcLine(node),3627 .decl_line = gz.calcLine(node),
3503 .astgen = astgen,3628 .astgen = astgen,
...@@ -3506,16 +3631,13 @@ fn structDeclInner(...@@ -3506,16 +3631,13 @@ fn structDeclInner(
3506 };3631 };
3507 defer block_scope.instructions.deinit(gpa);3632 defer block_scope.instructions.deinit(gpa);
35083633
3509 var namespace: Scope.Namespace = .{ .parent = scope, .node = node };
3510 defer namespace.decls.deinit(gpa);
3511
3512 try astgen.scanDecls(&namespace, container_decl.ast.members);3634 try astgen.scanDecls(&namespace, container_decl.ast.members);
35133635
3514 var wip_decls: WipDecls = .{};3636 var wip_decls: WipDecls = .{};
3515 defer wip_decls.deinit(gpa);3637 defer wip_decls.deinit(gpa);
35163638
3517 // We don't know which members are fields until we iterate, so cannot do3639 // 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 var fields_data = ArrayListUnmanaged(u32){};3641 var fields_data = ArrayListUnmanaged(u32){};
3520 defer fields_data.deinit(gpa);3642 defer fields_data.deinit(gpa);
35213643
...@@ -3713,7 +3835,6 @@ fn structDeclInner(...@@ -3713,7 +3835,6 @@ fn structDeclInner(
3713 }3835 }
3714 }3836 }
37153837
3716 const decl_inst = try gz.reserveInstructionIndex();
3717 if (block_scope.instructions.items.len != 0) {3838 if (block_scope.instructions.items.len != 0) {
3718 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);3839 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
3719 }3840 }
...@@ -3727,11 +3848,18 @@ fn structDeclInner(...@@ -3727,11 +3848,18 @@ fn structDeclInner(
3727 .known_has_bits = known_has_bits,3848 .known_has_bits = known_has_bits,
3728 });3849 });
37293850
3730 try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len +3851 // zig fmt: off
3731 @boolToInt(field_index != 0) + fields_data.items.len +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 block_scope.instructions.items.len +3856 block_scope.instructions.items.len +
3733 wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +3857 wip_decls.bit_bag.items.len +
3734 wip_decls.payload.items.len);3858 @boolToInt(field_index != 0) +
3859 fields_data.items.len
3860 );
3861 // zig fmt: on
3862
3735 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.3863 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.
3736 if (wip_decls.decl_index != 0) {3864 if (wip_decls.decl_index != 0) {
3737 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);3865 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
...@@ -3758,17 +3886,27 @@ fn unionDeclInner(...@@ -3758,17 +3886,27 @@ fn unionDeclInner(
3758 arg_node: Ast.Node.Index,3886 arg_node: Ast.Node.Index,
3759 have_auto_enum: bool,3887 have_auto_enum: bool,
3760) InnerError!Zir.Inst.Ref {3888) InnerError!Zir.Inst.Ref {
3889 const decl_inst = try gz.reserveInstructionIndex();
3890
3761 const astgen = gz.astgen;3891 const astgen = gz.astgen;
3762 const gpa = astgen.gpa;3892 const gpa = astgen.gpa;
3763 const tree = astgen.tree;3893 const tree = astgen.tree;
3764 const node_tags = tree.nodes.items(.tag);3894 const node_tags = tree.nodes.items(.tag);
3765 const node_datas = tree.nodes.items(.data);3895 const node_datas = tree.nodes.items(.data);
37663896
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 // The union_decl instruction introduces a scope in which the decls of the union3905 // The union_decl instruction introduces a scope in which the decls of the union
3768 // are in scope, so that field types, alignments, and default value expressions3906 // are in scope, so that field types, alignments, and default value expressions
3769 // can refer to decls within the union itself.3907 // can refer to decls within the union itself.
3770 var block_scope: GenZir = .{3908 var block_scope: GenZir = .{
3771 .parent = scope,3909 .parent = &namespace.base,
3772 .decl_node_index = node,3910 .decl_node_index = node,
3773 .decl_line = gz.calcLine(node),3911 .decl_line = gz.calcLine(node),
3774 .astgen = astgen,3912 .astgen = astgen,
...@@ -3777,13 +3915,10 @@ fn unionDeclInner(...@@ -3777,13 +3915,10 @@ fn unionDeclInner(
3777 };3915 };
3778 defer block_scope.instructions.deinit(gpa);3916 defer block_scope.instructions.deinit(gpa);
37793917
3780 var namespace: Scope.Namespace = .{ .parent = scope, .node = node };
3781 defer namespace.decls.deinit(gpa);
3782
3783 try astgen.scanDecls(&namespace, members);3918 try astgen.scanDecls(&namespace, members);
37843919
3785 const arg_inst: Zir.Inst.Ref = if (arg_node != 0)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 else3922 else
3788 .none;3923 .none;
37893924
...@@ -3791,7 +3926,7 @@ fn unionDeclInner(...@@ -3791,7 +3926,7 @@ fn unionDeclInner(
3791 defer wip_decls.deinit(gpa);3926 defer wip_decls.deinit(gpa);
37923927
3793 // We don't know which members are fields until we iterate, so cannot do3928 // 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 var fields_data = ArrayListUnmanaged(u32){};3930 var fields_data = ArrayListUnmanaged(u32){};
3796 defer fields_data.deinit(gpa);3931 defer fields_data.deinit(gpa);
37973932
...@@ -3996,7 +4131,6 @@ fn unionDeclInner(...@@ -3996,7 +4131,6 @@ fn unionDeclInner(
3996 }4131 }
3997 }4132 }
39984133
3999 const decl_inst = try gz.reserveInstructionIndex();
4000 if (block_scope.instructions.items.len != 0) {4134 if (block_scope.instructions.items.len != 0) {
4001 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);4135 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
4002 }4136 }
...@@ -4011,11 +4145,18 @@ fn unionDeclInner(...@@ -4011,11 +4145,18 @@ fn unionDeclInner(
4011 .auto_enum_tag = have_auto_enum,4145 .auto_enum_tag = have_auto_enum,
4012 });4146 });
40134147
4014 try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len +4148 // zig fmt: off
4015 1 + fields_data.items.len +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 block_scope.instructions.items.len +4153 block_scope.instructions.items.len +
4017 wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +4154 wip_decls.bit_bag.items.len +
4018 wip_decls.payload.items.len);4155 1 + // cur_bit_bag
4156 fields_data.items.len
4157 );
4158 // zig fmt: on
4159
4019 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.4160 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.
4020 if (wip_decls.decl_index != 0) {4161 if (wip_decls.decl_index != 0) {
4021 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);4162 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
...@@ -4178,10 +4319,20 @@ fn containerDecl(...@@ -4178,10 +4319,20 @@ fn containerDecl(
4178 // how structs are handled above.4319 // how structs are handled above.
4179 const nonexhaustive = counts.nonexhaustive_node != 0;4320 const nonexhaustive = counts.nonexhaustive_node != 0;
41804321
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 // The enum_decl instruction introduces a scope in which the decls of the enum4332 // The enum_decl instruction introduces a scope in which the decls of the enum
4182 // are in scope, so that tag values can refer to decls within the enum itself.4333 // are in scope, so that tag values can refer to decls within the enum itself.
4183 var block_scope: GenZir = .{4334 var block_scope: GenZir = .{
4184 .parent = scope,4335 .parent = &namespace.base,
4185 .decl_node_index = node,4336 .decl_node_index = node,
4186 .decl_line = gz.calcLine(node),4337 .decl_line = gz.calcLine(node),
4187 .astgen = astgen,4338 .astgen = astgen,
...@@ -4190,13 +4341,10 @@ fn containerDecl(...@@ -4190,13 +4341,10 @@ fn containerDecl(
4190 };4341 };
4191 defer block_scope.instructions.deinit(gpa);4342 defer block_scope.instructions.deinit(gpa);
41924343
4193 var namespace: Scope.Namespace = .{ .parent = scope, .node = node };
4194 defer namespace.decls.deinit(gpa);
4195
4196 try astgen.scanDecls(&namespace, container_decl.ast.members);4344 try astgen.scanDecls(&namespace, container_decl.ast.members);
41974345
4198 const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)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 else4348 else
4201 .none;4349 .none;
42024350
...@@ -4391,7 +4539,6 @@ fn containerDecl(...@@ -4391,7 +4539,6 @@ fn containerDecl(
4391 }4539 }
4392 }4540 }
43934541
4394 const decl_inst = try gz.reserveInstructionIndex();
4395 if (block_scope.instructions.items.len != 0) {4542 if (block_scope.instructions.items.len != 0) {
4396 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);4543 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
4397 }4544 }
...@@ -4405,11 +4552,18 @@ fn containerDecl(...@@ -4405,11 +4552,18 @@ fn containerDecl(
4405 .decls_len = @intCast(u32, wip_decls.decl_index),4552 .decls_len = @intCast(u32, wip_decls.decl_index),
4406 });4553 });
44074554
4408 try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len +4555 // zig fmt: off
4409 1 + fields_data.items.len +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 block_scope.instructions.items.len +4560 block_scope.instructions.items.len +
4411 wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +4561 wip_decls.bit_bag.items.len +
4412 wip_decls.payload.items.len);4562 1 + // cur_bit_bag
4563 fields_data.items.len
4564 );
4565 // zig fmt: on
4566
4413 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.4567 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.
4414 if (wip_decls.decl_index != 0) {4568 if (wip_decls.decl_index != 0) {
4415 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);4569 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
...@@ -4426,8 +4580,15 @@ fn containerDecl(...@@ -4426,8 +4580,15 @@ fn containerDecl(
4426 .keyword_opaque => {4580 .keyword_opaque => {
4427 assert(container_decl.ast.arg == 0);4581 assert(container_decl.ast.arg == 0);
44284582
4429 var namespace: Scope.Namespace = .{ .parent = scope, .node = node };4583 const decl_inst = try gz.reserveInstructionIndex();
4430 defer namespace.decls.deinit(gpa);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);
44314592
4432 try astgen.scanDecls(&namespace, container_decl.ast.members);4593 try astgen.scanDecls(&namespace, container_decl.ast.members);
44334594
...@@ -4565,21 +4726,20 @@ fn containerDecl(...@@ -4565,21 +4726,20 @@ fn containerDecl(
4565 wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field);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);
45754729
4576 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).Struct.fields.len +4730 try gz.setOpaque(decl_inst, .{
4577 wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +4731 .src_node = node,
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{
4581 .decls_len = @intCast(u32, wip_decls.decl_index),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 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.4743 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.
4584 if (wip_decls.decl_index != 0) {4744 if (wip_decls.decl_index != 0) {
4585 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);4745 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
...@@ -4865,6 +5025,21 @@ fn fieldAccess(...@@ -4865,6 +5025,21 @@ fn fieldAccess(
4865 scope: *Scope,5025 scope: *Scope,
4866 rl: ResultLoc,5026 rl: ResultLoc,
4867 node: Ast.Node.Index,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
5037fn addFieldAccess(
5038 tag: Zir.Inst.Tag,
5039 gz: *GenZir,
5040 scope: *Scope,
5041 lhs_rl: ResultLoc,
5042 node: Ast.Node.Index,
4868) InnerError!Zir.Inst.Ref {5043) InnerError!Zir.Inst.Ref {
4869 const astgen = gz.astgen;5044 const astgen = gz.astgen;
4870 const tree = astgen.tree;5045 const tree = astgen.tree;
...@@ -4875,16 +5050,11 @@ fn fieldAccess(...@@ -4875,16 +5050,11 @@ fn fieldAccess(
4875 const dot_token = main_tokens[node];5050 const dot_token = main_tokens[node];
4876 const field_ident = dot_token + 1;5051 const field_ident = dot_token + 1;
4877 const str_index = try astgen.identAsString(field_ident);5052 const str_index = try astgen.identAsString(field_ident);
4878 switch (rl) {5053
4879 .ref => return gz.addPlNode(.field_ptr, node, Zir.Inst.Field{5054 return gz.addPlNode(tag, node, Zir.Inst.Field{
4880 .lhs = try expr(gz, scope, .ref, object_node),5055 .lhs = try expr(gz, scope, lhs_rl, object_node),
4881 .field_name_start = str_index,5056 .field_name_start = str_index,
4882 }),5057 });
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 }
4888}5058}
48895059
4890fn arrayAccess(5060fn arrayAccess(
...@@ -6320,6 +6490,7 @@ fn identifier(...@@ -6320,6 +6490,7 @@ fn identifier(
63206490
6321 const astgen = gz.astgen;6491 const astgen = gz.astgen;
6322 const tree = astgen.tree;6492 const tree = astgen.tree;
6493 const gpa = astgen.gpa;
6323 const main_tokens = tree.nodes.items(.main_token);6494 const main_tokens = tree.nodes.items(.main_token);
63246495
6325 const ident_token = main_tokens[ident];6496 const ident_token = main_tokens[ident];
...@@ -6366,16 +6537,28 @@ fn identifier(...@@ -6366,16 +6537,28 @@ fn identifier(
6366 const name_str_index = try astgen.identAsString(ident_token);6537 const name_str_index = try astgen.identAsString(ident_token);
6367 var s = scope;6538 var s = scope;
6368 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already6539 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 while (true) switch (s.tag) {6542 while (true) switch (s.tag) {
6371 .local_val => {6543 .local_val => {
6372 const local_val = s.cast(Scope.LocalVal).?;6544 const local_val = s.cast(Scope.LocalVal).?;
63736545
6374 if (local_val.name == name_str_index) {6546 if (local_val.name == name_str_index) {
6375 local_val.used = true;
6376 // Locals cannot shadow anything, so we do not need to look for ambiguous6547 // Locals cannot shadow anything, so we do not need to look for ambiguous
6377 // references in this case.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 s = local_val.parent;6563 s = local_val.parent;
6381 },6564 },
...@@ -6383,16 +6566,29 @@ fn identifier(...@@ -6383,16 +6566,29 @@ fn identifier(
6383 const local_ptr = s.cast(Scope.LocalPtr).?;6566 const local_ptr = s.cast(Scope.LocalPtr).?;
6384 if (local_ptr.name == name_str_index) {6567 if (local_ptr.name == name_str_index) {
6385 local_ptr.used = true;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 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{6572 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
6388 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),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 switch (rl) {6588 switch (rl) {
6393 .ref, .none_or_ref => return local_ptr.ptr,6589 .ref, .none_or_ref => return ptr_inst,
6394 else => {6590 else => {
6395 const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident);6591 const loaded = try gz.addUnNode(.load, ptr_inst, ident);
6396 return rvalue(gz, rl, loaded, ident);6592 return rvalue(gz, rl, loaded, ident);
6397 },6593 },
6398 }6594 }
...@@ -6413,7 +6609,8 @@ fn identifier(...@@ -6413,7 +6609,8 @@ fn identifier(
6413 // We found a match but must continue looking for ambiguous references to decls.6609 // We found a match but must continue looking for ambiguous references to decls.
6414 found_already = i;6610 found_already = i;
6415 }6611 }
6416 hit_namespace = ns.node;6612 num_namespaces_out += 1;
6613 capturing_namespace = ns;
6417 s = ns.parent;6614 s = ns.parent;
6418 },6615 },
6419 .top => break,6616 .top => break,
...@@ -6433,6 +6630,37 @@ fn identifier(...@@ -6433,6 +6630,37 @@ fn identifier(
6433 }6630 }
6434}6631}
64356632
6633/// Adds a capture to a namespace, if needed.
6634/// Returns the index of the closure_capture instruction.
6635fn 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
6436fn stringLiteral(6664fn stringLiteral(
6437 gz: *GenZir,6665 gz: *GenZir,
6438 rl: ResultLoc,6666 rl: ResultLoc,
...@@ -6866,35 +7094,13 @@ fn asRlPtr(...@@ -6866,35 +7094,13 @@ fn asRlPtr(
6866 operand_node: Ast.Node.Index,7094 operand_node: Ast.Node.Index,
6867 dest_type: Zir.Inst.Ref,7095 dest_type: Zir.Inst.Ref,
6868) InnerError!Zir.Inst.Ref {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 const astgen = parent_gz.astgen;7097 const astgen = parent_gz.astgen;
68737098
6874 var as_scope = parent_gz.makeSubBlock(scope);7099 var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr);
6875 defer as_scope.instructions.deinit(astgen.gpa);7100 defer as_scope.instructions.deinit(astgen.gpa);
68767101
6877 as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
6878 const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node);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;7103 return as_scope.finishCoercion(parent_gz, rl, operand_node, result, dest_type);
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 }
6898}7104}
68997105
6900fn bitCast(7106fn bitCast(
...@@ -7030,16 +7236,15 @@ fn builtinCall(...@@ -7030,16 +7236,15 @@ fn builtinCall(
7030 return rvalue(gz, rl, result, node);7236 return rvalue(gz, rl, result, node);
7031 },7237 },
7032 .field => {7238 .field => {
7033 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
7034 if (rl == .ref) {7239 if (rl == .ref) {
7035 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{7240 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
7036 .lhs = try expr(gz, scope, .ref, params[0]),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 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{7245 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
7041 .lhs = try expr(gz, scope, .none, params[0]),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 return rvalue(gz, rl, result, node);7249 return rvalue(gz, rl, result, node);
7045 },7250 },
...@@ -7047,7 +7252,7 @@ fn builtinCall(...@@ -7047,7 +7252,7 @@ fn builtinCall(
7047 .bit_cast => return bitCast( gz, scope, rl, node, params[0], params[1]),7252 .bit_cast => return bitCast( gz, scope, rl, node, params[0], params[1]),
7048 .TypeOf => return typeOf( gz, scope, rl, node, params),7253 .TypeOf => return typeOf( gz, scope, rl, node, params),
7049 .union_init => return unionInit(gz, scope, rl, node, params),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]),
70517256
7052 .@"export" => {7257 .@"export" => {
7053 const node_tags = tree.nodes.items(.tag);7258 const node_tags = tree.nodes.items(.tag);
...@@ -7060,32 +7265,55 @@ fn builtinCall(...@@ -7060,32 +7265,55 @@ fn builtinCall(
7060 .identifier => {7265 .identifier => {
7061 const ident_token = main_tokens[params[0]];7266 const ident_token = main_tokens[params[0]];
7062 decl_name = try astgen.identAsString(ident_token);7267 decl_name = try astgen.identAsString(ident_token);
7063 {7268
7064 var s = scope;7269 var s = scope;
7065 while (true) switch (s.tag) {7270 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
7066 .local_val => {7271 while (true) switch (s.tag) {
7067 const local_val = s.cast(Scope.LocalVal).?;7272 .local_val => {
7068 if (local_val.name == decl_name) {7273 const local_val = s.cast(Scope.LocalVal).?;
7069 local_val.used = true;7274 if (local_val.name == decl_name) {
7070 break;7275 local_val.used = true;
7071 }7276 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
7072 s = local_val.parent;7277 .operand = local_val.inst,
7073 },7278 .options = try comptimeExpr(gz, scope, .{ .coerced_ty = .export_options_type }, params[1]),
7074 .local_ptr => {7279 });
7075 const local_ptr = s.cast(Scope.LocalPtr).?;7280 return rvalue(gz, rl, .void_value, node);
7076 if (local_ptr.name == decl_name) {7281 }
7077 if (!local_ptr.maybe_comptime)7282 s = local_val.parent;
7078 return astgen.failNode(params[0], "unable to export runtime-known value", .{});7283 },
7079 local_ptr.used = true;7284 .local_ptr => {
7080 break;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;7310 // We found a match but must continue looking for ambiguous references to decls.
7083 },7311 found_already = i;
7084 .gen_zir => s = s.cast(GenZir).?.parent,7312 }
7085 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,7313 s = ns.parent;
7086 .namespace, .top => break,7314 },
7087 };7315 .top => break,
7088 }7316 };
7089 },7317 },
7090 .field_access => {7318 .field_access => {
7091 const namespace_node = node_datas[params[0]].lhs;7319 const namespace_node = node_datas[params[0]].lhs;
...@@ -7116,9 +7344,13 @@ fn builtinCall(...@@ -7116,9 +7344,13 @@ fn builtinCall(
7116 });7344 });
7117 return rvalue(gz, rl, result, node);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 },
71197352
7120 .breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint),7353 .breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint),
7121 .fence => return simpleNoOpVoid(gz, rl, node, .fence),
71227354
7123 .This => return rvalue(gz, rl, try gz.addNodeExtended(.this, node), node),7355 .This => return rvalue(gz, rl, try gz.addNodeExtended(.this, node), node),
7124 .return_address => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr, node), node),7356 .return_address => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr, node), node),
...@@ -7230,6 +7462,7 @@ fn builtinCall(...@@ -7230,6 +7462,7 @@ fn builtinCall(
7230 return rvalue(gz, rl, result, node);7462 return rvalue(gz, rl, result, node);
7231 },7463 },
7232 .c_define => {7464 .c_define => {
7465 if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{});
7233 const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[0]);7466 const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[0]);
7234 const value = try comptimeExpr(gz, scope, .none, params[1]);7467 const value = try comptimeExpr(gz, scope, .none, params[1]);
7235 const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{7468 const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{
...@@ -7305,13 +7538,9 @@ fn builtinCall(...@@ -7305,13 +7538,9 @@ fn builtinCall(
7305 return rvalue(gz, rl, result, node);7538 return rvalue(gz, rl, result, node);
7306 },7539 },
73077540
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 .atomic_load => {7541 .atomic_load => {
7314 const int_type = try typeExpr(gz, scope, params[0]);7542 const int_type = try typeExpr(gz, scope, params[0]);
7543 // TODO allow this pointer type to be volatile
7315 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{7544 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
7316 .ptr_type_simple = .{7545 .ptr_type_simple = .{
7317 .is_allowzero = false,7546 .is_allowzero = false,
...@@ -7321,16 +7550,17 @@ fn builtinCall(...@@ -7321,16 +7550,17 @@ fn builtinCall(
7321 .elem_type = int_type,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 const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.Bin{7553 const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.Bin{
7327 .lhs = ptr,7554 // zig fmt: off
7328 .rhs = ordering,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 return rvalue(gz, rl, result, node);7559 return rvalue(gz, rl, result, node);
7331 },7560 },
7332 .atomic_rmw => {7561 .atomic_rmw => {
7333 const int_type = try typeExpr(gz, scope, params[0]);7562 const int_type = try typeExpr(gz, scope, params[0]);
7563 // TODO allow this pointer type to be volatile
7334 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{7564 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
7335 .ptr_type_simple = .{7565 .ptr_type_simple = .{
7336 .is_allowzero = false,7566 .is_allowzero = false,
...@@ -7340,20 +7570,19 @@ fn builtinCall(...@@ -7340,20 +7570,19 @@ fn builtinCall(
7340 .elem_type = int_type,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 const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{7573 const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
7348 .ptr = ptr,7574 // zig fmt: off
7349 .operation = operation,7575 .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]),
7350 .operand = operand,7576 .operation = try expr(gz, scope, .{ .coerced_ty = .atomic_rmw_op_type }, params[2]),
7351 .ordering = ordering,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 return rvalue(gz, rl, result, node);7581 return rvalue(gz, rl, result, node);
7354 },7582 },
7355 .atomic_store => {7583 .atomic_store => {
7356 const int_type = try typeExpr(gz, scope, params[0]);7584 const int_type = try typeExpr(gz, scope, params[0]);
7585 // TODO allow this pointer type to be volatile
7357 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{7586 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
7358 .ptr_type_simple = .{7587 .ptr_type_simple = .{
7359 .is_allowzero = false,7588 .is_allowzero = false,
...@@ -7363,13 +7592,12 @@ fn builtinCall(...@@ -7363,13 +7592,12 @@ fn builtinCall(
7363 .elem_type = int_type,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 const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{7595 const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
7370 .ptr = ptr,7596 // zig fmt: off
7371 .operand = operand,7597 .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]),
7372 .ordering = ordering,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 return rvalue(gz, rl, result, node);7602 return rvalue(gz, rl, result, node);
7375 },7603 },
...@@ -7387,7 +7615,7 @@ fn builtinCall(...@@ -7387,7 +7615,7 @@ fn builtinCall(
7387 },7615 },
7388 .call => {7616 .call => {
7389 const options = try comptimeExpr(gz, scope, .{ .ty = .call_options_type }, params[0]);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 const args = try expr(gz, scope, .none, params[2]);7619 const args = try expr(gz, scope, .none, params[2]);
7392 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{7620 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
7393 .options = options,7621 .options = options,
...@@ -7409,17 +7637,17 @@ fn builtinCall(...@@ -7409,17 +7637,17 @@ fn builtinCall(
7409 },7637 },
7410 .memcpy => {7638 .memcpy => {
7411 const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{7639 const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
7412 .dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]),7640 .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
7413 .source = try expr(gz, scope, .{ .ty = .manyptr_const_u8_type }, params[1]),7641 .source = try expr(gz, scope, .{ .coerced_ty = .manyptr_const_u8_type }, params[1]),
7414 .byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]),7642 .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
7415 });7643 });
7416 return rvalue(gz, rl, result, node);7644 return rvalue(gz, rl, result, node);
7417 },7645 },
7418 .memset => {7646 .memset => {
7419 const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{7647 const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
7420 .dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]),7648 .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
7421 .byte = try expr(gz, scope, .{ .ty = .u8_type }, params[1]),7649 .byte = try expr(gz, scope, .{ .coerced_ty = .u8_type }, params[1]),
7422 .byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]),7650 .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
7423 });7651 });
7424 return rvalue(gz, rl, result, node);7652 return rvalue(gz, rl, result, node);
7425 },7653 },
...@@ -7452,12 +7680,11 @@ fn builtinCall(...@@ -7452,12 +7680,11 @@ fn builtinCall(
7452 },7680 },
7453 .Vector => {7681 .Vector => {
7454 const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{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 .rhs = try typeExpr(gz, scope, params[1]),7684 .rhs = try typeExpr(gz, scope, params[1]),
7457 });7685 });
7458 return rvalue(gz, rl, result, node);7686 return rvalue(gz, rl, result, node);
7459 },7687 },
7460
7461 }7688 }
7462 // zig fmt: on7689 // zig fmt: on
7463}7690}
...@@ -7603,6 +7830,8 @@ fn simpleCBuiltin(...@@ -7603,6 +7830,8 @@ fn simpleCBuiltin(
7603 operand_node: Ast.Node.Index,7830 operand_node: Ast.Node.Index,
7604 tag: Zir.Inst.Extended,7831 tag: Zir.Inst.Extended,
7605) InnerError!Zir.Inst.Ref {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 const operand = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, operand_node);7835 const operand = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, operand_node);
7607 _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{7836 _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{
7608 .node = gz.nodeIndexToRelative(node),7837 .node = gz.nodeIndexToRelative(node),
...@@ -7651,7 +7880,6 @@ fn shiftOp(...@@ -7651,7 +7880,6 @@ fn shiftOp(
7651fn cImport(7880fn cImport(
7652 gz: *GenZir,7881 gz: *GenZir,
7653 scope: *Scope,7882 scope: *Scope,
7654 rl: ResultLoc,
7655 node: Ast.Node.Index,7883 node: Ast.Node.Index,
7656 body_node: Ast.Node.Index,7884 body_node: Ast.Node.Index,
7657) InnerError!Zir.Inst.Ref {7885) InnerError!Zir.Inst.Ref {
...@@ -7660,6 +7888,7 @@ fn cImport(...@@ -7660,6 +7888,7 @@ fn cImport(
76607888
7661 var block_scope = gz.makeSubBlock(scope);7889 var block_scope = gz.makeSubBlock(scope);
7662 block_scope.force_comptime = true;7890 block_scope.force_comptime = true;
7891 block_scope.c_import = true;
7663 defer block_scope.instructions.deinit(gpa);7892 defer block_scope.instructions.deinit(gpa);
76647893
7665 const block_inst = try gz.addBlock(.c_import, node);7894 const block_inst = try gz.addBlock(.c_import, node);
...@@ -7670,7 +7899,7 @@ fn cImport(...@@ -7670,7 +7899,7 @@ fn cImport(
7670 try block_scope.setBlockBody(block_inst);7899 try block_scope.setBlockBody(block_inst);
7671 try gz.instructions.append(gpa, block_inst);7900 try gz.instructions.append(gpa, block_inst);
76727901
7673 return rvalue(gz, rl, .void_value, node);7902 return indexToRef(block_inst);
7674}7903}
76757904
7676fn overflowArithmetic(7905fn overflowArithmetic(
...@@ -7703,24 +7932,6 @@ fn overflowArithmetic(...@@ -7703,24 +7932,6 @@ fn overflowArithmetic(
7703 return rvalue(gz, rl, result, node);7932 return rvalue(gz, rl, result, node);
7704}7933}
77057934
7706fn 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
7724fn callExpr(7935fn callExpr(
7725 gz: *GenZir,7936 gz: *GenZir,
7726 scope: *Scope,7937 scope: *Scope,
...@@ -7729,20 +7940,16 @@ fn callExpr(...@@ -7729,20 +7940,16 @@ fn callExpr(
7729 call: Ast.full.Call,7940 call: Ast.full.Call,
7730) InnerError!Zir.Inst.Ref {7941) InnerError!Zir.Inst.Ref {
7731 const astgen = gz.astgen;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);
77337945
7734 const args = try astgen.gpa.alloc(Zir.Inst.Ref, call.ast.params.len);7946 const args = try astgen.gpa.alloc(Zir.Inst.Ref, call.ast.params.len);
7735 defer astgen.gpa.free(args);7947 defer astgen.gpa.free(args);
77367948
7737 for (call.ast.params) |param_node, i| {7949 for (call.ast.params) |param_node, i| {
7738 const param_type = try gz.add(.{7950 // Parameters are always temporary values, they have no
7739 .tag = .param_type,7951 // meaningful result location. Sema will coerce them.
7740 .data = .{ .param_type = .{7952 args[i] = try expr(gz, scope, .none, param_node);
7741 .callee = lhs,
7742 .param_index = @intCast(u32, i),
7743 } },
7744 });
7745 args[i] = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node);
7746 }7953 }
77477954
7748 const modifier: std.builtin.CallOptions.Modifier = blk: {7955 const modifier: std.builtin.CallOptions.Modifier = blk: {
...@@ -7757,20 +7964,72 @@ fn callExpr(...@@ -7757,20 +7964,72 @@ fn callExpr(
7757 }7964 }
7758 break :blk .auto;7965 break :blk .auto;
7759 };7966 };
7760 const result: Zir.Inst.Ref = res: {7967 const call_inst = try gz.addCall(modifier, callee, args, node);
7761 const tag: Zir.Inst.Tag = switch (modifier) {7968 return rvalue(gz, rl, call_inst, node); // TODO function call with result location
7762 .auto => .call,7969}
7763 .async_kw => .call_async,7970
7764 .never_tail => unreachable,7971/// calleeExpr generates the function part of a call expression (f in f(x)), or the
7765 .never_inline => unreachable,7972/// callee argument to the @call() builtin. If the lhs is a field access or the
7766 .no_async => .call_nosuspend,7973/// @field() builtin, we need to generate a special field_call_bind instruction
7767 .always_tail => unreachable,7974/// instead of the normal field_val or field_ptr. If this is a inst.func() call,
7768 .always_inline => unreachable,7975/// this instruction will capture the value of the first argument before evaluating
7769 .compile_time => .call_compile_time,7976/// the other arguments. We need to use .ref here to guarantee we will be able to
7770 };7977/// promote an lvalue to an address if the first parameter requires it. This
7771 break :res try gz.addCall(tag, lhs, args, node);7978/// unfortunately also means we need to take a reference to any types on the lhs.
7772 };7979fn calleeExpr(
7773 return rvalue(gz, rl, result, node); // TODO function call with result location7980 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}
77758034
7776pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{8035pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{
...@@ -7892,27 +8151,33 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool...@@ -7892,27 +8151,33 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
7892 .asm_simple,8151 .asm_simple,
7893 .add,8152 .add,
7894 .add_wrap,8153 .add_wrap,
8154 .add_sat,
7895 .array_cat,8155 .array_cat,
7896 .array_mult,8156 .array_mult,
7897 .assign,8157 .assign,
7898 .assign_bit_and,8158 .assign_bit_and,
7899 .assign_bit_or,8159 .assign_bit_or,
7900 .assign_bit_shift_left,8160 .assign_shl,
7901 .assign_bit_shift_right,8161 .assign_shl_sat,
8162 .assign_shr,
7902 .assign_bit_xor,8163 .assign_bit_xor,
7903 .assign_div,8164 .assign_div,
7904 .assign_sub,8165 .assign_sub,
7905 .assign_sub_wrap,8166 .assign_sub_wrap,
8167 .assign_sub_sat,
7906 .assign_mod,8168 .assign_mod,
7907 .assign_add,8169 .assign_add,
7908 .assign_add_wrap,8170 .assign_add_wrap,
8171 .assign_add_sat,
7909 .assign_mul,8172 .assign_mul,
7910 .assign_mul_wrap,8173 .assign_mul_wrap,
8174 .assign_mul_sat,
7911 .bang_equal,8175 .bang_equal,
7912 .bit_and,8176 .bit_and,
7913 .bit_or,8177 .bit_or,
7914 .bit_shift_left,8178 .shl,
7915 .bit_shift_right,8179 .shl_sat,
8180 .shr,
7916 .bit_xor,8181 .bit_xor,
7917 .bool_and,8182 .bool_and,
7918 .bool_or,8183 .bool_or,
...@@ -7927,10 +8192,12 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool...@@ -7927,10 +8192,12 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
7927 .mod,8192 .mod,
7928 .mul,8193 .mul,
7929 .mul_wrap,8194 .mul_wrap,
8195 .mul_sat,
7930 .switch_range,8196 .switch_range,
7931 .field_access,8197 .field_access,
7932 .sub,8198 .sub,
7933 .sub_wrap,8199 .sub_wrap,
8200 .sub_sat,
7934 .slice,8201 .slice,
7935 .slice_open,8202 .slice_open,
7936 .slice_sentinel,8203 .slice_sentinel,
...@@ -8008,17 +8275,31 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool...@@ -8008,17 +8275,31 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
8008 }8275 }
8009 },8276 },
80108277
8011 .builtin_call,8278 .builtin_call_two, .builtin_call_two_comma => {
8012 .builtin_call_comma,8279 const builtin_token = main_tokens[node];
8013 .builtin_call_two,8280 const builtin_name = tree.tokenSlice(builtin_token);
8014 .builtin_call_two_comma,8281 // If the builtin is an invalid name, we don't cause an error here; instead
8015 => {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 const builtin_token = main_tokens[node];8293 const builtin_token = main_tokens[node];
8017 const builtin_name = tree.tokenSlice(builtin_token);8294 const builtin_name = tree.tokenSlice(builtin_token);
8018 // If the builtin is an invalid name, we don't cause an error here; instead8295 // If the builtin is an invalid name, we don't cause an error here; instead
8019 // let it pass, and the error will be "invalid builtin function" later.8296 // let it pass, and the error will be "invalid builtin function" later.
8020 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;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,27 +8406,33 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
8125 .tagged_union_enum_tag_trailing,8406 .tagged_union_enum_tag_trailing,
8126 .add,8407 .add,
8127 .add_wrap,8408 .add_wrap,
8409 .add_sat,
8128 .array_cat,8410 .array_cat,
8129 .array_mult,8411 .array_mult,
8130 .assign,8412 .assign,
8131 .assign_bit_and,8413 .assign_bit_and,
8132 .assign_bit_or,8414 .assign_bit_or,
8133 .assign_bit_shift_left,8415 .assign_shl,
8134 .assign_bit_shift_right,8416 .assign_shl_sat,
8417 .assign_shr,
8135 .assign_bit_xor,8418 .assign_bit_xor,
8136 .assign_div,8419 .assign_div,
8137 .assign_sub,8420 .assign_sub,
8138 .assign_sub_wrap,8421 .assign_sub_wrap,
8422 .assign_sub_sat,
8139 .assign_mod,8423 .assign_mod,
8140 .assign_add,8424 .assign_add,
8141 .assign_add_wrap,8425 .assign_add_wrap,
8426 .assign_add_sat,
8142 .assign_mul,8427 .assign_mul,
8143 .assign_mul_wrap,8428 .assign_mul_wrap,
8429 .assign_mul_sat,
8144 .bang_equal,8430 .bang_equal,
8145 .bit_and,8431 .bit_and,
8146 .bit_or,8432 .bit_or,
8147 .bit_shift_left,8433 .shl,
8148 .bit_shift_right,8434 .shl_sat,
8435 .shr,
8149 .bit_xor,8436 .bit_xor,
8150 .bool_and,8437 .bool_and,
8151 .bool_or,8438 .bool_or,
...@@ -8160,9 +8447,11 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never...@@ -8160,9 +8447,11 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
8160 .mod,8447 .mod,
8161 .mul,8448 .mul,
8162 .mul_wrap,8449 .mul_wrap,
8450 .mul_sat,
8163 .switch_range,8451 .switch_range,
8164 .sub,8452 .sub,
8165 .sub_wrap,8453 .sub_wrap,
8454 .sub_sat,
8166 .slice,8455 .slice,
8167 .slice_open,8456 .slice_open,
8168 .slice_sentinel,8457 .slice_sentinel,
...@@ -8297,27 +8586,33 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -8297,27 +8586,33 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
8297 .asm_simple,8586 .asm_simple,
8298 .add,8587 .add,
8299 .add_wrap,8588 .add_wrap,
8589 .add_sat,
8300 .array_cat,8590 .array_cat,
8301 .array_mult,8591 .array_mult,
8302 .assign,8592 .assign,
8303 .assign_bit_and,8593 .assign_bit_and,
8304 .assign_bit_or,8594 .assign_bit_or,
8305 .assign_bit_shift_left,8595 .assign_shl,
8306 .assign_bit_shift_right,8596 .assign_shl_sat,
8597 .assign_shr,
8307 .assign_bit_xor,8598 .assign_bit_xor,
8308 .assign_div,8599 .assign_div,
8309 .assign_sub,8600 .assign_sub,
8310 .assign_sub_wrap,8601 .assign_sub_wrap,
8602 .assign_sub_sat,
8311 .assign_mod,8603 .assign_mod,
8312 .assign_add,8604 .assign_add,
8313 .assign_add_wrap,8605 .assign_add_wrap,
8606 .assign_add_sat,
8314 .assign_mul,8607 .assign_mul,
8315 .assign_mul_wrap,8608 .assign_mul_wrap,
8609 .assign_mul_sat,
8316 .bang_equal,8610 .bang_equal,
8317 .bit_and,8611 .bit_and,
8318 .bit_or,8612 .bit_or,
8319 .bit_shift_left,8613 .shl,
8320 .bit_shift_right,8614 .shl_sat,
8615 .shr,
8321 .bit_xor,8616 .bit_xor,
8322 .bool_and,8617 .bool_and,
8323 .bool_or,8618 .bool_or,
...@@ -8332,10 +8627,12 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -8332,10 +8627,12 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
8332 .mod,8627 .mod,
8333 .mul,8628 .mul,
8334 .mul_wrap,8629 .mul_wrap,
8630 .mul_sat,
8335 .switch_range,8631 .switch_range,
8336 .field_access,8632 .field_access,
8337 .sub,8633 .sub,
8338 .sub_wrap,8634 .sub_wrap,
8635 .sub_sat,
8339 .slice,8636 .slice,
8340 .slice_open,8637 .slice_open,
8341 .slice_sentinel,8638 .slice_sentinel,
...@@ -8894,6 +9191,17 @@ const Scope = struct {...@@ -8894,6 +9191,17 @@ const Scope = struct {
8894 return @fieldParentPtr(T, "base", base);9191 return @fieldParentPtr(T, "base", base);
8895 }9192 }
88969193
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 const Tag = enum {9205 const Tag = enum {
8898 gen_zir,9206 gen_zir,
8899 local_val,9207 local_val,
...@@ -8919,7 +9227,7 @@ const Scope = struct {...@@ -8919,7 +9227,7 @@ const Scope = struct {
8919 const LocalVal = struct {9227 const LocalVal = struct {
8920 const base_tag: Tag = .local_val;9228 const base_tag: Tag = .local_val;
8921 base: Scope = Scope{ .tag = base_tag },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 parent: *Scope,9231 parent: *Scope,
8924 gen_zir: *GenZir,9232 gen_zir: *GenZir,
8925 inst: Zir.Inst.Ref,9233 inst: Zir.Inst.Ref,
...@@ -8938,7 +9246,7 @@ const Scope = struct {...@@ -8938,7 +9246,7 @@ const Scope = struct {
8938 const LocalPtr = struct {9246 const LocalPtr = struct {
8939 const base_tag: Tag = .local_ptr;9247 const base_tag: Tag = .local_ptr;
8940 base: Scope = Scope{ .tag = base_tag },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 parent: *Scope,9250 parent: *Scope,
8943 gen_zir: *GenZir,9251 gen_zir: *GenZir,
8944 ptr: Zir.Inst.Ref,9252 ptr: Zir.Inst.Ref,
...@@ -8956,7 +9264,7 @@ const Scope = struct {...@@ -8956,7 +9264,7 @@ const Scope = struct {
89569264
8957 const Defer = struct {9265 const Defer = struct {
8958 base: Scope,9266 base: Scope,
8959 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`.9267 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
8960 parent: *Scope,9268 parent: *Scope,
8961 defer_node: Ast.Node.Index,9269 defer_node: Ast.Node.Index,
8962 };9270 };
...@@ -8967,11 +9275,27 @@ const Scope = struct {...@@ -8967,11 +9275,27 @@ const Scope = struct {
8967 const base_tag: Tag = .namespace;9275 const base_tag: Tag = .namespace;
8968 base: Scope = Scope{ .tag = base_tag },9276 base: Scope = Scope{ .tag = base_tag },
89699277
9278 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
8970 parent: *Scope,9279 parent: *Scope,
8971 /// Maps string table index to the source location of declaration,9280 /// Maps string table index to the source location of declaration,
8972 /// for the purposes of reporting name shadowing compile errors.9281 /// for the purposes of reporting name shadowing compile errors.
8973 decls: std.AutoHashMapUnmanaged(u32, Ast.Node.Index) = .{},9282 decls: std.AutoHashMapUnmanaged(u32, Ast.Node.Index) = .{},
8974 node: Ast.Node.Index,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 };
89769300
8977 const Top = struct {9301 const Top = struct {
...@@ -8987,12 +9311,14 @@ const GenZir = struct {...@@ -8987,12 +9311,14 @@ const GenZir = struct {
8987 base: Scope = Scope{ .tag = base_tag },9311 base: Scope = Scope{ .tag = base_tag },
8988 force_comptime: bool,9312 force_comptime: bool,
8989 in_defer: bool,9313 in_defer: bool,
9314 c_import: bool = false,
8990 /// How decls created in this scope should be named.9315 /// How decls created in this scope should be named.
8991 anon_name_strategy: Zir.Inst.NameStrategy = .anon,9316 anon_name_strategy: Zir.Inst.NameStrategy = .anon,
8992 /// The containing decl AST node.9317 /// The containing decl AST node.
8993 decl_node_index: Ast.Node.Index,9318 decl_node_index: Ast.Node.Index,
8994 /// The containing decl line index, absolute.9319 /// The containing decl line index, absolute.
8995 decl_line: u32,9320 decl_line: u32,
9321 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
8996 parent: *Scope,9322 parent: *Scope,
8997 /// All `GenZir` scopes for the same ZIR share this.9323 /// All `GenZir` scopes for the same ZIR share this.
8998 astgen: *AstGen,9324 astgen: *AstGen,
...@@ -9028,10 +9354,17 @@ const GenZir = struct {...@@ -9028,10 +9354,17 @@ const GenZir = struct {
9028 suspend_node: Ast.Node.Index = 0,9354 suspend_node: Ast.Node.Index = 0,
9029 nosuspend_node: Ast.Node.Index = 0,9355 nosuspend_node: Ast.Node.Index = 0,
90309356
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 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {9363 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
9032 return .{9364 return .{
9033 .force_comptime = gz.force_comptime,9365 .force_comptime = gz.force_comptime,
9034 .in_defer = gz.in_defer,9366 .in_defer = gz.in_defer,
9367 .c_import = gz.c_import,
9035 .decl_node_index = gz.decl_node_index,9368 .decl_node_index = gz.decl_node_index,
9036 .decl_line = gz.decl_line,9369 .decl_line = gz.decl_line,
9037 .parent = scope,9370 .parent = scope,
...@@ -9041,6 +9374,52 @@ const GenZir = struct {...@@ -9041,6 +9374,52 @@ const GenZir = struct {
9041 };9374 };
9042 }9375 }
90439376
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 const Label = struct {9423 const Label = struct {
9045 token: Ast.TokenIndex,9424 token: Ast.TokenIndex,
9046 block_inst: Zir.Inst.Index,9425 block_inst: Zir.Inst.Index,
...@@ -9357,7 +9736,7 @@ const GenZir = struct {...@@ -9357,7 +9736,7 @@ const GenZir = struct {
93579736
9358 fn addCall(9737 fn addCall(
9359 gz: *GenZir,9738 gz: *GenZir,
9360 tag: Zir.Inst.Tag,9739 modifier: std.builtin.CallOptions.Modifier,
9361 callee: Zir.Inst.Ref,9740 callee: Zir.Inst.Ref,
9362 args: []const Zir.Inst.Ref,9741 args: []const Zir.Inst.Ref,
9363 /// Absolute node index. This function does the conversion to offset from Decl.9742 /// Absolute node index. This function does the conversion to offset from Decl.
...@@ -9366,20 +9745,24 @@ const GenZir = struct {...@@ -9366,20 +9745,24 @@ const GenZir = struct {
9366 assert(callee != .none);9745 assert(callee != .none);
9367 assert(src_node != 0);9746 assert(src_node != 0);
9368 const gpa = gz.astgen.gpa;9747 const gpa = gz.astgen.gpa;
9748 const Call = Zir.Inst.Call;
9369 try gz.instructions.ensureUnusedCapacity(gpa, 1);9749 try gz.instructions.ensureUnusedCapacity(gpa, 1);
9370 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);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 args.len);9752 args.len);
93739753
9374 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Call{9754 const payload_index = gz.astgen.addExtraAssumeCapacity(Call{
9375 .callee = callee,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 gz.astgen.appendRefsAssumeCapacity(args);9761 gz.astgen.appendRefsAssumeCapacity(args);
93799762
9380 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);9763 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
9381 gz.astgen.instructions.appendAssumeCapacity(.{9764 gz.astgen.instructions.appendAssumeCapacity(.{
9382 .tag = tag,9765 .tag = .call,
9383 .data = .{ .pl_node = .{9766 .data = .{ .pl_node = .{
9384 .src_node = gz.nodeIndexToRelative(src_node),9767 .src_node = gz.nodeIndexToRelative(src_node),
9385 .payload_index = payload_index,9768 .payload_index = payload_index,
...@@ -9695,6 +10078,22 @@ const GenZir = struct {...@@ -9695,6 +10078,22 @@ const GenZir = struct {
9695 });10078 });
9696 }10079 }
969710080
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 fn addNodeExtended(10097 fn addNodeExtended(
9699 gz: *GenZir,10098 gz: *GenZir,
9700 opcode: Zir.Inst.Extended,10099 opcode: Zir.Inst.Extended,
...@@ -9996,6 +10395,37 @@ const GenZir = struct {...@@ -9996,6 +10395,37 @@ const GenZir = struct {
9996 });10395 });
9997 }10396 }
999810397
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 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {10429 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
10000 return indexToRef(try gz.addAsIndex(inst));10430 return indexToRef(try gz.addAsIndex(inst));
10001 }10431 }
src/BuiltinFn.zig+17-41
...@@ -2,7 +2,6 @@ const std = @import("std");...@@ -2,7 +2,6 @@ const std = @import("std");
22
3pub const Tag = enum {3pub const Tag = enum {
4 add_with_overflow,4 add_with_overflow,
5 add_with_saturation,
6 align_cast,5 align_cast,
7 align_of,6 align_of,
8 as,7 as,
...@@ -66,7 +65,6 @@ pub const Tag = enum {...@@ -66,7 +65,6 @@ pub const Tag = enum {
66 wasm_memory_grow,65 wasm_memory_grow,
67 mod,66 mod,
68 mul_with_overflow,67 mul_with_overflow,
69 mul_with_saturation,
70 panic,68 panic,
71 pop_count,69 pop_count,
72 ptr_cast,70 ptr_cast,
...@@ -81,12 +79,10 @@ pub const Tag = enum {...@@ -81,12 +79,10 @@ pub const Tag = enum {
81 set_runtime_safety,79 set_runtime_safety,
82 shl_exact,80 shl_exact,
83 shl_with_overflow,81 shl_with_overflow,
84 shl_with_saturation,
85 shr_exact,82 shr_exact,
86 shuffle,83 shuffle,
87 size_of,84 size_of,
88 splat,85 splat,
89 sub_with_saturation,
90 reduce,86 reduce,
91 src,87 src,
92 sqrt,88 sqrt,
...@@ -114,10 +110,19 @@ pub const Tag = enum {...@@ -114,10 +110,19 @@ pub const Tag = enum {
114 Vector,110 Vector,
115};111};
116112
113pub 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
117tag: Tag,122tag: Tag,
118123
119/// `true` if the builtin call can take advantage of a result location pointer.124/// Info about the builtin call's ability to take advantage of a result location pointer.
120needs_mem_loc: bool = false,125needs_mem_loc: MemLocRequirement = .never,
121/// `true` if the builtin call can be the left-hand side of an expression (assigned to).126/// `true` if the builtin call can be the left-hand side of an expression (assigned to).
122allows_lvalue: bool = false,127allows_lvalue: bool = false,
123/// The number of parameters to this builtin function. `null` means variable number128/// The number of parameters to this builtin function. `null` means variable number
...@@ -152,7 +157,7 @@ pub const list = list: {...@@ -152,7 +157,7 @@ pub const list = list: {
152 "@as",157 "@as",
153 .{158 .{
154 .tag = .as,159 .tag = .as,
155 .needs_mem_loc = true,160 .needs_mem_loc = .forward1,
156 .param_count = 2,161 .param_count = 2,
157 },162 },
158 },163 },
...@@ -188,7 +193,7 @@ pub const list = list: {...@@ -188,7 +193,7 @@ pub const list = list: {
188 "@bitCast",193 "@bitCast",
189 .{194 .{
190 .tag = .bit_cast,195 .tag = .bit_cast,
191 .needs_mem_loc = true,196 .needs_mem_loc = .forward1,
192 .param_count = 2,197 .param_count = 2,
193 },198 },
194 },199 },
...@@ -252,7 +257,7 @@ pub const list = list: {...@@ -252,7 +257,7 @@ pub const list = list: {
252 "@call",257 "@call",
253 .{258 .{
254 .tag = .call,259 .tag = .call,
255 .needs_mem_loc = true,260 .needs_mem_loc = .always,
256 .param_count = 3,261 .param_count = 3,
257 },262 },
258 },263 },
...@@ -414,7 +419,7 @@ pub const list = list: {...@@ -414,7 +419,7 @@ pub const list = list: {
414 "@field",419 "@field",
415 .{420 .{
416 .tag = .field,421 .tag = .field,
417 .needs_mem_loc = true,422 .needs_mem_loc = .always,
418 .param_count = 2,423 .param_count = 2,
419 .allows_lvalue = true,424 .allows_lvalue = true,
420 },425 },
...@@ -531,34 +536,6 @@ pub const list = list: {...@@ -531,34 +536,6 @@ pub const list = list: {
531 .param_count = 2,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 "@memcpy",540 "@memcpy",
564 .{541 .{
...@@ -731,7 +708,6 @@ pub const list = list: {...@@ -731,7 +708,6 @@ pub const list = list: {
731 "@splat",708 "@splat",
732 .{709 .{
733 .tag = .splat,710 .tag = .splat,
734 .needs_mem_loc = true,
735 .param_count = 2,711 .param_count = 2,
736 },712 },
737 },713 },
...@@ -746,7 +722,7 @@ pub const list = list: {...@@ -746,7 +722,7 @@ pub const list = list: {
746 "@src",722 "@src",
747 .{723 .{
748 .tag = .src,724 .tag = .src,
749 .needs_mem_loc = true,725 .needs_mem_loc = .always,
750 .param_count = 0,726 .param_count = 0,
751 },727 },
752 },728 },
...@@ -901,7 +877,7 @@ pub const list = list: {...@@ -901,7 +877,7 @@ pub const list = list: {
901 "@unionInit",877 "@unionInit",
902 .{878 .{
903 .tag = .union_init,879 .tag = .union_init,
904 .needs_mem_loc = true,880 .needs_mem_loc = .always,
905 .param_count = 3,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,7 +812,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
812812
813 const needs_c_symbols = !options.skip_linker_dependencies and is_exe_or_dyn_lib;813 const needs_c_symbols = !options.skip_linker_dependencies and is_exe_or_dyn_lib;
814814
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 const wasi_exec_model = if (options.target.os.tag != .wasi) undefined else options.wasi_exec_model orelse .command;816 const wasi_exec_model = if (options.target.os.tag != .wasi) undefined else options.wasi_exec_model orelse .command;
817817
818 const comp: *Compilation = comp: {818 const comp: *Compilation = comp: {
...@@ -849,10 +849,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -849,10 +849,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
849 if (options.use_llvm) |explicit|849 if (options.use_llvm) |explicit|
850 break :blk explicit;850 break :blk explicit;
851851
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 // If we are outputting .c code we must use Zig backend.852 // If we are outputting .c code we must use Zig backend.
857 if (ofmt == .c)853 if (ofmt == .c)
858 break :blk false;854 break :blk false;
...@@ -861,6 +857,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -861,6 +857,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
861 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)857 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)
862 break :blk true;858 break :blk true;
863859
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 // The stage1 compiler depends on the stage1 C++ LLVM backend864 // The stage1 compiler depends on the stage1 C++ LLVM backend
865 // to compile zig code.865 // to compile zig code.
866 if (use_stage1)866 if (use_stage1)
...@@ -876,9 +876,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -876,9 +876,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
876 if (options.use_llvm == true) {876 if (options.use_llvm == true) {
877 return error.ZigCompilerNotBuiltWithLLVMExtensions;877 return error.ZigCompilerNotBuiltWithLLVMExtensions;
878 }878 }
879 if (options.machine_code_model != .default) {
880 return error.MachineCodeModelNotSupportedWithoutLlvm;
881 }
882 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {879 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {
883 return error.EmittingLlvmModuleRequiresUsingLlvmBackend;880 return error.EmittingLlvmModuleRequiresUsingLlvmBackend;
884 }881 }
...@@ -1100,7 +1097,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1100,7 +1097,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
11001097
1101 if (feature.llvm_name) |llvm_name| {1098 if (feature.llvm_name) |llvm_name| {
1102 const plus_or_minus = "-+"[@boolToInt(is_enabled)];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 buf.appendAssumeCapacity(plus_or_minus);1101 buf.appendAssumeCapacity(plus_or_minus);
1105 buf.appendSliceAssumeCapacity(llvm_name);1102 buf.appendSliceAssumeCapacity(llvm_name);
1106 buf.appendSliceAssumeCapacity(",");1103 buf.appendSliceAssumeCapacity(",");
...@@ -1180,6 +1177,8 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1180,6 +1177,8 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1180 }1177 }
1181 hash.add(valgrind);1178 hash.add(valgrind);
1182 hash.add(single_threaded);1179 hash.add(single_threaded);
1180 hash.add(use_stage1);
1181 hash.add(use_llvm);
1183 hash.add(dll_export_fns);1182 hash.add(dll_export_fns);
1184 hash.add(options.is_test);1183 hash.add(options.is_test);
1185 hash.add(options.skip_linker_dependencies);1184 hash.add(options.skip_linker_dependencies);
...@@ -1350,7 +1349,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1350,7 +1349,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
13501349
1351 var system_libs: std.StringArrayHashMapUnmanaged(void) = .{};1350 var system_libs: std.StringArrayHashMapUnmanaged(void) = .{};
1352 errdefer system_libs.deinit(gpa);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 for (options.system_libs) |lib_name| {1353 for (options.system_libs) |lib_name| {
1355 system_libs.putAssumeCapacity(lib_name, {});1354 system_libs.putAssumeCapacity(lib_name, {});
1356 }1355 }
...@@ -1486,7 +1485,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1486,7 +1485,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1486 errdefer comp.astgen_wait_group.deinit();1485 errdefer comp.astgen_wait_group.deinit();
14871486
1488 // Add a `CObject` for each `c_source_files`.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 for (options.c_source_files) |c_source_file| {1489 for (options.c_source_files) |c_source_file| {
1491 const c_object = try gpa.create(CObject);1490 const c_object = try gpa.create(CObject);
1492 errdefer gpa.destroy(c_object);1491 errdefer gpa.destroy(c_object);
...@@ -1577,25 +1576,30 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1577,25 +1576,30 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1577 // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler1576 // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler
1578 // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for1577 // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for
1579 // stage1 but not stage2.1578 // stage1 but not stage2.
1580 if (comp.bin_file.options.use_stage1) {1579 const capable_of_building_compiler_rt = comp.bin_file.options.use_stage1 or
1581 if (comp.bin_file.options.include_compiler_rt) {1580 comp.bin_file.options.use_llvm;
1582 if (is_exe_or_dyn_lib) {1581 const capable_of_building_zig_libc = comp.bin_file.options.use_stage1 or
1583 try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });1582 comp.bin_file.options.use_llvm;
1584 } else if (options.output_mode != .Obj) {1583 const capable_of_building_ssp = comp.bin_file.options.use_stage1;
1585 // If build-obj with -fcompiler-rt is requested, that is handled specially1584
1586 // elsewhere. In this case we are making a static library, so we ask1585 if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) {
1587 // for a compiler-rt object to put in it.1586 if (is_exe_or_dyn_lib) {
1588 try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });1587 try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });
1589 }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) {1594 }
1592 // MinGW provides no libssp, use our own implementation.1595 if (needs_c_symbols) {
1593 if (comp.getTarget().isMinGW()) {1596 // MinGW provides no libssp, use our own implementation.
1594 try comp.work_queue.writeItem(.{ .libssp = {} });1597 if (comp.getTarget().isMinGW() and capable_of_building_ssp) {
1595 }1598 try comp.work_queue.writeItem(.{ .libssp = {} });
1596 if (!comp.bin_file.options.link_libc) {1599 }
1597 try comp.work_queue.writeItem(.{ .zig_libc = {} });1600
1598 }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,6 +1651,9 @@ pub fn destroy(self: *Compilation) void {
1647 if (self.compiler_rt_static_lib) |*crt_file| {1651 if (self.compiler_rt_static_lib) |*crt_file| {
1648 crt_file.deinit(gpa);1652 crt_file.deinit(gpa);
1649 }1653 }
1654 if (self.compiler_rt_obj) |*crt_file| {
1655 crt_file.deinit(gpa);
1656 }
1650 if (self.libssp_static_lib) |*crt_file| {1657 if (self.libssp_static_lib) |*crt_file| {
1651 crt_file.deinit(gpa);1658 crt_file.deinit(gpa);
1652 }1659 }
...@@ -1793,6 +1800,10 @@ pub fn update(self: *Compilation) !void {...@@ -1793,6 +1800,10 @@ pub fn update(self: *Compilation) !void {
1793 }1800 }
1794 }1801 }
17951802
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 // If there are any errors, we anticipate the source files being loaded1807 // If there are any errors, we anticipate the source files being loaded
1797 // to report error messages. Otherwise we unload all source files to save memory.1808 // to report error messages. Otherwise we unload all source files to save memory.
1798 // The ZIR needs to stay loaded in memory because (1) Decl objects contain references1809 // 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,6 +1819,37 @@ pub fn update(self: *Compilation) !void {
1808 }1819 }
1809}1820}
18101821
1822fn 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/// Having the file open for writing is problematic as far as executing the1853/// Having the file open for writing is problematic as far as executing the
1812/// binary is concerned. This will remove the write flag, or close the file,1854/// binary is concerned. This will remove the write flag, or close the file,
1813/// or whatever is needed so that it can be executed.1855/// or whatever is needed so that it can be executed.
...@@ -2113,7 +2155,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2113,7 +2155,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2113 const module = self.bin_file.options.module.?;2155 const module = self.bin_file.options.module.?;
2114 const decl = func.owner_decl;2156 const decl = func.owner_decl;
21152157
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 error.AnalysisFail => {2163 error.AnalysisFail => {
2118 assert(func.state != .in_progress);2164 assert(func.state != .in_progress);
2119 continue;2165 continue;
...@@ -2175,16 +2221,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2175,16 +2221,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2175 const decl_emit_h = decl.getEmitH(module);2221 const decl_emit_h = decl.getEmitH(module);
2176 const fwd_decl = &decl_emit_h.fwd_decl;2222 const fwd_decl = &decl_emit_h.fwd_decl;
2177 fwd_decl.shrinkRetainingCapacity(0);2223 fwd_decl.shrinkRetainingCapacity(0);
2224 var typedefs_arena = std.heap.ArenaAllocator.init(gpa);
2225 defer typedefs_arena.deinit();
21782226
2179 var dg: c_codegen.DeclGen = .{2227 var dg: c_codegen.DeclGen = .{
2228 .gpa = gpa,
2180 .module = module,2229 .module = module,
2181 .error_msg = null,2230 .error_msg = null,
2182 .decl = decl,2231 .decl = decl,
2183 .fwd_decl = fwd_decl.toManaged(gpa),2232 .fwd_decl = fwd_decl.toManaged(gpa),
2184 // we don't want to emit optionals and error unions to headers since they have no ABI2233 .typedefs = c_codegen.TypedefMap.init(gpa),
2185 .typedefs = undefined,2234 .typedefs_arena = &typedefs_arena.allocator,
2186 };2235 };
2187 defer dg.fwd_decl.deinit();2236 defer dg.fwd_decl.deinit();
2237 defer dg.typedefs.deinit();
21882238
2189 c_codegen.genHeader(&dg) catch |err| switch (err) {2239 c_codegen.genHeader(&dg) catch |err| switch (err) {
2190 error.AnalysisFail => {2240 error.AnalysisFail => {
...@@ -2612,7 +2662,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {...@@ -2612,7 +2662,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
26122662
2613 const dep_basename = std.fs.path.basename(out_dep_path);2663 const dep_basename = std.fs.path.basename(out_dep_path);
2614 try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);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);
26162666
2617 const digest = man.final();2667 const digest = man.final();
2618 const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });2668 const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
...@@ -2721,16 +2771,12 @@ fn reportRetryableAstGenError(...@@ -2721,16 +2771,12 @@ fn reportRetryableAstGenError(
2721 try Module.ErrorMsg.create(2771 try Module.ErrorMsg.create(
2722 gpa,2772 gpa,
2723 src_loc,2773 src_loc,
2724 "unable to load '{'}" ++ std.fs.path.sep_str ++ "{'}': {s}",2774 "unable to load '{s}" ++ std.fs.path.sep_str ++ "{s}': {s}",
2725 .{2775 .{ dir_path, file.sub_file_path, @errorName(err) },
2726 std.zig.fmtEscapes(dir_path),
2727 std.zig.fmtEscapes(file.sub_file_path),
2728 @errorName(err),
2729 },
2730 )2776 )
2731 else2777 else
2732 try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{'}': {s}", .{2778 try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{s}': {s}", .{
2733 std.zig.fmtEscapes(file.sub_file_path), @errorName(err),2779 file.sub_file_path, @errorName(err),
2734 });2780 });
2735 errdefer err_msg.destroy(gpa);2781 errdefer err_msg.destroy(gpa);
27362782
...@@ -2766,6 +2812,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -2766,6 +2812,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2766 defer man.deinit();2812 defer man.deinit();
27672813
2768 man.hash.add(comp.clang_preprocessor_mode);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);
27692818
2770 try man.hashCSource(c_object.src);2819 try man.hashCSource(c_object.src);
27712820
...@@ -2789,16 +2838,29 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -2789,16 +2838,29 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2789 comp.bin_file.options.root_name2838 comp.bin_file.options.root_name
2790 else2839 else
2791 c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len];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 });
27962841
2842 const o_ext = comp.bin_file.options.object_format.fileExt(comp.bin_file.options.target.cpu.arch);
2797 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {2843 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
2798 var argv = std.ArrayList([]const u8).init(comp.gpa);2844 var argv = std.ArrayList([]const u8).init(comp.gpa);
2799 defer argv.deinit();2845 defer argv.deinit();
28002846
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 const out_obj_path = try comp.tmpFilePath(arena, o_basename);2864 const out_obj_path = try comp.tmpFilePath(arena, o_basename);
2803 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});2865 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
2804 defer zig_cache_tmp_dir.close();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,15 +2874,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2812 try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});2874 try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});
2813 try comp.addCCArgs(arena, &argv, ext, out_dep_path);2875 try comp.addCCArgs(arena, &argv, ext, out_dep_path);
28142876
2815 try argv.ensureCapacity(argv.items.len + 3);2877 try argv.ensureUnusedCapacity(6 + c_object.src.extra_flags.len);
2816 switch (comp.clang_preprocessor_mode) {2878 switch (comp.clang_preprocessor_mode) {
2817 .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),2879 .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
2818 .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),2880 .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
2819 .stdout => argv.appendAssumeCapacity("-E"),2881 .stdout => argv.appendAssumeCapacity("-E"),
2820 }2882 }
28212883 if (comp.clang_passthrough_mode) {
2822 try argv.append(c_object.src.src_path);2884 if (comp.emit_asm != null) {
2823 try argv.appendSlice(c_object.src.extra_flags);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);
28242894
2825 if (comp.verbose_cc) {2895 if (comp.verbose_cc) {
2826 dump_argv(argv.items);2896 dump_argv(argv.items);
...@@ -2840,8 +2910,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -2840,8 +2910,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2840 switch (term) {2910 switch (term) {
2841 .Exited => |code| {2911 .Exited => |code| {
2842 if (code != 0) {2912 if (code != 0) {
2843 // TODO https://github.com/ziglang/zig/issues/63422913 std.process.exit(code);
2844 std.process.exit(1);
2845 }2914 }
2846 if (comp.clang_preprocessor_mode == .stdout)2915 if (comp.clang_preprocessor_mode == .stdout)
2847 std.process.exit(0);2916 std.process.exit(0);
...@@ -2857,9 +2926,6 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -2857,9 +2926,6 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
28572926
2858 const stderr_reader = child.stderr.?.reader();2927 const stderr_reader = child.stderr.?.reader();
28592928
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 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);2929 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
28642930
2865 const term = child.wait() catch |err| {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,6 +2975,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
2909 break :blk digest;2975 break :blk digest;
2910 };2976 };
29112977
2978 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext });
2979
2912 c_object.status = .{2980 c_object.status = .{
2913 .success = .{2981 .success = .{
2914 .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{2982 .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{
...@@ -3032,7 +3100,7 @@ pub fn addCCArgs(...@@ -3032,7 +3100,7 @@ pub fn addCCArgs(
30323100
3033 // It would be really nice if there was a more compact way to communicate this info to Clang.3101 // It would be really nice if there was a more compact way to communicate this info to Clang.
3034 const all_features_list = target.cpu.arch.allFeaturesList();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 for (all_features_list) |feature, index_usize| {3104 for (all_features_list) |feature, index_usize| {
3037 const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);3105 const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
3038 const is_enabled = target.cpu.features.isEnabled(index);3106 const is_enabled = target.cpu.features.isEnabled(index);
...@@ -3533,7 +3601,7 @@ fn detectLibCIncludeDirs(...@@ -3533,7 +3601,7 @@ fn detectLibCIncludeDirs(
35333601
3534fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {3602fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
3535 var list = std.ArrayList([]const u8).init(arena);3603 var list = std.ArrayList([]const u8).init(arena);
3536 try list.ensureCapacity(4);3604 try list.ensureTotalCapacity(4);
35373605
3538 list.appendAssumeCapacity(lci.include_dir.?);3606 list.appendAssumeCapacity(lci.include_dir.?);
35393607
...@@ -3640,7 +3708,7 @@ fn setMiscFailure(...@@ -3640,7 +3708,7 @@ fn setMiscFailure(
3640 comptime format: []const u8,3708 comptime format: []const u8,
3641 args: anytype,3709 args: anytype,
3642) Allocator.Error!void {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 const msg = try std.fmt.allocPrint(comp.gpa, format, args);3712 const msg = try std.fmt.allocPrint(comp.gpa, format, args);
3645 comp.misc_failures.putAssumeCapacityNoClobber(tag, .{ .msg = msg });3713 comp.misc_failures.putAssumeCapacityNoClobber(tag, .{ .msg = msg });
3646}3714}
...@@ -3662,6 +3730,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc...@@ -3662,6 +3730,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
3662 const target = comp.getTarget();3730 const target = comp.getTarget();
3663 const generic_arch_name = target.cpu.arch.genericName();3731 const generic_arch_name = target.cpu.arch.genericName();
3664 const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1;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);
36653735
3666 @setEvalBranchQuota(4000);3736 @setEvalBranchQuota(4000);
3667 try buffer.writer().print(3737 try buffer.writer().print(
...@@ -3673,6 +3743,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc...@@ -3673,6 +3743,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
3673 \\pub const zig_is_stage2 = {};3743 \\pub const zig_is_stage2 = {};
3674 \\/// Temporary until self-hosted supports the `cpu.arch` value.3744 \\/// Temporary until self-hosted supports the `cpu.arch` value.
3675 \\pub const stage2_arch: std.Target.Cpu.Arch = .{};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 \\pub const output_mode = std.builtin.OutputMode.{};3749 \\pub const output_mode = std.builtin.OutputMode.{};
3678 \\pub const link_mode = std.builtin.LinkMode.{};3750 \\pub const link_mode = std.builtin.LinkMode.{};
...@@ -3688,6 +3760,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc...@@ -3688,6 +3760,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
3688 build_options.version,3760 build_options.version,
3689 !use_stage1,3761 !use_stage1,
3690 std.zig.fmtId(@tagName(target.cpu.arch)),3762 std.zig.fmtId(@tagName(target.cpu.arch)),
3763 stage2_x86_cx16,
3691 std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)),3764 std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)),
3692 std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)),3765 std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)),
3693 comp.bin_file.options.is_test,3766 comp.bin_file.options.is_test,
...@@ -3912,6 +3985,7 @@ fn buildOutputFromZig(...@@ -3912,6 +3985,7 @@ fn buildOutputFromZig(
3912 },3985 },
3913 .root_src_path = src_basename,3986 .root_src_path = src_basename,
3914 };3987 };
3988 defer main_pkg.deinitTable(comp.gpa);
3915 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];3989 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];
3916 const target = comp.getTarget();3990 const target = comp.getTarget();
3917 const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{3991 const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{
...@@ -3970,7 +4044,7 @@ fn buildOutputFromZig(...@@ -3970,7 +4044,7 @@ fn buildOutputFromZig(
3970 defer if (!keep_errors) errors.deinit(sub_compilation.gpa);4044 defer if (!keep_errors) errors.deinit(sub_compilation.gpa);
39714045
3972 if (errors.list.len != 0) {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 comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{4048 comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{
3975 .msg = try std.fmt.allocPrint(comp.gpa, "sub-compilation of {s} failed", .{4049 .msg = try std.fmt.allocPrint(comp.gpa, "sub-compilation of {s} failed", .{
3976 @tagName(misc_task_tag),4050 @tagName(misc_task_tag),
...@@ -4402,7 +4476,7 @@ pub fn build_crt_file(...@@ -4402,7 +4476,7 @@ pub fn build_crt_file(
44024476
4403 try sub_compilation.updateSubCompilation();4477 try sub_compilation.updateSubCompilation();
44044478
4405 try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);4479 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
44064480
4407 comp.crt_files.putAssumeCapacityNoClobber(basename, .{4481 comp.crt_files.putAssumeCapacityNoClobber(basename, .{
4408 .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{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,7 +30,7 @@ tomb_bits: []usize,
30/// The main tomb bits are still used and the extra ones are starting with the lsb of the30/// The main tomb bits are still used and the extra ones are starting with the lsb of the
31/// value here.31/// value here.
32special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),32special: 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.
34extra: []const u32,34extra: []const u32,
3535
36/// Trailing is the set of instructions whose lifetimes end at the start of the then branch,36/// Trailing is the set of instructions whose lifetimes end at the start of the then branch,
...@@ -226,12 +226,16 @@ fn analyzeInst(...@@ -226,12 +226,16 @@ fn analyzeInst(
226 switch (inst_tags[inst]) {226 switch (inst_tags[inst]) {
227 .add,227 .add,
228 .addwrap,228 .addwrap,
229 .add_sat,
229 .sub,230 .sub,
230 .subwrap,231 .subwrap,
232 .sub_sat,
231 .mul,233 .mul,
232 .mulwrap,234 .mulwrap,
235 .mul_sat,
233 .div,236 .div,
234 .rem,237 .rem,
238 .mod,
235 .ptr_add,239 .ptr_add,
236 .ptr_sub,240 .ptr_sub,
237 .bit_and,241 .bit_and,
...@@ -251,7 +255,14 @@ fn analyzeInst(...@@ -251,7 +255,14 @@ fn analyzeInst(
251 .ptr_elem_val,255 .ptr_elem_val,
252 .ptr_ptr_elem_val,256 .ptr_ptr_elem_val,
253 .shl,257 .shl,
258 .shl_exact,
259 .shl_sat,
254 .shr,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 const o = inst_datas[inst].bin_op;267 const o = inst_datas[inst].bin_op;
257 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });268 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
...@@ -264,12 +275,14 @@ fn analyzeInst(...@@ -264,12 +275,14 @@ fn analyzeInst(
264 .breakpoint,275 .breakpoint,
265 .dbg_stmt,276 .dbg_stmt,
266 .unreach,277 .unreach,
278 .fence,
267 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),279 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
268280
269 .not,281 .not,
270 .bitcast,282 .bitcast,
271 .load,283 .load,
272 .floatcast,284 .fpext,
285 .fptrunc,
273 .intcast,286 .intcast,
274 .trunc,287 .trunc,
275 .optional_payload,288 .optional_payload,
...@@ -288,6 +301,11 @@ fn analyzeInst(...@@ -288,6 +301,11 @@ fn analyzeInst(
288 .struct_field_ptr_index_2,301 .struct_field_ptr_index_2,
289 .struct_field_ptr_index_3,302 .struct_field_ptr_index_3,
290 .array_to_slice,303 .array_to_slice,
304 .float_to_int,
305 .int_to_float,
306 .get_union_tag,
307 .clz,
308 .ctz,
291 => {309 => {
292 const o = inst_datas[inst].ty_op;310 const o = inst_datas[inst].ty_op;
293 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });311 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
...@@ -344,6 +362,20 @@ fn analyzeInst(...@@ -344,6 +362,20 @@ fn analyzeInst(
344 const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data;362 const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data;
345 return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value });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 .br => {379 .br => {
348 const br = inst_datas[inst].br;380 const br = inst_datas[inst].br;
349 return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });381 return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });
...@@ -440,7 +472,7 @@ fn analyzeInst(...@@ -440,7 +472,7 @@ fn analyzeInst(
440 }472 }
441 // Now we have to correctly populate new_set.473 // Now we have to correctly populate new_set.
442 if (new_set) |ns| {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 var it = then_table.keyIterator();476 var it = then_table.keyIterator();
445 while (it.next()) |key| {477 while (it.next()) |key| {
446 _ = ns.putAssumeCapacity(key.*, {});478 _ = ns.putAssumeCapacity(key.*, {});
src/Module.zig+368-165
...@@ -64,11 +64,16 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},...@@ -64,11 +64,16 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
64/// The set of all the generic function instantiations. This is used so that when a generic64/// The set of all the generic function instantiations. This is used so that when a generic
65/// function is called twice with the same comptime parameter arguments, both calls dispatch65/// function is called twice with the same comptime parameter arguments, both calls dispatch
66/// to the same function.66/// to the same function.
67/// TODO: remove functions from this set when they are destroyed.
67monomorphed_funcs: MonomorphedFuncsSet = .{},68monomorphed_funcs: MonomorphedFuncsSet = .{},
68
69/// The set of all comptime function calls that have been cached so that future calls69/// The set of all comptime function calls that have been cached so that future calls
70/// with the same parameters will get the same return value.70/// with the same parameters will get the same return value.
71memoized_calls: MemoizedCallSet = .{},71memoized_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.
76align_stack_fns: std.AutoHashMapUnmanaged(*const Fn, SetAlignStack) = .{},
7277
73/// We optimize memory usage for a compilation with no compile errors by storing the78/// We optimize memory usage for a compilation with no compile errors by storing the
74/// error messages and mapping outside of `Decl`.79/// error messages and mapping outside of `Decl`.
...@@ -215,6 +220,13 @@ pub const MemoizedCall = struct {...@@ -215,6 +220,13 @@ pub const MemoizedCall = struct {
215 }220 }
216};221};
217222
223pub 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/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.230/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.
219pub const GlobalEmitH = struct {231pub const GlobalEmitH = struct {
220 /// Where to put the output.232 /// Where to put the output.
...@@ -236,6 +248,9 @@ pub const Export = struct {...@@ -236,6 +248,9 @@ pub const Export = struct {
236 link: link.File.Export,248 link: link.File.Export,
237 /// The Decl that performs the export. Note that this is *not* the Decl being exported.249 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
238 owner_decl: *Decl,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 /// The Decl being exported. Note this is *not* the Decl performing the export.254 /// The Decl being exported. Note this is *not* the Decl performing the export.
240 exported_decl: *Decl,255 exported_decl: *Decl,
241 status: enum {256 status: enum {
...@@ -249,8 +264,8 @@ pub const Export = struct {...@@ -249,8 +264,8 @@ pub const Export = struct {
249264
250 pub fn getSrcLoc(exp: Export) SrcLoc {265 pub fn getSrcLoc(exp: Export) SrcLoc {
251 return .{266 return .{
252 .file_scope = exp.owner_decl.namespace.file_scope,267 .file_scope = exp.src_decl.namespace.file_scope,
253 .parent_decl_node = exp.owner_decl.src_node,268 .parent_decl_node = exp.src_decl.src_node,
254 .lazy = exp.src,269 .lazy = exp.src,
255 };270 };
256 }271 }
...@@ -263,6 +278,56 @@ pub const DeclPlusEmitH = struct {...@@ -263,6 +278,56 @@ pub const DeclPlusEmitH = struct {
263 emit_h: EmitH,278 emit_h: EmitH,
264};279};
265280
281pub 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
291pub 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
266pub const Decl = struct {331pub const Decl = struct {
267 /// Allocated with Module's allocator; outlives the ZIR code.332 /// Allocated with Module's allocator; outlives the ZIR code.
268 name: [*:0]const u8,333 name: [*:0]const u8,
...@@ -276,7 +341,9 @@ pub const Decl = struct {...@@ -276,7 +341,9 @@ pub const Decl = struct {
276 align_val: Value,341 align_val: Value,
277 /// Populated when `has_tv`.342 /// Populated when `has_tv`.
278 linksection_val: Value,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 /// If this is `null` then there is no memory management needed.347 /// If this is `null` then there is no memory management needed.
281 value_arena: ?*std.heap.ArenaAllocator.State = null,348 value_arena: ?*std.heap.ArenaAllocator.State = null,
282 /// The direct parent namespace of the Decl.349 /// The direct parent namespace of the Decl.
...@@ -285,6 +352,11 @@ pub const Decl = struct {...@@ -285,6 +352,11 @@ pub const Decl = struct {
285 /// the namespace of the struct, since there is no parent.352 /// the namespace of the struct, since there is no parent.
286 namespace: *Scope.Namespace,353 namespace: *Scope.Namespace,
287354
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 /// An integer that can be checked against the corresponding incrementing360 /// An integer that can be checked against the corresponding incrementing
289 /// generation field of Module. This is used to determine whether `complete` status361 /// generation field of Module. This is used to determine whether `complete` status
290 /// represents pre- or post- re-analysis.362 /// represents pre- or post- re-analysis.
...@@ -339,7 +411,7 @@ pub const Decl = struct {...@@ -339,7 +411,7 @@ pub const Decl = struct {
339 /// to require re-analysis.411 /// to require re-analysis.
340 outdated,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 has_tv: bool,415 has_tv: bool,
344 /// If `true` it means the `Decl` is the resource owner of the type/value associated416 /// If `true` it means the `Decl` is the resource owner of the type/value associated
345 /// with it. That means when `Decl` is destroyed, the cleanup code should additionally417 /// with it. That means when `Decl` is destroyed, the cleanup code should additionally
...@@ -354,8 +426,8 @@ pub const Decl = struct {...@@ -354,8 +426,8 @@ pub const Decl = struct {
354 is_exported: bool,426 is_exported: bool,
355 /// Whether the ZIR code provides an align instruction.427 /// Whether the ZIR code provides an align instruction.
356 has_align: bool,428 has_align: bool,
357 /// Whether the ZIR code provides a linksection instruction.429 /// Whether the ZIR code provides a linksection and address space instruction.
358 has_linksection: bool,430 has_linksection_or_addrspace: bool,
359 /// Flag used by garbage collection to mark and sweep.431 /// Flag used by garbage collection to mark and sweep.
360 /// Decls which correspond to an AST node always have this field set to `true`.432 /// Decls which correspond to an AST node always have this field set to `true`.
361 /// Anonymous Decls are initialized with this field set to `false` and then it433 /// Anonymous Decls are initialized with this field set to `false` and then it
...@@ -477,14 +549,22 @@ pub const Decl = struct {...@@ -477,14 +549,22 @@ pub const Decl = struct {
477 if (!decl.has_align) return .none;549 if (!decl.has_align) return .none;
478 assert(decl.zir_decl_index != 0);550 assert(decl.zir_decl_index != 0);
479 const zir = decl.namespace.file_scope.zir;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 }
482554
483 pub fn zirLinksectionRef(decl: Decl) Zir.Inst.Ref {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 assert(decl.zir_decl_index != 0);565 assert(decl.zir_decl_index != 0);
486 const zir = decl.namespace.file_scope.zir;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 return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);568 return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
489 }569 }
490570
...@@ -542,11 +622,11 @@ pub const Decl = struct {...@@ -542,11 +622,11 @@ pub const Decl = struct {
542 return decl.namespace.renderFullyQualifiedName(unqualified_name, writer);622 return decl.namespace.renderFullyQualifiedName(unqualified_name, writer);
543 }623 }
544624
545 pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![]u8 {625 pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![:0]u8 {
546 var buffer = std.ArrayList(u8).init(gpa);626 var buffer = std.ArrayList(u8).init(gpa);
547 defer buffer.deinit();627 defer buffer.deinit();
548 try decl.renderFullyQualifiedName(buffer.writer());628 try decl.renderFullyQualifiedName(buffer.writer());
549 return buffer.toOwnedSlice();629 return buffer.toOwnedSliceSentinel(0);
550 }630 }
551631
552 pub fn typedValue(decl: Decl) error{AnalysisFail}!TypedValue {632 pub fn typedValue(decl: Decl) error{AnalysisFail}!TypedValue {
...@@ -588,7 +668,7 @@ pub const Decl = struct {...@@ -588,7 +668,7 @@ pub const Decl = struct {
588668
589 /// If the Decl has a value and it is a function, return it,669 /// If the Decl has a value and it is a function, return it,
590 /// otherwise null.670 /// otherwise null.
591 pub fn getFunction(decl: *Decl) ?*Fn {671 pub fn getFunction(decl: *const Decl) ?*Fn {
592 if (!decl.owns_tv) return null;672 if (!decl.owns_tv) return null;
593 const func = (decl.val.castTag(.function) orelse return null).data;673 const func = (decl.val.castTag(.function) orelse return null).data;
594 assert(func.owner_decl == decl);674 assert(func.owner_decl == decl);
...@@ -733,7 +813,7 @@ pub const Struct = struct {...@@ -733,7 +813,7 @@ pub const Struct = struct {
733 is_comptime: bool,813 is_comptime: bool,
734 };814 };
735815
736 pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 {816 pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![:0]u8 {
737 return s.owner_decl.getFullyQualifiedName(gpa);817 return s.owner_decl.getFullyQualifiedName(gpa);
738 }818 }
739819
...@@ -779,6 +859,36 @@ pub const EnumSimple = struct {...@@ -779,6 +859,36 @@ pub const EnumSimple = struct {
779 }859 }
780};860};
781861
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.
865pub 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/// Represents the data that an enum declaration provides, when there is892/// Represents the data that an enum declaration provides, when there is
783/// at least one tag value explicitly specified, or at least one declaration.893/// at least one tag value explicitly specified, or at least one declaration.
784pub const EnumFull = struct {894pub const EnumFull = struct {
...@@ -788,16 +898,17 @@ pub const EnumFull = struct {...@@ -788,16 +898,17 @@ pub const EnumFull = struct {
788 /// Whether zig chooses this type or the user specifies it, it is stored here.898 /// Whether zig chooses this type or the user specifies it, it is stored here.
789 tag_ty: Type,899 tag_ty: Type,
790 /// Set of field names in declaration order.900 /// Set of field names in declaration order.
791 fields: std.StringArrayHashMapUnmanaged(void),901 fields: NameMap,
792 /// Maps integer tag value to field index.902 /// Maps integer tag value to field index.
793 /// Entries are in declaration order, same as `fields`.903 /// Entries are in declaration order, same as `fields`.
794 /// If this hash map is empty, it means the enum tags are auto-numbered.904 /// If this hash map is empty, it means the enum tags are auto-numbered.
795 values: ValueMap,905 values: ValueMap,
796 /// Represents the declarations inside this struct.906 /// Represents the declarations inside this enum.
797 namespace: Scope.Namespace,907 namespace: Scope.Namespace,
798 /// Offset from `owner_decl`, points to the enum decl AST node.908 /// Offset from `owner_decl`, points to the enum decl AST node.
799 node_offset: i32,909 node_offset: i32,
800910
911 pub const NameMap = std.StringArrayHashMapUnmanaged(void);
801 pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false);912 pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false);
802913
803 pub fn srcLoc(self: EnumFull) SrcLoc {914 pub fn srcLoc(self: EnumFull) SrcLoc {
...@@ -853,6 +964,44 @@ pub const Union = struct {...@@ -853,6 +964,44 @@ pub const Union = struct {
853 .lazy = .{ .node_offset = self.node_offset },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};
8571006
858/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.1007/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
...@@ -881,6 +1030,7 @@ pub const Fn = struct {...@@ -881,6 +1030,7 @@ pub const Fn = struct {
8811030
882 state: Analysis,1031 state: Analysis,
883 is_cold: bool = false,1032 is_cold: bool = false,
1033 is_noinline: bool = false,
8841034
885 pub const Analysis = enum {1035 pub const Analysis = enum {
886 queued,1036 queued,
...@@ -936,19 +1086,21 @@ pub const Scope = struct {...@@ -936,19 +1086,21 @@ pub const Scope = struct {
936 return @fieldParentPtr(T, "base", base);1086 return @fieldParentPtr(T, "base", base);
937 }1087 }
9381088
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 return switch (scope.tag) {1091 return switch (scope.tag) {
941 .block => scope.cast(Block).?.sema.owner_decl,1092 .block => scope.cast(Block).?.src_decl,
942 .file => null,1093 .file => null,
943 .namespace => null,1094 .namespace => scope.cast(Namespace).?.getDecl(),
944 };1095 };
945 }1096 }
9461097
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 return switch (scope.tag) {1100 return switch (scope.tag) {
949 .block => scope.cast(Block).?.src_decl,1101 .block => scope.cast(Block).?.wip_capture_scope,
950 .file => null,1102 .file => null,
951 .namespace => scope.cast(Namespace).?.getDecl(),1103 .namespace => scope.cast(Namespace).?.getDecl().src_scope,
952 };1104 };
953 }1105 }
9541106
...@@ -1288,6 +1440,9 @@ pub const Scope = struct {...@@ -1288,6 +1440,9 @@ pub const Scope = struct {
1288 instructions: ArrayListUnmanaged(Air.Inst.Index),1440 instructions: ArrayListUnmanaged(Air.Inst.Index),
1289 // `param` instructions are collected here to be used by the `func` instruction.1441 // `param` instructions are collected here to be used by the `func` instruction.
1290 params: std.ArrayListUnmanaged(Param) = .{},1442 params: std.ArrayListUnmanaged(Param) = .{},
1443
1444 wip_capture_scope: *CaptureScope,
1445
1291 label: ?*Label = null,1446 label: ?*Label = null,
1292 inlining: ?*Inlining,1447 inlining: ?*Inlining,
1293 /// If runtime_index is not 0 then one of these is guaranteed to be non null.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,6 +1457,8 @@ pub const Scope = struct {
1302 /// when null, it is determined by build mode, changed by @setRuntimeSafety1457 /// when null, it is determined by build mode, changed by @setRuntimeSafety
1303 want_safety: ?bool = null,1458 want_safety: ?bool = null,
13041459
1460 c_import_buf: ?*std.ArrayList(u8) = null,
1461
1305 const Param = struct {1462 const Param = struct {
1306 /// `noreturn` means `anytype`.1463 /// `noreturn` means `anytype`.
1307 ty: Type,1464 ty: Type,
...@@ -1347,6 +1504,7 @@ pub const Scope = struct {...@@ -1347,6 +1504,7 @@ pub const Scope = struct {
1347 .sema = parent.sema,1504 .sema = parent.sema,
1348 .src_decl = parent.src_decl,1505 .src_decl = parent.src_decl,
1349 .instructions = .{},1506 .instructions = .{},
1507 .wip_capture_scope = parent.wip_capture_scope,
1350 .label = null,1508 .label = null,
1351 .inlining = parent.inlining,1509 .inlining = parent.inlining,
1352 .is_comptime = parent.is_comptime,1510 .is_comptime = parent.is_comptime,
...@@ -1354,6 +1512,7 @@ pub const Scope = struct {...@@ -1354,6 +1512,7 @@ pub const Scope = struct {
1354 .runtime_loop = parent.runtime_loop,1512 .runtime_loop = parent.runtime_loop,
1355 .runtime_index = parent.runtime_index,1513 .runtime_index = parent.runtime_index,
1356 .want_safety = parent.want_safety,1514 .want_safety = parent.want_safety,
1515 .c_import_buf = parent.c_import_buf,
1357 };1516 };
1358 }1517 }
13591518
...@@ -1453,6 +1612,40 @@ pub const Scope = struct {...@@ -1453,6 +1612,40 @@ pub const Scope = struct {
1453 });1612 });
1454 }1613 }
14551614
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 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {1649 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
1457 return Air.indexToRef(try block.addInstAsIndex(inst));1650 return Air.indexToRef(try block.addInstAsIndex(inst));
1458 }1651 }
...@@ -2092,39 +2285,39 @@ pub const LazySrcLoc = union(enum) {...@@ -2092,39 +2285,39 @@ pub const LazySrcLoc = union(enum) {
2092 node_offset_bin_op: i32,2285 node_offset_bin_op: i32,
2093 /// The source location points to the LHS of a binary expression, found2286 /// The source location points to the LHS of a binary expression, found
2094 /// by taking this AST node index offset from the containing Decl AST node,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 /// The Decl is determined contextually.2289 /// The Decl is determined contextually.
2097 node_offset_bin_lhs: i32,2290 node_offset_bin_lhs: i32,
2098 /// The source location points to the RHS of a binary expression, found2291 /// The source location points to the RHS of a binary expression, found
2099 /// by taking this AST node index offset from the containing Decl AST node,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 /// The Decl is determined contextually.2294 /// The Decl is determined contextually.
2102 node_offset_bin_rhs: i32,2295 node_offset_bin_rhs: i32,
2103 /// The source location points to the operand of a switch expression, found2296 /// The source location points to the operand of a switch expression, found
2104 /// by taking this AST node index offset from the containing Decl AST node,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 /// The Decl is determined contextually.2299 /// The Decl is determined contextually.
2107 node_offset_switch_operand: i32,2300 node_offset_switch_operand: i32,
2108 /// The source location points to the else/`_` prong of a switch expression, found2301 /// The source location points to the else/`_` prong of a switch expression, found
2109 /// by taking this AST node index offset from the containing Decl AST node,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 /// The Decl is determined contextually.2304 /// The Decl is determined contextually.
2112 node_offset_switch_special_prong: i32,2305 node_offset_switch_special_prong: i32,
2113 /// The source location points to all the ranges of a switch expression, found2306 /// The source location points to all the ranges of a switch expression, found
2114 /// by taking this AST node index offset from the containing Decl AST node,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 the2308 /// which points to a switch expression AST node. Next, navigate to any of the
2116 /// range nodes. The error applies to all of them.2309 /// range nodes. The error applies to all of them.
2117 /// The Decl is determined contextually.2310 /// The Decl is determined contextually.
2118 node_offset_switch_range: i32,2311 node_offset_switch_range: i32,
2119 /// The source location points to the calling convention of a function type2312 /// The source location points to the calling convention of a function type
2120 /// expression, found by taking this AST node index offset from the containing2313 /// 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 to2314 /// Decl AST node, which points to a function type AST node. Next, navigate to
2122 /// the calling convention node.2315 /// the calling convention node.
2123 /// The Decl is determined contextually.2316 /// The Decl is determined contextually.
2124 node_offset_fn_type_cc: i32,2317 node_offset_fn_type_cc: i32,
2125 /// The source location points to the return type of a function type2318 /// The source location points to the return type of a function type
2126 /// expression, found by taking this AST node index offset from the containing2319 /// 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 to2320 /// Decl AST node, which points to a function type AST node. Next, navigate to
2128 /// the return type node.2321 /// the return type node.
2129 /// The Decl is determined contextually.2322 /// The Decl is determined contextually.
2130 node_offset_fn_type_ret_ty: i32,2323 node_offset_fn_type_ret_ty: i32,
...@@ -2347,6 +2540,7 @@ pub fn deinit(mod: *Module) void {...@@ -2347,6 +2540,7 @@ pub fn deinit(mod: *Module) void {
23472540
2348 mod.error_name_list.deinit(gpa);2541 mod.error_name_list.deinit(gpa);
2349 mod.test_functions.deinit(gpa);2542 mod.test_functions.deinit(gpa);
2543 mod.align_stack_fns.deinit(gpa);
2350 mod.monomorphed_funcs.deinit(gpa);2544 mod.monomorphed_funcs.deinit(gpa);
23512545
2352 {2546 {
...@@ -2362,6 +2556,7 @@ pub fn deinit(mod: *Module) void {...@@ -2362,6 +2556,7 @@ pub fn deinit(mod: *Module) void {
2362fn freeExportList(gpa: *Allocator, export_list: []*Export) void {2556fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
2363 for (export_list) |exp| {2557 for (export_list) |exp| {
2364 gpa.free(exp.options.name);2558 gpa.free(exp.options.name);
2559 if (exp.options.section) |s| gpa.free(s);
2365 gpa.destroy(exp);2560 gpa.destroy(exp);
2366 }2561 }
2367 gpa.free(export_list);2562 gpa.free(export_list);
...@@ -2372,7 +2567,7 @@ const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;...@@ -2372,7 +2567,7 @@ const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;
2372// We need a better language feature for initializing a union with2567// We need a better language feature for initializing a union with
2373// a runtime known tag.2568// a runtime known tag.
2374const Stage1DataLayout = extern struct {2569const Stage1DataLayout = extern struct {
2375 data: [8]u8 align(8),2570 data: [8]u8 align(@alignOf(Zir.Inst.Data)),
2376 safety_tag: u8,2571 safety_tag: u8,
2377};2572};
2378comptime {2573comptime {
...@@ -2873,12 +3068,10 @@ pub fn mapOldZirToNew(...@@ -2873,12 +3068,10 @@ pub fn mapOldZirToNew(
2873 var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{};3068 var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{};
2874 defer match_stack.deinit(gpa);3069 defer match_stack.deinit(gpa);
28753070
2876 const old_main_struct_inst = old_zir.getMainStruct();3071 // Main struct inst is always the same
2877 const new_main_struct_inst = new_zir.getMainStruct();
2878
2879 try match_stack.append(gpa, .{3072 try match_stack.append(gpa, .{
2880 .old_inst = old_main_struct_inst,3073 .old_inst = Zir.main_struct_inst,
2881 .new_inst = new_main_struct_inst,3074 .new_inst = Zir.main_struct_inst,
2882 });3075 });
28833076
2884 var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa);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,6 +3229,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
3036 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);3229 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);
3037 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);3230 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);
3038 const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty);3231 const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty);
3232 const ty_ty = comptime Type.initTag(.type);
3039 struct_obj.* = .{3233 struct_obj.* = .{
3040 .owner_decl = undefined, // set below3234 .owner_decl = undefined, // set below
3041 .fields = .{},3235 .fields = .{},
...@@ -3050,7 +3244,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {...@@ -3050,7 +3244,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
3050 .file_scope = file,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 file.root_decl = new_decl;3248 file.root_decl = new_decl;
3055 struct_obj.owner_decl = new_decl;3249 struct_obj.owner_decl = new_decl;
3056 new_decl.src_line = 0;3250 new_decl.src_line = 0;
...@@ -3058,8 +3252,8 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {...@@ -3058,8 +3252,8 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
3058 new_decl.is_pub = true;3252 new_decl.is_pub = true;
3059 new_decl.is_exported = false;3253 new_decl.is_exported = false;
3060 new_decl.has_align = false;3254 new_decl.has_align = false;
3061 new_decl.has_linksection = false;3255 new_decl.has_linksection_or_addrspace = false;
3062 new_decl.ty = struct_ty;3256 new_decl.ty = ty_ty;
3063 new_decl.val = struct_val;3257 new_decl.val = struct_val;
3064 new_decl.has_tv = true;3258 new_decl.has_tv = true;
3065 new_decl.owns_tv = true;3259 new_decl.owns_tv = true;
...@@ -3069,7 +3263,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {...@@ -3069,7 +3263,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
30693263
3070 if (file.status == .success_zir) {3264 if (file.status == .success_zir) {
3071 assert(file.zir_loaded);3265 assert(file.zir_loaded);
3072 const main_struct_inst = file.zir.getMainStruct();3266 const main_struct_inst = Zir.main_struct_inst;
3073 struct_obj.zir_index = main_struct_inst;3267 struct_obj.zir_index = main_struct_inst;
30743268
3075 var sema_arena = std.heap.ArenaAllocator.init(gpa);3269 var sema_arena = std.heap.ArenaAllocator.init(gpa);
...@@ -3079,6 +3273,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {...@@ -3079,6 +3273,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
3079 .mod = mod,3273 .mod = mod,
3080 .gpa = gpa,3274 .gpa = gpa,
3081 .arena = &sema_arena.allocator,3275 .arena = &sema_arena.allocator,
3276 .perm_arena = &new_decl_arena.allocator,
3082 .code = file.zir,3277 .code = file.zir,
3083 .owner_decl = new_decl,3278 .owner_decl = new_decl,
3084 .namespace = &struct_obj.namespace,3279 .namespace = &struct_obj.namespace,
...@@ -3087,10 +3282,15 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {...@@ -3087,10 +3282,15 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
3087 .owner_func = null,3282 .owner_func = null,
3088 };3283 };
3089 defer sema.deinit();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 var block_scope: Scope.Block = .{3289 var block_scope: Scope.Block = .{
3091 .parent = null,3290 .parent = null,
3092 .sema = &sema,3291 .sema = &sema,
3093 .src_decl = new_decl,3292 .src_decl = new_decl,
3293 .wip_capture_scope = wip_captures.scope,
3094 .instructions = .{},3294 .instructions = .{},
3095 .inlining = null,3295 .inlining = null,
3096 .is_comptime = true,3296 .is_comptime = true,
...@@ -3098,6 +3298,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {...@@ -3098,6 +3298,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
3098 defer block_scope.instructions.deinit(gpa);3298 defer block_scope.instructions.deinit(gpa);
30993299
3100 if (sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj)) |_| {3300 if (sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj)) |_| {
3301 try wip_captures.finalize();
3101 new_decl.analysis = .complete;3302 new_decl.analysis = .complete;
3102 } else |err| switch (err) {3303 } else |err| switch (err) {
3103 error.OutOfMemory => return error.OutOfMemory,3304 error.OutOfMemory => return error.OutOfMemory,
...@@ -3127,6 +3328,10 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3127,6 +3328,10 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
31273328
3128 decl.analysis = .in_progress;3329 decl.analysis = .in_progress;
31293330
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 var analysis_arena = std.heap.ArenaAllocator.init(gpa);3335 var analysis_arena = std.heap.ArenaAllocator.init(gpa);
3131 defer analysis_arena.deinit();3336 defer analysis_arena.deinit();
31323337
...@@ -3134,6 +3339,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3134,6 +3339,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3134 .mod = mod,3339 .mod = mod,
3135 .gpa = gpa,3340 .gpa = gpa,
3136 .arena = &analysis_arena.allocator,3341 .arena = &analysis_arena.allocator,
3342 .perm_arena = &decl_arena.allocator,
3137 .code = zir,3343 .code = zir,
3138 .owner_decl = decl,3344 .owner_decl = decl,
3139 .namespace = decl.namespace,3345 .namespace = decl.namespace,
...@@ -3145,7 +3351,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3145,7 +3351,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
31453351
3146 if (decl.isRoot()) {3352 if (decl.isRoot()) {
3147 log.debug("semaDecl root {*} ({s})", .{ decl, decl.name });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 const struct_obj = decl.getStruct().?;3355 const struct_obj = decl.getStruct().?;
3150 // This might not have gotten set in `semaFile` if the first time had3356 // This might not have gotten set in `semaFile` if the first time had
3151 // a ZIR failure, so we set it here in case.3357 // a ZIR failure, so we set it here in case.
...@@ -3157,10 +3363,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3157,10 +3363,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3157 }3363 }
3158 log.debug("semaDecl {*} ({s})", .{ decl, decl.name });3364 log.debug("semaDecl {*} ({s})", .{ decl, decl.name });
31593365
3366 var wip_captures = try WipCaptureScope.init(gpa, &decl_arena.allocator, decl.src_scope);
3367 defer wip_captures.deinit();
3368
3160 var block_scope: Scope.Block = .{3369 var block_scope: Scope.Block = .{
3161 .parent = null,3370 .parent = null,
3162 .sema = &sema,3371 .sema = &sema,
3163 .src_decl = decl,3372 .src_decl = decl,
3373 .wip_capture_scope = wip_captures.scope,
3164 .instructions = .{},3374 .instructions = .{},
3165 .inlining = null,3375 .inlining = null,
3166 .is_comptime = true,3376 .is_comptime = true,
...@@ -3175,6 +3385,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3175,6 +3385,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3175 const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index);3385 const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index);
3176 const body = zir.extra[extra.end..][0..extra.data.body_len];3386 const body = zir.extra[extra.end..][0..extra.data.body_len];
3177 const break_index = try sema.analyzeBody(&block_scope, body);3387 const break_index = try sema.analyzeBody(&block_scope, body);
3388 try wip_captures.finalize();
3178 const result_ref = zir_datas[break_index].@"break".operand;3389 const result_ref = zir_datas[break_index].@"break".operand;
3179 const src: LazySrcLoc = .{ .node_offset = 0 };3390 const src: LazySrcLoc = .{ .node_offset = 0 };
3180 const decl_tv = try sema.resolveInstValue(&block_scope, src, result_ref);3391 const decl_tv = try sema.resolveInstValue(&block_scope, src, result_ref);
...@@ -3188,14 +3399,29 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3188,14 +3399,29 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3188 if (linksection_ref == .none) break :blk Value.initTag(.null_value);3399 if (linksection_ref == .none) break :blk Value.initTag(.null_value);
3189 break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val;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 // Note this resolves the type of the Decl, not the value; if this Decl3420 // Note this resolves the type of the Decl, not the value; if this Decl
3192 // is a struct, for example, this resolves `type` (which needs no resolution),3421 // is a struct, for example, this resolves `type` (which needs no resolution),
3193 // not the struct itself.3422 // not the struct itself.
3194 try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty);3423 try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty);
31953424
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 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);3425 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
32003426
3201 if (decl.is_usingnamespace) {3427 if (decl.is_usingnamespace) {
...@@ -3244,6 +3470,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3244,6 +3470,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3244 decl.val = try decl_tv.val.copy(&decl_arena.allocator);3470 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
3245 decl.align_val = try align_val.copy(&decl_arena.allocator);3471 decl.align_val = try align_val.copy(&decl_arena.allocator);
3246 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);3472 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3473 decl.@"addrspace" = address_space;
3247 decl.has_tv = true;3474 decl.has_tv = true;
3248 decl.owns_tv = owns_tv;3475 decl.owns_tv = owns_tv;
3249 decl_arena_state.* = decl_arena.state;3476 decl_arena_state.* = decl_arena.state;
...@@ -3271,7 +3498,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3271,7 +3498,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3271 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});3498 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});
3272 }3499 }
3273 // The scope needs to have the decl in it.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 return type_changed or is_inline != prev_is_inline;3504 return type_changed or is_inline != prev_is_inline;
3277 }3505 }
...@@ -3305,6 +3533,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3305,6 +3533,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3305 decl.val = try decl_tv.val.copy(&decl_arena.allocator);3533 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
3306 decl.align_val = try align_val.copy(&decl_arena.allocator);3534 decl.align_val = try align_val.copy(&decl_arena.allocator);
3307 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);3535 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3536 decl.@"addrspace" = address_space;
3308 decl.has_tv = true;3537 decl.has_tv = true;
3309 decl_arena_state.* = decl_arena.state;3538 decl_arena_state.* = decl_arena.state;
3310 decl.value_arena = decl_arena_state;3539 decl.value_arena = decl_arena_state;
...@@ -3329,7 +3558,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3329,7 +3558,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3329 if (decl.is_exported) {3558 if (decl.is_exported) {
3330 const export_src = src; // TODO point to the export token3559 const export_src = src; // TODO point to the export token
3331 // The scope needs to have the decl in it.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 }
33343564
3335 return type_changed;3565 return type_changed;
...@@ -3490,7 +3720,7 @@ pub fn scanNamespace(...@@ -3490,7 +3720,7 @@ pub fn scanNamespace(
3490 const zir = namespace.file_scope.zir;3720 const zir = namespace.file_scope.zir;
34913721
3492 try mod.comp.work_queue.ensureUnusedCapacity(decls_len);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);
34943724
3495 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;3725 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
3496 var extra_index = extra_start + bit_bags_count;3726 var extra_index = extra_start + bit_bags_count;
...@@ -3512,8 +3742,8 @@ pub fn scanNamespace(...@@ -3512,8 +3742,8 @@ pub fn scanNamespace(
35123742
3513 const decl_sub_index = extra_index;3743 const decl_sub_index = extra_index;
3514 extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1)3744 extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1)
3515 extra_index += @truncate(u1, flags >> 2);3745 extra_index += @truncate(u1, flags >> 2); // Align
3516 extra_index += @truncate(u1, flags >> 3);3746 extra_index += @as(u2, @truncate(u1, flags >> 3)) * 2; // Link section or address space, consists of 2 Refs
35173747
3518 try scanDecl(&scan_decl_iter, decl_sub_index, flags);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,10 +3769,10 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
3539 const zir = namespace.file_scope.zir;3769 const zir = namespace.file_scope.zir;
35403770
3541 // zig fmt: off3771 // zig fmt: off
3542 const is_pub = (flags & 0b0001) != 0;3772 const is_pub = (flags & 0b0001) != 0;
3543 const export_bit = (flags & 0b0010) != 0;3773 const export_bit = (flags & 0b0010) != 0;
3544 const has_align = (flags & 0b0100) != 0;3774 const has_align = (flags & 0b0100) != 0;
3545 const has_linksection = (flags & 0b1000) != 0;3775 const has_linksection_or_addrspace = (flags & 0b1000) != 0;
3546 // zig fmt: on3776 // zig fmt: on
35473777
3548 const line = iter.parent_decl.relativeToLine(zir.extra[decl_sub_index + 4]);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,7 +3818,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
3588 // We create a Decl for it regardless of analysis status.3818 // We create a Decl for it regardless of analysis status.
3589 const gop = try namespace.decls.getOrPut(gpa, decl_name);3819 const gop = try namespace.decls.getOrPut(gpa, decl_name);
3590 if (!gop.found_existing) {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 if (is_usingnamespace) {3822 if (is_usingnamespace) {
3593 namespace.usingnamespace_set.putAssumeCapacity(new_decl, is_pub);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,7 +3855,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
3625 new_decl.is_exported = is_exported;3855 new_decl.is_exported = is_exported;
3626 new_decl.is_usingnamespace = is_usingnamespace;3856 new_decl.is_usingnamespace = is_usingnamespace;
3627 new_decl.has_align = has_align;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 new_decl.zir_decl_index = @intCast(u32, decl_sub_index);3859 new_decl.zir_decl_index = @intCast(u32, decl_sub_index);
3630 new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive.3860 new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive.
3631 return;3861 return;
...@@ -3642,7 +3872,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi...@@ -3642,7 +3872,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
3642 decl.is_exported = is_exported;3872 decl.is_exported = is_exported;
3643 decl.is_usingnamespace = is_usingnamespace;3873 decl.is_usingnamespace = is_usingnamespace;
3644 decl.has_align = has_align;3874 decl.has_align = has_align;
3645 decl.has_linksection = has_linksection;3875 decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
3646 decl.zir_decl_index = @intCast(u32, decl_sub_index);3876 decl.zir_decl_index = @intCast(u32, decl_sub_index);
3647 if (decl.getFunction()) |_| {3877 if (decl.getFunction()) |_| {
3648 switch (mod.comp.bin_file.tag) {3878 switch (mod.comp.bin_file.tag) {
...@@ -3660,7 +3890,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi...@@ -3660,7 +3890,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
3660 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });3890 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
3661 },3891 },
3662 .plan9 => {3892 .plan9 => {
3663 // TODO implement for plan93893 // 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 .c, .wasm, .spirv => {},3897 .c, .wasm, .spirv => {},
3666 }3898 }
...@@ -3706,13 +3938,6 @@ pub fn clearDecl(...@@ -3706,13 +3938,6 @@ pub fn clearDecl(
3706 dep.removeDependency(decl);3938 dep.removeDependency(decl);
3707 if (outdated_decls) |map| {3939 if (outdated_decls) |map| {
3708 map.putAssumeCapacity(dep, {});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 decl.dependants.clearRetainingCapacity();3943 decl.dependants.clearRetainingCapacity();
...@@ -3740,7 +3965,7 @@ pub fn clearDecl(...@@ -3740,7 +3965,7 @@ pub fn clearDecl(
3740 .elf => .{ .elf = link.File.Elf.TextBlock.empty },3965 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
3741 .macho => .{ .macho = link.File.MachO.TextBlock.empty },3966 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
3742 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },3967 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
3743 .c => .{ .c = link.File.C.DeclBlock.empty },3968 .c => .{ .c = {} },
3744 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },3969 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
3745 .spirv => .{ .spirv = {} },3970 .spirv => .{ .spirv = {} },
3746 };3971 };
...@@ -3749,7 +3974,7 @@ pub fn clearDecl(...@@ -3749,7 +3974,7 @@ pub fn clearDecl(
3749 .elf => .{ .elf = link.File.Elf.SrcFn.empty },3974 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
3750 .macho => .{ .macho = link.File.MachO.SrcFn.empty },3975 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
3751 .plan9 => .{ .plan9 = {} },3976 .plan9 => .{ .plan9 = {} },
3752 .c => .{ .c = link.File.C.FnBlock.empty },3977 .c => .{ .c = {} },
3753 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },3978 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
3754 .spirv => .{ .spirv = .{} },3979 .spirv => .{ .spirv = .{} },
3755 };3980 };
...@@ -3779,10 +4004,13 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {...@@ -3779,10 +4004,13 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
3779 // about the Decl in the first place.4004 // about the Decl in the first place.
3780 // Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we4005 // Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we
3781 // must call `freeDecl` in the linker backend now.4006 // must call `freeDecl` in the linker backend now.
3782 if (decl.has_tv) {4007 switch (mod.comp.bin_file.tag) {
3783 if (decl.ty.hasCodeGenBits()) {4008 .c => {}, // this linker backend has already migrated to the new API
3784 mod.comp.bin_file.freeDecl(decl);4009 else => if (decl.has_tv) {
3785 }4010 if (decl.ty.hasCodeGenBits()) {
4011 mod.comp.bin_file.freeDecl(decl);
4012 }
4013 },
3786 }4014 }
37874015
3788 const dependants = decl.dependants.keys();4016 const dependants = decl.dependants.keys();
...@@ -3800,7 +4028,7 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {...@@ -3800,7 +4028,7 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
38004028
3801pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {4029pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {
3802 log.debug("deleteAnonDecl {*} ({s})", .{ decl, decl.name });4030 log.debug("deleteAnonDecl {*} ({s})", .{ decl, decl.name });
3803 const scope_decl = scope.ownerDecl().?;4031 const scope_decl = scope.srcDecl().?;
3804 assert(scope_decl.namespace.anon_decls.swapRemove(decl));4032 assert(scope_decl.namespace.anon_decls.swapRemove(decl));
3805 decl.destroy(mod);4033 decl.destroy(mod);
3806}4034}
...@@ -3844,22 +4072,21 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {...@@ -3844,22 +4072,21 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {
3844 mod.gpa.free(kv.value);4072 mod.gpa.free(kv.value);
3845}4073}
38464074
3847pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {4075pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: *Allocator) SemaError!Air {
3848 const tracy = trace(@src());4076 const tracy = trace(@src());
3849 defer tracy.end();4077 defer tracy.end();
38504078
3851 const gpa = mod.gpa;4079 const gpa = mod.gpa;
38524080
3853 // Use the Decl's arena for function memory.4081 // Use the Decl's arena for captured values.
3854 var arena = decl.value_arena.?.promote(gpa);4082 var decl_arena = decl.value_arena.?.promote(gpa);
3855 defer decl.value_arena.?.* = arena.state;4083 defer decl.value_arena.?.* = decl_arena.state;
3856
3857 const fn_ty = decl.ty;
38584084
3859 var sema: Sema = .{4085 var sema: Sema = .{
3860 .mod = mod,4086 .mod = mod,
3861 .gpa = gpa,4087 .gpa = gpa,
3862 .arena = &arena.allocator,4088 .arena = arena,
4089 .perm_arena = &decl_arena.allocator,
3863 .code = decl.namespace.file_scope.zir,4090 .code = decl.namespace.file_scope.zir,
3864 .owner_decl = decl,4091 .owner_decl = decl,
3865 .namespace = decl.namespace,4092 .namespace = decl.namespace,
...@@ -3874,10 +4101,14 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {...@@ -3874,10 +4101,14 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3874 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);4101 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
3875 sema.air_extra.items.len += reserved_count;4102 sema.air_extra.items.len += reserved_count;
38764103
4104 var wip_captures = try WipCaptureScope.init(gpa, &decl_arena.allocator, decl.src_scope);
4105 defer wip_captures.deinit();
4106
3877 var inner_block: Scope.Block = .{4107 var inner_block: Scope.Block = .{
3878 .parent = null,4108 .parent = null,
3879 .sema = &sema,4109 .sema = &sema,
3880 .src_decl = decl,4110 .src_decl = decl,
4111 .wip_capture_scope = wip_captures.scope,
3881 .instructions = .{},4112 .instructions = .{},
3882 .inlining = null,4113 .inlining = null,
3883 .is_comptime = false,4114 .is_comptime = false,
...@@ -3893,6 +4124,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {...@@ -3893,6 +4124,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3893 // This could be a generic function instantiation, however, in which case we need to4124 // This could be a generic function instantiation, however, in which case we need to
3894 // map the comptime parameters to constant values and only emit arg AIR instructions4125 // map the comptime parameters to constant values and only emit arg AIR instructions
3895 // for the runtime ones.4126 // for the runtime ones.
4127 const fn_ty = decl.ty;
3896 const runtime_params_len = @intCast(u32, fn_ty.fnParamLen());4128 const runtime_params_len = @intCast(u32, fn_ty.fnParamLen());
3897 try inner_block.instructions.ensureTotalCapacity(gpa, runtime_params_len);4129 try inner_block.instructions.ensureTotalCapacity(gpa, runtime_params_len);
3898 try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len * 2); // * 2 for the `addType`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,6 +4184,8 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3952 else => |e| return e,4184 else => |e| return e,
3953 };4185 };
39544186
4187 try wip_captures.finalize();
4188
3955 // Copy the block into place and mark that as the main block.4189 // Copy the block into place and mark that as the main block.
3956 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +4190 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
3957 inner_block.instructions.items.len);4191 inner_block.instructions.items.len);
...@@ -3977,6 +4211,12 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {...@@ -3977,6 +4211,12 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
3977 if (mod.failed_decls.fetchSwapRemove(decl)) |kv| {4211 if (mod.failed_decls.fetchSwapRemove(decl)) |kv| {
3978 kv.value.destroy(mod.gpa);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 if (mod.emit_h) |emit_h| {4220 if (mod.emit_h) |emit_h| {
3981 if (emit_h.failed_decls.fetchSwapRemove(decl)) |kv| {4221 if (emit_h.failed_decls.fetchSwapRemove(decl)) |kv| {
3982 kv.value.destroy(mod.gpa);4222 kv.value.destroy(mod.gpa);
...@@ -3986,7 +4226,7 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {...@@ -3986,7 +4226,7 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
3986 decl.analysis = .outdated;4226 decl.analysis = .outdated;
3987}4227}
39884228
3989pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.Node.Index) !*Decl {4229pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.Node.Index, src_scope: ?*CaptureScope) !*Decl {
3990 // If we have emit-h then we must allocate a bigger structure to store the emit-h state.4230 // If we have emit-h then we must allocate a bigger structure to store the emit-h state.
3991 const new_decl: *Decl = if (mod.emit_h != null) blk: {4231 const new_decl: *Decl = if (mod.emit_h != null) blk: {
3992 const parent_struct = try mod.gpa.create(DeclPlusEmitH);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,15 +4248,17 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
4008 .val = undefined,4248 .val = undefined,
4009 .align_val = undefined,4249 .align_val = undefined,
4010 .linksection_val = undefined,4250 .linksection_val = undefined,
4251 .@"addrspace" = undefined,
4011 .analysis = .unreferenced,4252 .analysis = .unreferenced,
4012 .deletion_flag = false,4253 .deletion_flag = false,
4013 .zir_decl_index = 0,4254 .zir_decl_index = 0,
4255 .src_scope = src_scope,
4014 .link = switch (mod.comp.bin_file.tag) {4256 .link = switch (mod.comp.bin_file.tag) {
4015 .coff => .{ .coff = link.File.Coff.TextBlock.empty },4257 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
4016 .elf => .{ .elf = link.File.Elf.TextBlock.empty },4258 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
4017 .macho => .{ .macho = link.File.MachO.TextBlock.empty },4259 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
4018 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },4260 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
4019 .c => .{ .c = link.File.C.DeclBlock.empty },4261 .c => .{ .c = {} },
4020 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },4262 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
4021 .spirv => .{ .spirv = {} },4263 .spirv => .{ .spirv = {} },
4022 },4264 },
...@@ -4025,18 +4267,19 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast....@@ -4025,18 +4267,19 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
4025 .elf => .{ .elf = link.File.Elf.SrcFn.empty },4267 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
4026 .macho => .{ .macho = link.File.MachO.SrcFn.empty },4268 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
4027 .plan9 => .{ .plan9 = {} },4269 .plan9 => .{ .plan9 = {} },
4028 .c => .{ .c = link.File.C.FnBlock.empty },4270 .c => .{ .c = {} },
4029 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },4271 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
4030 .spirv => .{ .spirv = .{} },4272 .spirv => .{ .spirv = .{} },
4031 },4273 },
4032 .generation = 0,4274 .generation = 0,
4033 .is_pub = false,4275 .is_pub = false,
4034 .is_exported = false,4276 .is_exported = false,
4035 .has_linksection = false,4277 .has_linksection_or_addrspace = false,
4036 .has_align = false,4278 .has_align = false,
4037 .alive = false,4279 .alive = false,
4038 .is_usingnamespace = false,4280 .is_usingnamespace = false,
4039 };4281 };
4282
4040 return new_decl;4283 return new_decl;
4041}4284}
40424285
...@@ -4051,7 +4294,7 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged...@@ -4051,7 +4294,7 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged
4051 }4294 }
40524295
4053 errdefer assert(mod.global_error_set.remove(name));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 gop.key_ptr.* = try mod.gpa.dupe(u8, name);4298 gop.key_ptr.* = try mod.gpa.dupe(u8, name);
4056 gop.value_ptr.* = @intCast(ErrorInt, mod.error_name_list.items.len);4299 gop.value_ptr.* = @intCast(ErrorInt, mod.error_name_list.items.len);
4057 mod.error_name_list.appendAssumeCapacity(gop.key_ptr.*);4300 mod.error_name_list.appendAssumeCapacity(gop.key_ptr.*);
...@@ -4063,34 +4306,44 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged...@@ -4063,34 +4306,44 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged
40634306
4064pub fn analyzeExport(4307pub fn analyzeExport(
4065 mod: *Module,4308 mod: *Module,
4066 scope: *Scope,4309 block: *Scope.Block,
4067 src: LazySrcLoc,4310 src: LazySrcLoc,
4068 borrowed_symbol_name: []const u8,4311 borrowed_options: std.builtin.ExportOptions,
4069 exported_decl: *Decl,4312 exported_decl: *Decl,
4070) !void {4313) !void {
4071 try mod.ensureDeclAnalyzed(exported_decl);4314 try mod.ensureDeclAnalyzed(exported_decl);
4072 switch (exported_decl.ty.zigTypeTag()) {4315 switch (exported_decl.ty.zigTypeTag()) {
4073 .Fn => {},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 }
40764319
4077 try mod.decl_exports.ensureUnusedCapacity(mod.gpa, 1);4320 const gpa = mod.gpa;
4078 try mod.export_owners.ensureUnusedCapacity(mod.gpa, 1);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);
40794327
4080 const new_export = try mod.gpa.create(Export);4328 const symbol_name = try gpa.dupe(u8, borrowed_options.name);
4081 errdefer mod.gpa.destroy(new_export);4329 errdefer gpa.free(symbol_name);
40824330
4083 const symbol_name = try mod.gpa.dupe(u8, borrowed_symbol_name);4331 const section: ?[]const u8 = if (borrowed_options.section) |s| try gpa.dupe(u8, s) else null;
4084 errdefer mod.gpa.free(symbol_name);4332 errdefer if (section) |s| gpa.free(s);
40854333
4086 const owner_decl = scope.ownerDecl().?;4334 const src_decl = block.src_decl;
4335 const owner_decl = block.sema.owner_decl;
40874336
4088 log.debug("exporting Decl '{s}' as symbol '{s}' from Decl '{s}'", .{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 });
40914340
4092 new_export.* = .{4341 new_export.* = .{
4093 .options = .{ .name = symbol_name },4342 .options = .{
4343 .name = symbol_name,
4344 .linkage = borrowed_options.linkage,
4345 .section = section,
4346 },
4094 .src = src,4347 .src = src,
4095 .link = switch (mod.comp.bin_file.tag) {4348 .link = switch (mod.comp.bin_file.tag) {
4096 .coff => .{ .coff = {} },4349 .coff => .{ .coff = {} },
...@@ -4102,6 +4355,7 @@ pub fn analyzeExport(...@@ -4102,6 +4355,7 @@ pub fn analyzeExport(
4102 .spirv => .{ .spirv = {} },4355 .spirv => .{ .spirv = {} },
4103 },4356 },
4104 .owner_decl = owner_decl,4357 .owner_decl = owner_decl,
4358 .src_decl = src_decl,
4105 .exported_decl = exported_decl,4359 .exported_decl = exported_decl,
4106 .status = .in_progress,4360 .status = .in_progress,
4107 };4361 };
...@@ -4111,18 +4365,18 @@ pub fn analyzeExport(...@@ -4111,18 +4365,18 @@ pub fn analyzeExport(
4111 if (!eo_gop.found_existing) {4365 if (!eo_gop.found_existing) {
4112 eo_gop.value_ptr.* = &[0]*Export{};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 eo_gop.value_ptr.*[eo_gop.value_ptr.len - 1] = new_export;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);
41174371
4118 // Add to exported_decl table.4372 // Add to exported_decl table.
4119 const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl);4373 const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl);
4120 if (!de_gop.found_existing) {4374 if (!de_gop.found_existing) {
4121 de_gop.value_ptr.* = &[0]*Export{};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 de_gop.value_ptr.*[de_gop.value_ptr.len - 1] = new_export;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}
41274381
4128/// Takes ownership of `name` even if it returns an error.4382/// Takes ownership of `name` even if it returns an error.
...@@ -4132,39 +4386,43 @@ pub fn createAnonymousDeclNamed(...@@ -4132,39 +4386,43 @@ pub fn createAnonymousDeclNamed(
4132 typed_value: TypedValue,4386 typed_value: TypedValue,
4133 name: [:0]u8,4387 name: [:0]u8,
4134) !*Decl {4388) !*Decl {
4135 return mod.createAnonymousDeclFromDeclNamed(scope.ownerDecl().?, typed_value, name);4389 return mod.createAnonymousDeclFromDeclNamed(scope.srcDecl().?, scope.srcScope(), typed_value, name);
4136}4390}
41374391
4138pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl {4392pub 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}
41414395
4142pub fn createAnonymousDeclFromDecl(mod: *Module, owner_decl: *Decl, tv: TypedValue) !*Decl {4396pub fn createAnonymousDeclFromDecl(mod: *Module, src_decl: *Decl, src_scope: ?*CaptureScope, tv: TypedValue) !*Decl {
4143 const name_index = mod.getNextAnonNameIndex();4397 const name_index = mod.getNextAnonNameIndex();
4144 const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{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}
41494403
4150/// Takes ownership of `name` even if it returns an error.4404/// Takes ownership of `name` even if it returns an error.
4151pub fn createAnonymousDeclFromDeclNamed(4405pub fn createAnonymousDeclFromDeclNamed(
4152 mod: *Module,4406 mod: *Module,
4153 owner_decl: *Decl,4407 src_decl: *Decl,
4408 src_scope: ?*CaptureScope,
4154 typed_value: TypedValue,4409 typed_value: TypedValue,
4155 name: [:0]u8,4410 name: [:0]u8,
4156) !*Decl {4411) !*Decl {
4157 errdefer mod.gpa.free(name);4412 errdefer mod.gpa.free(name);
41584413
4159 const namespace = owner_decl.namespace;4414 const namespace = src_decl.namespace;
4160 try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1);4415 try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1);
41614416
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);
41634418
4164 new_decl.name = name;4419 new_decl.name = name;
4165 new_decl.src_line = owner_decl.src_line;4420 new_decl.src_line = src_decl.src_line;
4166 new_decl.ty = typed_value.ty;4421 new_decl.ty = typed_value.ty;
4167 new_decl.val = typed_value.val;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 new_decl.has_tv = true;4426 new_decl.has_tv = true;
4169 new_decl.analysis = .complete;4427 new_decl.analysis = .complete;
4170 new_decl.generation = mod.generation;4428 new_decl.generation = mod.generation;
...@@ -4305,61 +4563,6 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) Co...@@ -4305,61 +4563,6 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) Co
4305 return error.AnalysisFail;4563 return error.AnalysisFail;
4306}4564}
43074565
4308pub 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
4335pub 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
4363pub fn optionalType(arena: *Allocator, child_type: Type) Allocator.Error!Type {4566pub fn optionalType(arena: *Allocator, child_type: Type) Allocator.Error!Type {
4364 switch (child_type.tag()) {4567 switch (child_type.tag()) {
4365 .single_const_pointer => return Type.Tag.optional_single_const_pointer.create(4568 .single_const_pointer => return Type.Tag.optional_single_const_pointer.create(
...@@ -4689,7 +4892,7 @@ pub fn populateTestFunctions(mod: *Module) !void {...@@ -4689,7 +4892,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
4689 const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file;4892 const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file;
4690 const builtin_namespace = builtin_file.root_decl.?.namespace;4893 const builtin_namespace = builtin_file.root_decl.?.namespace;
4691 const decl = builtin_namespace.decls.get("test_functions").?;4894 const decl = builtin_namespace.decls.get("test_functions").?;
4692 var buf: Type.Payload.ElemType = undefined;4895 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
4693 const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType();4896 const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType();
46944897
4695 const array_decl = d: {4898 const array_decl = d: {
...@@ -4700,7 +4903,7 @@ pub fn populateTestFunctions(mod: *Module) !void {...@@ -4700,7 +4903,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
4700 const arena = &new_decl_arena.allocator;4903 const arena = &new_decl_arena.allocator;
47014904
4702 const test_fn_vals = try arena.alloc(Value, mod.test_functions.count());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 .ty = try Type.Tag.array.create(arena, .{4907 .ty = try Type.Tag.array.create(arena, .{
4705 .len = test_fn_vals.len,4908 .len = test_fn_vals.len,
4706 .elem_type = try tmp_test_fn_ty.copy(arena),4909 .elem_type = try tmp_test_fn_ty.copy(arena),
...@@ -4713,7 +4916,7 @@ pub fn populateTestFunctions(mod: *Module) !void {...@@ -4713,7 +4916,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
4713 var name_decl_arena = std.heap.ArenaAllocator.init(gpa);4916 var name_decl_arena = std.heap.ArenaAllocator.init(gpa);
4714 errdefer name_decl_arena.deinit();4917 errdefer name_decl_arena.deinit();
4715 const bytes = try name_decl_arena.allocator.dupe(u8, test_name_slice);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 .ty = try Type.Tag.array_u8.create(&name_decl_arena.allocator, bytes.len),4920 .ty = try Type.Tag.array_u8.create(&name_decl_arena.allocator, bytes.len),
4718 .val = try Value.Tag.bytes.create(&name_decl_arena.allocator, bytes),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,19 +99,22 @@ pub fn destroy(pkg: *Package, gpa: *Allocator) void {
99 }99 }
100 }100 }
101101
102 {102 pkg.deinitTable(gpa);
103 var it = pkg.table.keyIterator();103 gpa.destroy(pkg);
104 while (it.next()) |key| {104}
105 gpa.free(key.*);105
106 }106/// Only frees memory associated with the table.
107pub fn deinitTable(pkg: *Package, gpa: *Allocator) void {
108 var it = pkg.table.keyIterator();
109 while (it.next()) |key| {
110 gpa.free(key.*);
107 }111 }
108112
109 pkg.table.deinit(gpa);113 pkg.table.deinit(gpa);
110 gpa.destroy(pkg);
111}114}
112115
113pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package) !void {116pub 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 const name_dupe = try mem.dupe(gpa, u8, name);118 const name_dupe = try mem.dupe(gpa, u8, name);
116 pkg.table.putAssumeCapacityNoClobber(name_dupe, package);119 pkg.table.putAssumeCapacityNoClobber(name_dupe, package);
117}120}
src/Sema.zig+2886-736
...@@ -8,8 +8,12 @@...@@ -8,8 +8,12 @@
8mod: *Module,8mod: *Module,
9/// Alias to `mod.gpa`.9/// Alias to `mod.gpa`.
10gpa: *Allocator,10gpa: *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.
12arena: *Allocator,13arena: *Allocator,
14/// Points to the arena allocator for the owner_decl.
15/// This arena will persist until the decl is invalidated.
16perm_arena: *Allocator,
13code: Zir,17code: Zir,
14air_instructions: std.MultiArrayList(Air.Inst) = .{},18air_instructions: std.MultiArrayList(Air.Inst) = .{},
15air_extra: std.ArrayListUnmanaged(u32) = .{},19air_extra: std.ArrayListUnmanaged(u32) = .{},
...@@ -80,9 +84,13 @@ const Scope = Module.Scope;...@@ -80,9 +84,13 @@ const Scope = Module.Scope;
80const CompileError = Module.CompileError;84const CompileError = Module.CompileError;
81const SemaError = Module.SemaError;85const SemaError = Module.SemaError;
82const Decl = Module.Decl;86const Decl = Module.Decl;
87const CaptureScope = Module.CaptureScope;
88const WipCaptureScope = Module.WipCaptureScope;
83const LazySrcLoc = Module.LazySrcLoc;89const LazySrcLoc = Module.LazySrcLoc;
84const RangeSet = @import("RangeSet.zig");90const RangeSet = @import("RangeSet.zig");
85const target_util = @import("target.zig");91const target_util = @import("target.zig");
92const Package = @import("Package.zig");
93const crash_report = @import("crash_report.zig");
8694
87pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref);95pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref);
8896
...@@ -128,15 +136,34 @@ pub fn analyzeBody(...@@ -128,15 +136,34 @@ pub fn analyzeBody(
128) CompileError!Zir.Inst.Index {136) CompileError!Zir.Inst.Index {
129 // No tracy calls here, to avoid interfering with the tail call mechanism.137 // No tracy calls here, to avoid interfering with the tail call mechanism.
130138
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 const map = &block.sema.inst_map;151 const map = &block.sema.inst_map;
132 const tags = block.sema.code.instructions.items(.tag);152 const tags = block.sema.code.instructions.items(.tag);
133 const datas = block.sema.code.instructions.items(.data);153 const datas = block.sema.code.instructions.items(.data);
134154
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 // We use a while(true) loop here to avoid a redundant way of breaking out of161 // We use a while(true) loop here to avoid a redundant way of breaking out of
136 // the loop. The only way to break out of the loop is with a `noreturn`162 // the loop. The only way to break out of the loop is with a `noreturn`
137 // instruction.163 // instruction.
138 var i: usize = 0;164 var i: usize = 0;
139 while (true) {165 const result = while (true) {
166 crash_info.setBodyIndex(i);
140 const inst = body[i];167 const inst = body[i];
141 const air_inst: Air.Inst.Ref = switch (tags[inst]) {168 const air_inst: Air.Inst.Ref = switch (tags[inst]) {
142 // zig fmt: off169 // zig fmt: off
...@@ -164,11 +191,8 @@ pub fn analyzeBody(...@@ -164,11 +191,8 @@ pub fn analyzeBody(
164 .bool_br_and => try sema.zirBoolBr(block, inst, false),191 .bool_br_and => try sema.zirBoolBr(block, inst, false),
165 .bool_br_or => try sema.zirBoolBr(block, inst, true),192 .bool_br_or => try sema.zirBoolBr(block, inst, true),
166 .c_import => try sema.zirCImport(block, inst),193 .c_import => try sema.zirCImport(block, inst),
167 .call => try sema.zirCall(block, inst, .auto, false),194 .call => try sema.zirCall(block, inst),
168 .call_chkused => try sema.zirCall(block, inst, .auto, true),195 .closure_get => try sema.zirClosureGet(block, inst),
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),
172 .cmp_lt => try sema.zirCmp(block, inst, .lt),196 .cmp_lt => try sema.zirCmp(block, inst, .lt),
173 .cmp_lte => try sema.zirCmp(block, inst, .lte),197 .cmp_lte => try sema.zirCmp(block, inst, .lte),
174 .cmp_eq => try sema.zirCmpEq(block, inst, .eq, .cmp_eq),198 .cmp_eq => try sema.zirCmpEq(block, inst, .eq, .cmp_eq),
...@@ -201,6 +225,8 @@ pub fn analyzeBody(...@@ -201,6 +225,8 @@ pub fn analyzeBody(
201 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),225 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
202 .field_val => try sema.zirFieldVal(block, inst),226 .field_val => try sema.zirFieldVal(block, inst),
203 .field_val_named => try sema.zirFieldValNamed(block, inst),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 .func => try sema.zirFunc(block, inst, false),230 .func => try sema.zirFunc(block, inst, false),
205 .func_inferred => try sema.zirFunc(block, inst, true),231 .func_inferred => try sema.zirFunc(block, inst, true),
206 .import => try sema.zirImport(block, inst),232 .import => try sema.zirImport(block, inst),
...@@ -222,12 +248,10 @@ pub fn analyzeBody(...@@ -222,12 +248,10 @@ pub fn analyzeBody(
222 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),248 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),
223 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),249 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),
224 .optional_type => try sema.zirOptionalType(block, inst),250 .optional_type => try sema.zirOptionalType(block, inst),
225 .param_type => try sema.zirParamType(block, inst),
226 .ptr_type => try sema.zirPtrType(block, inst),251 .ptr_type => try sema.zirPtrType(block, inst),
227 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),252 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
228 .ref => try sema.zirRef(block, inst),253 .ref => try sema.zirRef(block, inst),
229 .ret_err_value_code => try sema.zirRetErrValueCode(block, inst),254 .ret_err_value_code => try sema.zirRetErrValueCode(block, inst),
230 .shl => try sema.zirShl(block, inst),
231 .shr => try sema.zirShr(block, inst),255 .shr => try sema.zirShr(block, inst),
232 .slice_end => try sema.zirSliceEnd(block, inst),256 .slice_end => try sema.zirSliceEnd(block, inst),
233 .slice_sentinel => try sema.zirSliceSentinel(block, inst),257 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
...@@ -300,9 +324,6 @@ pub fn analyzeBody(...@@ -300,9 +324,6 @@ pub fn analyzeBody(
300 .div_exact => try sema.zirDivExact(block, inst),324 .div_exact => try sema.zirDivExact(block, inst),
301 .div_floor => try sema.zirDivFloor(block, inst),325 .div_floor => try sema.zirDivFloor(block, inst),
302 .div_trunc => try sema.zirDivTrunc(block, inst),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 .shr_exact => try sema.zirShrExact(block, inst),327 .shr_exact => try sema.zirShrExact(block, inst),
307 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),328 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),
308 .offset_of => try sema.zirOffsetOf(block, inst),329 .offset_of => try sema.zirOffsetOf(block, inst),
...@@ -314,14 +335,11 @@ pub fn analyzeBody(...@@ -314,14 +335,11 @@ pub fn analyzeBody(
314 .select => try sema.zirSelect(block, inst),335 .select => try sema.zirSelect(block, inst),
315 .atomic_load => try sema.zirAtomicLoad(block, inst),336 .atomic_load => try sema.zirAtomicLoad(block, inst),
316 .atomic_rmw => try sema.zirAtomicRmw(block, inst),337 .atomic_rmw => try sema.zirAtomicRmw(block, inst),
317 .atomic_store => try sema.zirAtomicStore(block, inst),
318 .mul_add => try sema.zirMulAdd(block, inst),338 .mul_add => try sema.zirMulAdd(block, inst),
319 .builtin_call => try sema.zirBuiltinCall(block, inst),339 .builtin_call => try sema.zirBuiltinCall(block, inst),
320 .field_ptr_type => try sema.zirFieldPtrType(block, inst),340 .field_ptr_type => try sema.zirFieldPtrType(block, inst),
321 .field_parent_ptr => try sema.zirFieldParentPtr(block, inst),341 .field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
322 .maximum => try sema.zirMaximum(block, inst),342 .maximum => try sema.zirMaximum(block, inst),
323 .memcpy => try sema.zirMemcpy(block, inst),
324 .memset => try sema.zirMemset(block, inst),
325 .minimum => try sema.zirMinimum(block, inst),343 .minimum => try sema.zirMinimum(block, inst),
326 .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),344 .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
327 .@"resume" => try sema.zirResume(block, inst),345 .@"resume" => try sema.zirResume(block, inst),
...@@ -343,32 +361,38 @@ pub fn analyzeBody(...@@ -343,32 +361,38 @@ pub fn analyzeBody(
343 .trunc => try sema.zirUnaryMath(block, inst),361 .trunc => try sema.zirUnaryMath(block, inst),
344 .round => try sema.zirUnaryMath(block, inst),362 .round => try sema.zirUnaryMath(block, inst),
345363
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 .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent),364 .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent),
350 .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon),365 .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon),
351 .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func),366 .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func),
352367
353 .add => try sema.zirArithmetic(block, inst),368 .add => try sema.zirArithmetic(block, inst, .add),
354 .addwrap => try sema.zirArithmetic(block, inst),369 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),
355 .div => try sema.zirArithmetic(block, inst),370 .add_sat => try sema.zirArithmetic(block, inst, .add_sat),
356 .mod_rem => try sema.zirArithmetic(block, inst),371 .div => try sema.zirArithmetic(block, inst, .div),
357 .mul => try sema.zirArithmetic(block, inst),372 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
358 .mulwrap => try sema.zirArithmetic(block, inst),373 .mod => try sema.zirArithmetic(block, inst, .mod),
359 .sub => try sema.zirArithmetic(block, inst),374 .rem => try sema.zirArithmetic(block, inst, .rem),
360 .subwrap => try sema.zirArithmetic(block, inst),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),
361385
362 // Instructions that we know to *always* be noreturn based solely on their tag.386 // Instructions that we know to *always* be noreturn based solely on their tag.
363 // These functions match the return type of analyzeBody so that we can387 // These functions match the return type of analyzeBody so that we can
364 // tail call them here.388 // tail call them here.
365 .compile_error => return sema.zirCompileError(block, inst),389 .compile_error => break sema.zirCompileError(block, inst),
366 .ret_coerce => return sema.zirRetCoerce(block, inst),390 .ret_coerce => break sema.zirRetCoerce(block, inst),
367 .ret_node => return sema.zirRetNode(block, inst),391 .ret_node => break sema.zirRetNode(block, inst),
368 .ret_load => return sema.zirRetLoad(block, inst),392 .ret_load => break sema.zirRetLoad(block, inst),
369 .ret_err_value => return sema.zirRetErrValue(block, inst),393 .ret_err_value => break sema.zirRetErrValue(block, inst),
370 .@"unreachable" => return sema.zirUnreachable(block, inst),394 .@"unreachable" => break sema.zirUnreachable(block, inst),
371 .panic => return sema.zirPanic(block, inst),395 .panic => break sema.zirPanic(block, inst),
372 // zig fmt: on396 // zig fmt: on
373397
374 // Instructions that we know can *never* be noreturn based solely on398 // Instructions that we know can *never* be noreturn based solely on
...@@ -377,7 +401,9 @@ pub fn analyzeBody(...@@ -377,7 +401,9 @@ pub fn analyzeBody(
377 // We also know that they cannot be referenced later, so we avoid401 // We also know that they cannot be referenced later, so we avoid
378 // putting them into the map.402 // putting them into the map.
379 .breakpoint => {403 .breakpoint => {
380 try sema.zirBreakpoint(block, inst);404 if (!block.is_comptime) {
405 _ = try block.addNoOp(.breakpoint);
406 }
381 i += 1;407 i += 1;
382 continue;408 continue;
383 },409 },
...@@ -411,6 +437,11 @@ pub fn analyzeBody(...@@ -411,6 +437,11 @@ pub fn analyzeBody(
411 i += 1;437 i += 1;
412 continue;438 continue;
413 },439 },
440 .atomic_store => {
441 try sema.zirAtomicStore(block, inst);
442 i += 1;
443 continue;
444 },
414 .store => {445 .store => {
415 try sema.zirStore(block, inst);446 try sema.zirStore(block, inst);
416 i += 1;447 i += 1;
...@@ -451,6 +482,11 @@ pub fn analyzeBody(...@@ -451,6 +482,11 @@ pub fn analyzeBody(
451 i += 1;482 i += 1;
452 continue;483 continue;
453 },484 },
485 .export_value => {
486 try sema.zirExportValue(block, inst);
487 i += 1;
488 continue;
489 },
454 .set_align_stack => {490 .set_align_stack => {
455 try sema.zirSetAlignStack(block, inst);491 try sema.zirSetAlignStack(block, inst);
456 i += 1;492 i += 1;
...@@ -491,34 +527,59 @@ pub fn analyzeBody(...@@ -491,34 +527,59 @@ pub fn analyzeBody(
491 i += 1;527 i += 1;
492 continue;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 },
494545
495 // Special case instructions to handle comptime control flow.546 // Special case instructions to handle comptime control flow.
496 .@"break" => {547 .@"break" => {
497 if (block.is_comptime) {548 if (block.is_comptime) {
498 return inst; // same as break_inline549 break inst; // same as break_inline
499 } else {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 .repeat => {555 .repeat => {
505 if (block.is_comptime) {556 if (block.is_comptime) {
506 // Send comptime control flow back to the beginning of this block.557 // Send comptime control flow back to the beginning of this block.
507 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };558 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
508 try sema.emitBackwardBranch(block, src);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 i = 0;565 i = 0;
510 continue;566 continue;
511 } else {567 } else {
512 const src_node = sema.code.instructions.items(.data)[inst].node;568 const src_node = sema.code.instructions.items(.data)[inst].node;
513 const src: LazySrcLoc = .{ .node_offset = src_node };569 const src: LazySrcLoc = .{ .node_offset = src_node };
514 try sema.requireRuntimeBlock(block, src);570 try sema.requireRuntimeBlock(block, src);
515 return always_noreturn;571 break always_noreturn;
516 }572 }
517 },573 },
518 .repeat_inline => {574 .repeat_inline => {
519 // Send comptime control flow back to the beginning of this block.575 // Send comptime control flow back to the beginning of this block.
520 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };576 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
521 try sema.emitBackwardBranch(block, src);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 i = 0;583 i = 0;
523 continue;584 continue;
524 },585 },
...@@ -533,7 +594,7 @@ pub fn analyzeBody(...@@ -533,7 +594,7 @@ pub fn analyzeBody(
533 if (inst == break_data.block_inst) {594 if (inst == break_data.block_inst) {
534 break :blk sema.resolveInst(break_data.operand);595 break :blk sema.resolveInst(break_data.operand);
535 } else {596 } else {
536 return break_inst;597 break break_inst;
537 }598 }
538 },599 },
539 .block => blk: {600 .block => blk: {
...@@ -547,7 +608,7 @@ pub fn analyzeBody(...@@ -547,7 +608,7 @@ pub fn analyzeBody(
547 if (inst == break_data.block_inst) {608 if (inst == break_data.block_inst) {
548 break :blk sema.resolveInst(break_data.operand);609 break :blk sema.resolveInst(break_data.operand);
549 } else {610 } else {
550 return break_inst;611 break break_inst;
551 }612 }
552 },613 },
553 .block_inline => blk: {614 .block_inline => blk: {
...@@ -560,11 +621,11 @@ pub fn analyzeBody(...@@ -560,11 +621,11 @@ pub fn analyzeBody(
560 if (inst == break_data.block_inst) {621 if (inst == break_data.block_inst) {
561 break :blk sema.resolveInst(break_data.operand);622 break :blk sema.resolveInst(break_data.operand);
562 } else {623 } else {
563 return break_inst;624 break break_inst;
564 }625 }
565 },626 },
566 .condbr => blk: {627 .condbr => blk: {
567 if (!block.is_comptime) return sema.zirCondbr(block, inst);628 if (!block.is_comptime) break sema.zirCondbr(block, inst);
568 // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220629 // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220
569 const inst_data = datas[inst].pl_node;630 const inst_data = datas[inst].pl_node;
570 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };631 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
...@@ -578,7 +639,7 @@ pub fn analyzeBody(...@@ -578,7 +639,7 @@ pub fn analyzeBody(
578 if (inst == break_data.block_inst) {639 if (inst == break_data.block_inst) {
579 break :blk sema.resolveInst(break_data.operand);640 break :blk sema.resolveInst(break_data.operand);
580 } else {641 } else {
581 return break_inst;642 break break_inst;
582 }643 }
583 },644 },
584 .condbr_inline => blk: {645 .condbr_inline => blk: {
...@@ -594,15 +655,22 @@ pub fn analyzeBody(...@@ -594,15 +655,22 @@ pub fn analyzeBody(
594 if (inst == break_data.block_inst) {655 if (inst == break_data.block_inst) {
595 break :blk sema.resolveInst(break_data.operand);656 break :blk sema.resolveInst(break_data.operand);
596 } else {657 } else {
597 return break_inst;658 break break_inst;
598 }659 }
599 },660 },
600 };661 };
601 if (sema.typeOf(air_inst).isNoReturn())662 if (sema.typeOf(air_inst).isNoReturn())
602 return always_noreturn;663 break always_noreturn;
603 try map.put(sema.gpa, inst, air_inst);664 try map.put(sema.gpa, inst, air_inst);
604 i += 1;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}
607675
608fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {676fn 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,6 +682,7 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
614 .struct_decl => return sema.zirStructDecl( block, extended, inst),682 .struct_decl => return sema.zirStructDecl( block, extended, inst),
615 .enum_decl => return sema.zirEnumDecl( block, extended),683 .enum_decl => return sema.zirEnumDecl( block, extended),
616 .union_decl => return sema.zirUnionDecl( block, extended, inst),684 .union_decl => return sema.zirUnionDecl( block, extended, inst),
685 .opaque_decl => return sema.zirOpaqueDecl( block, extended, inst),
617 .ret_ptr => return sema.zirRetPtr( block, extended),686 .ret_ptr => return sema.zirRetPtr( block, extended),
618 .ret_type => return sema.zirRetType( block, extended),687 .ret_type => return sema.zirRetType( block, extended),
619 .this => return sema.zirThis( block, extended),688 .this => return sema.zirThis( block, extended),
...@@ -636,10 +705,6 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -636,10 +705,6 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
636 .c_define => return sema.zirCDefine( block, extended),705 .c_define => return sema.zirCDefine( block, extended),
637 .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),706 .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),
638 .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),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 // zig fmt: on708 // zig fmt: on
644 }709 }
645}710}
...@@ -828,9 +893,18 @@ fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) Compile...@@ -828,9 +893,18 @@ fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) Compile
828 return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{});893 return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{});
829}894}
830895
896fn 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
900fn 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/// Appropriate to call when the coercion has already been done by result904/// Appropriate to call when the coercion has already been done by result
832/// location semantics. Asserts the value fits in the provided `Int` type.905/// location semantics. Asserts the value fits in the provided `Int` type.
833/// Only supports `Int` types 64 bits or less.906/// Only supports `Int` types 64 bits or less.
907/// TODO don't ever call this since we're migrating towards ResultLoc.coerced_ty.
834fn resolveAlreadyCoercedInt(908fn resolveAlreadyCoercedInt(
835 sema: *Sema,909 sema: *Sema,
836 block: *Scope.Block,910 block: *Scope.Block,
...@@ -847,6 +921,23 @@ fn resolveAlreadyCoercedInt(...@@ -847,6 +921,23 @@ fn resolveAlreadyCoercedInt(
847 }921 }
848}922}
849923
924fn 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
850fn resolveInt(941fn resolveInt(
851 sema: *Sema,942 sema: *Sema,
852 block: *Scope.Block,943 block: *Scope.Block,
...@@ -900,10 +991,38 @@ fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) C...@@ -900,10 +991,38 @@ fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) C
900}991}
901992
902fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {993fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
903 _ = inst;
904 const tracy = trace(@src());994 const tracy = trace(@src());
905 defer tracy.end();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}
9081027
909pub fn analyzeStructDecl(1028pub fn analyzeStructDecl(
...@@ -981,7 +1100,6 @@ fn zirStructDecl(...@@ -981,7 +1100,6 @@ fn zirStructDecl(
981}1100}
9821101
983fn createTypeName(sema: *Sema, block: *Scope.Block, name_strategy: Zir.Inst.NameStrategy) ![:0]u8 {1102fn createTypeName(sema: *Sema, block: *Scope.Block, name_strategy: Zir.Inst.NameStrategy) ![:0]u8 {
984 _ = block;
985 switch (name_strategy) {1103 switch (name_strategy) {
986 .anon => {1104 .anon => {
987 // It would be neat to have "struct:line:column" but this name has1105 // 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,14 +1108,14 @@ fn createTypeName(sema: *Sema, block: *Scope.Block, name_strategy: Zir.Inst.Name
990 // semantically analyzed.1108 // semantically analyzed.
991 const name_index = sema.mod.getNextAnonNameIndex();1109 const name_index = sema.mod.getNextAnonNameIndex();
992 return std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{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 .func => {1115 .func => {
998 const name_index = sema.mod.getNextAnonNameIndex();1116 const name_index = sema.mod.getNextAnonNameIndex();
999 const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{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 log.warn("TODO: handle NameStrategy.func correctly instead of using anon name '{s}'", .{1120 log.warn("TODO: handle NameStrategy.func correctly instead of using anon name '{s}'", .{
1003 name,1121 name,
...@@ -1053,17 +1171,6 @@ fn zirEnumDecl(...@@ -1053,17 +1171,6 @@ fn zirEnumDecl(
1053 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);1171 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
1054 errdefer new_decl_arena.deinit();1172 errdefer new_decl_arena.deinit();
10551173
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 const enum_obj = try new_decl_arena.allocator.create(Module.EnumFull);1174 const enum_obj = try new_decl_arena.allocator.create(Module.EnumFull);
1068 const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumFull);1175 const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumFull);
1069 enum_ty_payload.* = .{1176 enum_ty_payload.* = .{
...@@ -1082,7 +1189,7 @@ fn zirEnumDecl(...@@ -1082,7 +1189,7 @@ fn zirEnumDecl(
10821189
1083 enum_obj.* = .{1190 enum_obj.* = .{
1084 .owner_decl = new_decl,1191 .owner_decl = new_decl,
1085 .tag_ty = tag_ty,1192 .tag_ty = Type.initTag(.@"null"),
1086 .fields = .{},1193 .fields = .{},
1087 .values = .{},1194 .values = .{},
1088 .node_offset = src.node_offset,1195 .node_offset = src.node_offset,
...@@ -1110,16 +1217,6 @@ fn zirEnumDecl(...@@ -1110,16 +1217,6 @@ fn zirEnumDecl(
1110 const body_end = extra_index;1217 const body_end = extra_index;
1111 extra_index += bit_bags_count;1218 extra_index += bit_bags_count;
11121219
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 // We create a block for the field type instructions because they1221 // We create a block for the field type instructions because they
1125 // may need to reference Decls from inside the enum namespace.1222 // may need to reference Decls from inside the enum namespace.
...@@ -1142,10 +1239,14 @@ fn zirEnumDecl(...@@ -1142,10 +1239,14 @@ fn zirEnumDecl(
1142 sema.func = null;1239 sema.func = null;
1143 defer sema.func = prev_func;1240 defer sema.func = prev_func;
11441241
1242 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope);
1243 defer wip_captures.deinit();
1244
1145 var enum_block: Scope.Block = .{1245 var enum_block: Scope.Block = .{
1146 .parent = null,1246 .parent = null,
1147 .sema = sema,1247 .sema = sema,
1148 .src_decl = new_decl,1248 .src_decl = new_decl,
1249 .wip_capture_scope = wip_captures.scope,
1149 .instructions = .{},1250 .instructions = .{},
1150 .inlining = null,1251 .inlining = null,
1151 .is_comptime = true,1252 .is_comptime = true,
...@@ -1155,7 +1256,30 @@ fn zirEnumDecl(...@@ -1155,7 +1256,30 @@ fn zirEnumDecl(
1155 if (body.len != 0) {1256 if (body.len != 0) {
1156 _ = try sema.analyzeBody(&enum_block, body);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 var bit_bag_index: usize = body_end;1283 var bit_bag_index: usize = body_end;
1160 var cur_bit_bag: u32 = undefined;1284 var cur_bit_bag: u32 = undefined;
1161 var field_i: u32 = 0;1285 var field_i: u32 = 0;
...@@ -1194,10 +1318,10 @@ fn zirEnumDecl(...@@ -1194,10 +1318,10 @@ fn zirEnumDecl(
1194 // that points to this default value expression rather than the struct.1318 // that points to this default value expression rather than the struct.
1195 // But only resolve the source location if we need to emit a compile error.1319 // But only resolve the source location if we need to emit a compile error.
1196 const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val;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 } else if (any_values) {1322 } else if (any_values) {
1199 const tag_val = try Value.Tag.int_u64.create(&new_decl_arena.allocator, field_i);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 }
12031327
...@@ -1237,7 +1361,13 @@ fn zirUnionDecl(...@@ -1237,7 +1361,13 @@ fn zirUnionDecl(
1237 errdefer new_decl_arena.deinit();1361 errdefer new_decl_arena.deinit();
12381362
1239 const union_obj = try new_decl_arena.allocator.create(Module.Union);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 const union_val = try Value.Tag.ty.create(&new_decl_arena.allocator, union_ty);1371 const union_val = try Value.Tag.ty.create(&new_decl_arena.allocator, union_ty);
1242 const type_name = try sema.createTypeName(block, small.name_strategy);1372 const type_name = try sema.createTypeName(block, small.name_strategy);
1243 const new_decl = try sema.mod.createAnonymousDeclNamed(&block.base, .{1373 const new_decl = try sema.mod.createAnonymousDeclNamed(&block.base, .{
...@@ -1275,20 +1405,14 @@ fn zirUnionDecl(...@@ -1275,20 +1405,14 @@ fn zirUnionDecl(
1275fn zirOpaqueDecl(1405fn zirOpaqueDecl(
1276 sema: *Sema,1406 sema: *Sema,
1277 block: *Scope.Block,1407 block: *Scope.Block,
1408 extended: Zir.Inst.Extended.InstData,
1278 inst: Zir.Inst.Index,1409 inst: Zir.Inst.Index,
1279 name_strategy: Zir.Inst.NameStrategy,
1280) CompileError!Air.Inst.Ref {1410) CompileError!Air.Inst.Ref {
1281 const tracy = trace(@src());1411 const tracy = trace(@src());
1282 defer tracy.end();1412 defer tracy.end();
12831413
1284 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;1414 _ = extended;
1285 const src = inst_data.src();1415 _ = inst;
1286 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
1287
1288 _ = name_strategy;
1289 _ = inst_data;
1290 _ = src;
1291 _ = extra;
1292 return sema.mod.fail(&block.base, sema.src, "TODO implement zirOpaqueDecl", .{});1416 return sema.mod.fail(&block.base, sema.src, "TODO implement zirOpaqueDecl", .{});
1293}1417}
12941418
...@@ -1349,7 +1473,10 @@ fn zirRetPtr(...@@ -1349,7 +1473,10 @@ fn zirRetPtr(
1349 return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty);1473 return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty);
1350 }1474 }
13511475
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 return block.addTy(.alloc, ptr_type);1480 return block.addTy(.alloc, ptr_type);
1354}1481}
13551482
...@@ -1466,7 +1593,42 @@ fn zirAllocExtended(...@@ -1466,7 +1593,42 @@ fn zirAllocExtended(
1466) CompileError!Air.Inst.Ref {1593) CompileError!Air.Inst.Ref {
1467 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);1594 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
1468 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };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}
14711633
1472fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {1634fn 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,7 +1659,10 @@ fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError
1497 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };1659 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
1498 const var_decl_src = inst_data.src();1660 const var_decl_src = inst_data.src();
1499 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);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 try sema.requireRuntimeBlock(block, var_decl_src);1666 try sema.requireRuntimeBlock(block, var_decl_src);
1502 return block.addTy(.alloc, ptr_type);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,8 +1678,11 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
1513 if (block.is_comptime) {1678 if (block.is_comptime) {
1514 return sema.analyzeComptimeAlloc(block, var_type);1679 return sema.analyzeComptimeAlloc(block, var_type);
1515 }1680 }
1516 try sema.validateVarType(block, ty_src, var_type);1681 try sema.validateVarType(block, ty_src, var_type, false);
1517 const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);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 try sema.requireRuntimeBlock(block, var_decl_src);1686 try sema.requireRuntimeBlock(block, var_decl_src);
1519 return block.addTy(.alloc, ptr_type);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,7 +1742,10 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
1574 try sema.mod.declareDeclDependency(sema.owner_decl, decl);1742 try sema.mod.declareDeclDependency(sema.owner_decl, decl);
15751743
1576 const final_elem_ty = try decl.ty.copy(sema.arena);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 const final_ptr_ty_inst = try sema.addType(final_ptr_ty);1749 const final_ptr_ty_inst = try sema.addType(final_ptr_ty);
1579 sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst;1750 sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst;
15801751
...@@ -1593,10 +1764,13 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde...@@ -1593,10 +1764,13 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
1593 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;1764 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
1594 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);1765 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
1595 if (var_is_mut) {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 // Change it to a normal alloc.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 sema.air_instructions.set(ptr_inst, .{1774 sema.air_instructions.set(ptr_inst, .{
1601 .tag = .alloc,1775 .tag = .alloc,
1602 .data = .{ .ty = final_ptr_ty },1776 .data = .{ .ty = final_ptr_ty },
...@@ -1609,19 +1783,82 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind...@@ -1609,19 +1783,82 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
1609 const tracy = trace(@src());1783 const tracy = trace(@src());
1610 defer tracy.end();1784 defer tracy.end();
16111785
1612 const gpa = sema.gpa;
1613 const mod = sema.mod;
1614 const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;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 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);1788 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
1617 const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];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}
16181811
1619 const struct_obj: *Module.Struct = s: {1812fn validateUnionInitPtr(
1620 const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;1813 sema: *Sema,
1621 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;1814 block: *Scope.Block,
1622 const object_ptr = sema.resolveInst(field_ptr_extra.lhs);1815 union_obj: *Module.Union,
1623 break :s sema.typeOf(object_ptr).elemType().castTag(.@"struct").?.data;1816 init_src: LazySrcLoc,
1624 };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
1853fn 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;
16251862
1626 // Maps field index to field_ptr index of where it was already initialized.1863 // Maps field index to field_ptr index of where it was already initialized.
1627 const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count());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,9 +1897,9 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
1660 const template = "missing struct field: {s}";1897 const template = "missing struct field: {s}";
1661 const args = .{field_name};1898 const args = .{field_name};
1662 if (root_msg) |msg| {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 } else {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 if (root_msg) |msg| {1905 if (root_msg) |msg| {
...@@ -1750,7 +1987,11 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co...@@ -1750,7 +1987,11 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
1750 }1987 }
1751 const ptr = sema.resolveInst(bin_inst.lhs);1988 const ptr = sema.resolveInst(bin_inst.lhs);
1752 const value = sema.resolveInst(bin_inst.rhs);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 // TODO detect when this store should be done at compile-time. For example,1995 // TODO detect when this store should be done at compile-time. For example,
1755 // if expressions should force it when the condition is compile-time known.1996 // if expressions should force it when the condition is compile-time known.
1756 const src: LazySrcLoc = .unneeded;1997 const src: LazySrcLoc = .unneeded;
...@@ -1797,7 +2038,10 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)...@@ -1797,7 +2038,10 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
1797 // for the inferred allocation.2038 // for the inferred allocation.
1798 try inferred_alloc.data.stored_inst_list.append(sema.arena, operand);2039 try inferred_alloc.data.stored_inst_list.append(sema.arena, operand);
1799 // Create a runtime bitcast instruction with exactly the type the pointer wants.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 const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr);2045 const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr);
1802 return sema.storePtr(block, src, bitcasted_ptr, operand);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,45 +2078,6 @@ fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
1834 return sema.storePtr(block, src, ptr, value);2078 return sema.storePtr(block, src, ptr, value);
1835}2079}
18362080
1837fn 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
1876fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {2081fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1877 const tracy = trace(@src());2082 const tracy = trace(@src());
1878 defer tracy.end();2083 defer tracy.end();
...@@ -2070,10 +2275,78 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Com...@@ -2070,10 +2275,78 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Com
2070 const tracy = trace(@src());2275 const tracy = trace(@src());
2071 defer tracy.end();2276 defer tracy.end();
20722277
2073 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;2278 const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
2074 const src = inst_data.src();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);
20752339
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}
20782351
2079fn zirSuspendBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {2352fn zirSuspendBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -2118,6 +2391,7 @@ fn zirBlock(...@@ -2118,6 +2391,7 @@ fn zirBlock(
2118 .parent = parent_block,2391 .parent = parent_block,
2119 .sema = sema,2392 .sema = sema,
2120 .src_decl = parent_block.src_decl,2393 .src_decl = parent_block.src_decl,
2394 .wip_capture_scope = parent_block.wip_capture_scope,
2121 .instructions = .{},2395 .instructions = .{},
2122 .label = &label,2396 .label = &label,
2123 .inlining = parent_block.inlining,2397 .inlining = parent_block.inlining,
...@@ -2142,8 +2416,12 @@ fn resolveBlockBody(...@@ -2142,8 +2416,12 @@ fn resolveBlockBody(
2142 body: []const Zir.Inst.Index,2416 body: []const Zir.Inst.Index,
2143 merges: *Scope.Block.Merges,2417 merges: *Scope.Block.Merges,
2144) CompileError!Air.Inst.Ref {2418) CompileError!Air.Inst.Ref {
2145 _ = try sema.analyzeBody(child_block, body);2419 if (child_block.is_comptime) {
2146 return sema.analyzeBlockBody(parent_block, src, child_block, merges);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}
21482426
2149fn analyzeBlockBody(2427fn analyzeBlockBody(
...@@ -2256,36 +2534,66 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro...@@ -2256,36 +2534,66 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro
2256 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;2534 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2257 const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;2535 const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
2258 const src = inst_data.src();2536 const src = inst_data.src();
2259 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };2537 const operand_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 };2538 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2261 const decl_name = sema.code.nullTerminatedString(extra.decl_name);2539 const decl_name = sema.code.nullTerminatedString(extra.decl_name);
2262 if (extra.namespace != .none) {2540 if (extra.namespace != .none) {
2263 return sema.mod.fail(&block.base, src, "TODO: implement exporting with field access", .{});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);2543 const decl = try sema.lookupIdentifier(block, operand_src, decl_name);
2266 const options = try sema.resolveInstConst(block, rhs_src, extra.options);2544 const options = try sema.resolveExportOptions(block, options_src, extra.options);
2267 const struct_obj = options.ty.castTag(.@"struct").?.data;2545 try sema.mod.analyzeExport(block, src, options, decl);
2268 const fields = options.val.castTag(.@"struct").?.data[0..struct_obj.fields.count()];2546}
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);
22742547
2275 if (linkage != .Strong) {2548fn zirExportValue(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
2276 return sema.mod.fail(&block.base, src, "TODO: implement exporting with non-strong linkage", .{});2549 const tracy = trace(@src());
2277 }2550 defer tracy.end();
2278 if (!fields[section_index].isNull()) {
2279 return sema.mod.fail(&block.base, src, "TODO: implement exporting with linksection", .{});
2280 }
22812551
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}
22842565
2285fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {2566fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
2567 const mod = sema.mod;
2286 const inst_data = sema.code.instructions.items(.data)[inst].un_node;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 const src: LazySrcLoc = inst_data.src();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}
22902598
2291fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {2599fn 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,20 +2616,21 @@ fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) C
2308 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand);2616 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand);
2309}2617}
23102618
2311fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {2619fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
2312 const tracy = trace(@src());2620 if (block.is_comptime) return;
2313 defer tracy.end();
23142621
2315 const src_node = sema.code.instructions.items(.data)[inst].node;2622 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2316 const src: LazySrcLoc = .{ .node_offset = src_node };2623 const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2317 try sema.requireRuntimeBlock(block, src);2624 const order = try sema.resolveAtomicOrder(block, order_src, inst_data.operand);
2318 _ = try block.addNoOp(.breakpoint);
2319}
23202625
2321fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {2626 if (@enumToInt(order) < @enumToInt(std.builtin.AtomicOrder.Acquire)) {
2322 const src_node = sema.code.instructions.items(.data)[inst].node;2627 return sema.mod.fail(&block.base, order_src, "atomic ordering must be Acquire or stricter", .{});
2323 const src: LazySrcLoc = .{ .node_offset = src_node };2628 }
2324 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirFence", .{});2629
2630 _ = try block.addInst(.{
2631 .tag = .fence,
2632 .data = .{ .fence = order },
2633 });
2325}2634}
23262635
2327fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {2636fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
...@@ -2489,8 +2798,6 @@ fn zirCall(...@@ -2489,8 +2798,6 @@ fn zirCall(
2489 sema: *Sema,2798 sema: *Sema,
2490 block: *Scope.Block,2799 block: *Scope.Block,
2491 inst: Zir.Inst.Index,2800 inst: Zir.Inst.Index,
2492 modifier: std.builtin.CallOptions.Modifier,
2493 ensure_result_used: bool,
2494) CompileError!Air.Inst.Ref {2801) CompileError!Air.Inst.Ref {
2495 const tracy = trace(@src());2802 const tracy = trace(@src());
2496 defer tracy.end();2803 defer tracy.end();
...@@ -2499,14 +2806,31 @@ fn zirCall(...@@ -2499,14 +2806,31 @@ fn zirCall(
2499 const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };2806 const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
2500 const call_src = inst_data.src();2807 const call_src = inst_data.src();
2501 const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index);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;
25032816
2504 const func = sema.resolveInst(extra.data.callee);2817 const func_type = sema.typeOf(func);
2505 // TODO handle function calls of generic functions2818
2506 const resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len);2819 // Desugar bound functions here
2507 for (args) |zir_arg, i| {2820 if (func_type.tag() == .bound_fn) {
2508 // the args are already casted to the result of a param type instruction.2821 const bound_func = try sema.resolveValue(block, func_src, func);
2509 resolved_args[i] = sema.resolveInst(zir_arg);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 }
25112835
2512 return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);2836 return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);
...@@ -2694,10 +3018,14 @@ fn analyzeCall(...@@ -2694,10 +3018,14 @@ fn analyzeCall(
2694 sema.func = module_fn;3018 sema.func = module_fn;
2695 defer sema.func = parent_func;3019 defer sema.func = parent_func;
26963020
3021 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, module_fn.owner_decl.src_scope);
3022 defer wip_captures.deinit();
3023
2697 var child_block: Scope.Block = .{3024 var child_block: Scope.Block = .{
2698 .parent = null,3025 .parent = null,
2699 .sema = sema,3026 .sema = sema,
2700 .src_decl = module_fn.owner_decl,3027 .src_decl = module_fn.owner_decl,
3028 .wip_capture_scope = wip_captures.scope,
2701 .instructions = .{},3029 .instructions = .{},
2702 .label = null,3030 .label = null,
2703 .inlining = &inlining,3031 .inlining = &inlining,
...@@ -2835,6 +3163,8 @@ fn analyzeCall(...@@ -2835,6 +3163,8 @@ fn analyzeCall(
28353163
2836 // TODO: check whether any external comptime memory was mutated by the3164 // TODO: check whether any external comptime memory was mutated by the
2837 // comptime function call. If so, then do not memoize the call here.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 var arena_allocator = std.heap.ArenaAllocator.init(gpa);3169 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
2840 errdefer arena_allocator.deinit();3170 errdefer arena_allocator.deinit();
...@@ -2845,7 +3175,7 @@ fn analyzeCall(...@@ -2845,7 +3175,7 @@ fn analyzeCall(
2845 }3175 }
28463176
2847 try mod.memoized_calls.put(gpa, memoized_call_key, .{3177 try mod.memoized_calls.put(gpa, memoized_call_key, .{
2848 .val = result_val,3178 .val = try result_val.copy(arena),
2849 .arena = arena_allocator.state,3179 .arena = arena_allocator.state,
2850 });3180 });
2851 delete_memoized_call_key = false;3181 delete_memoized_call_key = false;
...@@ -2860,6 +3190,9 @@ fn analyzeCall(...@@ -2860,6 +3190,9 @@ fn analyzeCall(
28603190
2861 break :res2 result;3191 break :res2 result;
2862 };3192 };
3193
3194 try wip_captures.finalize();
3195
2863 break :res res2;3196 break :res res2;
2864 } else if (func_ty_info.is_generic) res: {3197 } else if (func_ty_info.is_generic) res: {
2865 const func_val = try sema.resolveConstValue(block, func_src, func);3198 const func_val = try sema.resolveConstValue(block, func_src, func);
...@@ -2942,7 +3275,8 @@ fn analyzeCall(...@@ -2942,7 +3275,8 @@ fn analyzeCall(
2942 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);3275 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
29433276
2944 // Create a Decl for the new function.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 // TODO better names for generic function instantiations3280 // TODO better names for generic function instantiations
2947 const name_index = mod.getNextAnonNameIndex();3281 const name_index = mod.getNextAnonNameIndex();
2948 new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{3282 new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{
...@@ -2952,7 +3286,8 @@ fn analyzeCall(...@@ -2952,7 +3286,8 @@ fn analyzeCall(
2952 new_decl.is_pub = module_fn.owner_decl.is_pub;3286 new_decl.is_pub = module_fn.owner_decl.is_pub;
2953 new_decl.is_exported = module_fn.owner_decl.is_exported;3287 new_decl.is_exported = module_fn.owner_decl.is_exported;
2954 new_decl.has_align = module_fn.owner_decl.has_align;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 new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index;3291 new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index;
2957 new_decl.alive = true; // This Decl is called at runtime.3292 new_decl.alive = true; // This Decl is called at runtime.
2958 new_decl.has_tv = true;3293 new_decl.has_tv = true;
...@@ -2973,6 +3308,7 @@ fn analyzeCall(...@@ -2973,6 +3308,7 @@ fn analyzeCall(
2973 .mod = mod,3308 .mod = mod,
2974 .gpa = gpa,3309 .gpa = gpa,
2975 .arena = sema.arena,3310 .arena = sema.arena,
3311 .perm_arena = &new_decl_arena.allocator,
2976 .code = fn_zir,3312 .code = fn_zir,
2977 .owner_decl = new_decl,3313 .owner_decl = new_decl,
2978 .namespace = namespace,3314 .namespace = namespace,
...@@ -2985,10 +3321,14 @@ fn analyzeCall(...@@ -2985,10 +3321,14 @@ fn analyzeCall(
2985 };3321 };
2986 defer child_sema.deinit();3322 defer child_sema.deinit();
29873323
3324 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope);
3325 defer wip_captures.deinit();
3326
2988 var child_block: Scope.Block = .{3327 var child_block: Scope.Block = .{
2989 .parent = null,3328 .parent = null,
2990 .sema = &child_sema,3329 .sema = &child_sema,
2991 .src_decl = new_decl,3330 .src_decl = new_decl,
3331 .wip_capture_scope = wip_captures.scope,
2992 .instructions = .{},3332 .instructions = .{},
2993 .inlining = null,3333 .inlining = null,
2994 .is_comptime = true,3334 .is_comptime = true,
...@@ -3022,14 +3362,16 @@ fn analyzeCall(...@@ -3022,14 +3362,16 @@ fn analyzeCall(
3022 }3362 }
3023 const arg_src = call_src; // TODO: better source location3363 const arg_src = call_src; // TODO: better source location
3024 const arg = uncasted_args[arg_i];3364 const arg = uncasted_args[arg_i];
3025 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {3365 if (is_comptime) {
3026 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);3366 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
3027 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);3367 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
3028 } else if (is_comptime) {3368 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
3029 return sema.failWithNeededComptime(block, arg_src);3369 } else {
3370 return sema.failWithNeededComptime(block, arg_src);
3371 }
3030 } else if (is_anytype) {3372 } else if (is_anytype) {
3031 // We insert into the map an instruction which is runtime-known3373 // 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 const child_arg = try child_block.addArg(sema.typeOf(arg), 0);3375 const child_arg = try child_block.addArg(sema.typeOf(arg), 0);
3034 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);3376 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
3035 }3377 }
...@@ -3076,6 +3418,8 @@ fn analyzeCall(...@@ -3076,6 +3418,8 @@ fn analyzeCall(
3076 arg_i += 1;3418 arg_i += 1;
3077 }3419 }
30783420
3421 try wip_captures.finalize();
3422
3079 // Populate the Decl ty/val with the function and its type.3423 // Populate the Decl ty/val with the function and its type.
3080 new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator);3424 new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator);
3081 new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func);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,7 +3780,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com
3436 },3780 },
3437 .error_set => {3781 .error_set => {
3438 const lhs_set = lhs_ty.castTag(.error_set).?.data;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 for (lhs_set.names_ptr[0..lhs_set.names_len]) |name| {3784 for (lhs_set.names_ptr[0..lhs_set.names_len]) |name| {
3441 set.putAssumeCapacityNoClobber(name, {});3785 set.putAssumeCapacityNoClobber(name, {});
3442 }3786 }
...@@ -3450,7 +3794,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com...@@ -3450,7 +3794,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com
3450 },3794 },
3451 .error_set => {3795 .error_set => {
3452 const rhs_set = rhs_ty.castTag(.error_set).?.data;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 for (rhs_set.names_ptr[0..rhs_set.names_len]) |name| {3798 for (rhs_set.names_ptr[0..rhs_set.names_len]) |name| {
3455 set.putAssumeCapacity(name, {});3799 set.putAssumeCapacity(name, {});
3456 }3800 }
...@@ -3606,7 +3950,11 @@ fn zirOptionalPayloadPtr(...@@ -3606,7 +3950,11 @@ fn zirOptionalPayloadPtr(
3606 }3950 }
36073951
3608 const child_type = try opt_type.optionalChildAlloc(sema.arena);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 });
36103958
3611 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {3959 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
3612 if (try pointer_val.pointerDeref(sema.arena)) |val| {3960 if (try pointer_val.pointerDeref(sema.arena)) |val| {
...@@ -3721,7 +4069,11 @@ fn zirErrUnionPayloadPtr(...@@ -3721,7 +4069,11 @@ fn zirErrUnionPayloadPtr(
3721 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand_ty.elemType()});4069 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand_ty.elemType()});
37224070
3723 const payload_ty = operand_ty.elemType().errorUnionPayload();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 });
37254077
3726 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {4078 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
3727 if (try pointer_val.pointerDeref(sema.arena)) |val| {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,6 +4588,19 @@ fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
4236 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);4588 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
4237}4589}
42384590
4591fn 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
4239fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {4604fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4240 const tracy = trace(@src());4605 const tracy = trace(@src());
4241 defer tracy.end();4606 defer tracy.end();
...@@ -4262,6 +4627,19 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp...@@ -4262,6 +4627,19 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp
4262 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);4627 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
4263}4628}
42644629
4630fn 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
4265fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {4643fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4266 const tracy = trace(@src());4644 const tracy = trace(@src());
4267 defer tracy.end();4645 defer tracy.end();
...@@ -4275,8 +4653,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr...@@ -4275,8 +4653,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
4275 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);4653 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
4276 const operand = sema.resolveInst(extra.rhs);4654 const operand = sema.resolveInst(extra.rhs);
42774655
4278 const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_type);4656 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_type);
4279 _ = try sema.requireIntegerType(block, operand_src, sema.typeOf(operand));4657 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
42804658
4281 if (try sema.isComptimeKnown(block, operand_src, operand)) {4659 if (try sema.isComptimeKnown(block, operand_src, operand)) {
4282 return sema.coerce(block, dest_type, operand, operand_src);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,7 +4662,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
4284 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_int'", .{});4662 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_int'", .{});
4285 }4663 }
42864664
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}
42894668
4290fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {4669fn 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,11 +4717,18 @@ fn zirFloatCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
43384717
4339 if (try sema.isComptimeKnown(block, operand_src, operand)) {4718 if (try sema.isComptimeKnown(block, operand_src, operand)) {
4340 return sema.coerce(block, dest_type, operand, operand_src);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 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_float'", .{});4722 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_float'", .{});
4343 }4723 }
43444724 const target = sema.mod.getTarget();
4345 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten float", .{});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}
43474733
4348fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {4734fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -4778,8 +5164,8 @@ fn analyzeSwitch(...@@ -4778,8 +5164,8 @@ fn analyzeSwitch(
4778 var arena = std.heap.ArenaAllocator.init(gpa);5164 var arena = std.heap.ArenaAllocator.init(gpa);
4779 defer arena.deinit();5165 defer arena.deinit();
47805166
4781 const min_int = try operand_ty.minInt(&arena, mod.getTarget());5167 const min_int = try operand_ty.minInt(&arena.allocator, mod.getTarget());
4782 const max_int = try operand_ty.maxInt(&arena, mod.getTarget());5168 const max_int = try operand_ty.maxInt(&arena.allocator, mod.getTarget());
4783 if (try range_set.spans(min_int, max_int, operand_ty)) {5169 if (try range_set.spans(min_int, max_int, operand_ty)) {
4784 if (special_prong == .@"else") {5170 if (special_prong == .@"else") {
4785 return mod.fail(5171 return mod.fail(
...@@ -4971,6 +5357,7 @@ fn analyzeSwitch(...@@ -4971,6 +5357,7 @@ fn analyzeSwitch(
4971 .parent = block,5357 .parent = block,
4972 .sema = sema,5358 .sema = sema,
4973 .src_decl = block.src_decl,5359 .src_decl = block.src_decl,
5360 .wip_capture_scope = block.wip_capture_scope,
4974 .instructions = .{},5361 .instructions = .{},
4975 .label = &label,5362 .label = &label,
4976 .inlining = block.inlining,5363 .inlining = block.inlining,
...@@ -5075,12 +5462,19 @@ fn analyzeSwitch(...@@ -5075,12 +5462,19 @@ fn analyzeSwitch(
5075 const body = sema.code.extra[extra_index..][0..body_len];5462 const body = sema.code.extra[extra_index..][0..body_len];
5076 extra_index += body_len;5463 extra_index += body_len;
50775464
5465 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
5466 defer wip_captures.deinit();
5467
5078 case_block.instructions.shrinkRetainingCapacity(0);5468 case_block.instructions.shrinkRetainingCapacity(0);
5469 case_block.wip_capture_scope = wip_captures.scope;
5470
5079 const item = sema.resolveInst(item_ref);5471 const item = sema.resolveInst(item_ref);
5080 // `item` is already guaranteed to be constant known.5472 // `item` is already guaranteed to be constant known.
50815473
5082 _ = try sema.analyzeBody(&case_block, body);5474 _ = try sema.analyzeBody(&case_block, body);
50835475
5476 try wip_captures.finalize();
5477
5084 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);5478 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
5085 cases_extra.appendAssumeCapacity(1); // items_len5479 cases_extra.appendAssumeCapacity(1); // items_len
5086 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));5480 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
...@@ -5108,6 +5502,7 @@ fn analyzeSwitch(...@@ -5108,6 +5502,7 @@ fn analyzeSwitch(
5108 extra_index += items_len;5502 extra_index += items_len;
51095503
5110 case_block.instructions.shrinkRetainingCapacity(0);5504 case_block.instructions.shrinkRetainingCapacity(0);
5505 case_block.wip_capture_scope = child_block.wip_capture_scope;
51115506
5112 var any_ok: Air.Inst.Ref = .none;5507 var any_ok: Air.Inst.Ref = .none;
51135508
...@@ -5186,11 +5581,18 @@ fn analyzeSwitch(...@@ -5186,11 +5581,18 @@ fn analyzeSwitch(
5186 var cond_body = case_block.instructions.toOwnedSlice(gpa);5581 var cond_body = case_block.instructions.toOwnedSlice(gpa);
5187 defer gpa.free(cond_body);5582 defer gpa.free(cond_body);
51885583
5584 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
5585 defer wip_captures.deinit();
5586
5189 case_block.instructions.shrinkRetainingCapacity(0);5587 case_block.instructions.shrinkRetainingCapacity(0);
5588 case_block.wip_capture_scope = wip_captures.scope;
5589
5190 const body = sema.code.extra[extra_index..][0..body_len];5590 const body = sema.code.extra[extra_index..][0..body_len];
5191 extra_index += body_len;5591 extra_index += body_len;
5192 _ = try sema.analyzeBody(&case_block, body);5592 _ = try sema.analyzeBody(&case_block, body);
51935593
5594 try wip_captures.finalize();
5595
5194 if (is_first) {5596 if (is_first) {
5195 is_first = false;5597 is_first = false;
5196 first_else_body = cond_body;5598 first_else_body = cond_body;
...@@ -5216,9 +5618,16 @@ fn analyzeSwitch(...@@ -5216,9 +5618,16 @@ fn analyzeSwitch(
52165618
5217 var final_else_body: []const Air.Inst.Index = &.{};5619 var final_else_body: []const Air.Inst.Index = &.{};
5218 if (special.body.len != 0) {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 case_block.instructions.shrinkRetainingCapacity(0);5624 case_block.instructions.shrinkRetainingCapacity(0);
5625 case_block.wip_capture_scope = wip_captures.scope;
5626
5220 _ = try sema.analyzeBody(&case_block, special.body);5627 _ = try sema.analyzeBody(&case_block, special.body);
52215628
5629 try wip_captures.finalize();
5630
5222 if (is_first) {5631 if (is_first) {
5223 final_else_body = case_block.instructions.items;5632 final_else_body = case_block.instructions.items;
5224 } else {5633 } else {
...@@ -5500,7 +5909,7 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro...@@ -5500,7 +5909,7 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro
5500 try mod.semaFile(result.file);5909 try mod.semaFile(result.file);
5501 const file_root_decl = result.file.root_decl.?;5910 const file_root_decl = result.file.root_decl.?;
5502 try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl);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}
55055914
5506fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5915fn 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,29 +5918,57 @@ fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
5509 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});5918 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});
5510}5919}
55115920
5512fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5921fn 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 const tracy = trace(@src());5927 const tracy = trace(@src());
5514 defer tracy.end();5928 defer tracy.end();
55155929
5516 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;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 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };5931 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
5519 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };5932 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
5520 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;5933 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
5521 const lhs = sema.resolveInst(extra.lhs);5934 const lhs = sema.resolveInst(extra.lhs);
5522 const rhs = sema.resolveInst(extra.rhs);5935 const rhs = sema.resolveInst(extra.rhs);
55235936
5524 if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {5937 // TODO coerce rhs if air_tag is not shl_sat
5525 if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {5938
5526 if (lhs_val.isUndef() or rhs_val.isUndef()) {5939 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
5527 return sema.addConstUndef(sema.typeOf(lhs));5940 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
5528 }5941
5529 return sema.mod.fail(&block.base, src, "TODO implement comptime shl", .{});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 };
55325967
5533 try sema.requireRuntimeBlock(block, src);5968 // TODO: insert runtime safety check for shl_exact
5534 return block.addBinOp(.shl, lhs, rhs);5969
5970 try sema.requireRuntimeBlock(block, runtime_src);
5971 return block.addBinOp(air_tag, lhs, rhs);
5535}5972}
55365973
5537fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5974fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -5619,10 +6056,13 @@ fn zirBitwise(...@@ -5619,10 +6056,13 @@ fn zirBitwise(
56196056
5620 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {6057 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
5621 if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {6058 if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
5622 if (lhs_val.isUndef() or rhs_val.isUndef()) {6059 const result_val = switch (air_tag) {
5623 return sema.addConstUndef(resolved_type);6060 .bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena),
5624 }6061 .bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena),
5625 return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});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 }
56286068
...@@ -5671,38 +6111,36 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -5671,38 +6111,36 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
5671 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {6111 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
5672 if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {6112 if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
5673 const final_len = lhs_info.len + rhs_info.len;6113 const final_len = lhs_info.len + rhs_info.len;
5674 if (lhs_ty.zigTypeTag() == .Pointer) {6114 const is_pointer = lhs_ty.zigTypeTag() == .Pointer;
5675 var anon_decl = try block.startAnonDecl();6115 var anon_decl = try block.startAnonDecl();
5676 defer anon_decl.deinit();6116 defer anon_decl.deinit();
56776117
5678 const lhs_sub_val = (try lhs_val.pointerDeref(anon_decl.arena())).?;6118 const lhs_sub_val = if (is_pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val;
5679 const rhs_sub_val = (try rhs_val.pointerDeref(anon_decl.arena())).?;6119 const rhs_sub_val = if (is_pointer) (try rhs_val.pointerDeref(anon_decl.arena())).? else rhs_val;
5680 const buf = try anon_decl.arena().alloc(Value, final_len);6120 const buf = try anon_decl.arena().alloc(Value, final_len);
5681 {6121 {
5682 var i: u64 = 0;6122 var i: u64 = 0;
5683 while (i < lhs_info.len) : (i += 1) {6123 while (i < lhs_info.len) : (i += 1) {
5684 const val = try lhs_sub_val.elemValue(sema.arena, i);6124 const val = try lhs_sub_val.elemValue(sema.arena, i);
5685 buf[i] = try val.copy(anon_decl.arena());6125 buf[i] = try val.copy(anon_decl.arena());
5686 }
5687 }6126 }
5688 {6127 }
5689 var i: u64 = 0;6128 {
5690 while (i < rhs_info.len) : (i += 1) {6129 var i: u64 = 0;
5691 const val = try rhs_sub_val.elemValue(sema.arena, i);6130 while (i < rhs_info.len) : (i += 1) {
5692 buf[lhs_info.len + i] = try val.copy(anon_decl.arena());6131 const val = try rhs_sub_val.elemValue(sema.arena, i);
5693 }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 } else {6144 } else {
5707 return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_cat", .{});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,32 +6180,30 @@ fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
57426180
5743 const final_len = std.math.mul(u64, mulinfo.len, tomulby) catch return sema.mod.fail(&block.base, rhs_src, "operation results in overflow", .{});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 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {6182 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
5745 if (lhs_ty.zigTypeTag() == .Pointer) {6183 var anon_decl = try block.startAnonDecl();
5746 var anon_decl = try block.startAnonDecl();6184 defer anon_decl.deinit();
5747 defer anon_decl.deinit();6185 const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val;
5748 const lhs_sub_val = (try lhs_val.pointerDeref(anon_decl.arena())).?;6186 const final_ty = if (mulinfo.sentinel) |sent|
57496187 try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type, .sentinel = sent })
5750 const final_ty = if (mulinfo.sentinel) |sent|6188 else
5751 try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type, .sentinel = sent })6189 try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type });
5752 else6190 const buf = try anon_decl.arena().alloc(Value, final_len);
5753 try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type });6191
57546192 // the actual loop
5755 const buf = try anon_decl.arena().alloc(Value, final_len);6193 var i: u64 = 0;
5756 var i: u64 = 0;6194 while (i < tomulby) : (i += 1) {
5757 while (i < tomulby) : (i += 1) {6195 var j: u64 = 0;
5758 var j: u64 = 0;6196 while (j < mulinfo.len) : (j += 1) {
5759 while (j < mulinfo.len) : (j += 1) {6197 const val = try lhs_sub_val.elemValue(sema.arena, j);
5760 const val = try lhs_sub_val.elemValue(sema.arena, j);6198 buf[mulinfo.len * i + j] = try val.copy(anon_decl.arena());
5761 buf[mulinfo.len * i + j] = try val.copy(anon_decl.arena());
5762 }
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 return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_mul", .{});6208 return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_mul", .{});
5773}6209}
...@@ -5791,11 +6227,15 @@ fn zirNegate(...@@ -5791,11 +6227,15 @@ fn zirNegate(
5791 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);6227 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
5792}6228}
57936229
5794fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6230fn 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 const tracy = trace(@src());6236 const tracy = trace(@src());
5796 defer tracy.end();6237 defer tracy.end();
57976238
5798 const tag_override = block.sema.code.instructions.items(.tag)[inst];
5799 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6239 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5800 sema.src = .{ .node_offset_bin_op = inst_data.src_node };6240 sema.src = .{ .node_offset_bin_op = inst_data.src_node };
5801 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };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,7 +6244,7 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile
5804 const lhs = sema.resolveInst(extra.lhs);6244 const lhs = sema.resolveInst(extra.lhs);
5805 const rhs = sema.resolveInst(extra.rhs);6245 const rhs = sema.resolveInst(extra.rhs);
58066246
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}
58096249
5810fn zirOverflowArithmetic(6250fn zirOverflowArithmetic(
...@@ -5821,22 +6261,10 @@ fn zirOverflowArithmetic(...@@ -5821,22 +6261,10 @@ fn zirOverflowArithmetic(
5821 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{});6261 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{});
5822}6262}
58236263
5824fn 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
5837fn analyzeArithmetic(6264fn analyzeArithmetic(
5838 sema: *Sema,6265 sema: *Sema,
5839 block: *Scope.Block,6266 block: *Scope.Block,
6267 /// TODO performance investigation: make this comptime?
5840 zir_tag: Zir.Inst.Tag,6268 zir_tag: Zir.Inst.Tag,
5841 lhs: Air.Inst.Ref,6269 lhs: Air.Inst.Ref,
5842 rhs: Air.Inst.Ref,6270 rhs: Air.Inst.Ref,
...@@ -5854,7 +6282,7 @@ fn analyzeArithmetic(...@@ -5854,7 +6282,7 @@ fn analyzeArithmetic(
5854 lhs_ty.arrayLen(), rhs_ty.arrayLen(),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 } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) {6286 } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) {
5859 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{6287 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
5860 lhs_ty, rhs_ty,6288 lhs_ty, rhs_ty,
...@@ -5897,7 +6325,9 @@ fn analyzeArithmetic(...@@ -5897,7 +6325,9 @@ fn analyzeArithmetic(
5897 };6325 };
58986326
5899 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };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 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);6331 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
5902 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);6332 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
59036333
...@@ -5917,132 +6347,627 @@ fn analyzeArithmetic(...@@ -5917,132 +6347,627 @@ fn analyzeArithmetic(
5917 });6347 });
5918 }6348 }
59196349
5920 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {6350 const target = sema.mod.getTarget();
5921 if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {6351 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs);
5922 if (lhs_val.isUndef() or rhs_val.isUndef()) {6352 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs);
5923 return sema.addConstUndef(resolved_type);6353 const rs: struct { src: LazySrcLoc, air_tag: Air.Inst.Tag } = rs: {
5924 }6354 switch (zir_tag) {
5925 // incase rhs is 0, simply return lhs without doing any calculations6355 .add => {
5926 // TODO Once division is implemented we should throw an error when dividing by 0.6356 // For integers:
5927 if (rhs_val.compareWithZero(.eq)) {6357 // If either of the operands are zero, then the other operand is
5928 switch (zir_tag) {6358 // returned, even if it is undefined.
5929 .add, .addwrap, .sub, .subwrap => {6359 // If either of the operands are undefined, it's a compile error
5930 return sema.addConstant(scalar_type, lhs_val);6360 // because there is a possible value for which the addition would
5931 },6361 // overflow (max_int), causing illegal behavior.
5932 else => {},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 }6368 if (maybe_rhs_val) |rhs_val| {
59356369 if (rhs_val.isUndef()) {
5936 const value = switch (zir_tag) {6370 if (is_int) {
5937 .add => blk: {6371 return sema.failWithUseOfUndef(block, rhs_src);
5938 const val = if (is_int)6372 } else {
5939 try lhs_val.intAdd(rhs_val, sema.arena)6373 return sema.addConstUndef(scalar_type);
5940 else6374 }
5941 try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena);6375 }
5942 break :blk val;6376 if (rhs_val.compareWithZero(.eq)) {
5943 },6377 return casted_lhs;
5944 .sub => blk: {6378 }
5945 const val = if (is_int)6379 }
5946 try lhs_val.intSub(rhs_val, sema.arena)6380 if (maybe_lhs_val) |lhs_val| {
5947 else6381 if (lhs_val.isUndef()) {
5948 try lhs_val.floatSub(rhs_val, scalar_type, sema.arena);6382 if (is_int) {
5949 break :blk val;6383 return sema.failWithUseOfUndef(block, lhs_src);
5950 },6384 } else {
5951 .div => blk: {6385 return sema.addConstUndef(scalar_type);
5952 const val = if (is_int)6386 }
5953 try lhs_val.intDiv(rhs_val, sema.arena)6387 }
5954 else6388 if (maybe_rhs_val) |rhs_val| {
5955 try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena);6389 if (is_int) {
5956 break :blk val;6390 return sema.addConstant(
5957 },6391 scalar_type,
5958 .mul => blk: {6392 try lhs_val.intAdd(rhs_val, sema.arena),
5959 const val = if (is_int)6393 );
5960 try lhs_val.intMul(rhs_val, sema.arena)6394 } else {
5961 else6395 return sema.addConstant(
5962 try lhs_val.floatMul(rhs_val, scalar_type, sema.arena);6396 scalar_type,
5963 break :blk val;6397 try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena),
5964 },6398 );
5965 else => return sema.mod.fail(&block.base, src, "TODO implement comptime arithmetic for operand '{s}'", .{@tagName(zir_tag)}),6399 }
5966 };6400 } else break :rs .{ .src = rhs_src, .air_tag = .add };
59676401 } else break :rs .{ .src = lhs_src, .air_tag = .add };
5968 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });6402 },
59696403 .addwrap => {
5970 return sema.addConstant(scalar_type, value);6404 // Integers only; floats are checked above.
5971 } else {6405 // If either of the operands are zero, the other operand is returned.
5972 try sema.requireRuntimeBlock(block, rhs_src);6406 // If either of the operands are undefined, the result is undefined.
5973 }6407 if (maybe_lhs_val) |lhs_val| {
5974 } else {6408 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
5975 try sema.requireRuntimeBlock(block, lhs_src);6409 return casted_rhs;
5976 }6410 }
59776411 }
5978 if (zir_tag == .mod_rem) {6412 if (maybe_rhs_val) |rhs_val| {
5979 const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isFloat();6413 if (rhs_val.isUndef()) {
5980 const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isFloat();6414 return sema.addConstUndef(scalar_type);
5981 if (dirty_lhs or dirty_rhs) {6415 }
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 });6416 if (rhs_val.compareWithZero(.eq)) {
5983 }6417 return casted_lhs;
5984 }6418 }
59856419 if (maybe_lhs_val) |lhs_val| {
5986 const air_tag: Air.Inst.Tag = switch (zir_tag) {6420 return sema.addConstant(
5987 .add => .add,6421 scalar_type,
5988 .addwrap => .addwrap,6422 try lhs_val.numberAddWrap(rhs_val, scalar_type, sema.arena, target),
5989 .sub => .sub,6423 );
5990 .subwrap => .subwrap,6424 } else break :rs .{ .src = lhs_src, .air_tag = .addwrap };
5991 .mul => .mul,6425 } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };
5992 .mulwrap => .mulwrap,6426 },
5993 .div => .div,6427 .add_sat => {
5994 .mod_rem => .rem,6428 // Integers only; floats are checked above.
5995 .rem => .rem,6429 // If either of the operands are zero, then the other operand is returned.
5996 else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}'", .{@tagName(zir_tag)}),6430 // If either of the operands are undefined, the result is undefined.
5997 };6431 if (maybe_lhs_val) |lhs_val| {
59986432 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
5999 return block.addBinOp(air_tag, casted_lhs, casted_rhs);6433 return casted_rhs;
6000}6434 }
60016435 }
6002fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6436 if (maybe_rhs_val) |rhs_val| {
6003 const tracy = trace(@src());6437 if (rhs_val.isUndef()) {
6004 defer tracy.end();6438 return sema.addConstUndef(scalar_type);
60056439 }
6006 const inst_data = sema.code.instructions.items(.data)[inst].un_node;6440 if (rhs_val.compareWithZero(.eq)) {
6007 const src = inst_data.src();6441 return casted_lhs;
6008 const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node };6442 }
6009 const ptr = sema.resolveInst(inst_data.operand);6443 if (maybe_lhs_val) |lhs_val| {
6010 return sema.analyzeLoad(block, src, ptr, ptr_src);6444 return sema.addConstant(
6011}6445 scalar_type,
60126446 try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target),
6013fn zirAsm(6447 );
6014 sema: *Sema,6448 } else break :rs .{ .src = lhs_src, .air_tag = .add_sat };
6015 block: *Scope.Block,6449 } else break :rs .{ .src = rhs_src, .air_tag = .add_sat };
6016 extended: Zir.Inst.Extended.InstData,6450 },
6017 inst: Zir.Inst.Index,6451 .sub => {
6018) CompileError!Air.Inst.Ref {6452 // For integers:
6019 const tracy = trace(@src());6453 // If the rhs is zero, then the other operand is
6020 defer tracy.end();6454 // returned, even if it is undefined.
60216455 // If either of the operands are undefined, it's a compile error
6022 const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);6456 // because there is a possible value for which the subtraction would
6023 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };6457 // overflow, causing illegal behavior.
6024 const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node };6458 // For floats: either operand being undef makes the result undef.
6025 const outputs_len = @truncate(u5, extended.small);6459 if (maybe_rhs_val) |rhs_val| {
6026 const inputs_len = @truncate(u5, extended.small >> 5);6460 if (rhs_val.isUndef()) {
6027 const clobbers_len = @truncate(u5, extended.small >> 10);6461 if (is_int) {
60286462 return sema.failWithUseOfUndef(block, rhs_src);
6029 if (outputs_len > 1) {6463 } else {
6030 return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with more than 1 output", .{});6464 return sema.addConstUndef(scalar_type);
6031 }6465 }
60326466 }
6033 var extra_i = extra.end;6467 if (rhs_val.compareWithZero(.eq)) {
6034 var output_type_bits = extra.data.output_type_bits;6468 return casted_lhs;
60356469 }
6036 const Output = struct { constraint: []const u8, ty: Type };6470 }
6037 const output: ?Output = if (outputs_len == 0) null else blk: {6471 if (maybe_lhs_val) |lhs_val| {
6038 const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i);6472 if (lhs_val.isUndef()) {
6039 extra_i = output.end;6473 if (is_int) {
60406474 return sema.failWithUseOfUndef(block, lhs_src);
6041 const is_type = @truncate(u1, output_type_bits) != 0;6475 } else {
6042 output_type_bits >>= 1;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 }
60436587
6044 if (!is_type) {6588 if (maybe_rhs_val) |rhs_val| {
6045 return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with non `->` output", .{});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
6927fn 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
6938fn 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 }
60476972
6048 const constraint = sema.code.nullTerminatedString(output.data.constraint);6973 const constraint = sema.code.nullTerminatedString(output.data.constraint);
...@@ -6134,10 +7059,11 @@ fn zirCmpEq(...@@ -6134,10 +7059,11 @@ fn zirCmpEq(
6134 const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty;7059 const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty;
6135 return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type});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) or7062 if (lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) {
6138 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))7063 return sema.analyzeCmpUnionTag(block, rhs, rhs_src, lhs, lhs_src, op);
6139 {7064 }
6140 return mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});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 if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {7068 if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
6143 const runtime_src: LazySrcLoc = src: {7069 const runtime_src: LazySrcLoc = src: {
...@@ -6178,6 +7104,28 @@ fn zirCmpEq(...@@ -6178,6 +7104,28 @@ fn zirCmpEq(
6178 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true);7104 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true);
6179}7105}
61807106
7107fn 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/// Only called for non-equality operators. See also `zirCmpEq`.7129/// Only called for non-equality operators. See also `zirCmpEq`.
6182fn zirCmp(7130fn zirCmp(
6183 sema: *Sema,7131 sema: *Sema,
...@@ -6224,10 +7172,21 @@ fn analyzeCmp(...@@ -6224,10 +7172,21 @@ fn analyzeCmp(
6224 @tagName(op), resolved_type,7172 @tagName(op), resolved_type,
6225 });7173 });
6226 }7174 }
6227
6228 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);7175 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
6229 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);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}
62307179
7180fn 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 const runtime_src: LazySrcLoc = src: {7190 const runtime_src: LazySrcLoc = src: {
6232 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {7191 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
6233 if (lhs_val.isUndef()) return sema.addConstUndef(resolved_type);7192 if (lhs_val.isUndef()) return sema.addConstUndef(resolved_type);
...@@ -6294,10 +7253,42 @@ fn runtimeBoolCmp(...@@ -6294,10 +7253,42 @@ fn runtimeBoolCmp(
62947253
6295fn zirSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7254fn zirSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6296 const inst_data = sema.code.instructions.items(.data)[inst].un_node;7255 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
7256 const src = inst_data.src();
6297 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };7257 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
6298 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);7258 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
7259 try sema.resolveTypeLayout(block, src, operand_ty);
6299 const target = sema.mod.getTarget();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 return sema.addIntUnsigned(Type.initTag(.comptime_int), abi_size);7292 return sema.addIntUnsigned(Type.initTag(.comptime_int), abi_size);
6302}7293}
63037294
...@@ -6315,8 +7306,45 @@ fn zirThis(...@@ -6315,8 +7306,45 @@ fn zirThis(
6315 block: *Scope.Block,7306 block: *Scope.Block,
6316 extended: Zir.Inst.Extended.InstData,7307 extended: Zir.Inst.Extended.InstData,
6317) CompileError!Air.Inst.Ref {7308) CompileError!Air.Inst.Ref {
7309 const this_decl = block.base.namespace().getDecl();
6318 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };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
7314fn 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
7328fn 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}
63217349
6322fn zirRetAddr(7350fn zirRetAddr(
...@@ -6345,19 +7373,80 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -6345,19 +7373,80 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
6345 const target = sema.mod.getTarget();7373 const target = sema.mod.getTarget();
63467374
6347 switch (ty.zigTypeTag()) {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 .Fn => {7439 .Fn => {
7440 const info = ty.fnInfo();
6349 const field_values = try sema.arena.alloc(Value, 6);7441 const field_values = try sema.arena.alloc(Value, 6);
6350 // calling_convention: CallingConvention,7442 // calling_convention: CallingConvention,
6351 field_values[0] = try Value.Tag.enum_field_index.create(7443 field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.cc));
6352 sema.arena,
6353 @enumToInt(ty.fnCallingConvention()),
6354 );
6355 // alignment: comptime_int,7444 // alignment: comptime_int,
6356 field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(target));7445 field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(target));
6357 // is_generic: bool,7446 // is_generic: bool,
6358 field_values[2] = Value.initTag(.bool_false); // TODO7447 field_values[2] = if (info.is_generic) Value.initTag(.bool_true) else Value.initTag(.bool_false);
6359 // is_var_args: bool,7448 // is_var_args: bool,
6360 field_values[3] = Value.initTag(.bool_false); // TODO7449 field_values[3] = if (info.is_var_args) Value.initTag(.bool_true) else Value.initTag(.bool_false);
6361 // return_type: ?type,7450 // return_type: ?type,
6362 field_values[4] = try Value.Tag.ty.create(sema.arena, ty.fnReturnType());7451 field_values[4] = try Value.Tag.ty.create(sema.arena, ty.fnReturnType());
6363 // args: []const FnArg,7452 // args: []const FnArg,
...@@ -6366,10 +7455,7 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -6366,10 +7455,7 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
6366 return sema.addConstant(7455 return sema.addConstant(
6367 type_info_ty,7456 type_info_ty,
6368 try Value.Tag.@"union".create(sema.arena, .{7457 try Value.Tag.@"union".create(sema.arena, .{
6369 .tag = try Value.Tag.enum_field_index.create(7458 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Fn)),
6370 sema.arena,
6371 @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Fn),
6372 ),
6373 .val = try Value.Tag.@"struct".create(sema.arena, field_values),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,10 +7474,92 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
6388 return sema.addConstant(7474 return sema.addConstant(
6389 type_info_ty,7475 type_info_ty,
6390 try Value.Tag.@"union".create(sema.arena, .{7476 try Value.Tag.@"union".create(sema.arena, .{
6391 .tag = try Value.Tag.enum_field_index.create(7477 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Int)),
6392 sema.arena,7478 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
6393 @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Int),7479 }),
6394 ),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 .val = try Value.Tag.@"struct".create(sema.arena, field_values),7563 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
6396 }),7564 }),
6397 );7565 );
...@@ -6808,7 +7976,7 @@ fn analyzeRet(...@@ -6808,7 +7976,7 @@ fn analyzeRet(
6808fn floatOpAllowed(tag: Zir.Inst.Tag) bool {7976fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
6809 // extend this swich as additional operators are implemented7977 // extend this swich as additional operators are implemented
6810 return switch (tag) {7978 return switch (tag) {
6811 .add, .sub, .mul, .div => true,7979 .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true,
6812 else => false,7980 else => false,
6813 };7981 };
6814}7982}
...@@ -6819,18 +7987,14 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp...@@ -6819,18 +7987,14 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp
68197987
6820 const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple;7988 const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple;
6821 const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type);7989 const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type);
6822 const ty = try Module.ptrType(7990 const ty = try Type.ptr(sema.arena, .{
6823 sema.arena,7991 .pointee_type = elem_type,
6824 elem_type,7992 .@"addrspace" = .generic,
6825 null,7993 .mutable = inst_data.is_mutable,
6826 0,7994 .@"allowzero" = inst_data.is_allowzero,
6827 0,7995 .@"volatile" = inst_data.is_volatile,
6828 0,7996 .size = inst_data.size,
6829 inst_data.is_mutable,7997 });
6830 inst_data.is_allowzero,
6831 inst_data.is_volatile,
6832 inst_data.size,
6833 );
6834 return sema.addType(ty);7998 return sema.addType(ty);
6835}7999}
68368000
...@@ -6856,6 +8020,12 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr...@@ -6856,6 +8020,12 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
6856 break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32);8020 break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32);
6857 } else 0;8021 } else 0;
68588022
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 const bit_start = if (inst_data.flags.has_bit_range) blk: {8029 const bit_start = if (inst_data.flags.has_bit_range) blk: {
6860 const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);8030 const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
6861 extra_i += 1;8031 extra_i += 1;
...@@ -6873,18 +8043,18 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr...@@ -6873,18 +8043,18 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
68738043
6874 const elem_type = try sema.resolveType(block, .unneeded, extra.data.elem_type);8044 const elem_type = try sema.resolveType(block, .unneeded, extra.data.elem_type);
68758045
6876 const ty = try Module.ptrType(8046 const ty = try Type.ptr(sema.arena, .{
6877 sema.arena,8047 .pointee_type = elem_type,
6878 elem_type,8048 .sentinel = sentinel,
6879 sentinel,8049 .@"align" = abi_align,
6880 abi_align,8050 .@"addrspace" = address_space,
6881 bit_start,8051 .bit_offset = bit_start,
6882 bit_end,8052 .host_size = bit_end,
6883 inst_data.flags.is_mutable,8053 .mutable = inst_data.flags.is_mutable,
6884 inst_data.flags.is_allowzero,8054 .@"allowzero" = inst_data.flags.is_allowzero,
6885 inst_data.flags.is_volatile,8055 .@"volatile" = inst_data.flags.is_volatile,
6886 inst_data.size,8056 .size = inst_data.size,
6887 );8057 });
6888 return sema.addType(ty);8058 return sema.addType(ty);
6889}8059}
68908060
...@@ -7059,8 +8229,49 @@ fn zirArrayInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:...@@ -7059,8 +8229,49 @@ fn zirArrayInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
7059 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;8229 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7060 const src = inst_data.src();8230 const src = inst_data.src();
70618231
7062 _ = is_ref;8232 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
7063 return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInit", .{});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}
70658276
7066fn zirArrayInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {8277fn zirArrayInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
...@@ -7132,8 +8343,11 @@ fn zirFrameAddress(...@@ -7132,8 +8343,11 @@ fn zirFrameAddress(
71328343
7133fn zirAlignOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8344fn zirAlignOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7134 const inst_data = sema.code.instructions.items(.data)[inst].un_node;8345 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
7135 const src = inst_data.src();8346 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
7136 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAlignOf", .{});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}
71388352
7139fn zirBoolToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8353fn 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,13 +8460,30 @@ fn zirFrameSize(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
7246fn zirFloatToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8460fn zirFloatToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7247 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;8461 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7248 const src = inst_data.src();8462 const src = inst_data.src();
8463 // TODO don't forget the safety check!
7249 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{});8464 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{});
7250}8465}
72518466
7252fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8467fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7253 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;8468 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7254 const src = inst_data.src();8469 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
7255 return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToFloat", .{});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}
72578488
7258fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8489fn 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,8 +8582,8 @@ fn zirTruncate(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
7351 const operand = sema.resolveInst(extra.rhs);8582 const operand = sema.resolveInst(extra.rhs);
7352 const operand_ty = sema.typeOf(operand);8583 const operand_ty = sema.typeOf(operand);
7353 const mod = sema.mod;8584 const mod = sema.mod;
7354 const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_ty);8585 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
7355 const src_is_comptime_int = try sema.requireIntegerType(block, operand_src, operand_ty);8586 const src_is_comptime_int = try sema.checkIntType(block, operand_src, operand_ty);
73568587
7357 if (dest_is_comptime_int) {8588 if (dest_is_comptime_int) {
7358 return sema.coerce(block, dest_ty, operand, operand_src);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,14 +8641,56 @@ fn zirAlignCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
74108641
7411fn zirClz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8642fn zirClz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7412 const inst_data = sema.code.instructions.items(.data)[inst].un_node;8643 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
7413 const src = inst_data.src();8644 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
7414 return sema.mod.fail(&block.base, src, "TODO: Sema.zirClz", .{});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}
74168668
7417fn zirCtz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8669fn zirCtz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7418 const inst_data = sema.code.instructions.items(.data)[inst].un_node;8670 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
7419 const src = inst_data.src();8671 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
7420 return sema.mod.fail(&block.base, src, "TODO: Sema.zirCtz", .{});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}
74228695
7423fn zirPopCount(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8696fn 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,26 +8723,10 @@ fn zirDivFloor(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
7450 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivFloor", .{});8723 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivFloor", .{});
7451}8724}
74528725
7453fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8726fn 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
7459fn 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
7465fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7466 return sema.zirArithmetic(block, inst);
7467}
7468
7469fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7470 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;8727 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7471 const src = inst_data.src();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}
74748731
7475fn zirShrExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8732fn 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,6 +8747,29 @@ fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
7490 return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});8747 return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});
7491}8748}
74928749
8750/// Returns `true` if the type was a comptime_int.
8751fn 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
8759fn 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
7493fn checkAtomicOperandType(8773fn checkAtomicOperandType(
7494 sema: *Sema,8774 sema: *Sema,
7495 block: *Scope.Block,8775 block: *Scope.Block,
...@@ -7501,7 +8781,7 @@ fn checkAtomicOperandType(...@@ -7501,7 +8781,7 @@ fn checkAtomicOperandType(
7501 const max_atomic_bits = target_util.largestAtomicBits(target);8781 const max_atomic_bits = target_util.largestAtomicBits(target);
7502 const int_ty = switch (ty.zigTypeTag()) {8782 const int_ty = switch (ty.zigTypeTag()) {
7503 .Int => ty,8783 .Int => ty,
7504 .Enum => ty.enumTagType(&buffer),8784 .Enum => ty.intTagType(&buffer),
7505 .Float => {8785 .Float => {
7506 const bit_count = ty.floatBits(target);8786 const bit_count = ty.floatBits(target);
7507 if (bit_count > max_atomic_bits) {8787 if (bit_count > max_atomic_bits) {
...@@ -7515,12 +8795,16 @@ fn checkAtomicOperandType(...@@ -7515,12 +8795,16 @@ fn checkAtomicOperandType(
7515 return;8795 return;
7516 },8796 },
7517 .Bool => return, // Will be treated as `u8`.8797 .Bool => return, // Will be treated as `u8`.
7518 else => return sema.mod.fail(8798 else => {
7519 &block.base,8799 if (ty.isPtrAtRuntime()) return;
7520 ty_src,8800
7521 "expected bool, integer, float, enum, or pointer type; found {}",8801 return sema.mod.fail(
7522 .{ty},8802 &block.base,
7523 ),8803 ty_src,
8804 "expected bool, integer, float, enum, or pointer type; found {}",
8805 .{ty},
8806 );
8807 },
7524 };8808 };
7525 const bit_count = int_ty.intInfo(target).bits;8809 const bit_count = int_ty.intInfo(target).bits;
7526 if (bit_count > max_atomic_bits) {8810 if (bit_count > max_atomic_bits) {
...@@ -7533,6 +8817,31 @@ fn checkAtomicOperandType(...@@ -7533,6 +8817,31 @@ fn checkAtomicOperandType(
7533 }8817 }
7534}8818}
75358819
8820fn 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
7536fn resolveAtomicOrder(8845fn resolveAtomicOrder(
7537 sema: *Sema,8846 sema: *Sema,
7538 block: *Scope.Block,8847 block: *Scope.Block,
...@@ -7546,6 +8855,19 @@ fn resolveAtomicOrder(...@@ -7546,6 +8855,19 @@ fn resolveAtomicOrder(
7546 return val.toEnum(std.builtin.AtomicOrder);8855 return val.toEnum(std.builtin.AtomicOrder);
7547}8856}
75488857
8858fn 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
7549fn zirCmpxchg(8871fn zirCmpxchg(
7550 sema: *Sema,8872 sema: *Sema,
7551 block: *Scope.Block,8873 block: *Scope.Block,
...@@ -7604,6 +8926,8 @@ fn zirCmpxchg(...@@ -7604,6 +8926,8 @@ fn zirCmpxchg(
7604 if (try sema.resolveMaybeUndefVal(block, expected_src, expected_value)) |expected_val| {8926 if (try sema.resolveMaybeUndefVal(block, expected_src, expected_value)) |expected_val| {
7605 if (try sema.resolveMaybeUndefVal(block, new_value_src, new_value)) |new_val| {8927 if (try sema.resolveMaybeUndefVal(block, new_value_src, new_value)) |new_val| {
7606 if (expected_val.isUndef() or new_val.isUndef()) {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 return sema.addConstUndef(result_ty);8931 return sema.addConstUndef(result_ty);
7608 }8932 }
7609 const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src;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,20 +8985,160 @@ fn zirSelect(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro
76618985
7662fn zirAtomicLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8986fn zirAtomicLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7663 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;8987 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7664 const src = inst_data.src();8988 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
7665 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicLoad", .{});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}
76679030
7668fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9031fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9032 const mod = sema.mod;
7669 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;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 const src = inst_data.src();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}
76739109
7674fn zirAtomicStore(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9110fn zirAtomicStore(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
7675 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;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 const src = inst_data.src();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}
76799143
7680fn zirMulAdd(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9144fn 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,16 +9171,115 @@ fn zirMaximum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
7707 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{});9171 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{});
7708}9172}
77099173
7710fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9174fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
7711 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;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 const src = inst_data.src();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}
77159238
7716fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9239fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
7717 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;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 const src = inst_data.src();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}
77219284
7722fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9285fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -7761,7 +9324,6 @@ fn zirVarExtended(...@@ -7761,7 +9324,6 @@ fn zirVarExtended(
7761 const mut_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at mut token9324 const mut_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at mut token
7762 const init_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at init expr9325 const init_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at init expr
7763 const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small);9326 const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small);
7764 const var_ty = try sema.resolveType(block, ty_src, extra.data.var_type);
77659327
7766 var extra_index: usize = extra.end;9328 var extra_index: usize = extra.end;
77679329
...@@ -7781,19 +9343,29 @@ fn zirVarExtended(...@@ -7781,19 +9343,29 @@ fn zirVarExtended(
7781 // break :blk align_tv.val;9343 // break :blk align_tv.val;
7782 //} else Value.initTag(.null_value);9344 //} else Value.initTag(.null_value);
77839345
7784 const init_val: Value = if (small.has_init) blk: {9346 const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: {
7785 const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);9347 const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
7786 extra_index += 1;9348 extra_index += 1;
7787 const init_air_inst = sema.resolveInst(init_ref);9349 break :blk sema.resolveInst(init_ref);
7788 break :blk (try sema.resolveMaybeUndefVal(block, init_src, init_air_inst)) orelse9350 } 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 return sema.failWithNeededComptime(block, init_src);9365 return sema.failWithNeededComptime(block, init_src);
7790 } else Value.initTag(.unreachable_value);9366 } else Value.initTag(.unreachable_value);
77919367
7792 if (!var_ty.isValidVarType(small.is_extern)) {9368 try sema.validateVarType(block, mut_src, var_ty, small.is_extern);
7793 return sema.mod.fail(&block.base, mut_src, "variable of type '{}' must be const", .{
7794 var_ty,
7795 });
7796 }
77979369
7798 if (lib_name != null) {9370 if (lib_name != null) {
7799 // Look at the sema code for functions which has this logic, it just needs to9371 // Look at the sema code for functions which has this logic, it just needs to
...@@ -7890,7 +9462,10 @@ fn zirCUndef(...@@ -7890,7 +9462,10 @@ fn zirCUndef(
7890) CompileError!Air.Inst.Ref {9462) CompileError!Air.Inst.Ref {
7891 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;9463 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
7892 const src: LazySrcLoc = .{ .node_offset = extra.node };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}
78959470
7896fn zirCInclude(9471fn zirCInclude(
...@@ -7900,7 +9475,10 @@ fn zirCInclude(...@@ -7900,7 +9475,10 @@ fn zirCInclude(
7900) CompileError!Air.Inst.Ref {9475) CompileError!Air.Inst.Ref {
7901 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;9476 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
7902 const src: LazySrcLoc = .{ .node_offset = extra.node };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}
79059483
7906fn zirCDefine(9484fn zirCDefine(
...@@ -7910,7 +9488,15 @@ fn zirCDefine(...@@ -7910,7 +9488,15 @@ fn zirCDefine(
7910) CompileError!Air.Inst.Ref {9488) CompileError!Air.Inst.Ref {
7911 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;9489 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
7912 const src: LazySrcLoc = .{ .node_offset = extra.node };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}
79159501
7916fn zirWasmMemorySize(9502fn zirWasmMemorySize(
...@@ -7956,17 +9542,54 @@ fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void...@@ -7956,17 +9542,54 @@ fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void
7956 try sema.requireFunctionBlock(block, src);9542 try sema.requireFunctionBlock(block, src);
7957}9543}
79589544
7959fn requireIntegerType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !bool {9545/// Emit a compile error if type cannot be used for a runtime variable.
7960 switch (ty.zigTypeTag()) {9546fn validateVarType(
7961 .ComptimeInt => return true,9547 sema: *Sema,
7962 .Int => return false,9548 block: *Scope.Block,
7963 else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}),9549 src: LazySrcLoc,
7964 }9550 var_ty: Type,
7965}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(),
79669584
7967fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {9585 .Fn => @panic("TODO fn validateVarType"),
7968 if (!ty.isValidVarType(false)) {9586 .Struct, .Union => {
7969 return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty});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}
79729595
...@@ -7990,6 +9613,7 @@ fn addSafetyCheck(...@@ -7990,6 +9613,7 @@ fn addSafetyCheck(
7990 var fail_block: Scope.Block = .{9613 var fail_block: Scope.Block = .{
7991 .parent = parent_block,9614 .parent = parent_block,
7992 .sema = sema,9615 .sema = sema,
9616 .wip_capture_scope = parent_block.wip_capture_scope,
7993 .src_decl = parent_block.src_decl,9617 .src_decl = parent_block.src_decl,
7994 .instructions = .{},9618 .instructions = .{},
7995 .inlining = parent_block.inlining,9619 .inlining = parent_block.inlining,
...@@ -8067,7 +9691,10 @@ fn panicWithMsg(...@@ -8067,7 +9691,10 @@ fn panicWithMsg(
8067 const panic_fn = try sema.getBuiltin(block, src, "panic");9691 const panic_fn = try sema.getBuiltin(block, src, "panic");
8068 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");9692 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
8069 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);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 const null_stack_trace = try sema.addConstant(9698 const null_stack_trace = try sema.addConstant(
8072 try Module.optionalType(arena, ptr_stack_trace_ty),9699 try Module.optionalType(arena, ptr_stack_trace_ty),
8073 Value.initTag(.null_value),9700 Value.initTag(.null_value),
...@@ -8083,7 +9710,7 @@ fn safetyPanic(...@@ -8083,7 +9710,7 @@ fn safetyPanic(
8083 block: *Scope.Block,9710 block: *Scope.Block,
8084 src: LazySrcLoc,9711 src: LazySrcLoc,
8085 panic_id: PanicId,9712 panic_id: PanicId,
8086) !Zir.Inst.Index {9713) CompileError!Zir.Inst.Index {
8087 const msg = switch (panic_id) {9714 const msg = switch (panic_id) {
8088 .unreach => "reached unreachable code",9715 .unreach => "reached unreachable code",
8089 .unwrap_null => "attempt to use null value",9716 .unwrap_null => "attempt to use null value",
...@@ -8151,7 +9778,7 @@ fn fieldVal(...@@ -8151,7 +9778,7 @@ fn fieldVal(
8151 .Pointer => switch (object_ty.ptrSize()) {9778 .Pointer => switch (object_ty.ptrSize()) {
8152 .Slice => {9779 .Slice => {
8153 if (mem.eql(u8, field_name, "ptr")) {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 const result_ty = object_ty.slicePtrFieldType(buf);9782 const result_ty = object_ty.slicePtrFieldType(buf);
8156 if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {9783 if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
8157 if (val.isUndef()) return sema.addConstUndef(result_ty);9784 if (val.isUndef()) return sema.addConstUndef(result_ty);
...@@ -8185,21 +9812,32 @@ fn fieldVal(...@@ -8185,21 +9812,32 @@ fn fieldVal(
8185 }9812 }
8186 },9813 },
8187 .One => {9814 .One => {
8188 const elem_ty = object_ty.elemType();9815 const ptr_child = object_ty.elemType();
8189 if (elem_ty.zigTypeTag() == .Array) {9816 switch (ptr_child.zigTypeTag()) {
8190 if (mem.eql(u8, field_name, "len")) {9817 .Array => {
8191 return sema.addConstant(9818 if (mem.eql(u8, field_name, "len")) {
8192 Type.initTag(.comptime_int),9819 return sema.addConstant(
8193 try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()),9820 Type.initTag(.comptime_int),
8194 );9821 try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
8195 } else {9822 );
8196 return mod.fail(9823 } else {
8197 &block.base,9824 return mod.fail(
8198 field_name_src,9825 &block.base,
8199 "no member named '{s}' in '{}'",9826 field_name_src,
8200 .{ field_name, object_ty },9827 "no member named '{s}' in '{}'",
8201 );9828 .{ field_name, object_ty },
8202 }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 .Many, .C => {},9843 .Many, .C => {},
...@@ -8323,9 +9961,8 @@ fn fieldPtr(...@@ -8323,9 +9961,8 @@ fn fieldPtr(
8323 );9961 );
8324 }9962 }
8325 },9963 },
8326 .Pointer => {9964 .Pointer => switch (object_ty.ptrSize()) {
8327 const ptr_child = object_ty.elemType();9965 .Slice => {
8328 if (ptr_child.isSlice()) {
8329 // Here for the ptr and len fields what we need to do is the situation9966 // Here for the ptr and len fields what we need to do is the situation
8330 // when a temporary has its address taken, e.g. `&a[c..d].len`.9967 // when a temporary has its address taken, e.g. `&a[c..d].len`.
8331 // This value may be known at compile-time or runtime. In the former9968 // This value may be known at compile-time or runtime. In the former
...@@ -8355,26 +9992,39 @@ fn fieldPtr(...@@ -8355,26 +9992,39 @@ fn fieldPtr(
8355 .{ field_name, object_ty },9992 .{ field_name, object_ty },
8356 );9993 );
8357 }9994 }
8358 } else switch (ptr_child.zigTypeTag()) {9995 },
8359 .Array => {9996 .One => {
8360 if (mem.eql(u8, field_name, "len")) {9997 const ptr_child = object_ty.elemType();
8361 var anon_decl = try block.startAnonDecl();9998 switch (ptr_child.zigTypeTag()) {
8362 defer anon_decl.deinit();9999 .Array => {
8363 return sema.analyzeDeclRef(try anon_decl.finish(10000 if (mem.eql(u8, field_name, "len")) {
8364 Type.initTag(.comptime_int),10001 var anon_decl = try block.startAnonDecl();
8365 try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),10002 defer anon_decl.deinit();
8366 ));10003 return sema.analyzeDeclRef(try anon_decl.finish(
8367 } else {10004 Type.initTag(.comptime_int),
8368 return mod.fail(10005 try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
8369 &block.base,10006 ));
8370 field_name_src,10007 } else {
8371 "no member named '{s}' in '{}'",10008 return mod.fail(
8372 .{ field_name, object_ty },10009 &block.base,
8373 );10010 field_name_src,
8374 }10011 "no member named '{s}' in '{}'",
8375 },10012 .{ field_name, object_ty },
8376 else => {},10013 );
8377 }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 .Type => {10029 .Type => {
8380 _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);10030 _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);
...@@ -8460,11 +10110,153 @@ fn fieldPtr(...@@ -8460,11 +10110,153 @@ fn fieldPtr(
8460 else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),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),10113 .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),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
10120fn 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 else => {},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}
846910261
8470fn namespaceLookup(10262fn namespaceLookup(
...@@ -8516,13 +10308,19 @@ fn structFieldPtr(...@@ -8516,13 +10308,19 @@ fn structFieldPtr(
8516 const arena = sema.arena;10308 const arena = sema.arena;
8517 assert(unresolved_struct_ty.zigTypeTag() == .Struct);10309 assert(unresolved_struct_ty.zigTypeTag() == .Struct);
851810310
10311 const struct_ptr_ty = sema.typeOf(struct_ptr);
8519 const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);10312 const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);
8520 const struct_obj = struct_ty.castTag(.@"struct").?.data;10313 const struct_obj = struct_ty.castTag(.@"struct").?.data;
852110314
8522 const field_index = struct_obj.fields.getIndex(field_name) orelse10315 const field_index_big = struct_obj.fields.getIndex(field_name) orelse
8523 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);10316 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
10317 const field_index = @intCast(u32, field_index_big);
8524 const field = struct_obj.fields.values()[field_index];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 });
852610324
8527 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {10325 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
8528 return sema.addConstant(10326 return sema.addConstant(
...@@ -8535,31 +10333,7 @@ fn structFieldPtr(...@@ -8535,31 +10333,7 @@ fn structFieldPtr(
8535 }10333 }
853610334
8537 try sema.requireRuntimeBlock(block, src);10335 try sema.requireRuntimeBlock(block, src);
8538 const tag: Air.Inst.Tag = switch (field_index) {10336 return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
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 });
8563}10337}
856410338
8565fn structFieldVal(10339fn structFieldVal(
...@@ -8609,18 +10383,23 @@ fn unionFieldPtr(...@@ -8609,18 +10383,23 @@ fn unionFieldPtr(
8609 field_name_src: LazySrcLoc,10383 field_name_src: LazySrcLoc,
8610 unresolved_union_ty: Type,10384 unresolved_union_ty: Type,
8611) CompileError!Air.Inst.Ref {10385) CompileError!Air.Inst.Ref {
8612 const mod = sema.mod;
8613 const arena = sema.arena;10386 const arena = sema.arena;
8614 assert(unresolved_union_ty.zigTypeTag() == .Union);10387 assert(unresolved_union_ty.zigTypeTag() == .Union);
861510388
10389 const union_ptr_ty = sema.typeOf(union_ptr);
8616 const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);10390 const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
8617 const union_obj = union_ty.cast(Type.Payload.Union).?.data;10391 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
861810392
8619 const field_index = union_obj.fields.getIndex(field_name) orelse10393 const field_index_big = union_obj.fields.getIndex(field_name) orelse
8620 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);10394 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
10395 const field_index = @intCast(u32, field_index_big);
862110396
8622 const field = union_obj.fields.values()[field_index];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 });
862410403
8625 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {10404 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {
8626 // TODO detect inactive union field and emit compile error10405 // TODO detect inactive union field and emit compile error
...@@ -8634,7 +10413,7 @@ fn unionFieldPtr(...@@ -8634,7 +10413,7 @@ fn unionFieldPtr(
8634 }10413 }
863510414
8636 try sema.requireRuntimeBlock(block, src);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}
863910418
8640fn unionFieldVal(10419fn unionFieldVal(
...@@ -8796,10 +10575,11 @@ fn elemPtrArray(...@@ -8796,10 +10575,11 @@ fn elemPtrArray(
8796) CompileError!Air.Inst.Ref {10575) CompileError!Air.Inst.Ref {
8797 const array_ptr_ty = sema.typeOf(array_ptr);10576 const array_ptr_ty = sema.typeOf(array_ptr);
8798 const pointee_type = array_ptr_ty.elemType().elemType();10577 const pointee_type = array_ptr_ty.elemType().elemType();
8799 const result_ty = if (array_ptr_ty.ptrIsMutable())10578 const result_ty = try Type.ptr(sema.arena, .{
8800 try Type.Tag.single_mut_pointer.create(sema.arena, pointee_type)10579 .pointee_type = pointee_type,
8801 else10580 .mutable = array_ptr_ty.ptrIsMutable(),
8802 try Type.Tag.single_const_pointer.create(sema.arena, pointee_type);10581 .@"addrspace" = array_ptr_ty.ptrAddressSpace(),
10582 });
880310583
8804 if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {10584 if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
8805 if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {10585 if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {
...@@ -8845,14 +10625,14 @@ fn coerce(...@@ -8845,14 +10625,14 @@ fn coerce(
8845 if (dest_type.eql(inst_ty))10625 if (dest_type.eql(inst_ty))
8846 return inst;10626 return inst;
884710627
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 if (in_memory_result == .ok) {10632 if (in_memory_result == .ok) {
8850 return sema.bitcast(block, dest_type, inst, inst_src);10633 return sema.bitcast(block, dest_type, inst, inst_src);
8851 }10634 }
885210635
8853 const mod = sema.mod;
8854 const arena = sema.arena;
8855
8856 // undefined to anything10636 // undefined to anything
8857 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {10637 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
8858 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {10638 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
...@@ -8887,11 +10667,13 @@ fn coerce(...@@ -8887,11 +10667,13 @@ fn coerce(
8887 const array_type = inst_ty.elemType();10667 const array_type = inst_ty.elemType();
8888 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;10668 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
8889 const array_elem_type = array_type.elemType();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 if (inst_ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;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;
889210674
8893 const dst_elem_type = dest_type.elemType();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 .ok => {},10677 .ok => {},
8896 .no_match => break :src_array_ptr,10678 .no_match => break :src_array_ptr,
8897 }10679 }
...@@ -8932,7 +10714,7 @@ fn coerce(...@@ -8932,7 +10714,7 @@ fn coerce(
8932 const src_info = inst_ty.intInfo(target);10714 const src_info = inst_ty.intInfo(target);
8933 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or10715 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
8934 // small enough unsigned ints can get casted to large enough signed ints10716 // 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 try sema.requireRuntimeBlock(block, inst_src);10719 try sema.requireRuntimeBlock(block, inst_src);
8938 return block.addTyOp(.intcast, dest_type, inst);10720 return block.addTyOp(.intcast, dest_type, inst);
...@@ -8948,13 +10730,13 @@ fn coerce(...@@ -8948,13 +10730,13 @@ fn coerce(
8948 const dst_bits = dest_type.floatBits(target);10730 const dst_bits = dest_type.floatBits(target);
8949 if (dst_bits >= src_bits) {10731 if (dst_bits >= src_bits) {
8950 try sema.requireRuntimeBlock(block, inst_src);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 => {10737 .Enum => switch (inst_ty.zigTypeTag()) {
8956 // enum literal to enum10738 .EnumLiteral => {
8957 if (inst_ty.zigTypeTag() == .EnumLiteral) {10739 // enum literal to enum
8958 const val = try sema.resolveConstValue(block, inst_src, inst);10740 const val = try sema.resolveConstValue(block, inst_src, inst);
8959 const bytes = val.castTag(.enum_literal).?.data;10741 const bytes = val.castTag(.enum_literal).?.data;
8960 const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type);10742 const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type);
...@@ -8981,7 +10763,15 @@ fn coerce(...@@ -8981,7 +10763,15 @@ fn coerce(
8981 resolved_dest_type,10763 resolved_dest_type,
8982 try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)),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 .ErrorUnion => {10776 .ErrorUnion => {
8987 // T to E!T or E to E!T10777 // T to E!T or E to E!T
...@@ -8998,10 +10788,92 @@ const InMemoryCoercionResult = enum {...@@ -8998,10 +10788,92 @@ const InMemoryCoercionResult = enum {
8998 no_match,10788 no_match,
8999};10789};
900010790
9001fn 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
10802fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
9002 if (dest_type.eql(src_type))10803 if (dest_type.eql(src_type))
9003 return .ok;10804 return .ok;
900410805
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 // TODO: implement more of this function10877 // TODO: implement more of this function
900610878
9007 return .no_match;10879 return .no_match;
...@@ -9021,33 +10893,56 @@ fn coerceNum(...@@ -9021,33 +10893,56 @@ fn coerceNum(
902110893
9022 const target = sema.mod.getTarget();10894 const target = sema.mod.getTarget();
902310895
9024 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {10896 switch (dst_zig_tag) {
9025 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {10897 .ComptimeInt, .Int => switch (src_zig_tag) {
9026 if (val.floatHasFraction()) {10898 .Float, .ComptimeFloat => {
9027 return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty });10899 if (val.floatHasFraction()) {
9028 }10900 return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_type });
9029 return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});10901 }
9030 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {10902 return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});
9031 if (!val.intFitsInType(dest_type, target)) {10903 },
9032 return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });10904 .Int, .ComptimeInt => {
9033 }10905 if (!val.intFitsInType(dest_type, target)) {
9034 return try sema.addConstant(dest_type, val);10906 return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });
9035 }10907 }
9036 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {10908 return try sema.addConstant(dest_type, val);
9037 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {10909 },
9038 const res = val.floatCast(sema.arena, dest_type) catch |err| switch (err) {10910 else => {},
9039 error.Overflow => return sema.mod.fail(10911 },
9040 &block.base,10912 .ComptimeFloat, .Float => switch (src_zig_tag) {
9041 inst_src,10913 .ComptimeFloat => {
9042 "cast of value {} to type '{}' loses information",10914 const result_val = try val.floatCast(sema.arena, dest_type);
9043 .{ val, dest_type },10915 return try sema.addConstant(dest_type, result_val);
9044 ),10916 },
9045 error.OutOfMemory => return error.OutOfMemory,10917 .Float => {
9046 };10918 const result_val = try val.floatCast(sema.arena, dest_type);
9047 return try sema.addConstant(dest_type, res);10919 if (!val.eql(result_val, dest_type)) {
9048 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {10920 return sema.mod.fail(
9049 return sema.mod.fail(&block.base, inst_src, "TODO int to float", .{});10921 &block.base,
9050 }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 return null;10947 return null;
9053}10948}
...@@ -9067,66 +10962,97 @@ fn coerceVarArgParam(...@@ -9067,66 +10962,97 @@ fn coerceVarArgParam(
9067 return inst;10962 return inst;
9068}10963}
906910964
10965// TODO migrate callsites to use storePtr2 instead.
9070fn storePtr(10966fn storePtr(
9071 sema: *Sema,10967 sema: *Sema,
9072 block: *Scope.Block,10968 block: *Scope.Block,
9073 src: LazySrcLoc,10969 src: LazySrcLoc,
9074 ptr: Air.Inst.Ref,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
10976fn 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) !void {10985) !void {
9077 const ptr_ty = sema.typeOf(ptr);10986 const ptr_ty = sema.typeOf(ptr);
9078 if (ptr_ty.isConstPtr())10987 if (ptr_ty.isConstPtr())
9079 return sema.mod.fail(&block.base, src, "cannot assign to constant", .{});10988 return sema.mod.fail(&block.base, src, "cannot assign to constant", .{});
908010989
9081 const elem_ty = ptr_ty.elemType();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 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)10992 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
9084 return;10993 return;
908510994
9086 if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| {10995 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
9087 if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| {10996 const operand_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse
9088 const const_val = (try sema.resolveMaybeUndefVal(block, src, value)) orelse10997 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
9089 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});10998 if (ptr_val.tag() == .decl_ref_mut) {
909010999 try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
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();
9123 return;11000 return;
9124 }11001 }
9125 }11002 break :rs operand_src;
11003 } else ptr_src;
11004
9126 // TODO handle if the element type requires comptime11005 // TODO handle if the element type requires comptime
912711006
9128 try sema.requireRuntimeBlock(block, src);11007 try sema.requireRuntimeBlock(block, runtime_src);
9129 _ = try block.addBinOp(.store, ptr, value);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.
11013fn 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}
913111057
9132fn bitcast(11058fn bitcast(
...@@ -9171,7 +11097,8 @@ fn coerceArrayPtrToMany(...@@ -9171,7 +11097,8 @@ fn coerceArrayPtrToMany(
9171 // The comptime Value representation is compatible with both types.11097 // The comptime Value representation is compatible with both types.
9172 return sema.addConstant(dest_type, val);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}
917611103
9177fn analyzeDeclVal(11104fn analyzeDeclVal(
...@@ -9211,11 +11138,19 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {...@@ -9211,11 +11138,19 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
9211 const decl_tv = try decl.typedValue();11138 const decl_tv = try decl.typedValue();
9212 if (decl_tv.val.castTag(.variable)) |payload| {11139 if (decl_tv.val.castTag(.variable)) |payload| {
9213 const variable = payload.data;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 return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl));11146 return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl));
9216 }11147 }
9217 return sema.addConstant(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 try Value.Tag.decl_ref.create(sema.arena, decl),11154 try Value.Tag.decl_ref.create(sema.arena, decl),
9220 );11155 );
9221}11156}
...@@ -9232,14 +11167,22 @@ fn analyzeRef(...@@ -9232,14 +11167,22 @@ fn analyzeRef(
9232 var anon_decl = try block.startAnonDecl();11167 var anon_decl = try block.startAnonDecl();
9233 defer anon_decl.deinit();11168 defer anon_decl.deinit();
9234 return sema.analyzeDeclRef(try anon_decl.finish(11169 return sema.analyzeDeclRef(try anon_decl.finish(
9235 operand_ty,11170 try operand_ty.copy(anon_decl.arena()),
9236 try val.copy(anon_decl.arena()),11171 try val.copy(anon_decl.arena()),
9237 ));11172 ));
9238 }11173 }
923911174
9240 try sema.requireRuntimeBlock(block, src);11175 try sema.requireRuntimeBlock(block, src);
9241 const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);11176 const address_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local);
9242 const mut_ptr_type = try Module.simplePtrType(sema.arena, operand_ty, true, .One);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 const alloc = try block.addTy(.alloc, mut_ptr_type);11186 const alloc = try block.addTy(.alloc, mut_ptr_type);
9244 try sema.storePtr(block, src, alloc, operand);11187 try sema.storePtr(block, src, alloc, operand);
924511188
...@@ -9395,18 +11338,16 @@ fn analyzeSlice(...@@ -9395,18 +11338,16 @@ fn analyzeSlice(
9395 }11338 }
9396 }11339 }
9397 }11340 }
9398 const return_type = try Module.ptrType(11341 const return_type = try Type.ptr(sema.arena, .{
9399 sema.arena,11342 .pointee_type = return_elem_type,
9400 return_elem_type,11343 .sentinel = if (end_opt == .none) slice_sentinel else null,
9401 if (end_opt == .none) slice_sentinel else null,11344 .@"align" = 0, // TODO alignment
9402 0, // TODO alignment11345 .@"addrspace" = if (ptr_child.zigTypeTag() == .Pointer) ptr_child.ptrAddressSpace() else .generic,
9403 0,11346 .mutable = !ptr_child.isConstPtr(),
9404 0,11347 .@"allowzero" = ptr_child.isAllowzeroPtr(),
9405 !ptr_child.isConstPtr(),11348 .@"volatile" = ptr_child.isVolatilePtr(),
9406 ptr_child.isAllowzeroPtr(),11349 .size = return_ptr_size,
9407 ptr_child.isVolatilePtr(),11350 });
9408 return_ptr_size,
9409 );
9410 _ = return_type;11351 _ = return_type;
941111352
9412 return sema.mod.fail(&block.base, src, "TODO implement analysis of slice", .{});11353 return sema.mod.fail(&block.base, src, "TODO implement analysis of slice", .{});
...@@ -9508,11 +11449,11 @@ fn cmpNumeric(...@@ -9508,11 +11449,11 @@ fn cmpNumeric(
9508 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|11449 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
9509 lhs_val.compareWithZero(.lt)11450 lhs_val.compareWithZero(.lt)
9510 else11451 else
9511 (lhs_ty.isFloat() or lhs_ty.isSignedInt());11452 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt());
9512 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|11453 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
9513 rhs_val.compareWithZero(.lt)11454 rhs_val.compareWithZero(.lt)
9514 else11455 else
9515 (rhs_ty.isFloat() or rhs_ty.isSignedInt());11456 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt());
9516 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;11457 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
951711458
9518 var dest_float_type: ?Type = null;11459 var dest_float_type: ?Type = null;
...@@ -9692,6 +11633,20 @@ fn wrapErrorUnion(...@@ -9692,6 +11633,20 @@ fn wrapErrorUnion(
9692 }11633 }
9693}11634}
969411635
11636fn 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
9695fn resolvePeerTypes(11650fn resolvePeerTypes(
9696 sema: *Sema,11651 sema: *Sema,
9697 block: *Scope.Block,11652 block: *Scope.Block,
...@@ -9738,7 +11693,7 @@ fn resolvePeerTypes(...@@ -9738,7 +11693,7 @@ fn resolvePeerTypes(
9738 }11693 }
9739 continue;11694 continue;
9740 }11695 }
9741 if (chosen_ty.isFloat() and candidate_ty.isFloat()) {11696 if (chosen_ty.isRuntimeFloat() and candidate_ty.isRuntimeFloat()) {
9742 if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {11697 if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
9743 chosen = candidate;11698 chosen = candidate;
9744 chosen_i = candidate_i + 1;11699 chosen_i = candidate_i + 1;
...@@ -9756,13 +11711,13 @@ fn resolvePeerTypes(...@@ -9756,13 +11711,13 @@ fn resolvePeerTypes(
9756 continue;11711 continue;
9757 }11712 }
975811713
9759 if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isFloat()) {11714 if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isRuntimeFloat()) {
9760 chosen = candidate;11715 chosen = candidate;
9761 chosen_i = candidate_i + 1;11716 chosen_i = candidate_i + 1;
9762 continue;11717 continue;
9763 }11718 }
976411719
9765 if (chosen_ty.isFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {11720 if (chosen_ty.isRuntimeFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {
9766 continue;11721 continue;
9767 }11722 }
976811723
...@@ -9813,9 +11768,6 @@ pub fn resolveTypeLayout(...@@ -9813,9 +11768,6 @@ pub fn resolveTypeLayout(
9813 ty: Type,11768 ty: Type,
9814) CompileError!void {11769) CompileError!void {
9815 switch (ty.zigTypeTag()) {11770 switch (ty.zigTypeTag()) {
9816 .Pointer => {
9817 return sema.resolveTypeLayout(block, src, ty.elemType());
9818 },
9819 .Struct => {11771 .Struct => {
9820 const resolved_ty = try sema.resolveTypeFields(block, src, ty);11772 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
9821 const struct_obj = resolved_ty.castTag(.@"struct").?.data;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,6 +11815,10 @@ pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty:
9863 sema.namespace = &struct_obj.namespace;11815 sema.namespace = &struct_obj.namespace;
9864 defer sema.namespace = prev_namespace;11816 defer sema.namespace = prev_namespace;
986511817
11818 const old_src = block.src_decl;
11819 defer block.src_decl = old_src;
11820 block.src_decl = struct_obj.owner_decl;
11821
9866 struct_obj.status = .field_types_wip;11822 struct_obj.status = .field_types_wip;
9867 try sema.analyzeStructFields(block, struct_obj);11823 try sema.analyzeStructFields(block, struct_obj);
9868 struct_obj.status = .have_field_types;11824 struct_obj.status = .have_field_types;
...@@ -9881,6 +11837,10 @@ pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty:...@@ -9881,6 +11837,10 @@ pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty:
9881 sema.namespace = &union_obj.namespace;11837 sema.namespace = &union_obj.namespace;
9882 defer sema.namespace = prev_namespace;11838 defer sema.namespace = prev_namespace;
988311839
11840 const old_src = block.src_decl;
11841 defer block.src_decl = old_src;
11842 block.src_decl = union_obj.owner_decl;
11843
9884 union_obj.status = .field_types_wip;11844 union_obj.status = .field_types_wip;
9885 try sema.analyzeUnionFields(block, union_obj);11845 try sema.analyzeUnionFields(block, union_obj);
9886 union_obj.status = .have_field_types;11846 union_obj.status = .have_field_types;
...@@ -9907,6 +11867,7 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type...@@ -9907,6 +11867,7 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type
9907 .atomic_order => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrder"),11867 .atomic_order => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrder"),
9908 .atomic_rmw_op => return sema.resolveBuiltinTypeFields(block, src, "AtomicRmwOp"),11868 .atomic_rmw_op => return sema.resolveBuiltinTypeFields(block, src, "AtomicRmwOp"),
9909 .calling_convention => return sema.resolveBuiltinTypeFields(block, src, "CallingConvention"),11869 .calling_convention => return sema.resolveBuiltinTypeFields(block, src, "CallingConvention"),
11870 .address_space => return sema.resolveBuiltinTypeFields(block, src, "AddressSpace"),
9910 .float_mode => return sema.resolveBuiltinTypeFields(block, src, "FloatMode"),11871 .float_mode => return sema.resolveBuiltinTypeFields(block, src, "FloatMode"),
9911 .reduce_op => return sema.resolveBuiltinTypeFields(block, src, "ReduceOp"),11872 .reduce_op => return sema.resolveBuiltinTypeFields(block, src, "ReduceOp"),
9912 .call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"),11873 .call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"),
...@@ -10081,9 +12042,11 @@ fn analyzeUnionFields(...@@ -10081,9 +12042,11 @@ fn analyzeUnionFields(
10081 const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset };12042 const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset };
10082 extra_index += @boolToInt(small.has_src_node);12043 extra_index += @boolToInt(small.has_src_node);
1008312044
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 extra_index += 1;12047 extra_index += 1;
10086 }12048 break :blk ty_ref;
12049 } else .none;
1008712050
10088 const body_len = if (small.has_body_len) blk: {12051 const body_len = if (small.has_body_len) blk: {
10089 const body_len = zir.extra[extra_index];12052 const body_len = zir.extra[extra_index];
...@@ -10118,11 +12081,33 @@ fn analyzeUnionFields(...@@ -10118,11 +12081,33 @@ fn analyzeUnionFields(
10118 var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa);12081 var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa);
10119 defer union_obj.owner_decl.value_arena.?.* = decl_arena.state;12082 defer union_obj.owner_decl.value_arena.?.* = decl_arena.state;
1012012083
10121 try union_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len);12084 try union_obj.fields.ensureTotalCapacity(&decl_arena.allocator, fields_len);
1012212085
10123 if (body.len != 0) {12086 if (body.len != 0) {
10124 _ = try sema.analyzeBody(block, body);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 }
1012612111
10127 const bits_per_field = 4;12112 const bits_per_field = 4;
10128 const fields_per_u32 = 32 / bits_per_field;12113 const fields_per_u32 = 32 / bits_per_field;
...@@ -10161,12 +12146,25 @@ fn analyzeUnionFields(...@@ -10161,12 +12146,25 @@ fn analyzeUnionFields(
10161 break :blk align_ref;12146 break :blk align_ref;
10162 } else .none;12147 } else .none;
1016312148
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 extra_index += 1;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 }
1016712161
10168 // This string needs to outlive the ZIR code.12162 // This string needs to outlive the ZIR code.
10169 const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);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 const field_ty: Type = if (field_type_ref == .none)12168 const field_ty: Type = if (field_type_ref == .none)
10171 Type.initTag(.void)12169 Type.initTag(.void)
10172 else12170 else
...@@ -10188,10 +12186,84 @@ fn analyzeUnionFields(...@@ -10188,10 +12186,84 @@ fn analyzeUnionFields(
10188 // But only resolve the source location if we need to emit a compile error.12186 // But only resolve the source location if we need to emit a compile error.
10189 const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val;12187 const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val;
10190 gop.value_ptr.abi_align = try abi_align_val.copy(&decl_arena.allocator);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
12195fn 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
12236fn 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);
1019312257
10194 // TODO resolve the union tag_type_ref12258 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}
1019612268
10197fn getBuiltin(12269fn getBuiltin(
...@@ -10231,7 +12303,7 @@ fn getBuiltinType(...@@ -10231,7 +12303,7 @@ fn getBuiltinType(
10231}12303}
1023212304
10233/// There is another implementation of this in `Type.onePossibleValue`. This one12305/// There is another implementation of this in `Type.onePossibleValue`. This one
10234/// in `Sema` is for calling during semantic analysis, and peforms field resolution12306/// in `Sema` is for calling during semantic analysis, and performs field resolution
10235/// to get the answer. The one in `Type` is for calling during codegen and asserts12307/// to get the answer. The one in `Type` is for calling during codegen and asserts
10236/// that the types are already resolved.12308/// that the types are already resolved.
10237fn typeHasOnePossibleValue(12309fn typeHasOnePossibleValue(
...@@ -10301,6 +12373,7 @@ fn typeHasOnePossibleValue(...@@ -10301,6 +12373,7 @@ fn typeHasOnePossibleValue(
10301 .atomic_order,12373 .atomic_order,
10302 .atomic_rmw_op,12374 .atomic_rmw_op,
10303 .calling_convention,12375 .calling_convention,
12376 .address_space,
10304 .float_mode,12377 .float_mode,
10305 .reduce_op,12378 .reduce_op,
10306 .call_options,12379 .call_options,
...@@ -10316,6 +12389,7 @@ fn typeHasOnePossibleValue(...@@ -10316,6 +12389,7 @@ fn typeHasOnePossibleValue(
10316 .single_const_pointer,12389 .single_const_pointer,
10317 .single_mut_pointer,12390 .single_mut_pointer,
10318 .pointer,12391 .pointer,
12392 .bound_fn,
10319 => return null,12393 => return null,
1032012394
10321 .@"struct" => {12395 .@"struct" => {
...@@ -10328,11 +12402,28 @@ fn typeHasOnePossibleValue(...@@ -10328,11 +12402,28 @@ fn typeHasOnePossibleValue(
10328 }12402 }
10329 return Value.initTag(.empty_struct_value);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 .enum_full => {12418 .enum_full => {
10332 const resolved_ty = try sema.resolveTypeFields(block, src, ty);12419 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
10333 const enum_full = resolved_ty.castTag(.enum_full).?.data;12420 const enum_obj = resolved_ty.castTag(.enum_full).?.data;
10334 if (enum_full.fields.count() == 1) {12421 if (enum_obj.fields.count() == 1) {
10335 return enum_full.values.keys()[0];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 } else {12427 } else {
10337 return null;12428 return null;
10338 }12429 }
...@@ -10486,6 +12577,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {...@@ -10486,6 +12577,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
10486 .atomic_order => return .atomic_order_type,12577 .atomic_order => return .atomic_order_type,
10487 .atomic_rmw_op => return .atomic_rmw_op_type,12578 .atomic_rmw_op => return .atomic_rmw_op_type,
10488 .calling_convention => return .calling_convention_type,12579 .calling_convention => return .calling_convention_type,
12580 .address_space => return .address_space_type,
10489 .float_mode => return .float_mode_type,12581 .float_mode => return .float_mode_type,
10490 .reduce_op => return .reduce_op_type,12582 .reduce_op => return .reduce_op_type,
10491 .call_options => return .call_options_type,12583 .call_options => return .call_options_type,
...@@ -10581,7 +12673,10 @@ fn analyzeComptimeAlloc(...@@ -10581,7 +12673,10 @@ fn analyzeComptimeAlloc(
10581 block: *Scope.Block,12673 block: *Scope.Block,
10582 var_type: Type,12674 var_type: Type,
10583) CompileError!Air.Inst.Ref {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 });
1058512680
10586 var anon_decl = try block.startAnonDecl();12681 var anon_decl = try block.startAnonDecl();
10587 defer anon_decl.deinit();12682 defer anon_decl.deinit();
...@@ -10597,3 +12692,58 @@ fn analyzeComptimeAlloc(...@@ -10597,3 +12692,58 @@ fn analyzeComptimeAlloc(
10597 .decl = decl,12692 .decl = decl,
10598 }));12693 }));
10599}12694}
12695
12696/// The places where a user can specify an address space attribute
12697pub 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
12714pub 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,8 +49,6 @@ pub const Header = extern struct {
49};49};
5050
51pub const ExtraIndex = enum(u32) {51pub const ExtraIndex = enum(u32) {
52 /// Ref. The main struct decl for this file.
53 main_struct,
54 /// If this is 0, no compile errors. Otherwise there is a `CompileErrors`52 /// If this is 0, no compile errors. Otherwise there is a `CompileErrors`
55 /// payload at this index.53 /// payload at this index.
56 compile_errors,54 compile_errors,
...@@ -61,11 +59,6 @@ pub const ExtraIndex = enum(u32) {...@@ -61,11 +59,6 @@ pub const ExtraIndex = enum(u32) {
61 _,59 _,
62};60};
6361
64pub 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/// Returns the requested data, as well as the new index which is at the start of the62/// Returns the requested data, as well as the new index which is at the start of the
70/// trailers for the object.63/// trailers for the object.
71pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } {64pub 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,6 +70,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en
77 u32 => code.extra[i],70 u32 => code.extra[i],
78 Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),71 Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
79 i32 => @bitCast(i32, code.extra[i]),72 i32 => @bitCast(i32, code.extra[i]),
73 Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]),
80 else => @compileError("bad field type"),74 else => @compileError("bad field type"),
81 };75 };
82 i += 1;76 i += 1;
...@@ -112,50 +106,9 @@ pub fn deinit(code: *Zir, gpa: *Allocator) void {...@@ -112,50 +106,9 @@ pub fn deinit(code: *Zir, gpa: *Allocator) void {
112 code.* = undefined;106 code.* = undefined;
113}107}
114108
115/// Write human-readable, debug formatted ZIR code to a file.109/// ZIR is structured so that the outermost "main" struct of any file
116pub fn renderAsTextToFile(110/// is always at index 0.
117 gpa: *Allocator,111pub const main_struct_inst: Inst.Index = 0;
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}
159112
160/// These are untyped instructions generated from an Abstract Syntax Tree.113/// These are untyped instructions generated from an Abstract Syntax Tree.
161/// The data here is immutable because it is possible to have multiple114/// The data here is immutable because it is possible to have multiple
...@@ -173,6 +126,64 @@ pub const Inst = struct {...@@ -173,6 +126,64 @@ pub const Inst = struct {
173 /// Twos complement wrapping integer addition.126 /// Twos complement wrapping integer addition.
174 /// Uses the `pl_node` union field. Payload is `Bin`.127 /// Uses the `pl_node` union field. Payload is `Bin`.
175 addwrap,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 /// Declares a parameter of the current function. Used for:187 /// Declares a parameter of the current function. Used for:
177 /// * debug info188 /// * debug info
178 /// * checking shadowing against declarations in the current namespace189 /// * checking shadowing against declarations in the current namespace
...@@ -270,17 +281,9 @@ pub const Inst = struct {...@@ -270,17 +281,9 @@ pub const Inst = struct {
270 break_inline,281 break_inline,
271 /// Uses the `node` union field.282 /// Uses the `node` union field.
272 breakpoint,283 breakpoint,
273 /// Function call with modifier `.auto`.284 /// Function call.
274 /// Uses `pl_node`. AST node is the function call. Payload is `Call`.285 /// Uses `pl_node`. AST node is the function call. Payload is `Call`.
275 call,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 /// Uses the `pl_node` union field. Payload is `Bin`.288 /// Uses the `pl_node` union field. Payload is `Bin`.
286 cmp_lt,289 cmp_lt,
...@@ -312,11 +315,6 @@ pub const Inst = struct {...@@ -312,11 +315,6 @@ pub const Inst = struct {
312 /// only the taken branch is analyzed. The then block and else block must315 /// only the taken branch is analyzed. The then block and else block must
313 /// terminate with an "inline" variant of a noreturn instruction.316 /// terminate with an "inline" variant of a noreturn instruction.
314 condbr_inline,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 /// An error set type definition. Contains a list of field names.318 /// An error set type definition. Contains a list of field names.
321 /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.319 /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.
322 error_set_decl,320 error_set_decl,
...@@ -364,9 +362,13 @@ pub const Inst = struct {...@@ -364,9 +362,13 @@ pub const Inst = struct {
364 /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.362 /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
365 error_value,363 error_value,
366 /// Implements the `@export` builtin function, based on either an identifier to a Decl,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 /// Uses the `pl_node` union field. Payload is `Export`.366 /// Uses the `pl_node` union field. Payload is `Export`.
369 @"export",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 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer372 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
371 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.373 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
372 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.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,6 +378,15 @@ pub const Inst = struct {
376 /// This instruction also accepts a pointer.378 /// This instruction also accepts a pointer.
377 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.379 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
378 field_val,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 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer390 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
380 /// to the named field. The field name is a comptime instruction. Used by @field.391 /// to the named field. The field name is a comptime instruction. Used by @field.
381 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.392 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
...@@ -384,6 +395,15 @@ pub const Inst = struct {...@@ -384,6 +395,15 @@ pub const Inst = struct {
384 /// The field name is a comptime instruction. Used by @field.395 /// The field name is a comptime instruction. Used by @field.
385 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.396 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
386 field_val_named,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 /// Returns a function type, or a function instance, depending on whether407 /// Returns a function type, or a function instance, depending on whether
388 /// the body_len is 0. Calling convention is auto.408 /// the body_len is 0. Calling convention is auto.
389 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.409 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.
...@@ -433,25 +453,6 @@ pub const Inst = struct {...@@ -433,25 +453,6 @@ pub const Inst = struct {
433 /// Merge two error sets into one, `E1 || E2`.453 /// Merge two error sets into one, `E1 || E2`.
434 /// Uses the `pl_node` field with payload `Bin`.454 /// Uses the `pl_node` field with payload `Bin`.
435 merge_error_sets,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 /// Turns an R-Value into a const L-Value. In other words, it takes a value,456 /// Turns an R-Value into a const L-Value. In other words, it takes a value,
456 /// stores it in a memory location, and returns a const pointer to it. If the value457 /// stores it in a memory location, and returns a const pointer to it. If the value
457 /// is `comptime`, the memory location is global static constant data. Otherwise,458 /// is `comptime`, the memory location is global static constant data. Otherwise,
...@@ -488,10 +489,10 @@ pub const Inst = struct {...@@ -488,10 +489,10 @@ pub const Inst = struct {
488 /// this instruction; a following 'ret' instruction will do the diversion.489 /// this instruction; a following 'ret' instruction will do the diversion.
489 /// Uses the `str_tok` union field.490 /// Uses the `str_tok` union field.
490 ret_err_value_code,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 /// Uses the `ptr_type_simple` union field.493 /// Uses the `ptr_type_simple` union field.
493 ptr_type_simple,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 /// Uses the `ptr_type` union field.496 /// Uses the `ptr_type` union field.
496 ptr_type,497 ptr_type,
497 /// Slice operation `lhs[rhs..]`. No sentinel and no end offset.498 /// Slice operation `lhs[rhs..]`. No sentinel and no end offset.
...@@ -528,12 +529,6 @@ pub const Inst = struct {...@@ -528,12 +529,6 @@ pub const Inst = struct {
528 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.529 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
529 /// Uses the `str` union field.530 /// Uses the `str` union field.
530 str,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 /// Arithmetic negation. Asserts no integer overflow.532 /// Arithmetic negation. Asserts no integer overflow.
538 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.533 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.
539 /// Uses `un_node`.534 /// Uses `un_node`.
...@@ -731,7 +726,7 @@ pub const Inst = struct {...@@ -731,7 +726,7 @@ pub const Inst = struct {
731 size_of,726 size_of,
732 /// Implements the `@bitSizeOf` builtin. Uses `un_node`.727 /// Implements the `@bitSizeOf` builtin. Uses `un_node`.
733 bit_size_of,728 bit_size_of,
734 /// Implements the `@fence` builtin. Uses `node`.729 /// Implements the `@fence` builtin. Uses `un_node`.
735 fence,730 fence,
736731
737 /// Implement builtin `@ptrToInt`. Uses `un_node`.732 /// Implement builtin `@ptrToInt`. Uses `un_node`.
...@@ -859,35 +854,6 @@ pub const Inst = struct {...@@ -859,35 +854,6 @@ pub const Inst = struct {
859 /// Implements the `@bitReverse` builtin. Uses the `un_node` union field.854 /// Implements the `@bitReverse` builtin. Uses the `un_node` union field.
860 bit_reverse,855 bit_reverse,
861856
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 /// Implements the `@bitOffsetOf` builtin.857 /// Implements the `@bitOffsetOf` builtin.
892 /// Uses the `pl_node` union field with payload `Bin`.858 /// Uses the `pl_node` union field with payload `Bin`.
893 bit_offset_of,859 bit_offset_of,
...@@ -982,6 +948,17 @@ pub const Inst = struct {...@@ -982,6 +948,17 @@ pub const Inst = struct {
982 @"await",948 @"await",
983 await_nosuspend,949 await_nosuspend,
984950
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 /// The ZIR instruction tag is one of the `Extended` ones.962 /// The ZIR instruction tag is one of the `Extended` ones.
986 /// Uses the `extended` union field.963 /// Uses the `extended` union field.
987 extended,964 extended,
...@@ -996,6 +973,7 @@ pub const Inst = struct {...@@ -996,6 +973,7 @@ pub const Inst = struct {
996 .param_anytype_comptime,973 .param_anytype_comptime,
997 .add,974 .add,
998 .addwrap,975 .addwrap,
976 .add_sat,
999 .alloc,977 .alloc,
1000 .alloc_mut,978 .alloc_mut,
1001 .alloc_comptime,979 .alloc_comptime,
...@@ -1026,10 +1004,6 @@ pub const Inst = struct {...@@ -1026,10 +1004,6 @@ pub const Inst = struct {
1026 .breakpoint,1004 .breakpoint,
1027 .fence,1005 .fence,
1028 .call,1006 .call,
1029 .call_chkused,
1030 .call_compile_time,
1031 .call_nosuspend,
1032 .call_async,
1033 .cmp_lt,1007 .cmp_lt,
1034 .cmp_lte,1008 .cmp_lte,
1035 .cmp_eq,1009 .cmp_eq,
...@@ -1037,9 +1011,6 @@ pub const Inst = struct {...@@ -1037,9 +1011,6 @@ pub const Inst = struct {
1037 .cmp_gt,1011 .cmp_gt,
1038 .cmp_neq,1012 .cmp_neq,
1039 .coerce_result_ptr,1013 .coerce_result_ptr,
1040 .opaque_decl,
1041 .opaque_decl_anon,
1042 .opaque_decl_func,
1043 .error_set_decl,1014 .error_set_decl,
1044 .error_set_decl_anon,1015 .error_set_decl_anon,
1045 .error_set_decl_func,1016 .error_set_decl_func,
...@@ -1055,10 +1026,13 @@ pub const Inst = struct {...@@ -1055,10 +1026,13 @@ pub const Inst = struct {
1055 .ensure_result_used,1026 .ensure_result_used,
1056 .ensure_result_non_error,1027 .ensure_result_non_error,
1057 .@"export",1028 .@"export",
1029 .export_value,
1058 .field_ptr,1030 .field_ptr,
1059 .field_val,1031 .field_val,
1032 .field_call_bind,
1060 .field_ptr_named,1033 .field_ptr_named,
1061 .field_val_named,1034 .field_val_named,
1035 .field_call_bind_named,
1062 .func,1036 .func,
1063 .func_inferred,1037 .func_inferred,
1064 .has_decl,1038 .has_decl,
...@@ -1074,9 +1048,10 @@ pub const Inst = struct {...@@ -1074,9 +1048,10 @@ pub const Inst = struct {
1074 .mod_rem,1048 .mod_rem,
1075 .mul,1049 .mul,
1076 .mulwrap,1050 .mulwrap,
1077 .param_type,1051 .mul_sat,
1078 .ref,1052 .ref,
1079 .shl,1053 .shl,
1054 .shl_sat,
1080 .shr,1055 .shr,
1081 .store,1056 .store,
1082 .store_node,1057 .store_node,
...@@ -1085,6 +1060,7 @@ pub const Inst = struct {...@@ -1085,6 +1060,7 @@ pub const Inst = struct {
1085 .str,1060 .str,
1086 .sub,1061 .sub,
1087 .subwrap,1062 .subwrap,
1063 .sub_sat,
1088 .negate,1064 .negate,
1089 .negate_wrap,1065 .negate_wrap,
1090 .typeof,1066 .typeof,
...@@ -1231,6 +1207,8 @@ pub const Inst = struct {...@@ -1231,6 +1207,8 @@ pub const Inst = struct {
1231 .await_nosuspend,1207 .await_nosuspend,
1232 .ret_err_value_code,1208 .ret_err_value_code,
1233 .extended,1209 .extended,
1210 .closure_get,
1211 .closure_capture,
1234 => false,1212 => false,
12351213
1236 .@"break",1214 .@"break",
...@@ -1256,6 +1234,14 @@ pub const Inst = struct {...@@ -1256,6 +1234,14 @@ pub const Inst = struct {
1256 break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{1234 break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{
1257 .add = .pl_node,1235 .add = .pl_node,
1258 .addwrap = .pl_node,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 .param = .pl_tok,1245 .param = .pl_tok,
1260 .param_comptime = .pl_tok,1246 .param_comptime = .pl_tok,
1261 .param_anytype = .str_tok,1247 .param_anytype = .str_tok,
...@@ -1285,10 +1271,6 @@ pub const Inst = struct {...@@ -1285,10 +1271,6 @@ pub const Inst = struct {
1285 .break_inline = .@"break",1271 .break_inline = .@"break",
1286 .breakpoint = .node,1272 .breakpoint = .node,
1287 .call = .pl_node,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 .cmp_lt = .pl_node,1274 .cmp_lt = .pl_node,
1293 .cmp_lte = .pl_node,1275 .cmp_lte = .pl_node,
1294 .cmp_eq = .pl_node,1276 .cmp_eq = .pl_node,
...@@ -1298,9 +1280,6 @@ pub const Inst = struct {...@@ -1298,9 +1280,6 @@ pub const Inst = struct {
1298 .coerce_result_ptr = .bin,1280 .coerce_result_ptr = .bin,
1299 .condbr = .pl_node,1281 .condbr = .pl_node,
1300 .condbr_inline = .pl_node,1282 .condbr_inline = .pl_node,
1301 .opaque_decl = .pl_node,
1302 .opaque_decl_anon = .pl_node,
1303 .opaque_decl_func = .pl_node,
1304 .error_set_decl = .pl_node,1283 .error_set_decl = .pl_node,
1305 .error_set_decl_anon = .pl_node,1284 .error_set_decl_anon = .pl_node,
1306 .error_set_decl_func = .pl_node,1285 .error_set_decl_func = .pl_node,
...@@ -1318,10 +1297,13 @@ pub const Inst = struct {...@@ -1318,10 +1297,13 @@ pub const Inst = struct {
1318 .error_union_type = .pl_node,1297 .error_union_type = .pl_node,
1319 .error_value = .str_tok,1298 .error_value = .str_tok,
1320 .@"export" = .pl_node,1299 .@"export" = .pl_node,
1300 .export_value = .pl_node,
1321 .field_ptr = .pl_node,1301 .field_ptr = .pl_node,
1322 .field_val = .pl_node,1302 .field_val = .pl_node,
1323 .field_ptr_named = .pl_node,1303 .field_ptr_named = .pl_node,
1324 .field_val_named = .pl_node,1304 .field_val_named = .pl_node,
1305 .field_call_bind = .pl_node,
1306 .field_call_bind_named = .pl_node,
1325 .func = .pl_node,1307 .func = .pl_node,
1326 .func_inferred = .pl_node,1308 .func_inferred = .pl_node,
1327 .import = .str_tok,1309 .import = .str_tok,
...@@ -1339,9 +1321,6 @@ pub const Inst = struct {...@@ -1339,9 +1321,6 @@ pub const Inst = struct {
1339 .repeat_inline = .node,1321 .repeat_inline = .node,
1340 .merge_error_sets = .pl_node,1322 .merge_error_sets = .pl_node,
1341 .mod_rem = .pl_node,1323 .mod_rem = .pl_node,
1342 .mul = .pl_node,
1343 .mulwrap = .pl_node,
1344 .param_type = .param_type,
1345 .ref = .un_tok,1324 .ref = .un_tok,
1346 .ret_node = .un_node,1325 .ret_node = .un_node,
1347 .ret_load = .un_node,1326 .ret_load = .un_node,
...@@ -1358,8 +1337,6 @@ pub const Inst = struct {...@@ -1358,8 +1337,6 @@ pub const Inst = struct {
1358 .store_to_block_ptr = .bin,1337 .store_to_block_ptr = .bin,
1359 .store_to_inferred_ptr = .bin,1338 .store_to_inferred_ptr = .bin,
1360 .str = .str,1339 .str = .str,
1361 .sub = .pl_node,
1362 .subwrap = .pl_node,
1363 .negate = .un_node,1340 .negate = .un_node,
1364 .negate_wrap = .un_node,1341 .negate_wrap = .un_node,
1365 .typeof = .un_node,1342 .typeof = .un_node,
...@@ -1416,7 +1393,7 @@ pub const Inst = struct {...@@ -1416,7 +1393,7 @@ pub const Inst = struct {
1416 .type_info = .un_node,1393 .type_info = .un_node,
1417 .size_of = .un_node,1394 .size_of = .un_node,
1418 .bit_size_of = .un_node,1395 .bit_size_of = .un_node,
1419 .fence = .node,1396 .fence = .un_node,
14201397
1421 .ptr_to_int = .un_node,1398 .ptr_to_int = .un_node,
1422 .error_to_int = .un_node,1399 .error_to_int = .un_node,
...@@ -1480,6 +1457,7 @@ pub const Inst = struct {...@@ -1480,6 +1457,7 @@ pub const Inst = struct {
14801457
1481 .shl = .pl_node,1458 .shl = .pl_node,
1482 .shl_exact = .pl_node,1459 .shl_exact = .pl_node,
1460 .shl_sat = .pl_node,
1483 .shr = .pl_node,1461 .shr = .pl_node,
1484 .shr_exact = .pl_node,1462 .shr_exact = .pl_node,
14851463
...@@ -1517,6 +1495,9 @@ pub const Inst = struct {...@@ -1517,6 +1495,9 @@ pub const Inst = struct {
1517 .@"await" = .un_node,1495 .@"await" = .un_node,
1518 .await_nosuspend = .un_node,1496 .await_nosuspend = .un_node,
15191497
1498 .closure_capture = .un_tok,
1499 .closure_get = .inst_node,
1500
1520 .extended = .extended,1501 .extended = .extended,
1521 });1502 });
1522 };1503 };
...@@ -1549,6 +1530,10 @@ pub const Inst = struct {...@@ -1549,6 +1530,10 @@ pub const Inst = struct {
1549 /// `operand` is payload index to `UnionDecl`.1530 /// `operand` is payload index to `UnionDecl`.
1550 /// `small` is `UnionDecl.Small`.1531 /// `small` is `UnionDecl.Small`.
1551 union_decl,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 /// Obtains a pointer to the return value.1537 /// Obtains a pointer to the return value.
1553 /// `operand` is `src_node: i32`.1538 /// `operand` is `src_node: i32`.
1554 ret_ptr,1539 ret_ptr,
...@@ -1629,22 +1614,6 @@ pub const Inst = struct {...@@ -1629,22 +1614,6 @@ pub const Inst = struct {
1629 wasm_memory_size,1614 wasm_memory_size,
1630 /// `operand` is payload index to `BinNode`.1615 /// `operand` is payload index to `BinNode`.
1631 wasm_memory_grow,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,
16481617
1649 pub const InstData = struct {1618 pub const InstData = struct {
1650 opcode: Extended,1619 opcode: Extended,
...@@ -1717,6 +1686,7 @@ pub const Inst = struct {...@@ -1717,6 +1686,7 @@ pub const Inst = struct {
1717 atomic_order_type,1686 atomic_order_type,
1718 atomic_rmw_op_type,1687 atomic_rmw_op_type,
1719 calling_convention_type,1688 calling_convention_type,
1689 address_space_type,
1720 float_mode_type,1690 float_mode_type,
1721 reduce_op_type,1691 reduce_op_type,
1722 call_options_type,1692 call_options_type,
...@@ -1973,6 +1943,10 @@ pub const Inst = struct {...@@ -1973,6 +1943,10 @@ pub const Inst = struct {
1973 .ty = Type.initTag(.type),1943 .ty = Type.initTag(.type),
1974 .val = Value.initTag(.calling_convention_type),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 .float_mode_type = .{1950 .float_mode_type = .{
1977 .ty = Type.initTag(.type),1951 .ty = Type.initTag(.type),
1978 .val = Value.initTag(.float_mode_type),1952 .val = Value.initTag(.float_mode_type),
...@@ -2174,8 +2148,9 @@ pub const Inst = struct {...@@ -2174,8 +2148,9 @@ pub const Inst = struct {
2174 is_volatile: bool,2148 is_volatile: bool,
2175 has_sentinel: bool,2149 has_sentinel: bool,
2176 has_align: bool,2150 has_align: bool,
2151 has_addrspace: bool,
2177 has_bit_range: bool,2152 has_bit_range: bool,
2178 _: u2 = undefined,2153 _: u1 = undefined,
2179 },2154 },
2180 size: std.builtin.TypeInfo.Pointer.Size,2155 size: std.builtin.TypeInfo.Pointer.Size,
2181 /// Index into extra. See `PtrType`.2156 /// Index into extra. See `PtrType`.
...@@ -2197,10 +2172,6 @@ pub const Inst = struct {...@@ -2197,10 +2172,6 @@ pub const Inst = struct {
2197 /// Points to a `Block`.2172 /// Points to a `Block`.
2198 payload_index: u32,2173 payload_index: u32,
2199 },2174 },
2200 param_type: struct {
2201 callee: Ref,
2202 param_index: u32,
2203 },
2204 @"unreachable": struct {2175 @"unreachable": struct {
2205 /// Offset from Decl AST node index.2176 /// Offset from Decl AST node index.
2206 /// `Tag` determines which kind of AST node this points to.2177 /// `Tag` determines which kind of AST node this points to.
...@@ -2227,6 +2198,18 @@ pub const Inst = struct {...@@ -2227,6 +2198,18 @@ pub const Inst = struct {
2227 line: u32,2198 line: u32,
2228 column: u32,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 },
22302213
2231 // Make sure we don't accidentally add a field to make this union2214 // Make sure we don't accidentally add a field to make this union
2232 // bigger than expected. Note that in Debug builds, Zig is allowed2215 // bigger than expected. Note that in Debug builds, Zig is allowed
...@@ -2259,11 +2242,11 @@ pub const Inst = struct {...@@ -2259,11 +2242,11 @@ pub const Inst = struct {
2259 ptr_type,2242 ptr_type,
2260 int_type,2243 int_type,
2261 bool_br,2244 bool_br,
2262 param_type,
2263 @"unreachable",2245 @"unreachable",
2264 @"break",2246 @"break",
2265 switch_capture,2247 switch_capture,
2266 dbg_stmt,2248 dbg_stmt,
2249 inst_node,
2267 };2250 };
2268 };2251 };
22692252
...@@ -2386,8 +2369,27 @@ pub const Inst = struct {...@@ -2386,8 +2369,27 @@ pub const Inst = struct {
2386 /// Stored inside extra, with trailing arguments according to `args_len`.2369 /// Stored inside extra, with trailing arguments according to `args_len`.
2387 /// Each argument is a `Ref`.2370 /// Each argument is a `Ref`.
2388 pub const Call = struct {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 callee: Ref,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 };
23922394
2393 pub const BuiltinCall = struct {2395 pub const BuiltinCall = struct {
...@@ -2405,12 +2407,13 @@ pub const Inst = struct {...@@ -2405,12 +2407,13 @@ pub const Inst = struct {
2405 else_body_len: u32,2407 else_body_len: u32,
2406 };2408 };
24072409
2408 /// Stored in extra. Depending on the flags in Data, there will be up to 42410 /// Stored in extra. Depending on the flags in Data, there will be up to 5
2409 /// trailing Ref fields:2411 /// trailing Ref fields:
2410 /// 0. sentinel: Ref // if `has_sentinel` flag is set2412 /// 0. sentinel: Ref // if `has_sentinel` flag is set
2411 /// 1. align: Ref // if `has_align` flag is set2413 /// 1. align: Ref // if `has_align` flag is set
2412 /// 2. bit_start: Ref // if `has_bit_range` flag is set2414 /// 2. address_space: Ref // if `has_addrspace` flag is set
2413 /// 3. bit_end: Ref // if `has_bit_range` flag is set2415 /// 3. bit_start: Ref // if `has_bit_range` flag is set
2416 /// 4. bit_end: Ref // if `has_bit_range` flag is set
2414 pub const PtrType = struct {2417 pub const PtrType = struct {
2415 elem_type: Ref,2418 elem_type: Ref,
2416 };2419 };
...@@ -2528,7 +2531,7 @@ pub const Inst = struct {...@@ -2528,7 +2531,7 @@ pub const Inst = struct {
2528 /// 0b000X: whether corresponding decl is pub2531 /// 0b000X: whether corresponding decl is pub
2529 /// 0b00X0: whether corresponding decl is exported2532 /// 0b00X0: whether corresponding decl is exported
2530 /// 0b0X00: whether corresponding decl has an align expression2533 /// 0b0X00: whether corresponding decl has an align expression
2531 /// 0bX000: whether corresponding decl has a linksection expression2534 /// 0bX000: whether corresponding decl has a linksection or an address space expression
2532 /// 5. decl: { // for every decls_len2535 /// 5. decl: { // for every decls_len
2533 /// src_hash: [4]u32, // hash of source bytes2536 /// src_hash: [4]u32, // hash of source bytes
2534 /// line: u32, // line number of decl, relative to parent2537 /// line: u32, // line number of decl, relative to parent
...@@ -2540,7 +2543,10 @@ pub const Inst = struct {...@@ -2540,7 +2543,10 @@ pub const Inst = struct {
2540 /// this is a test decl, and the name starts at `name+1`.2543 /// this is a test decl, and the name starts at `name+1`.
2541 /// value: Index,2544 /// value: Index,
2542 /// align: Ref, // if corresponding bit is set2545 /// align: Ref, // if corresponding bit is set
2543 /// link_section: Ref, // if corresponding bit is set2546 /// link_section_or_address_space: { // if corresponding bit is set.
2547 /// link_section: Ref,
2548 /// address_space: Ref,
2549 /// }
2544 /// }2550 /// }
2545 /// 6. inst: Index // for every body_len2551 /// 6. inst: Index // for every body_len
2546 /// 7. flags: u32 // for every 8 fields2552 /// 7. flags: u32 // for every 8 fields
...@@ -2592,7 +2598,7 @@ pub const Inst = struct {...@@ -2592,7 +2598,7 @@ pub const Inst = struct {
2592 /// 0b000X: whether corresponding decl is pub2598 /// 0b000X: whether corresponding decl is pub
2593 /// 0b00X0: whether corresponding decl is exported2599 /// 0b00X0: whether corresponding decl is exported
2594 /// 0b0X00: whether corresponding decl has an align expression2600 /// 0b0X00: whether corresponding decl has an align expression
2595 /// 0bX000: whether corresponding decl has a linksection expression2601 /// 0bX000: whether corresponding decl has a linksection or an address space expression
2596 /// 6. decl: { // for every decls_len2602 /// 6. decl: { // for every decls_len
2597 /// src_hash: [4]u32, // hash of source bytes2603 /// src_hash: [4]u32, // hash of source bytes
2598 /// line: u32, // line number of decl, relative to parent2604 /// line: u32, // line number of decl, relative to parent
...@@ -2604,7 +2610,10 @@ pub const Inst = struct {...@@ -2604,7 +2610,10 @@ pub const Inst = struct {
2604 /// this is a test decl, and the name starts at `name+1`.2610 /// this is a test decl, and the name starts at `name+1`.
2605 /// value: Index,2611 /// value: Index,
2606 /// align: Ref, // if corresponding bit is set2612 /// align: Ref, // if corresponding bit is set
2607 /// link_section: Ref, // if corresponding bit is set2613 /// link_section_or_address_space: { // if corresponding bit is set.
2614 /// link_section: Ref,
2615 /// address_space: Ref,
2616 /// }
2608 /// }2617 /// }
2609 /// 7. inst: Index // for every body_len2618 /// 7. inst: Index // for every body_len
2610 /// 8. has_bits: u32 // for every 32 fields2619 /// 8. has_bits: u32 // for every 32 fields
...@@ -2637,7 +2646,7 @@ pub const Inst = struct {...@@ -2637,7 +2646,7 @@ pub const Inst = struct {
2637 /// 0b000X: whether corresponding decl is pub2646 /// 0b000X: whether corresponding decl is pub
2638 /// 0b00X0: whether corresponding decl is exported2647 /// 0b00X0: whether corresponding decl is exported
2639 /// 0b0X00: whether corresponding decl has an align expression2648 /// 0b0X00: whether corresponding decl has an align expression
2640 /// 0bX000: whether corresponding decl has a linksection expression2649 /// 0bX000: whether corresponding decl has a linksection or an address space expression
2641 /// 6. decl: { // for every decls_len2650 /// 6. decl: { // for every decls_len
2642 /// src_hash: [4]u32, // hash of source bytes2651 /// src_hash: [4]u32, // hash of source bytes
2643 /// line: u32, // line number of decl, relative to parent2652 /// line: u32, // line number of decl, relative to parent
...@@ -2649,7 +2658,10 @@ pub const Inst = struct {...@@ -2649,7 +2658,10 @@ pub const Inst = struct {
2649 /// this is a test decl, and the name starts at `name+1`.2658 /// this is a test decl, and the name starts at `name+1`.
2650 /// value: Index,2659 /// value: Index,
2651 /// align: Ref, // if corresponding bit is set2660 /// align: Ref, // if corresponding bit is set
2652 /// link_section: Ref, // if corresponding bit is set2661 /// link_section_or_address_space: { // if corresponding bit is set.
2662 /// link_section: Ref,
2663 /// address_space: Ref,
2664 /// }
2653 /// }2665 /// }
2654 /// 7. inst: Index // for every body_len2666 /// 7. inst: Index // for every body_len
2655 /// 8. has_bits: u32 // for every 8 fields2667 /// 8. has_bits: u32 // for every 8 fields
...@@ -2673,21 +2685,27 @@ pub const Inst = struct {...@@ -2673,21 +2685,27 @@ pub const Inst = struct {
2673 has_decls_len: bool,2685 has_decls_len: bool,
2674 name_strategy: NameStrategy,2686 name_strategy: NameStrategy,
2675 layout: std.builtin.TypeInfo.ContainerLayout,2687 layout: std.builtin.TypeInfo.ContainerLayout,
2676 /// false: union(tag_type)2688 /// has_tag_type | auto_enum_tag | result
2677 /// true: union(enum(tag_type))2689 /// -------------------------------------
2690 /// false | false | union { }
2691 /// false | true | union(enum) { }
2692 /// true | true | union(enum(T)) { }
2693 /// true | false | union(T) { }
2678 auto_enum_tag: bool,2694 auto_enum_tag: bool,
2679 _: u6 = undefined,2695 _: u6 = undefined,
2680 };2696 };
2681 };2697 };
26822698
2683 /// Trailing:2699 /// Trailing:
2684 /// 0. decl_bits: u32 // for every 8 decls2700 /// 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 /// - sets of 4 bits:2703 /// - sets of 4 bits:
2686 /// 0b000X: whether corresponding decl is pub2704 /// 0b000X: whether corresponding decl is pub
2687 /// 0b00X0: whether corresponding decl is exported2705 /// 0b00X0: whether corresponding decl is exported
2688 /// 0b0X00: whether corresponding decl has an align expression2706 /// 0b0X00: whether corresponding decl has an align expression
2689 /// 0bX000: whether corresponding decl has a linksection expression2707 /// 0bX000: whether corresponding decl has a linksection or an address space expression
2690 /// 1. decl: { // for every decls_len2708 /// 3. decl: { // for every decls_len
2691 /// src_hash: [4]u32, // hash of source bytes2709 /// src_hash: [4]u32, // hash of source bytes
2692 /// line: u32, // line number of decl, relative to parent2710 /// line: u32, // line number of decl, relative to parent
2693 /// name: u32, // null terminated string index2711 /// name: u32, // null terminated string index
...@@ -2698,10 +2716,18 @@ pub const Inst = struct {...@@ -2698,10 +2716,18 @@ pub const Inst = struct {
2698 /// this is a test decl, and the name starts at `name+1`.2716 /// this is a test decl, and the name starts at `name+1`.
2699 /// value: Index,2717 /// value: Index,
2700 /// align: Ref, // if corresponding bit is set2718 /// align: Ref, // if corresponding bit is set
2701 /// link_section: Ref, // if corresponding bit is set2719 /// link_section_or_address_space: { // if corresponding bit is set.
2720 /// link_section: Ref,
2721 /// address_space: Ref,
2722 /// }
2702 /// }2723 /// }
2703 pub const OpaqueDecl = struct {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 };
27062732
2707 /// Trailing: field_name: u32 // for every field: null terminated string index2733 /// Trailing: field_name: u32 // for every field: null terminated string index
...@@ -2767,12 +2793,6 @@ pub const Inst = struct {...@@ -2767,12 +2793,6 @@ pub const Inst = struct {
2767 ptr: Ref,2793 ptr: Ref,
2768 };2794 };
27692795
2770 pub const SaturatingArithmetic = struct {
2771 node: i32,
2772 lhs: Ref,
2773 rhs: Ref,
2774 };
2775
2776 pub const Cmpxchg = struct {2796 pub const Cmpxchg = struct {
2777 ptr: Ref,2797 ptr: Ref,
2778 expected_value: Ref,2798 expected_value: Ref,
...@@ -2858,6 +2878,14 @@ pub const Inst = struct {...@@ -2858,6 +2878,14 @@ pub const Inst = struct {
2858 /// 1. align_inst: Ref, // if small 0b00X0 is set2878 /// 1. align_inst: Ref, // if small 0b00X0 is set
2859 pub const AllocExtended = struct {2879 pub const AllocExtended = struct {
2860 src_node: i32,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 };
28622890
2863 pub const Export = struct {2891 pub const Export = struct {
...@@ -2869,6 +2897,12 @@ pub const Inst = struct {...@@ -2869,6 +2897,12 @@ pub const Inst = struct {
2869 options: Ref,2897 options: Ref,
2870 };2898 };
28712899
2900 pub const ExportValue = struct {
2901 /// The comptime value to export.
2902 operand: Ref,
2903 options: Ref,
2904 };
2905
2872 /// Trailing: `CompileErrors.Item` for each `items_len`.2906 /// Trailing: `CompileErrors.Item` for each `items_len`.
2873 pub const CompileErrors = struct {2907 pub const CompileErrors = struct {
2874 items_len: u32,2908 items_len: u32,
...@@ -2904,1794 +2938,6 @@ pub const Inst = struct {...@@ -2904,1794 +2938,6 @@ pub const Inst = struct {
29042938
2905pub const SpecialProng = enum { none, @"else", under };2939pub const SpecialProng = enum { none, @"else", under };
29062940
2907const 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
4695pub const DeclIterator = struct {2941pub const DeclIterator = struct {
4696 extra_index: usize,2942 extra_index: usize,
4697 bit_bag_index: usize,2943 bit_bag_index: usize,
...@@ -4735,15 +2981,6 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator {...@@ -4735,15 +2981,6 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator {
4735 const tags = zir.instructions.items(.tag);2981 const tags = zir.instructions.items(.tag);
4736 const datas = zir.instructions.items(.data);2982 const datas = zir.instructions.items(.data);
4737 switch (tags[decl_inst]) {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 // Functions are allowed and yield no iterations.2984 // Functions are allowed and yield no iterations.
4748 // There is one case matching this in the extended instruction set below.2985 // There is one case matching this in the extended instruction set below.
4749 .func,2986 .func,
...@@ -4798,6 +3035,18 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator {...@@ -4798,6 +3035,18 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator {
47983035
4799 return declIteratorInner(zir, extra_index, decls_len);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 else => unreachable,3050 else => unreachable,
4802 }3051 }
4803 },3052 },
...@@ -4835,13 +3084,6 @@ fn findDeclsInner(...@@ -4835,13 +3084,6 @@ fn findDeclsInner(
4835 const datas = zir.instructions.items(.data);3084 const datas = zir.instructions.items(.data);
48363085
4837 switch (tags[inst]) {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 // Functions instructions are interesting and have a body.3087 // Functions instructions are interesting and have a body.
4846 .func,3088 .func,
4847 .func_inferred,3089 .func_inferred,
...@@ -4869,9 +3111,12 @@ fn findDeclsInner(...@@ -4869,9 +3111,12 @@ fn findDeclsInner(
4869 return zir.findDeclsBody(list, body);3111 return zir.findDeclsBody(list, body);
4870 },3112 },
48713113
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 .struct_decl,3116 .struct_decl,
4873 .union_decl,3117 .union_decl,
4874 .enum_decl,3118 .enum_decl,
3119 .opaque_decl,
4875 => return list.append(inst),3120 => return list.append(inst),
48763121
4877 else => return,3122 else => return,
src/arch/aarch64/bits.zig created+1230
...@@ -0,0 +1,1230 @@
1const std = @import("std");
2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6// zig fmt: off
7
8/// General purpose registers in the AArch64 instruction set
9pub 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
61pub const callee_preserved_regs = [_]Register{
62 .x19, .x20, .x21, .x22, .x23,
63 .x24, .x25, .x26, .x27, .x28,
64};
65
66pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
67pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
68
69test "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
80test "Register.size" {
81 try testing.expectEqual(@as(u7, 64), Register.x19.size());
82 try testing.expectEqual(@as(u7, 32), Register.w3.size());
83}
84
85test "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
96pub 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
171test "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
182test "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
190test "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
202pub 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
1065test {
1066 testing.refAllDecls(@This());
1067}
1068
1069test "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 @@
1const std = @import("std");
2const DW = std.dwarf;
3const testing = std.testing;
4
5/// The condition field specifies the flags necessary for an
6/// Instruction to be executed
7pub 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
90test "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
101test "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
107pub 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
177test "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
184pub const Psr = enum {
185 cpsr,
186 spsr,
187};
188
189pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };
190pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
191pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
192
193/// Represents an instruction in the ARM instruction set architecture
194pub 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
1267test "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
1356test "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 @@
1const std = @import("std");
2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6// TODO: this is only tagged to facilitate the monstrosity.
7// Once packed structs work make it packed.
8pub 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
386pub 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
397pub 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
433pub const callee_preserved_regs = [_]Register{
434 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
435};
436
437test "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 @@
1const std = @import("std");
2const DW = std.dwarf;
3
4// zig fmt: off
5pub 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
77pub 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 @@
1const std = @import("std");
2const testing = std.testing;
3const mem = std.mem;
4const assert = std.debug.assert;
5const ArrayList = std.ArrayList;
6const Allocator = std.mem.Allocator;
7const 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.
25pub 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.
144pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
145pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
146pub 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.
160pub 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
563test "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,7 +2434,14 @@ flagpd1("emit-codegen-only"),
2434flagpd1("emit-header-module"),2434flagpd1("emit-header-module"),
2435flagpd1("emit-html"),2435flagpd1("emit-html"),
2436flagpd1("emit-interface-stubs"),2436flagpd1("emit-interface-stubs"),
2437flagpd1("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},
2438flagpd1("emit-llvm-bc"),2445flagpd1("emit-llvm-bc"),
2439flagpd1("emit-llvm-only"),2446flagpd1("emit-llvm-only"),
2440flagpd1("emit-llvm-uselists"),2447flagpd1("emit-llvm-uselists"),
src/codegen.zig+281-68
...@@ -21,7 +21,7 @@ const log = std.log.scoped(.codegen);...@@ -21,7 +21,7 @@ const log = std.log.scoped(.codegen);
21const build_options = @import("build_options");21const build_options = @import("build_options");
22const RegisterManager = @import("register_manager.zig").RegisterManager;22const RegisterManager = @import("register_manager.zig").RegisterManager;
2323
24const X8664Encoder = @import("codegen/x86_64.zig").Encoder;24const X8664Encoder = @import("arch/x86_64/bits.zig").Encoder;
2525
26pub const FnResult = union(enum) {26pub const FnResult = union(enum) {
27 /// The `code` parameter passed to `generateSymbol` has the value appended.27 /// The `code` parameter passed to `generateSymbol` has the value appended.
...@@ -48,6 +48,28 @@ pub const DebugInfoOutput = union(enum) {...@@ -48,6 +48,28 @@ pub const DebugInfoOutput = union(enum) {
48 dbg_info: *std.ArrayList(u8),48 dbg_info: *std.ArrayList(u8),
49 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,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 none,73 none,
52};74};
5375
...@@ -141,7 +163,7 @@ pub fn generateSymbol(...@@ -141,7 +163,7 @@ pub fn generateSymbol(
141 // TODO populate .debug_info for the array163 // TODO populate .debug_info for the array
142 if (typed_value.val.castTag(.bytes)) |payload| {164 if (typed_value.val.castTag(.bytes)) |payload| {
143 if (typed_value.ty.sentinel()) |sentinel| {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 code.appendSliceAssumeCapacity(payload.data);167 code.appendSliceAssumeCapacity(payload.data);
146 switch (try generateSymbol(bin_file, src_loc, .{168 switch (try generateSymbol(bin_file, src_loc, .{
147 .ty = typed_value.ty.elemType(),169 .ty = typed_value.ty.elemType(),
...@@ -448,7 +470,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -448,7 +470,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
448 /// A branch in the ARM instruction set470 /// A branch in the ARM instruction set
449 arm_branch: struct {471 arm_branch: struct {
450 pos: usize,472 pos: usize,
451 cond: @import("codegen/arm.zig").Condition,473 cond: @import("arch/arm/bits.zig").Condition,
452 },474 },
453 };475 };
454476
...@@ -568,7 +590,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -568,7 +590,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
568 fn gen(self: *Self) !void {590 fn gen(self: *Self) !void {
569 switch (arch) {591 switch (arch) {
570 .x86_64 => {592 .x86_64 => {
571 try self.code.ensureCapacity(self.code.items.len + 11);593 try self.code.ensureUnusedCapacity(11);
572594
573 const cc = self.fn_type.fnCallingConvention();595 const cc = self.fn_type.fnCallingConvention();
574 if (cc != .Naked) {596 if (cc != .Naked) {
...@@ -607,7 +629,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -607,7 +629,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
607 // Important to be after the possible self.code.items.len -= 5 above.629 // Important to be after the possible self.code.items.len -= 5 above.
608 try self.dbgSetEpilogueBegin();630 try self.dbgSetEpilogueBegin();
609631
610 try self.code.ensureCapacity(self.code.items.len + 9);632 try self.code.ensureUnusedCapacity(9);
611 // add rsp, x633 // add rsp, x
612 if (aligned_stack_end > math.maxInt(i8)) {634 if (aligned_stack_end > math.maxInt(i8)) {
613 // example: 48 81 c4 ff ff ff 7f add rsp,0x7fffffff635 // 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,14 +824,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
802824
803 switch (air_tags[inst]) {825 switch (air_tags[inst]) {
804 // zig fmt: off826 // zig fmt: off
805 .add, .ptr_add => try self.airAdd(inst),827 .add, .ptr_add => try self.airAdd(inst),
806 .addwrap => try self.airAddWrap(inst),828 .addwrap => try self.airAddWrap(inst),
807 .sub, .ptr_sub => try self.airSub(inst),829 .add_sat => try self.airAddSat(inst),
808 .subwrap => try self.airSubWrap(inst),830 .sub, .ptr_sub => try self.airSub(inst),
809 .mul => try self.airMul(inst),831 .subwrap => try self.airSubWrap(inst),
810 .mulwrap => try self.airMulWrap(inst),832 .sub_sat => try self.airSubSat(inst),
811 .div => try self.airDiv(inst),833 .mul => try self.airMul(inst),
812 .rem => try self.airRem(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),
813841
814 .cmp_lt => try self.airCmp(inst, .lt),842 .cmp_lt => try self.airCmp(inst, .lt),
815 .cmp_lte => try self.airCmp(inst, .lte),843 .cmp_lte => try self.airCmp(inst, .lte),
...@@ -824,7 +852,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -824,7 +852,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
824 .bit_or => try self.airBitOr(inst),852 .bit_or => try self.airBitOr(inst),
825 .xor => try self.airXor(inst),853 .xor => try self.airXor(inst),
826 .shr => try self.airShr(inst),854 .shr => try self.airShr(inst),
827 .shl => try self.airShl(inst),
828855
829 .alloc => try self.airAlloc(inst),856 .alloc => try self.airAlloc(inst),
830 .arg => try self.airArg(inst),857 .arg => try self.airArg(inst),
...@@ -833,10 +860,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -833,10 +860,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
833 .block => try self.airBlock(inst),860 .block => try self.airBlock(inst),
834 .br => try self.airBr(inst),861 .br => try self.airBr(inst),
835 .breakpoint => try self.airBreakpoint(),862 .breakpoint => try self.airBreakpoint(),
863 .fence => try self.airFence(),
836 .call => try self.airCall(inst),864 .call => try self.airCall(inst),
837 .cond_br => try self.airCondBr(inst),865 .cond_br => try self.airCondBr(inst),
838 .dbg_stmt => try self.airDbgStmt(inst),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 .intcast => try self.airIntCast(inst),869 .intcast => try self.airIntCast(inst),
841 .trunc => try self.airTrunc(inst),870 .trunc => try self.airTrunc(inst),
842 .bool_to_int => try self.airBoolToInt(inst),871 .bool_to_int => try self.airBoolToInt(inst),
...@@ -857,8 +886,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -857,8 +886,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
857 .struct_field_ptr=> try self.airStructFieldPtr(inst),886 .struct_field_ptr=> try self.airStructFieldPtr(inst),
858 .struct_field_val=> try self.airStructFieldVal(inst),887 .struct_field_val=> try self.airStructFieldVal(inst),
859 .array_to_slice => try self.airArrayToSlice(inst),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 .cmpxchg_strong => try self.airCmpxchg(inst),891 .cmpxchg_strong => try self.airCmpxchg(inst),
861 .cmpxchg_weak => try self.airCmpxchg(inst),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),
862906
863 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),907 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
864 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),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,6 +949,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
905 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);949 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
906 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);950 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
907 },951 },
952 .plan9 => {},
908 .none => {},953 .none => {},
909 }954 }
910 }955 }
...@@ -915,15 +960,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -915,15 +960,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
915 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);960 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
916 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);961 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
917 },962 },
963 .plan9 => {},
918 .none => {},964 .none => {},
919 }965 }
920 }966 }
921967
922 fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {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 switch (self.debug_output) {971 switch (self.debug_output) {
924 .dwarf => |dbg_out| {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 // TODO Look into using the DWARF special opcodes to compress this data.973 // TODO Look into using the DWARF special opcodes to compress this data.
928 // It lets you emit single-byte opcodes that add different numbers to974 // It lets you emit single-byte opcodes that add different numbers to
929 // both the PC and the line number at the same time.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,12 +981,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
935 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;981 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
936 }982 }
937 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);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 .none => {},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 }
9451018
946 /// Asserts there is already capacity to insert into top branch inst_table.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,6 +1097,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1024 }1097 }
1025 try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));1098 try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));
1026 },1099 },
1100 .plan9 => {},
1027 .none => {},1101 .none => {},
1028 }1102 }
1029 }1103 }
...@@ -1110,10 +1184,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1110,10 +1184,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1110 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });1184 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1111 }1185 }
11121186
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 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1196 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1115 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {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 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });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,6 +1308,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1226 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1308 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1227 }1309 }
12281310
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 fn airSub(self: *Self, inst: Air.Inst.Index) !void {1319 fn airSub(self: *Self, inst: Air.Inst.Index) !void {
1230 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1320 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1231 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {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,6 +1334,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1244 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1334 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1245 }1335 }
12461336
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 fn airMul(self: *Self, inst: Air.Inst.Index) !void {1345 fn airMul(self: *Self, inst: Air.Inst.Index) !void {
1248 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1346 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1249 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {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,6 +1360,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1262 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1360 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1263 }1361 }
12641362
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 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {1371 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1266 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1372 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1267 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {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,6 +1384,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1278 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1384 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1279 }1385 }
12801386
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 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {1395 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
1282 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1396 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1283 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {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,6 +1430,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1316 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1430 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1317 }1431 }
13181432
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 fn airShr(self: *Self, inst: Air.Inst.Index) !void {1441 fn airShr(self: *Self, inst: Air.Inst.Index) !void {
1320 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1442 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1321 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {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,6 +1592,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1470 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1592 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1471 }1593 }
14721594
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 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {1627 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1474 if (!self.liveness.operandDies(inst, op_index))1628 if (!self.liveness.operandDies(inst, op_index))
1475 return false;1629 return false;
...@@ -1848,15 +2002,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1848,15 +2002,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1848 },2002 },
1849 .shl => {2003 .shl => {
1850 assert(!swap_lhs_and_rhs);2004 assert(!swap_lhs_and_rhs);
1851 const shift_amout = switch (operand) {2005 const shift_amount = switch (operand) {
1852 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),2006 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1853 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),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 .shr => {2011 .shr => {
1858 assert(!swap_lhs_and_rhs);2012 assert(!swap_lhs_and_rhs);
1859 const shift_amout = switch (operand) {2013 const shift_amount = switch (operand) {
1860 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),2014 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1861 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),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,7 +2019,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1865 .signed => Instruction.asr,2019 .signed => Instruction.asr,
1866 .unsigned => Instruction.lsr,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 else => unreachable, // not a binary instruction2024 else => unreachable, // not a binary instruction
1871 }2025 }
...@@ -1952,7 +2106,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1952,7 +2106,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1952 //2106 //
1953 // TODO: make this algorithm less bad2107 // TODO: make this algorithm less bad
19542108
1955 try self.code.ensureCapacity(self.code.items.len + 8);2109 try self.code.ensureUnusedCapacity(8);
19562110
1957 const lhs = try self.resolveInst(op_lhs);2111 const lhs = try self.resolveInst(op_lhs);
1958 const rhs = try self.resolveInst(op_rhs);2112 const rhs = try self.resolveInst(op_rhs);
...@@ -2439,16 +2593,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2439,16 +2593,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2439 .register => |reg| {2593 .register => |reg| {
2440 switch (self.debug_output) {2594 switch (self.debug_output) {
2441 .dwarf => |dbg_out| {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 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);2597 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
2444 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc2598 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
2445 1, // ULEB128 dwarf expression length2599 1, // ULEB128 dwarf expression length
2446 reg.dwarfLocOp(),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 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref42603 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
2450 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string2604 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
2451 },2605 },
2606 .plan9 => {},
2452 .none => {},2607 .none => {},
2453 }2608 }
2454 },2609 },
...@@ -2476,13 +2631,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2476,13 +2631,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2476 try dbg_out.dbg_info.append(DW.OP.breg11);2631 try dbg_out.dbg_info.append(DW.OP.breg11);
2477 try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset);2632 try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset);
24782633
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 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref42635 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
2481 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string2636 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
2482 },2637 },
2483 else => {},2638 else => {},
2484 }2639 }
2485 },2640 },
2641 .plan9 => {},
2486 .none => {},2642 .none => {},
2487 }2643 }
2488 },2644 },
...@@ -2549,6 +2705,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2549,6 +2705,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2549 return self.finishAirBookkeeping();2705 return self.finishAirBookkeeping();
2550 }2706 }
25512707
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 fn airCall(self: *Self, inst: Air.Inst.Index) !void {2713 fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2553 const pl_op = self.air.instructions.items(.data)[inst].pl_op;2714 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2554 const fn_ty = self.air.typeOf(pl_op.operand);2715 const fn_ty = self.air.typeOf(pl_op.operand);
...@@ -2613,7 +2774,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2613,7 +2774,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2613 unreachable;2774 unreachable;
26142775
2615 // ff 14 25 xx xx xx xx call [addr]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 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });2778 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
2618 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);2779 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
2619 } else if (func_value.castTag(.extern_fn)) |_| {2780 } else if (func_value.castTag(.extern_fn)) |_| {
...@@ -2826,7 +2987,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2826,7 +2987,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2826 .memory = func.owner_decl.link.macho.local_sym_index,2987 .memory = func.owner_decl.link.macho.local_sym_index,
2827 });2988 });
2828 // callq *%rax2989 // callq *%rax
2829 try self.code.ensureCapacity(self.code.items.len + 2);2990 try self.code.ensureUnusedCapacity(2);
2830 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });2991 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
2831 },2992 },
2832 .aarch64 => {2993 .aarch64 => {
...@@ -2840,12 +3001,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2840,12 +3001,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2840 }3001 }
2841 } else if (func_value.castTag(.extern_fn)) |func_payload| {3002 } else if (func_value.castTag(.extern_fn)) |func_payload| {
2842 const decl = func_payload.data;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 const offset = blk: {3005 const offset = blk: {
2845 switch (arch) {3006 switch (arch) {
2846 .x86_64 => {3007 .x86_64 => {
2847 // callq3008 // callq
2848 try self.code.ensureCapacity(self.code.items.len + 5);3009 try self.code.ensureUnusedCapacity(5);
2849 self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 });3010 self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 });
2850 break :blk @intCast(u32, self.code.items.len) - 4;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,8 +3022,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2861 // Add relocation to the decl.3022 // Add relocation to the decl.
2862 try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{3023 try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{
2863 .offset = offset,3024 .offset = offset,
2864 .where = .undef,3025 .where = switch (resolv.where) {
2865 .where_index = where_index,3026 .local => .local,
3027 .undef => .undef,
3028 },
3029 .where_index = resolv.where_index,
2866 .payload = .{ .branch = .{3030 .payload = .{ .branch = .{
2867 .arch = arch,3031 .arch = arch,
2868 } },3032 } },
...@@ -2911,12 +3075,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2911,12 +3075,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2911 }3075 }
2912 if (self.air.value(callee)) |func_value| {3076 if (self.air.value(callee)) |func_value| {
2913 if (func_value.castTag(.function)) |func_payload| {3077 if (func_value.castTag(.function)) |func_payload| {
3078 try p9.seeDecl(func_payload.data.owner_decl);
2914 const ptr_bits = self.target.cpu.arch.ptrBitWidth();3079 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2915 const ptr_bytes: u64 = @divExact(ptr_bits, 8);3080 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2916 const got_addr = p9.bases.data;3081 const got_addr = p9.bases.data;
2917 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;3082 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
2918 // ff 14 25 xx xx xx xx call [addr]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 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });3085 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
2921 const fn_got_addr = got_addr + got_index * ptr_bytes;3086 const fn_got_addr = got_addr + got_index * ptr_bytes;
2922 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr));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,6 +3123,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2958 }3123 }
2959 if (self.air.value(callee)) |func_value| {3124 if (self.air.value(callee)) |func_value| {
2960 if (func_value.castTag(.function)) |func_payload| {3125 if (func_value.castTag(.function)) |func_payload| {
3126 try p9.seeDecl(func_payload.data.owner_decl);
2961 const ptr_bits = self.target.cpu.arch.ptrBitWidth();3127 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2962 const ptr_bytes: u64 = @divExact(ptr_bits, 8);3128 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2963 const got_addr = p9.bases.data;3129 const got_addr = p9.bases.data;
...@@ -3059,7 +3225,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3059,7 +3225,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3059 const rhs = try self.resolveInst(bin_op.rhs);3225 const rhs = try self.resolveInst(bin_op.rhs);
3060 const result: MCValue = switch (arch) {3226 const result: MCValue = switch (arch) {
3061 .x86_64 => result: {3227 .x86_64 => result: {
3062 try self.code.ensureCapacity(self.code.items.len + 8);3228 try self.code.ensureUnusedCapacity(8);
30633229
3064 // There are 2 operands, destination and source.3230 // There are 2 operands, destination and source.
3065 // Either one, but not both, can be a memory operand.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,7 +3309,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31433309
3144 const reloc: Reloc = switch (arch) {3310 const reloc: Reloc = switch (arch) {
3145 .i386, .x86_64 => reloc: {3311 .i386, .x86_64 => reloc: {
3146 try self.code.ensureCapacity(self.code.items.len + 6);3312 try self.code.ensureUnusedCapacity(6);
31473313
3148 const opcode: u8 = switch (cond) {3314 const opcode: u8 = switch (cond) {
3149 .compare_flags_signed => |cmp_op| blk: {3315 .compare_flags_signed => |cmp_op| blk: {
...@@ -3503,7 +3669,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3503,7 +3669,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3503 fn jump(self: *Self, index: usize) !void {3669 fn jump(self: *Self, index: usize) !void {
3504 switch (arch) {3670 switch (arch) {
3505 .i386, .x86_64 => {3671 .i386, .x86_64 => {
3506 try self.code.ensureCapacity(self.code.items.len + 5);3672 try self.code.ensureUnusedCapacity(5);
3507 if (math.cast(i8, @intCast(i32, index) - (@intCast(i32, self.code.items.len + 2)))) |delta| {3673 if (math.cast(i8, @intCast(i32, index) - (@intCast(i32, self.code.items.len + 2)))) |delta| {
3508 self.code.appendAssumeCapacity(0xeb); // jmp rel83674 self.code.appendAssumeCapacity(0xeb); // jmp rel8
3509 self.code.appendAssumeCapacity(@bitCast(u8, delta));3675 self.code.appendAssumeCapacity(@bitCast(u8, delta));
...@@ -3535,7 +3701,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3535,7 +3701,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3535 try self.blocks.putNoClobber(self.gpa, inst, .{3701 try self.blocks.putNoClobber(self.gpa, inst, .{
3536 // A block is a setup to be able to jump to the end.3702 // A block is a setup to be able to jump to the end.
3537 .relocs = .{},3703 .relocs = .{},
3538 // It also acts as a receptical for break operands.3704 // It also acts as a receptacle for break operands.
3539 // Here we use `MCValue.none` to represent a null value so that the first3705 // Here we use `MCValue.none` to represent a null value so that the first
3540 // break instruction will choose a MCValue for the block result and overwrite3706 // break instruction will choose a MCValue for the block result and overwrite
3541 // this field. Following break instructions will use that MCValue to put their3707 // 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,7 +3755,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3589 return self.fail("TODO: enable larger branch offset", .{});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 arch3758 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,7 +3807,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3641 const block_data = self.blocks.getPtr(block).?;3807 const block_data = self.blocks.getPtr(block).?;
36423808
3643 // Emit a jump with a relocation. It will be patched up after the block ends.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);
36453811
3646 switch (arch) {3812 switch (arch) {
3647 .i386, .x86_64 => {3813 .i386, .x86_64 => {
...@@ -4025,7 +4191,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4025,7 +4191,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4025 if (adj_off > 128) {4191 if (adj_off > 128) {
4026 return self.fail("TODO implement set stack variable with large stack offset", .{});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 switch (abi_size) {4195 switch (abi_size) {
4030 1 => {4196 1 => {
4031 return self.fail("TODO implement set abi_size=1 stack variable with immediate", .{});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,7 +4217,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
40514217
4052 // 64 bit write to memory would take two mov's anyways so we4218 // 64 bit write to memory would take two mov's anyways so we
4053 // insted just use two 32 bit writes to avoid register allocation4219 // 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 var buf: [8]u8 = undefined;4221 var buf: [8]u8 = undefined;
4056 mem.writeIntLittle(u64, &buf, x_big);4222 mem.writeIntLittle(u64, &buf, x_big);
40574223
...@@ -4753,6 +4919,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4753,6 +4919,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4753 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });4919 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4754 }4920 }
47554921
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 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {4942 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
4757 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4943 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4758 const extra = self.air.extraData(Air.Block, ty_pl.payload);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,6 +4950,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4764 return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });4950 return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
4765 }4951 }
47664952
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 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {4979 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
4768 // First section of indexes correspond to a set number of constant values.4980 // First section of indexes correspond to a set number of constant values.
4769 const ref_int = @enumToInt(inst);4981 const ref_int = @enumToInt(inst);
...@@ -4841,7 +5053,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4841,7 +5053,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4841 switch (typed_value.ty.zigTypeTag()) {5053 switch (typed_value.ty.zigTypeTag()) {
4842 .Pointer => switch (typed_value.ty.ptrSize()) {5054 .Pointer => switch (typed_value.ty.ptrSize()) {
4843 .Slice => {5055 .Slice => {
4844 var buf: Type.Payload.ElemType = undefined;5056 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
4845 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);5057 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
4846 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });5058 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
4847 const slice_len = typed_value.val.sliceLen();5059 const slice_len = typed_value.val.sliceLen();
...@@ -4869,6 +5081,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4869,6 +5081,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4869 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;5081 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
4870 return MCValue{ .memory = got_addr };5082 return MCValue{ .memory = got_addr };
4871 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {5083 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
5084 try p9.seeDecl(decl);
4872 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;5085 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
4873 return MCValue{ .memory = got_addr };5086 return MCValue{ .memory = got_addr };
4874 } else {5087 } else {
...@@ -5216,11 +5429,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -5216,11 +5429,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
5216 }5429 }
52175430
5218 const Register = switch (arch) {5431 const Register = switch (arch) {
5219 .i386 => @import("codegen/x86.zig").Register,5432 .i386 => @import("arch/x86/bits.zig").Register,
5220 .x86_64 => @import("codegen/x86_64.zig").Register,5433 .x86_64 => @import("arch/x86_64/bits.zig").Register,
5221 .riscv64 => @import("codegen/riscv64.zig").Register,5434 .riscv64 => @import("arch/riscv64/bits.zig").Register,
5222 .arm, .armeb => @import("codegen/arm.zig").Register,5435 .arm, .armeb => @import("arch/arm/bits.zig").Register,
5223 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Register,5436 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Register,
5224 else => enum {5437 else => enum {
5225 dummy,5438 dummy,
52265439
...@@ -5232,39 +5445,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -5232,39 +5445,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
5232 };5445 };
52335446
5234 const Instruction = switch (arch) {5447 const Instruction = switch (arch) {
5235 .riscv64 => @import("codegen/riscv64.zig").Instruction,5448 .riscv64 => @import("arch/riscv64/bits.zig").Instruction,
5236 .arm, .armeb => @import("codegen/arm.zig").Instruction,5449 .arm, .armeb => @import("arch/arm/bits.zig").Instruction,
5237 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Instruction,5450 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Instruction,
5238 else => void,5451 else => void,
5239 };5452 };
52405453
5241 const Condition = switch (arch) {5454 const Condition = switch (arch) {
5242 .arm, .armeb => @import("codegen/arm.zig").Condition,5455 .arm, .armeb => @import("arch/arm/bits.zig").Condition,
5243 else => void,5456 else => void,
5244 };5457 };
52455458
5246 const callee_preserved_regs = switch (arch) {5459 const callee_preserved_regs = switch (arch) {
5247 .i386 => @import("codegen/x86.zig").callee_preserved_regs,5460 .i386 => @import("arch/x86/bits.zig").callee_preserved_regs,
5248 .x86_64 => @import("codegen/x86_64.zig").callee_preserved_regs,5461 .x86_64 => @import("arch/x86_64/bits.zig").callee_preserved_regs,
5249 .riscv64 => @import("codegen/riscv64.zig").callee_preserved_regs,5462 .riscv64 => @import("arch/riscv64/bits.zig").callee_preserved_regs,
5250 .arm, .armeb => @import("codegen/arm.zig").callee_preserved_regs,5463 .arm, .armeb => @import("arch/arm/bits.zig").callee_preserved_regs,
5251 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").callee_preserved_regs,5464 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").callee_preserved_regs,
5252 else => [_]Register{},5465 else => [_]Register{},
5253 };5466 };
52545467
5255 const c_abi_int_param_regs = switch (arch) {5468 const c_abi_int_param_regs = switch (arch) {
5256 .i386 => @import("codegen/x86.zig").c_abi_int_param_regs,5469 .i386 => @import("arch/x86/bits.zig").c_abi_int_param_regs,
5257 .x86_64 => @import("codegen/x86_64.zig").c_abi_int_param_regs,5470 .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_param_regs,
5258 .arm, .armeb => @import("codegen/arm.zig").c_abi_int_param_regs,5471 .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_param_regs,
5259 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_param_regs,5472 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_param_regs,
5260 else => [_]Register{},5473 else => [_]Register{},
5261 };5474 };
52625475
5263 const c_abi_int_return_regs = switch (arch) {5476 const c_abi_int_return_regs = switch (arch) {
5264 .i386 => @import("codegen/x86.zig").c_abi_int_return_regs,5477 .i386 => @import("arch/x86/bits.zig").c_abi_int_return_regs,
5265 .x86_64 => @import("codegen/x86_64.zig").c_abi_int_return_regs,5478 .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_return_regs,
5266 .arm, .armeb => @import("codegen/arm.zig").c_abi_int_return_regs,5479 .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_return_regs,
5267 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_return_regs,5480 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_return_regs,
5268 else => [_]Register{},5481 else => [_]Register{},
5269 };5482 };
52705483
src/codegen/aarch64.zig deleted-1230
...@@ -1,1230 +0,0 @@
1const std = @import("std");
2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6// zig fmt: off
7
8/// General purpose registers in the AArch64 instruction set
9pub 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
61pub const callee_preserved_regs = [_]Register{
62 .x19, .x20, .x21, .x22, .x23,
63 .x24, .x25, .x26, .x27, .x28,
64};
65
66pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
67pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
68
69test "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
80test "Register.size" {
81 try testing.expectEqual(@as(u7, 64), Register.x19.size());
82 try testing.expectEqual(@as(u7, 32), Register.w3.size());
83}
84
85test "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
96pub 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
171test "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
182test "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
190test "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
202pub 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
1065test {
1066 testing.refAllDecls(@This());
1067}
1068
1069test "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 @@
1const std = @import("std");
2const DW = std.dwarf;
3const testing = std.testing;
4
5/// The condition field specifies the flags neccessary for an
6/// Instruction to be executed
7pub 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
90test "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
101test "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
107pub 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
177test "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
184pub const Psr = enum {
185 cpsr,
186 spsr,
187};
188
189pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };
190pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
191pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
192
193/// Represents an instruction in the ARM instruction set architecture
194pub 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
1267test "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
1356test "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,55 +91,76 @@ pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) {
91 return .{ .data = ident };91 return .{ .data = ident };
92}92}
9393
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/// It is not available when generating .h file.95/// It is not available when generating .h file.
96pub const Object = struct {96pub const Function = struct {
97 dg: DeclGen,
98 air: Air,97 air: Air,
99 liveness: Liveness,98 liveness: Liveness,
100 gpa: *mem.Allocator,
101 code: std.ArrayList(u8),
102 value_map: CValueMap,99 value_map: CValueMap,
103 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},100 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
104 next_arg_index: usize = 0,101 next_arg_index: usize = 0,
105 next_local_index: usize = 0,102 next_local_index: usize = 0,
106 next_block_index: usize = 0,103 next_block_index: usize = 0,
107 indent_writer: IndentWriter(std.ArrayList(u8).Writer),104 object: Object,
105 func: *Module.Fn,
108106
109 fn resolveInst(o: *Object, inst: Air.Inst.Ref) !CValue {107 fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue {
110 if (o.air.value(inst)) |_| {108 if (f.air.value(inst)) |_| {
111 return CValue{ .constant = inst };109 return CValue{ .constant = inst };
112 }110 }
113 const index = Air.refToIndex(inst).?;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 }
116114
117 fn allocLocalValue(o: *Object) CValue {115 fn allocLocalValue(f: *Function) CValue {
118 const result = o.next_local_index;116 const result = f.next_local_index;
119 o.next_local_index += 1;117 f.next_local_index += 1;
120 return .{ .local = result };118 return .{ .local = result };
121 }119 }
122120
123 fn allocLocal(o: *Object, ty: Type, mutability: Mutability) !CValue {121 fn allocLocal(f: *Function, ty: Type, mutability: Mutability) !CValue {
124 const local_value = o.allocLocalValue();122 const local_value = f.allocLocalValue();
125 try o.renderTypeAndName(o.writer(), ty, local_value, mutability);123 try f.object.renderTypeAndName(f.object.writer(), ty, local_value, mutability);
126 return local_value;124 return local_value;
127 }125 }
128126
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.
149pub const Object = struct {
150 dg: DeclGen,
151 code: std.ArrayList(u8),
152 indent_writer: IndentWriter(std.ArrayList(u8).Writer),
153
129 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {154 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
130 return o.indent_writer.writer();155 return o.indent_writer.writer();
131 }156 }
132157
133 fn writeCValue(o: *Object, w: anytype, c_value: CValue) !void {158 fn writeCValue(w: anytype, c_value: CValue) !void {
134 switch (c_value) {159 switch (c_value) {
135 .none => unreachable,160 .none => unreachable,
136 .local => |i| return w.print("t{d}", .{i}),161 .local => |i| return w.print("t{d}", .{i}),
137 .local_ref => |i| return w.print("&t{d}", .{i}),162 .local_ref => |i| return w.print("&t{d}", .{i}),
138 .constant => |inst| {163 .constant => unreachable,
139 const ty = o.air.typeOf(inst);
140 const val = o.air.value(inst).?;
141 return o.dg.renderValue(w, ty, val);
142 },
143 .arg => |i| return w.print("a{d}", .{i}),164 .arg => |i| return w.print("a{d}", .{i}),
144 .decl => |decl| return w.writeAll(mem.span(decl.name)),165 .decl => |decl| return w.writeAll(mem.span(decl.name)),
145 .decl_ref => |decl| return w.print("&{s}", .{decl.name}),166 .decl_ref => |decl| return w.print("&{s}", .{decl.name}),
...@@ -153,7 +174,7 @@ pub const Object = struct {...@@ -153,7 +174,7 @@ pub const Object = struct {
153 name: CValue,174 name: CValue,
154 mutability: Mutability,175 mutability: Mutability,
155 ) error{ OutOfMemory, AnalysisFail }!void {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 defer suffix.deinit();178 defer suffix.deinit();
158179
159 var render_ty = ty;180 var render_ty = ty;
...@@ -177,7 +198,7 @@ pub const Object = struct {...@@ -177,7 +198,7 @@ pub const Object = struct {
177 .Const => try w.writeAll("const "),198 .Const => try w.writeAll("const "),
178 .Mut => {},199 .Mut => {},
179 }200 }
180 try o.writeCValue(w, name);201 try writeCValue(w, name);
181 try w.writeAll(")(");202 try w.writeAll(")(");
182 const param_len = render_ty.fnParamLen();203 const param_len = render_ty.fnParamLen();
183 const is_var_args = render_ty.fnIsVarArgs();204 const is_var_args = render_ty.fnIsVarArgs();
...@@ -205,7 +226,7 @@ pub const Object = struct {...@@ -205,7 +226,7 @@ pub const Object = struct {
205 .Mut => "",226 .Mut => "",
206 };227 };
207 try w.print(" {s}", .{const_prefix});228 try w.print(" {s}", .{const_prefix});
208 try o.writeCValue(w, name);229 try writeCValue(w, name);
209 }230 }
210 try w.writeAll(suffix.items);231 try w.writeAll(suffix.items);
211 }232 }
...@@ -213,11 +234,14 @@ pub const Object = struct {...@@ -213,11 +234,14 @@ pub const Object = struct {
213234
214/// This data is available both when outputting .c code and when outputting an .h file.235/// This data is available both when outputting .c code and when outputting an .h file.
215pub const DeclGen = struct {236pub const DeclGen = struct {
237 gpa: *std.mem.Allocator,
216 module: *Module,238 module: *Module,
217 decl: *Decl,239 decl: *Decl,
218 fwd_decl: std.ArrayList(u8),240 fwd_decl: std.ArrayList(u8),
219 error_msg: ?*Module.ErrorMsg,241 error_msg: ?*Module.ErrorMsg,
242 /// The key of this map is Type which has references to typedefs_arena.
220 typedefs: TypedefMap,243 typedefs: TypedefMap,
244 typedefs_arena: *std.mem.Allocator,
221245
222 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {246 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
223 @setCold(true);247 @setCold(true);
...@@ -251,7 +275,7 @@ pub const DeclGen = struct {...@@ -251,7 +275,7 @@ pub const DeclGen = struct {
251 try writer.writeByte('(');275 try writer.writeByte('(');
252 try dg.renderType(writer, t);276 try dg.renderType(writer, t);
253 try writer.writeAll("){");277 try writer.writeAll("){");
254 var buf: Type.Payload.ElemType = undefined;278 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
255 try dg.renderValue(writer, t.slicePtrFieldType(&buf), val);279 try dg.renderValue(writer, t.slicePtrFieldType(&buf), val);
256 try writer.writeAll(", ");280 try writer.writeAll(", ");
257 try writer.print("{d}", .{val.sliceLen()});281 try writer.print("{d}", .{val.sliceLen()});
...@@ -545,7 +569,10 @@ pub const DeclGen = struct {...@@ -545,7 +569,10 @@ pub const DeclGen = struct {
545569
546 try dg.typedefs.ensureUnusedCapacity(1);570 try dg.typedefs.ensureUnusedCapacity(1);
547 try w.writeAll(name);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 } else {576 } else {
550 try dg.renderType(w, t.elemType());577 try dg.renderType(w, t.elemType());
551 try w.writeAll(" *");578 try w.writeAll(" *");
...@@ -586,7 +613,10 @@ pub const DeclGen = struct {...@@ -586,7 +613,10 @@ pub const DeclGen = struct {
586613
587 try dg.typedefs.ensureUnusedCapacity(1);614 try dg.typedefs.ensureUnusedCapacity(1);
588 try w.writeAll(name);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 .ErrorSet => {621 .ErrorSet => {
592 comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2);622 comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2);
...@@ -626,7 +656,10 @@ pub const DeclGen = struct {...@@ -626,7 +656,10 @@ pub const DeclGen = struct {
626656
627 try dg.typedefs.ensureUnusedCapacity(1);657 try dg.typedefs.ensureUnusedCapacity(1);
628 try w.writeAll(name);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 .Struct => {664 .Struct => {
632 if (dg.typedefs.get(t)) |some| {665 if (dg.typedefs.get(t)) |some| {
...@@ -659,7 +692,10 @@ pub const DeclGen = struct {...@@ -659,7 +692,10 @@ pub const DeclGen = struct {
659692
660 try dg.typedefs.ensureUnusedCapacity(1);693 try dg.typedefs.ensureUnusedCapacity(1);
661 try w.writeAll(name);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 .Enum => {700 .Enum => {
665 // For enums, we simply use the integer tag type.701 // For enums, we simply use the integer tag type.
...@@ -724,6 +760,29 @@ pub const DeclGen = struct {...@@ -724,6 +760,29 @@ pub const DeclGen = struct {
724 }760 }
725};761};
726762
763pub 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
727pub fn genDecl(o: *Object) !void {786pub fn genDecl(o: *Object) !void {
728 const tracy = trace(@src());787 const tracy = trace(@src());
729 defer tracy.end();788 defer tracy.end();
...@@ -732,28 +791,6 @@ pub fn genDecl(o: *Object) !void {...@@ -732,28 +791,6 @@ pub fn genDecl(o: *Object) !void {
732 .ty = o.dg.decl.ty,791 .ty = o.dg.decl.ty,
733 .val = o.dg.decl.val,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 if (tv.val.tag() == .extern_fn) {794 if (tv.val.tag() == .extern_fn) {
758 const writer = o.writer();795 const writer = o.writer();
759 try writer.writeAll("ZIG_EXTERN_C ");796 try writer.writeAll("ZIG_EXTERN_C ");
...@@ -821,240 +858,263 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {...@@ -821,240 +858,263 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
821 }858 }
822}859}
823860
824fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {861fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
825 const writer = o.writer();862 const writer = f.object.writer();
826 if (body.len == 0) {863 if (body.len == 0) {
827 try writer.writeAll("{}");864 try writer.writeAll("{}");
828 return;865 return;
829 }866 }
830867
831 try writer.writeAll("{\n");868 try writer.writeAll("{\n");
832 o.indent_writer.pushIndent();869 f.object.indent_writer.pushIndent();
833870
834 const air_tags = o.air.instructions.items(.tag);871 const air_tags = f.air.instructions.items(.tag);
835872
836 for (body) |inst| {873 for (body) |inst| {
837 const result_value = switch (air_tags[inst]) {874 const result_value = switch (air_tags[inst]) {
838 // zig fmt: off875 // zig fmt: off
839 .constant => unreachable, // excluded from function bodies876 .constant => unreachable, // excluded from function bodies
840 .const_ty => unreachable, // excluded from function bodies877 .const_ty => unreachable, // excluded from function bodies
841 .arg => airArg(o),878 .arg => airArg(f),
842879
843 .breakpoint => try airBreakpoint(o),880 .breakpoint => try airBreakpoint(f),
844 .unreach => try airUnreach(o),881 .unreach => try airUnreach(f),
882 .fence => try airFence(f, inst),
845883
846 // TODO use a different strategy for add that communicates to the optimizer884 // TODO use a different strategy for add that communicates to the optimizer
847 // that wrapping is UB.885 // that wrapping is UB.
848 .add, .ptr_add => try airBinOp( o, inst, " + "),886 .add, .ptr_add => try airBinOp (f, inst, " + "),
849 .addwrap => try airWrapOp(o, inst, " + ", "addw_"),
850 // TODO use a different strategy for sub that communicates to the optimizer887 // TODO use a different strategy for sub that communicates to the optimizer
851 // that wrapping is UB.888 // that wrapping is UB.
852 .sub, .ptr_sub => try airBinOp( o, inst, " - "),889 .sub, .ptr_sub => try airBinOp (f, inst, " - "),
853 .subwrap => try airWrapOp(o, inst, " - ", "subw_"),
854 // TODO use a different strategy for mul that communicates to the optimizer890 // TODO use a different strategy for mul that communicates to the optimizer
855 // that wrapping is UB.891 // that wrapping is UB.
856 .mul => try airBinOp( o, inst, " * "),892 .mul => try airBinOp (f, inst, " * "),
857 .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"),
858 // TODO use a different strategy for div that communicates to the optimizer893 // TODO use a different strategy for div that communicates to the optimizer
859 // that wrapping is UB.894 // that wrapping is UB.
860 .div => try airBinOp( o, inst, " / "),895 .div => try airBinOp( f, inst, " / "),
861 .rem => try airBinOp( o, inst, " % "),896 .rem => try airBinOp( f, inst, " % "),
862897 .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division
863 .cmp_eq => try airBinOp(o, inst, " == "),898
864 .cmp_gt => try airBinOp(o, inst, " > "),899 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
865 .cmp_gte => try airBinOp(o, inst, " >= "),900 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
866 .cmp_lt => try airBinOp(o, inst, " < "),901 .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
867 .cmp_lte => try airBinOp(o, inst, " <= "),902
868 .cmp_neq => try airBinOp(o, inst, " != "),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, " != "),
869914
870 // bool_and and bool_or are non-short-circuit operations915 // bool_and and bool_or are non-short-circuit operations
871 .bool_and => try airBinOp(o, inst, " & "),916 .bool_and => try airBinOp(f, inst, " & "),
872 .bool_or => try airBinOp(o, inst, " | "),917 .bool_or => try airBinOp(f, inst, " | "),
873 .bit_and => try airBinOp(o, inst, " & "),918 .bit_and => try airBinOp(f, inst, " & "),
874 .bit_or => try airBinOp(o, inst, " | "),919 .bit_or => try airBinOp(f, inst, " | "),
875 .xor => try airBinOp(o, inst, " ^ "),920 .xor => try airBinOp(f, inst, " ^ "),
876921 .shr => try airBinOp(f, inst, " >> "),
877 .shr => try airBinOp(o, inst, " >> "),922 .shl, .shl_exact => try airBinOp(f, inst, " << "),
878 .shl => try airBinOp(o, inst, " << "),923 .not => try airNot (f, inst),
879924
880 .not => try airNot( o, inst),925 .optional_payload => try airOptionalPayload(f, inst),
881926 .optional_payload_ptr => try airOptionalPayload(f, inst),
882 .optional_payload => try airOptionalPayload(o, inst),927
883 .optional_payload_ptr => try airOptionalPayload(o, inst),928 .is_err => try airIsErr(f, inst, "", ".", "!="),
884929 .is_non_err => try airIsErr(f, inst, "", ".", "=="),
885 .is_err => try airIsErr(o, inst, "", ".", "!="),930 .is_err_ptr => try airIsErr(f, inst, "*", "->", "!="),
886 .is_non_err => try airIsErr(o, inst, "", ".", "=="),931 .is_non_err_ptr => try airIsErr(f, inst, "*", "->", "=="),
887 .is_err_ptr => try airIsErr(o, inst, "*", "->", "!="),932
888 .is_non_err_ptr => try airIsErr(o, inst, "*", "->", "=="),933 .is_null => try airIsNull(f, inst, "==", ""),
889934 .is_non_null => try airIsNull(f, inst, "!=", ""),
890 .is_null => try airIsNull(o, inst, "==", ""),935 .is_null_ptr => try airIsNull(f, inst, "==", "[0]"),
891 .is_non_null => try airIsNull(o, inst, "!=", ""),936 .is_non_null_ptr => try airIsNull(f, inst, "!=", "[0]"),
892 .is_null_ptr => try airIsNull(o, inst, "==", "[0]"),937
893 .is_non_null_ptr => try airIsNull(o, inst, "!=", "[0]"),938 .alloc => try airAlloc(f, inst),
894939 .assembly => try airAsm(f, inst),
895 .alloc => try airAlloc(o, inst),940 .block => try airBlock(f, inst),
896 .assembly => try airAsm(o, inst),941 .bitcast => try airBitcast(f, inst),
897 .block => try airBlock(o, inst),942 .call => try airCall(f, inst),
898 .bitcast => try airBitcast(o, inst),943 .dbg_stmt => try airDbgStmt(f, inst),
899 .call => try airCall(o, inst),944 .intcast => try airIntCast(f, inst),
900 .dbg_stmt => try airDbgStmt(o, inst),945 .trunc => try airTrunc(f, inst),
901 .intcast => try airIntCast(o, inst),946 .bool_to_int => try airBoolToInt(f, inst),
902 .trunc => try airTrunc(o, inst),947 .load => try airLoad(f, inst),
903 .bool_to_int => try airBoolToInt(o, inst),948 .ret => try airRet(f, inst),
904 .load => try airLoad(o, inst),949 .store => try airStore(f, inst),
905 .ret => try airRet(o, inst),950 .loop => try airLoop(f, inst),
906 .store => try airStore(o, inst),951 .cond_br => try airCondBr(f, inst),
907 .loop => try airLoop(o, inst),952 .br => try airBr(f, inst),
908 .cond_br => try airCondBr(o, inst),953 .switch_br => try airSwitchBr(f, inst),
909 .br => try airBr(o, inst),954 .wrap_optional => try airWrapOptional(f, inst),
910 .switch_br => try airSwitchBr(o, inst),955 .struct_field_ptr => try airStructFieldPtr(f, inst),
911 .wrap_optional => try airWrapOptional(o, inst),956 .array_to_slice => try airArrayToSlice(f, inst),
912 .struct_field_ptr => try airStructFieldPtr(o, inst),957 .cmpxchg_weak => try airCmpxchg(f, inst, "weak"),
913 .array_to_slice => try airArrayToSlice(o, inst),958 .cmpxchg_strong => try airCmpxchg(f, inst, "strong"),
914 .cmpxchg_weak => try airCmpxchg(o, inst, "weak"),959 .atomic_rmw => try airAtomicRmw(f, inst),
915 .cmpxchg_strong => try airCmpxchg(o, inst, "strong"),960 .atomic_load => try airAtomicLoad(f, inst),
916961 .memset => try airMemset(f, inst),
917 .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0),962 .memcpy => try airMemcpy(f, inst),
918 .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1),963 .set_union_tag => try airSetUnionTag(f, inst),
919 .struct_field_ptr_index_2 => try airStructFieldPtrIndex(o, inst, 2),964 .get_union_tag => try airGetUnionTag(f, inst),
920 .struct_field_ptr_index_3 => try airStructFieldPtrIndex(o, inst, 3),965 .clz => try airBuiltinCall(f, inst, "clz"),
921966 .ctz => try airBuiltinCall(f, inst, "ctz"),
922 .struct_field_val => try airStructFieldVal(o, inst),967
923 .slice_ptr => try airSliceField(o, inst, ".ptr;\n"),968 .int_to_float,
924 .slice_len => try airSliceField(o, inst, ".len;\n"),969 .float_to_int,
925970 .fptrunc,
926 .ptr_elem_val => try airPtrElemVal(o, inst, "["),971 .fpext,
927 .ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["),972 .ptrtoint,
928 .ptr_elem_ptr => try airPtrElemPtr(o, inst),973 => try airSimpleCast(f, inst),
929 .slice_elem_val => try airSliceElemVal(o, inst, "["),974
930 .ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["),975 .atomic_store_unordered => try airAtomicStore(f, inst, toMemoryOrder(.Unordered)),
931976 .atomic_store_monotonic => try airAtomicStore(f, inst, toMemoryOrder(.Monotonic)),
932 .unwrap_errunion_payload => try airUnwrapErrUnionPay(o, inst),977 .atomic_store_release => try airAtomicStore(f, inst, toMemoryOrder(.Release)),
933 .unwrap_errunion_err => try airUnwrapErrUnionErr(o, inst),978 .atomic_store_seq_cst => try airAtomicStore(f, inst, toMemoryOrder(.SeqCst)),
934 .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(o, inst),979
935 .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(o, inst),980 .struct_field_ptr_index_0 => try airStructFieldPtrIndex(f, inst, 0),
936 .wrap_errunion_payload => try airWrapErrUnionPay(o, inst),981 .struct_field_ptr_index_1 => try airStructFieldPtrIndex(f, inst, 1),
937 .wrap_errunion_err => try airWrapErrUnionErr(o, inst),982 .struct_field_ptr_index_2 => try airStructFieldPtrIndex(f, inst, 2),
938983 .struct_field_ptr_index_3 => try airStructFieldPtrIndex(f, inst, 3),
939 .ptrtoint => return o.dg.fail("TODO: C backend: implement codegen for ptrtoint", .{}),984
940 .floatcast => return o.dg.fail("TODO: C backend: implement codegen for floatcast", .{}),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 // zig fmt: on1001 // zig fmt: on
942 };1002 };
943 switch (result_value) {1003 switch (result_value) {
944 .none => {},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 }
9481008
949 o.indent_writer.popIndent();1009 f.object.indent_writer.popIndent();
950 try writer.writeAll("}");1010 try writer.writeAll("}");
951}1011}
9521012
953fn airSliceField(o: *Object, inst: Air.Inst.Index, suffix: []const u8) !CValue {1013fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue {
954 if (o.liveness.isUnused(inst))1014 if (f.liveness.isUnused(inst))
955 return CValue.none;1015 return CValue.none;
9561016
957 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1017 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
958 const operand = try o.resolveInst(ty_op.operand);1018 const operand = try f.resolveInst(ty_op.operand);
959 const writer = o.writer();1019 const writer = f.object.writer();
960 const local = try o.allocLocal(Type.initTag(.usize), .Const);1020 const local = try f.allocLocal(Type.initTag(.usize), .Const);
961 try writer.writeAll(" = ");1021 try writer.writeAll(" = ");
962 try o.writeCValue(writer, operand);1022 try f.writeCValue(writer, operand);
963 try writer.writeAll(suffix);1023 try writer.writeAll(suffix);
964 return local;1024 return local;
965}1025}
9661026
967fn airPtrElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {1027fn airPtrElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue {
968 const is_volatile = false; // TODO1028 const is_volatile = false; // TODO
969 if (!is_volatile and o.liveness.isUnused(inst))1029 if (!is_volatile and f.liveness.isUnused(inst))
970 return CValue.none;1030 return CValue.none;
9711031
972 _ = prefix;1032 _ = prefix;
973 return o.dg.fail("TODO: C backend: airPtrElemVal", .{});1033 return f.fail("TODO: C backend: airPtrElemVal", .{});
974}1034}
9751035
976fn airPtrElemPtr(o: *Object, inst: Air.Inst.Index) !CValue {1036fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
977 if (o.liveness.isUnused(inst))1037 if (f.liveness.isUnused(inst))
978 return CValue.none;1038 return CValue.none;
9791039
980 return o.dg.fail("TODO: C backend: airPtrElemPtr", .{});1040 return f.fail("TODO: C backend: airPtrElemPtr", .{});
981}1041}
9821042
983fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {1043fn airSliceElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue {
984 const is_volatile = false; // TODO1044 const is_volatile = false; // TODO
985 if (!is_volatile and o.liveness.isUnused(inst))1045 if (!is_volatile and f.liveness.isUnused(inst))
986 return CValue.none;1046 return CValue.none;
9871047
988 const bin_op = o.air.instructions.items(.data)[inst].bin_op;1048 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
989 const slice = try o.resolveInst(bin_op.lhs);1049 const slice = try f.resolveInst(bin_op.lhs);
990 const index = try o.resolveInst(bin_op.rhs);1050 const index = try f.resolveInst(bin_op.rhs);
991 const writer = o.writer();1051 const writer = f.object.writer();
992 const local = try o.allocLocal(o.air.typeOfIndex(inst), .Const);1052 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
993 try writer.writeAll(" = ");1053 try writer.writeAll(" = ");
994 try o.writeCValue(writer, slice);1054 try f.writeCValue(writer, slice);
995 try writer.writeAll(prefix);1055 try writer.writeAll(prefix);
996 try o.writeCValue(writer, index);1056 try f.writeCValue(writer, index);
997 try writer.writeAll("];\n");1057 try writer.writeAll("];\n");
998 return local;1058 return local;
999}1059}
10001060
1001fn airAlloc(o: *Object, inst: Air.Inst.Index) !CValue {1061fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
1002 const writer = o.writer();1062 const writer = f.object.writer();
1003 const inst_ty = o.air.typeOfIndex(inst);1063 const inst_ty = f.air.typeOfIndex(inst);
10041064
1005 // First line: the variable used as data storage.1065 // First line: the variable used as data storage.
1006 const elem_type = inst_ty.elemType();1066 const elem_type = inst_ty.elemType();
1007 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;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 try writer.writeAll(";\n");1069 try writer.writeAll(";\n");
10101070
1011 return CValue{ .local_ref = local.local };1071 return CValue{ .local_ref = local.local };
1012}1072}
10131073
1014fn airArg(o: *Object) CValue {1074fn airArg(f: *Function) CValue {
1015 const i = o.next_arg_index;1075 const i = f.next_arg_index;
1016 o.next_arg_index += 1;1076 f.next_arg_index += 1;
1017 return .{ .arg = i };1077 return .{ .arg = i };
1018}1078}
10191079
1020fn airLoad(o: *Object, inst: Air.Inst.Index) !CValue {1080fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
1021 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1081 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1022 const is_volatile = o.air.typeOf(ty_op.operand).isVolatilePtr();1082 const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr();
1023 if (!is_volatile and o.liveness.isUnused(inst))1083 if (!is_volatile and f.liveness.isUnused(inst))
1024 return CValue.none;1084 return CValue.none;
1025 const inst_ty = o.air.typeOfIndex(inst);1085 const inst_ty = f.air.typeOfIndex(inst);
1026 const operand = try o.resolveInst(ty_op.operand);1086 const operand = try f.resolveInst(ty_op.operand);
1027 const writer = o.writer();1087 const writer = f.object.writer();
1028 const local = try o.allocLocal(inst_ty, .Const);1088 const local = try f.allocLocal(inst_ty, .Const);
1029 switch (operand) {1089 switch (operand) {
1030 .local_ref => |i| {1090 .local_ref => |i| {
1031 const wrapped: CValue = .{ .local = i };1091 const wrapped: CValue = .{ .local = i };
1032 try writer.writeAll(" = ");1092 try writer.writeAll(" = ");
1033 try o.writeCValue(writer, wrapped);1093 try f.writeCValue(writer, wrapped);
1034 try writer.writeAll(";\n");1094 try writer.writeAll(";\n");
1035 },1095 },
1036 .decl_ref => |decl| {1096 .decl_ref => |decl| {
1037 const wrapped: CValue = .{ .decl = decl };1097 const wrapped: CValue = .{ .decl = decl };
1038 try writer.writeAll(" = ");1098 try writer.writeAll(" = ");
1039 try o.writeCValue(writer, wrapped);1099 try f.writeCValue(writer, wrapped);
1040 try writer.writeAll(";\n");1100 try writer.writeAll(";\n");
1041 },1101 },
1042 else => {1102 else => {
1043 try writer.writeAll(" = *");1103 try writer.writeAll(" = *");
1044 try o.writeCValue(writer, operand);1104 try f.writeCValue(writer, operand);
1045 try writer.writeAll(";\n");1105 try writer.writeAll(";\n");
1046 },1106 },
1047 }1107 }
1048 return local;1108 return local;
1049}1109}
10501110
1051fn airRet(o: *Object, inst: Air.Inst.Index) !CValue {1111fn airRet(f: *Function, inst: Air.Inst.Index) !CValue {
1052 const un_op = o.air.instructions.items(.data)[inst].un_op;1112 const un_op = f.air.instructions.items(.data)[inst].un_op;
1053 const writer = o.writer();1113 const writer = f.object.writer();
1054 if (o.air.typeOf(un_op).hasCodeGenBits()) {1114 if (f.air.typeOf(un_op).hasCodeGenBits()) {
1055 const operand = try o.resolveInst(un_op);1115 const operand = try f.resolveInst(un_op);
1056 try writer.writeAll("return ");1116 try writer.writeAll("return ");
1057 try o.writeCValue(writer, operand);1117 try f.writeCValue(writer, operand);
1058 try writer.writeAll(";\n");1118 try writer.writeAll(";\n");
1059 } else {1119 } else {
1060 try writer.writeAll("return;\n");1120 try writer.writeAll("return;\n");
...@@ -1062,75 +1122,75 @@ fn airRet(o: *Object, inst: Air.Inst.Index) !CValue {...@@ -1062,75 +1122,75 @@ fn airRet(o: *Object, inst: Air.Inst.Index) !CValue {
1062 return CValue.none;1122 return CValue.none;
1063}1123}
10641124
1065fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {1125fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
1066 if (o.liveness.isUnused(inst))1126 if (f.liveness.isUnused(inst))
1067 return CValue.none;1127 return CValue.none;
10681128
1069 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1129 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1070 const operand = try o.resolveInst(ty_op.operand);1130 const operand = try f.resolveInst(ty_op.operand);
10711131
1072 const writer = o.writer();1132 const writer = f.object.writer();
1073 const inst_ty = o.air.typeOfIndex(inst);1133 const inst_ty = f.air.typeOfIndex(inst);
1074 const local = try o.allocLocal(inst_ty, .Const);1134 const local = try f.allocLocal(inst_ty, .Const);
1075 try writer.writeAll(" = (");1135 try writer.writeAll(" = (");
1076 try o.dg.renderType(writer, inst_ty);1136 try f.renderType(writer, inst_ty);
1077 try writer.writeAll(")");1137 try writer.writeAll(")");
1078 try o.writeCValue(writer, operand);1138 try f.writeCValue(writer, operand);
1079 try writer.writeAll(";\n");1139 try writer.writeAll(";\n");
1080 return local;1140 return local;
1081}1141}
10821142
1083fn airTrunc(o: *Object, inst: Air.Inst.Index) !CValue {1143fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
1084 if (o.liveness.isUnused(inst))1144 if (f.liveness.isUnused(inst))
1085 return CValue.none;1145 return CValue.none;
10861146
1087 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1147 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1088 const operand = try o.resolveInst(ty_op.operand);1148 const operand = try f.resolveInst(ty_op.operand);
1089 _ = operand;1149 _ = operand;
1090 return o.dg.fail("TODO: C backend: airTrunc", .{});1150 return f.fail("TODO: C backend: airTrunc", .{});
1091}1151}
10921152
1093fn airBoolToInt(o: *Object, inst: Air.Inst.Index) !CValue {1153fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
1094 if (o.liveness.isUnused(inst))1154 if (f.liveness.isUnused(inst))
1095 return CValue.none;1155 return CValue.none;
1096 const un_op = o.air.instructions.items(.data)[inst].un_op;1156 const un_op = f.air.instructions.items(.data)[inst].un_op;
1097 const writer = o.writer();1157 const writer = f.object.writer();
1098 const inst_ty = o.air.typeOfIndex(inst);1158 const inst_ty = f.air.typeOfIndex(inst);
1099 const operand = try o.resolveInst(un_op);1159 const operand = try f.resolveInst(un_op);
1100 const local = try o.allocLocal(inst_ty, .Const);1160 const local = try f.allocLocal(inst_ty, .Const);
1101 try writer.writeAll(" = ");1161 try writer.writeAll(" = ");
1102 try o.writeCValue(writer, operand);1162 try f.writeCValue(writer, operand);
1103 try writer.writeAll(";\n");1163 try writer.writeAll(";\n");
1104 return local;1164 return local;
1105}1165}
11061166
1107fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {1167fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
1108 // *a = b;1168 // *a = b;
1109 const bin_op = o.air.instructions.items(.data)[inst].bin_op;1169 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1110 const dest_ptr = try o.resolveInst(bin_op.lhs);1170 const dest_ptr = try f.resolveInst(bin_op.lhs);
1111 const src_val = try o.resolveInst(bin_op.rhs);1171 const src_val = try f.resolveInst(bin_op.rhs);
11121172
1113 const writer = o.writer();1173 const writer = f.object.writer();
1114 switch (dest_ptr) {1174 switch (dest_ptr) {
1115 .local_ref => |i| {1175 .local_ref => |i| {
1116 const dest: CValue = .{ .local = i };1176 const dest: CValue = .{ .local = i };
1117 try o.writeCValue(writer, dest);1177 try f.writeCValue(writer, dest);
1118 try writer.writeAll(" = ");1178 try writer.writeAll(" = ");
1119 try o.writeCValue(writer, src_val);1179 try f.writeCValue(writer, src_val);
1120 try writer.writeAll(";\n");1180 try writer.writeAll(";\n");
1121 },1181 },
1122 .decl_ref => |decl| {1182 .decl_ref => |decl| {
1123 const dest: CValue = .{ .decl = decl };1183 const dest: CValue = .{ .decl = decl };
1124 try o.writeCValue(writer, dest);1184 try f.writeCValue(writer, dest);
1125 try writer.writeAll(" = ");1185 try writer.writeAll(" = ");
1126 try o.writeCValue(writer, src_val);1186 try f.writeCValue(writer, src_val);
1127 try writer.writeAll(";\n");1187 try writer.writeAll(";\n");
1128 },1188 },
1129 else => {1189 else => {
1130 try writer.writeAll("*");1190 try writer.writeAll("*");
1131 try o.writeCValue(writer, dest_ptr);1191 try f.writeCValue(writer, dest_ptr);
1132 try writer.writeAll(" = ");1192 try writer.writeAll(" = ");
1133 try o.writeCValue(writer, src_val);1193 try f.writeCValue(writer, src_val);
1134 try writer.writeAll(";\n");1194 try writer.writeAll(";\n");
1135 },1195 },
1136 }1196 }
...@@ -1138,17 +1198,17 @@ fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {...@@ -1138,17 +1198,17 @@ fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {
1138}1198}
11391199
1140fn airWrapOp(1200fn airWrapOp(
1141 o: *Object,1201 f: *Function,
1142 inst: Air.Inst.Index,1202 inst: Air.Inst.Index,
1143 str_op: [*:0]const u8,1203 str_op: [*:0]const u8,
1144 fn_op: [*:0]const u8,1204 fn_op: [*:0]const u8,
1145) !CValue {1205) !CValue {
1146 if (o.liveness.isUnused(inst))1206 if (f.liveness.isUnused(inst))
1147 return CValue.none;1207 return CValue.none;
11481208
1149 const bin_op = o.air.instructions.items(.data)[inst].bin_op;1209 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1150 const inst_ty = o.air.typeOfIndex(inst);1210 const inst_ty = f.air.typeOfIndex(inst);
1151 const int_info = inst_ty.intInfo(o.dg.module.getTarget());1211 const int_info = inst_ty.intInfo(f.object.dg.module.getTarget());
1152 const bits = int_info.bits;1212 const bits = int_info.bits;
11531213
1154 // if it's an unsigned int with non-arbitrary bit size then we can just add1214 // if it's an unsigned int with non-arbitrary bit size then we can just add
...@@ -1158,12 +1218,12 @@ fn airWrapOp(...@@ -1158,12 +1218,12 @@ fn airWrapOp(
1158 else => false,1218 else => false,
1159 };1219 };
1160 if (ok_bits or inst_ty.tag() != .int_unsigned) {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 }
11641224
1165 if (bits > 64) {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 }
11681228
1169 var min_buf: [80]u8 = undefined;1229 var min_buf: [80]u8 = undefined;
...@@ -1210,11 +1270,11 @@ fn airWrapOp(...@@ -1210,11 +1270,11 @@ fn airWrapOp(
1210 },1270 },
1211 };1271 };
12121272
1213 const lhs = try o.resolveInst(bin_op.lhs);1273 const lhs = try f.resolveInst(bin_op.lhs);
1214 const rhs = try o.resolveInst(bin_op.rhs);1274 const rhs = try f.resolveInst(bin_op.rhs);
1215 const w = o.writer();1275 const w = f.object.writer();
12161276
1217 const ret = try o.allocLocal(inst_ty, .Mut);1277 const ret = try f.allocLocal(inst_ty, .Mut);
1218 try w.print(" = zig_{s}", .{fn_op});1278 try w.print(" = zig_{s}", .{fn_op});
12191279
1220 switch (inst_ty.tag()) {1280 switch (inst_ty.tag()) {
...@@ -1240,71 +1300,179 @@ fn airWrapOp(...@@ -1240,71 +1300,179 @@ fn airWrapOp(
1240 }1300 }
12411301
1242 try w.writeByte('(');1302 try w.writeByte('(');
1243 try o.writeCValue(w, lhs);1303 try f.writeCValue(w, lhs);
1244 try w.writeAll(", ");1304 try w.writeAll(", ");
1245 try o.writeCValue(w, rhs);1305 try f.writeCValue(w, rhs);
12461306
1247 if (int_info.signedness == .signed) {1307 if (int_info.signedness == .signed) {
1248 try w.print(", {s}", .{min});1308 try w.print(", {s}", .{min});
1249 }1309 }
12501310
1251 try w.print(", {s});", .{max});1311 try w.print(", {s});", .{max});
1252 try o.indent_writer.insertNewline();1312 try f.object.indent_writer.insertNewline();
12531313
1254 return ret;1314 return ret;
1255}1315}
12561316
1257fn airNot(o: *Object, inst: Air.Inst.Index) !CValue {1317fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {
1258 if (o.liveness.isUnused(inst))1318 if (f.liveness.isUnused(inst))
1259 return CValue.none;1319 return CValue.none;
12601320
1261 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1321 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1262 const op = try o.resolveInst(ty_op.operand);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 }
12631330
1264 const writer = o.writer();1331 // if it's an unsigned int with non-arbitrary bit size then we can just add
1265 const inst_ty = o.air.typeOfIndex(inst);1332 if (bits > 64) {
1266 const local = try o.allocLocal(inst_ty, .Const);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
1425fn 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);
12671435
1268 try writer.writeAll(" = ");1436 try writer.writeAll(" = ");
1269 if (inst_ty.zigTypeTag() == .Bool)1437 if (inst_ty.zigTypeTag() == .Bool)
1270 try writer.writeAll("!")1438 try writer.writeAll("!")
1271 else1439 else
1272 try writer.writeAll("~");1440 try writer.writeAll("~");
1273 try o.writeCValue(writer, op);1441 try f.writeCValue(writer, op);
1274 try writer.writeAll(";\n");1442 try writer.writeAll(";\n");
12751443
1276 return local;1444 return local;
1277}1445}
12781446
1279fn airBinOp(o: *Object, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {1447fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1280 if (o.liveness.isUnused(inst))1448 if (f.liveness.isUnused(inst))
1281 return CValue.none;1449 return CValue.none;
12821450
1283 const bin_op = o.air.instructions.items(.data)[inst].bin_op;1451 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1284 const lhs = try o.resolveInst(bin_op.lhs);1452 const lhs = try f.resolveInst(bin_op.lhs);
1285 const rhs = try o.resolveInst(bin_op.rhs);1453 const rhs = try f.resolveInst(bin_op.rhs);
12861454
1287 const writer = o.writer();1455 const writer = f.object.writer();
1288 const inst_ty = o.air.typeOfIndex(inst);1456 const inst_ty = f.air.typeOfIndex(inst);
1289 const local = try o.allocLocal(inst_ty, .Const);1457 const local = try f.allocLocal(inst_ty, .Const);
12901458
1291 try writer.writeAll(" = ");1459 try writer.writeAll(" = ");
1292 try o.writeCValue(writer, lhs);1460 try f.writeCValue(writer, lhs);
1293 try writer.print("{s}", .{operator});1461 try writer.print("{s}", .{operator});
1294 try o.writeCValue(writer, rhs);1462 try f.writeCValue(writer, rhs);
1295 try writer.writeAll(";\n");1463 try writer.writeAll(";\n");
12961464
1297 return local;1465 return local;
1298}1466}
12991467
1300fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {1468fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {
1301 const pl_op = o.air.instructions.items(.data)[inst].pl_op;1469 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
1302 const extra = o.air.extraData(Air.Call, pl_op.payload);1470 const extra = f.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]);1471 const args = @bitCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]);
1304 const fn_ty = o.air.typeOf(pl_op.operand);1472 const fn_ty = f.air.typeOf(pl_op.operand);
1305 const ret_ty = fn_ty.fnReturnType();1473 const ret_ty = fn_ty.fnReturnType();
1306 const unused_result = o.liveness.isUnused(inst);1474 const unused_result = f.liveness.isUnused(inst);
1307 const writer = o.writer();1475 const writer = f.object.writer();
13081476
1309 var result_local: CValue = .none;1477 var result_local: CValue = .none;
1310 if (unused_result) {1478 if (unused_result) {
...@@ -1312,11 +1480,11 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {...@@ -1312,11 +1480,11 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
1312 try writer.print("(void)", .{});1480 try writer.print("(void)", .{});
1313 }1481 }
1314 } else {1482 } else {
1315 result_local = try o.allocLocal(ret_ty, .Const);1483 result_local = try f.allocLocal(ret_ty, .Const);
1316 try writer.writeAll(" = ");1484 try writer.writeAll(" = ");
1317 }1485 }
13181486
1319 if (o.air.value(pl_op.operand)) |func_val| {1487 if (f.air.value(pl_op.operand)) |func_val| {
1320 const fn_decl = if (func_val.castTag(.extern_fn)) |extern_fn|1488 const fn_decl = if (func_val.castTag(.extern_fn)) |extern_fn|
1321 extern_fn.data1489 extern_fn.data
1322 else if (func_val.castTag(.function)) |func_payload|1490 else if (func_val.castTag(.function)) |func_payload|
...@@ -1326,8 +1494,8 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {...@@ -1326,8 +1494,8 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
13261494
1327 try writer.writeAll(mem.spanZ(fn_decl.name));1495 try writer.writeAll(mem.spanZ(fn_decl.name));
1328 } else {1496 } else {
1329 const callee = try o.resolveInst(pl_op.operand);1497 const callee = try f.resolveInst(pl_op.operand);
1330 try o.writeCValue(writer, callee);1498 try f.writeCValue(writer, callee);
1331 }1499 }
13321500
1333 try writer.writeAll("(");1501 try writer.writeAll("(");
...@@ -1335,189 +1503,200 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {...@@ -1335,189 +1503,200 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
1335 if (i != 0) {1503 if (i != 0) {
1336 try writer.writeAll(", ");1504 try writer.writeAll(", ");
1337 }1505 }
1338 if (o.air.value(arg)) |val| {1506 if (f.air.value(arg)) |val| {
1339 try o.dg.renderValue(writer, o.air.typeOf(arg), val);1507 try f.object.dg.renderValue(writer, f.air.typeOf(arg), val);
1340 } else {1508 } else {
1341 const val = try o.resolveInst(arg);1509 const val = try f.resolveInst(arg);
1342 try o.writeCValue(writer, val);1510 try f.writeCValue(writer, val);
1343 }1511 }
1344 }1512 }
1345 try writer.writeAll(");\n");1513 try writer.writeAll(");\n");
1346 return result_local;1514 return result_local;
1347}1515}
13481516
1349fn airDbgStmt(o: *Object, inst: Air.Inst.Index) !CValue {1517fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
1350 const dbg_stmt = o.air.instructions.items(.data)[inst].dbg_stmt;1518 const dbg_stmt = f.air.instructions.items(.data)[inst].dbg_stmt;
1351 const writer = o.writer();1519 const writer = f.object.writer();
1352 try writer.print("#line {d}\n", .{dbg_stmt.line + 1});1520 try writer.print("#line {d}\n", .{dbg_stmt.line + 1});
1353 return CValue.none;1521 return CValue.none;
1354}1522}
13551523
1356fn airBlock(o: *Object, inst: Air.Inst.Index) !CValue {1524fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
1357 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;1525 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1358 const extra = o.air.extraData(Air.Block, ty_pl.payload);1526 const extra = f.air.extraData(Air.Block, ty_pl.payload);
1359 const body = o.air.extra[extra.end..][0..extra.data.body_len];1527 const body = f.air.extra[extra.end..][0..extra.data.body_len];
13601528
1361 const block_id: usize = o.next_block_index;1529 const block_id: usize = f.next_block_index;
1362 o.next_block_index += 1;1530 f.next_block_index += 1;
1363 const writer = o.writer();1531 const writer = f.object.writer();
13641532
1365 const inst_ty = o.air.typeOfIndex(inst);1533 const inst_ty = f.air.typeOfIndex(inst);
1366 const result = if (inst_ty.tag() != .void and !o.liveness.isUnused(inst)) blk: {1534 const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) blk: {
1367 // allocate a location for the result1535 // 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 try writer.writeAll(";\n");1537 try writer.writeAll(";\n");
1370 break :blk local;1538 break :blk local;
1371 } else CValue{ .none = {} };1539 } else CValue{ .none = {} };
13721540
1373 try o.blocks.putNoClobber(o.gpa, inst, .{1541 try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{
1374 .block_id = block_id,1542 .block_id = block_id,
1375 .result = result,1543 .result = result,
1376 });1544 });
13771545
1378 try genBody(o, body);1546 try genBody(f, body);
1379 try o.indent_writer.insertNewline();1547 try f.object.indent_writer.insertNewline();
1380 // label must be followed by an expression, add an empty one.1548 // label must be followed by an expression, add an empty one.
1381 try writer.print("zig_block_{d}:;\n", .{block_id});1549 try writer.print("zig_block_{d}:;\n", .{block_id});
1382 return result;1550 return result;
1383}1551}
13841552
1385fn airBr(o: *Object, inst: Air.Inst.Index) !CValue {1553fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
1386 const branch = o.air.instructions.items(.data)[inst].br;1554 const branch = f.air.instructions.items(.data)[inst].br;
1387 const block = o.blocks.get(branch.block_inst).?;1555 const block = f.blocks.get(branch.block_inst).?;
1388 const result = block.result;1556 const result = block.result;
1389 const writer = o.writer();1557 const writer = f.object.writer();
13901558
1391 // If result is .none then the value of the block is unused.1559 // If result is .none then the value of the block is unused.
1392 if (result != .none) {1560 if (result != .none) {
1393 const operand = try o.resolveInst(branch.operand);1561 const operand = try f.resolveInst(branch.operand);
1394 try o.writeCValue(writer, result);1562 try f.writeCValue(writer, result);
1395 try writer.writeAll(" = ");1563 try writer.writeAll(" = ");
1396 try o.writeCValue(writer, operand);1564 try f.writeCValue(writer, operand);
1397 try writer.writeAll(";\n");1565 try writer.writeAll(";\n");
1398 }1566 }
13991567
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 return CValue.none;1569 return CValue.none;
1402}1570}
14031571
1404fn airBitcast(o: *Object, inst: Air.Inst.Index) !CValue {1572fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
1405 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1573 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1406 const operand = try o.resolveInst(ty_op.operand);1574 const operand = try f.resolveInst(ty_op.operand);
14071575
1408 const writer = o.writer();1576 const writer = f.object.writer();
1409 const inst_ty = o.air.typeOfIndex(inst);1577 const inst_ty = f.air.typeOfIndex(inst);
1410 if (inst_ty.zigTypeTag() == .Pointer and1578 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 try writer.writeAll(" = (");1582 try writer.writeAll(" = (");
1415 try o.dg.renderType(writer, inst_ty);1583 try f.renderType(writer, inst_ty);
14161584
1417 try writer.writeAll(")");1585 try writer.writeAll(")");
1418 try o.writeCValue(writer, operand);1586 try f.writeCValue(writer, operand);
1419 try writer.writeAll(";\n");1587 try writer.writeAll(";\n");
1420 return local;1588 return local;
1421 }1589 }
14221590
1423 const local = try o.allocLocal(inst_ty, .Mut);1591 const local = try f.allocLocal(inst_ty, .Mut);
1424 try writer.writeAll(";\n");1592 try writer.writeAll(";\n");
14251593
1426 try writer.writeAll("memcpy(&");1594 try writer.writeAll("memcpy(&");
1427 try o.writeCValue(writer, local);1595 try f.writeCValue(writer, local);
1428 try writer.writeAll(", &");1596 try writer.writeAll(", &");
1429 try o.writeCValue(writer, operand);1597 try f.writeCValue(writer, operand);
1430 try writer.writeAll(", sizeof ");1598 try writer.writeAll(", sizeof ");
1431 try o.writeCValue(writer, local);1599 try f.writeCValue(writer, local);
1432 try writer.writeAll(");\n");1600 try writer.writeAll(");\n");
14331601
1434 return local;1602 return local;
1435}1603}
14361604
1437fn airBreakpoint(o: *Object) !CValue {1605fn airBreakpoint(f: *Function) !CValue {
1438 try o.writer().writeAll("zig_breakpoint();\n");1606 try f.object.writer().writeAll("zig_breakpoint();\n");
1607 return CValue.none;
1608}
1609
1610fn 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 return CValue.none;1618 return CValue.none;
1440}1619}
14411620
1442fn airUnreach(o: *Object) !CValue {1621fn airUnreach(f: *Function) !CValue {
1443 try o.writer().writeAll("zig_unreachable();\n");1622 try f.object.writer().writeAll("zig_unreachable();\n");
1444 return CValue.none;1623 return CValue.none;
1445}1624}
14461625
1447fn airLoop(o: *Object, inst: Air.Inst.Index) !CValue {1626fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue {
1448 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;1627 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1449 const loop = o.air.extraData(Air.Block, ty_pl.payload);1628 const loop = f.air.extraData(Air.Block, ty_pl.payload);
1450 const body = o.air.extra[loop.end..][0..loop.data.body_len];1629 const body = f.air.extra[loop.end..][0..loop.data.body_len];
1451 try o.writer().writeAll("while (true) ");1630 try f.object.writer().writeAll("while (true) ");
1452 try genBody(o, body);1631 try genBody(f, body);
1453 try o.indent_writer.insertNewline();1632 try f.object.indent_writer.insertNewline();
1454 return CValue.none;1633 return CValue.none;
1455}1634}
14561635
1457fn airCondBr(o: *Object, inst: Air.Inst.Index) !CValue {1636fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue {
1458 const pl_op = o.air.instructions.items(.data)[inst].pl_op;1637 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
1459 const cond = try o.resolveInst(pl_op.operand);1638 const cond = try f.resolveInst(pl_op.operand);
1460 const extra = o.air.extraData(Air.CondBr, pl_op.payload);1639 const extra = f.air.extraData(Air.CondBr, pl_op.payload);
1461 const then_body = o.air.extra[extra.end..][0..extra.data.then_body_len];1640 const then_body = f.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];1641 const else_body = f.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1463 const writer = o.writer();1642 const writer = f.object.writer();
14641643
1465 try writer.writeAll("if (");1644 try writer.writeAll("if (");
1466 try o.writeCValue(writer, cond);1645 try f.writeCValue(writer, cond);
1467 try writer.writeAll(") ");1646 try writer.writeAll(") ");
1468 try genBody(o, then_body);1647 try genBody(f, then_body);
1469 try writer.writeAll(" else ");1648 try writer.writeAll(" else ");
1470 try genBody(o, else_body);1649 try genBody(f, else_body);
1471 try o.indent_writer.insertNewline();1650 try f.object.indent_writer.insertNewline();
14721651
1473 return CValue.none;1652 return CValue.none;
1474}1653}
14751654
1476fn airSwitchBr(o: *Object, inst: Air.Inst.Index) !CValue {1655fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
1477 const pl_op = o.air.instructions.items(.data)[inst].pl_op;1656 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
1478 const condition = try o.resolveInst(pl_op.operand);1657 const condition = try f.resolveInst(pl_op.operand);
1479 const condition_ty = o.air.typeOf(pl_op.operand);1658 const condition_ty = f.air.typeOf(pl_op.operand);
1480 const switch_br = o.air.extraData(Air.SwitchBr, pl_op.payload);1659 const switch_br = f.air.extraData(Air.SwitchBr, pl_op.payload);
1481 const writer = o.writer();1660 const writer = f.object.writer();
14821661
1483 try writer.writeAll("switch (");1662 try writer.writeAll("switch (");
1484 try o.writeCValue(writer, condition);1663 try f.writeCValue(writer, condition);
1485 try writer.writeAll(") {");1664 try writer.writeAll(") {");
1486 o.indent_writer.pushIndent();1665 f.object.indent_writer.pushIndent();
14871666
1488 var extra_index: usize = switch_br.end;1667 var extra_index: usize = switch_br.end;
1489 var case_i: u32 = 0;1668 var case_i: u32 = 0;
1490 while (case_i < switch_br.data.cases_len) : (case_i += 1) {1669 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
1491 const case = o.air.extraData(Air.SwitchBr.Case, extra_index);1670 const case = f.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]);1671 const items = @bitCast([]const Air.Inst.Ref, f.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];1672 const case_body = f.air.extra[case.end + items.len ..][0..case.data.body_len];
1494 extra_index = case.end + case.data.items_len + case_body.len;1673 extra_index = case.end + case.data.items_len + case_body.len;
14951674
1496 for (items) |item| {1675 for (items) |item| {
1497 try o.indent_writer.insertNewline();1676 try f.object.indent_writer.insertNewline();
1498 try writer.writeAll("case ");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 try writer.writeAll(": ");1679 try writer.writeAll(": ");
1501 }1680 }
1502 // The case body must be noreturn so we don't need to insert a break.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 }
15051684
1506 const else_body = o.air.extra[extra_index..][0..switch_br.data.else_body_len];1685 const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len];
1507 try o.indent_writer.insertNewline();1686 try f.object.indent_writer.insertNewline();
1508 try writer.writeAll("default: ");1687 try writer.writeAll("default: ");
1509 try genBody(o, else_body);1688 try genBody(f, else_body);
1510 try o.indent_writer.insertNewline();1689 try f.object.indent_writer.insertNewline();
15111690
1512 o.indent_writer.popIndent();1691 f.object.indent_writer.popIndent();
1513 try writer.writeAll("}\n");1692 try writer.writeAll("}\n");
1514 return CValue.none;1693 return CValue.none;
1515}1694}
15161695
1517fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {1696fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
1518 const air_datas = o.air.instructions.items(.data);1697 const air_datas = f.air.instructions.items(.data);
1519 const air_extra = o.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);1698 const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
1520 const zir = o.dg.decl.namespace.file_scope.zir;1699 const zir = f.object.dg.decl.namespace.file_scope.zir;
1521 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;1700 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
1522 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);1701 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
1523 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);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,14 +1705,14 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
1526 const clobbers_len = @truncate(u5, extended.small >> 10);1705 const clobbers_len = @truncate(u5, extended.small >> 10);
1527 _ = clobbers_len; // TODO honor these1706 _ = clobbers_len; // TODO honor these
1528 const is_volatile = @truncate(u1, extended.small >> 15) != 0;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]);1708 const outputs = @bitCast([]const Air.Inst.Ref, f.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]);1709 const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]);
15311710
1532 if (outputs_len > 1) {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 }
15351714
1536 if (o.liveness.isUnused(inst) and !is_volatile)1715 if (f.liveness.isUnused(inst) and !is_volatile)
1537 return CValue.none;1716 return CValue.none;
15381717
1539 var extra_i: usize = zir_extra.end;1718 var extra_i: usize = zir_extra.end;
...@@ -1548,28 +1727,28 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {...@@ -1548,28 +1727,28 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
1548 };1727 };
1549 const args_extra_begin = extra_i;1728 const args_extra_begin = extra_i;
15501729
1551 const writer = o.writer();1730 const writer = f.object.writer();
1552 for (args) |arg| {1731 for (args) |arg| {
1553 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);1732 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
1554 extra_i = input.end;1733 extra_i = input.end;
1555 const constraint = zir.nullTerminatedString(input.data.constraint);1734 const constraint = zir.nullTerminatedString(input.data.constraint);
1556 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {1735 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
1557 const reg = constraint[1 .. constraint.len - 1];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 try writer.writeAll("register ");1738 try writer.writeAll("register ");
1560 try o.dg.renderType(writer, o.air.typeOf(arg));1739 try f.renderType(writer, f.air.typeOf(arg));
15611740
1562 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });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 try writer.writeAll(";\n");1743 try writer.writeAll(";\n");
1565 } else {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 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";1748 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";
1570 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });1749 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });
1571 if (output_constraint) |_| {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 if (args.len > 0) {1753 if (args.len > 0) {
1575 if (output_constraint == null) {1754 if (output_constraint == null) {
...@@ -1595,30 +1774,30 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {...@@ -1595,30 +1774,30 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
1595 }1774 }
1596 try writer.writeAll(");\n");1775 try writer.writeAll(");\n");
15971776
1598 if (o.liveness.isUnused(inst))1777 if (f.liveness.isUnused(inst))
1599 return CValue.none;1778 return CValue.none;
16001779
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}
16031782
1604fn airIsNull(1783fn airIsNull(
1605 o: *Object,1784 f: *Function,
1606 inst: Air.Inst.Index,1785 inst: Air.Inst.Index,
1607 operator: [*:0]const u8,1786 operator: [*:0]const u8,
1608 deref_suffix: [*:0]const u8,1787 deref_suffix: [*:0]const u8,
1609) !CValue {1788) !CValue {
1610 if (o.liveness.isUnused(inst))1789 if (f.liveness.isUnused(inst))
1611 return CValue.none;1790 return CValue.none;
16121791
1613 const un_op = o.air.instructions.items(.data)[inst].un_op;1792 const un_op = f.air.instructions.items(.data)[inst].un_op;
1614 const writer = o.writer();1793 const writer = f.object.writer();
1615 const operand = try o.resolveInst(un_op);1794 const operand = try f.resolveInst(un_op);
16161795
1617 const local = try o.allocLocal(Type.initTag(.bool), .Const);1796 const local = try f.allocLocal(Type.initTag(.bool), .Const);
1618 try writer.writeAll(" = (");1797 try writer.writeAll(" = (");
1619 try o.writeCValue(writer, operand);1798 try f.writeCValue(writer, operand);
16201799
1621 if (o.air.typeOf(un_op).isPtrLikeOptional()) {1800 if (f.air.typeOf(un_op).isPtrLikeOptional()) {
1622 // operand is a regular pointer, test `operand !=/== NULL`1801 // operand is a regular pointer, test `operand !=/== NULL`
1623 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });1802 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
1624 } else {1803 } else {
...@@ -1627,14 +1806,14 @@ fn airIsNull(...@@ -1627,14 +1806,14 @@ fn airIsNull(
1627 return local;1806 return local;
1628}1807}
16291808
1630fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue {1809fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
1631 if (o.liveness.isUnused(inst))1810 if (f.liveness.isUnused(inst))
1632 return CValue.none;1811 return CValue.none;
16331812
1634 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1813 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1635 const writer = o.writer();1814 const writer = f.object.writer();
1636 const operand = try o.resolveInst(ty_op.operand);1815 const operand = try f.resolveInst(ty_op.operand);
1637 const operand_ty = o.air.typeOf(ty_op.operand);1816 const operand_ty = f.air.typeOf(ty_op.operand);
16381817
1639 const opt_ty = if (operand_ty.zigTypeTag() == .Pointer)1818 const opt_ty = if (operand_ty.zigTypeTag() == .Pointer)
1640 operand_ty.elemType()1819 operand_ty.elemType()
...@@ -1647,98 +1826,98 @@ fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue {...@@ -1647,98 +1826,98 @@ fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue {
1647 return operand;1826 return operand;
1648 }1827 }
16491828
1650 const inst_ty = o.air.typeOfIndex(inst);1829 const inst_ty = f.air.typeOfIndex(inst);
1651 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";1830 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
1652 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";1831 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
16531832
1654 const local = try o.allocLocal(inst_ty, .Const);1833 const local = try f.allocLocal(inst_ty, .Const);
1655 try writer.print(" = {s}(", .{maybe_addrof});1834 try writer.print(" = {s}(", .{maybe_addrof});
1656 try o.writeCValue(writer, operand);1835 try f.writeCValue(writer, operand);
16571836
1658 try writer.print("){s}payload;\n", .{maybe_deref});1837 try writer.print("){s}payload;\n", .{maybe_deref});
1659 return local;1838 return local;
1660}1839}
16611840
1662fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue {1841fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {
1663 if (o.liveness.isUnused(inst))1842 if (f.liveness.isUnused(inst))
1664 // TODO this @as is needed because of a stage1 bug1843 // TODO this @as is needed because of a stage1 bug
1665 return @as(CValue, CValue.none);1844 return @as(CValue, CValue.none);
16661845
1667 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;1846 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1668 const extra = o.air.extraData(Air.StructField, ty_pl.payload).data;1847 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
1669 const struct_ptr = try o.resolveInst(extra.struct_operand);1848 const struct_ptr = try f.resolveInst(extra.struct_operand);
1670 const struct_ptr_ty = o.air.typeOf(extra.struct_operand);1849 const struct_ptr_ty = f.air.typeOf(extra.struct_operand);
1671 return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, extra.field_index);1850 return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, extra.field_index);
1672}1851}
16731852
1674fn airStructFieldPtrIndex(o: *Object, inst: Air.Inst.Index, index: u8) !CValue {1853fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue {
1675 if (o.liveness.isUnused(inst))1854 if (f.liveness.isUnused(inst))
1676 // TODO this @as is needed because of a stage1 bug1855 // TODO this @as is needed because of a stage1 bug
1677 return @as(CValue, CValue.none);1856 return @as(CValue, CValue.none);
16781857
1679 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1858 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1680 const struct_ptr = try o.resolveInst(ty_op.operand);1859 const struct_ptr = try f.resolveInst(ty_op.operand);
1681 const struct_ptr_ty = o.air.typeOf(ty_op.operand);1860 const struct_ptr_ty = f.air.typeOf(ty_op.operand);
1682 return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, index);1861 return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, index);
1683}1862}
16841863
1685fn structFieldPtr(o: *Object, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue {1864fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue {
1686 const writer = o.writer();1865 const writer = f.object.writer();
1687 const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;1866 const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;
1688 const field_name = struct_obj.fields.keys()[index];1867 const field_name = struct_obj.fields.keys()[index];
16891868
1690 const inst_ty = o.air.typeOfIndex(inst);1869 const inst_ty = f.air.typeOfIndex(inst);
1691 const local = try o.allocLocal(inst_ty, .Const);1870 const local = try f.allocLocal(inst_ty, .Const);
1692 switch (struct_ptr) {1871 switch (struct_ptr) {
1693 .local_ref => |i| {1872 .local_ref => |i| {
1694 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });1873 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });
1695 },1874 },
1696 else => {1875 else => {
1697 try writer.writeAll(" = &");1876 try writer.writeAll(" = &");
1698 try o.writeCValue(writer, struct_ptr);1877 try f.writeCValue(writer, struct_ptr);
1699 try writer.print("->{};\n", .{fmtIdent(field_name)});1878 try writer.print("->{};\n", .{fmtIdent(field_name)});
1700 },1879 },
1701 }1880 }
1702 return local;1881 return local;
1703}1882}
17041883
1705fn airStructFieldVal(o: *Object, inst: Air.Inst.Index) !CValue {1884fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
1706 if (o.liveness.isUnused(inst))1885 if (f.liveness.isUnused(inst))
1707 return CValue.none;1886 return CValue.none;
17081887
1709 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;1888 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1710 const extra = o.air.extraData(Air.StructField, ty_pl.payload).data;1889 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
1711 const writer = o.writer();1890 const writer = f.object.writer();
1712 const struct_byval = try o.resolveInst(extra.struct_operand);1891 const struct_byval = try f.resolveInst(extra.struct_operand);
1713 const struct_ty = o.air.typeOf(extra.struct_operand);1892 const struct_ty = f.air.typeOf(extra.struct_operand);
1714 const struct_obj = struct_ty.castTag(.@"struct").?.data;1893 const struct_obj = struct_ty.castTag(.@"struct").?.data;
1715 const field_name = struct_obj.fields.keys()[extra.field_index];1894 const field_name = struct_obj.fields.keys()[extra.field_index];
17161895
1717 const inst_ty = o.air.typeOfIndex(inst);1896 const inst_ty = f.air.typeOfIndex(inst);
1718 const local = try o.allocLocal(inst_ty, .Const);1897 const local = try f.allocLocal(inst_ty, .Const);
1719 try writer.writeAll(" = ");1898 try writer.writeAll(" = ");
1720 try o.writeCValue(writer, struct_byval);1899 try f.writeCValue(writer, struct_byval);
1721 try writer.print(".{};\n", .{fmtIdent(field_name)});1900 try writer.print(".{};\n", .{fmtIdent(field_name)});
1722 return local;1901 return local;
1723}1902}
17241903
1725// *(E!T) -> E NOT *E1904// *(E!T) -> E NOT *E
1726fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {1905fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
1727 if (o.liveness.isUnused(inst))1906 if (f.liveness.isUnused(inst))
1728 return CValue.none;1907 return CValue.none;
17291908
1730 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1909 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1731 const inst_ty = o.air.typeOfIndex(inst);1910 const inst_ty = f.air.typeOfIndex(inst);
1732 const writer = o.writer();1911 const writer = f.object.writer();
1733 const operand = try o.resolveInst(ty_op.operand);1912 const operand = try f.resolveInst(ty_op.operand);
1734 const operand_ty = o.air.typeOf(ty_op.operand);1913 const operand_ty = f.air.typeOf(ty_op.operand);
17351914
1736 const payload_ty = operand_ty.errorUnionPayload();1915 const payload_ty = operand_ty.errorUnionPayload();
1737 if (!payload_ty.hasCodeGenBits()) {1916 if (!payload_ty.hasCodeGenBits()) {
1738 if (operand_ty.zigTypeTag() == .Pointer) {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 try writer.writeAll(" = *");1919 try writer.writeAll(" = *");
1741 try o.writeCValue(writer, operand);1920 try f.writeCValue(writer, operand);
1742 try writer.writeAll(";\n");1921 try writer.writeAll(";\n");
1743 return local;1922 return local;
1744 } else {1923 } else {
...@@ -1748,154 +1927,189 @@ fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {...@@ -1748,154 +1927,189 @@ fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
17481927
1749 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";1928 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
17501929
1751 const local = try o.allocLocal(inst_ty, .Const);1930 const local = try f.allocLocal(inst_ty, .Const);
1752 try writer.writeAll(" = (");1931 try writer.writeAll(" = (");
1753 try o.writeCValue(writer, operand);1932 try f.writeCValue(writer, operand);
17541933
1755 try writer.print("){s}error;\n", .{maybe_deref});1934 try writer.print("){s}error;\n", .{maybe_deref});
1756 return local;1935 return local;
1757}1936}
17581937
1759fn airUnwrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {1938fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
1760 if (o.liveness.isUnused(inst))1939 if (f.liveness.isUnused(inst))
1761 return CValue.none;1940 return CValue.none;
17621941
1763 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1942 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1764 const writer = o.writer();1943 const writer = f.object.writer();
1765 const operand = try o.resolveInst(ty_op.operand);1944 const operand = try f.resolveInst(ty_op.operand);
1766 const operand_ty = o.air.typeOf(ty_op.operand);1945 const operand_ty = f.air.typeOf(ty_op.operand);
17671946
1768 const payload_ty = operand_ty.errorUnionPayload();1947 const payload_ty = operand_ty.errorUnionPayload();
1769 if (!payload_ty.hasCodeGenBits()) {1948 if (!payload_ty.hasCodeGenBits()) {
1770 return CValue.none;1949 return CValue.none;
1771 }1950 }
17721951
1773 const inst_ty = o.air.typeOfIndex(inst);1952 const inst_ty = f.air.typeOfIndex(inst);
1774 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";1953 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
1775 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";1954 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
17761955
1777 const local = try o.allocLocal(inst_ty, .Const);1956 const local = try f.allocLocal(inst_ty, .Const);
1778 try writer.print(" = {s}(", .{maybe_addrof});1957 try writer.print(" = {s}(", .{maybe_addrof});
1779 try o.writeCValue(writer, operand);1958 try f.writeCValue(writer, operand);
17801959
1781 try writer.print("){s}payload;\n", .{maybe_deref});1960 try writer.print("){s}payload;\n", .{maybe_deref});
1782 return local;1961 return local;
1783}1962}
17841963
1785fn airWrapOptional(o: *Object, inst: Air.Inst.Index) !CValue {1964fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
1786 if (o.liveness.isUnused(inst))1965 if (f.liveness.isUnused(inst))
1787 return CValue.none;1966 return CValue.none;
17881967
1789 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1968 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1790 const writer = o.writer();1969 const writer = f.object.writer();
1791 const operand = try o.resolveInst(ty_op.operand);1970 const operand = try f.resolveInst(ty_op.operand);
17921971
1793 const inst_ty = o.air.typeOfIndex(inst);1972 const inst_ty = f.air.typeOfIndex(inst);
1794 if (inst_ty.isPtrLikeOptional()) {1973 if (inst_ty.isPtrLikeOptional()) {
1795 // the operand is just a regular pointer, no need to do anything special.1974 // the operand is just a regular pointer, no need to do anything special.
1796 return operand;1975 return operand;
1797 }1976 }
17981977
1799 // .wrap_optional is used to convert non-optionals into optionals so it can never be null.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 try writer.writeAll(" = { .is_null = false, .payload =");1980 try writer.writeAll(" = { .is_null = false, .payload =");
1802 try o.writeCValue(writer, operand);1981 try f.writeCValue(writer, operand);
1803 try writer.writeAll("};\n");1982 try writer.writeAll("};\n");
1804 return local;1983 return local;
1805}1984}
1806fn airWrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {1985fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
1807 if (o.liveness.isUnused(inst))1986 if (f.liveness.isUnused(inst))
1808 return CValue.none;1987 return CValue.none;
18091988
1810 const writer = o.writer();1989 const writer = f.object.writer();
1811 const ty_op = o.air.instructions.items(.data)[inst].ty_op;1990 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1812 const operand = try o.resolveInst(ty_op.operand);1991 const operand = try f.resolveInst(ty_op.operand);
18131992
1814 const inst_ty = o.air.typeOfIndex(inst);1993 const inst_ty = f.air.typeOfIndex(inst);
1815 const local = try o.allocLocal(inst_ty, .Const);1994 const local = try f.allocLocal(inst_ty, .Const);
1816 try writer.writeAll(" = { .error = ");1995 try writer.writeAll(" = { .error = ");
1817 try o.writeCValue(writer, operand);1996 try f.writeCValue(writer, operand);
1818 try writer.writeAll(" };\n");1997 try writer.writeAll(" };\n");
1819 return local;1998 return local;
1820}1999}
18212000
1822fn airWrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {2001fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
1823 if (o.liveness.isUnused(inst))2002 if (f.liveness.isUnused(inst))
1824 return CValue.none;2003 return CValue.none;
18252004
1826 const ty_op = o.air.instructions.items(.data)[inst].ty_op;2005 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1827 const writer = o.writer();2006 const writer = f.object.writer();
1828 const operand = try o.resolveInst(ty_op.operand);2007 const operand = try f.resolveInst(ty_op.operand);
18292008
1830 const inst_ty = o.air.typeOfIndex(inst);2009 const inst_ty = f.air.typeOfIndex(inst);
1831 const local = try o.allocLocal(inst_ty, .Const);2010 const local = try f.allocLocal(inst_ty, .Const);
1832 try writer.writeAll(" = { .error = 0, .payload = ");2011 try writer.writeAll(" = { .error = 0, .payload = ");
1833 try o.writeCValue(writer, operand);2012 try f.writeCValue(writer, operand);
1834 try writer.writeAll(" };\n");2013 try writer.writeAll(" };\n");
1835 return local;2014 return local;
1836}2015}
18372016
1838fn airIsErr(2017fn airIsErr(
1839 o: *Object,2018 f: *Function,
1840 inst: Air.Inst.Index,2019 inst: Air.Inst.Index,
1841 deref_prefix: [*:0]const u8,2020 deref_prefix: [*:0]const u8,
1842 deref_suffix: [*:0]const u8,2021 deref_suffix: [*:0]const u8,
1843 op_str: [*:0]const u8,2022 op_str: [*:0]const u8,
1844) !CValue {2023) !CValue {
1845 if (o.liveness.isUnused(inst))2024 if (f.liveness.isUnused(inst))
1846 return CValue.none;2025 return CValue.none;
18472026
1848 const un_op = o.air.instructions.items(.data)[inst].un_op;2027 const un_op = f.air.instructions.items(.data)[inst].un_op;
1849 const writer = o.writer();2028 const writer = f.object.writer();
1850 const operand = try o.resolveInst(un_op);2029 const operand = try f.resolveInst(un_op);
1851 const operand_ty = o.air.typeOf(un_op);2030 const operand_ty = f.air.typeOf(un_op);
1852 const local = try o.allocLocal(Type.initTag(.bool), .Const);2031 const local = try f.allocLocal(Type.initTag(.bool), .Const);
1853 const payload_ty = operand_ty.errorUnionPayload();2032 const payload_ty = operand_ty.errorUnionPayload();
1854 if (!payload_ty.hasCodeGenBits()) {2033 if (!payload_ty.hasCodeGenBits()) {
1855 try writer.print(" = {s}", .{deref_prefix});2034 try writer.print(" = {s}", .{deref_prefix});
1856 try o.writeCValue(writer, operand);2035 try f.writeCValue(writer, operand);
1857 try writer.print(" {s} 0;\n", .{op_str});2036 try writer.print(" {s} 0;\n", .{op_str});
1858 } else {2037 } else {
1859 try writer.writeAll(" = ");2038 try writer.writeAll(" = ");
1860 try o.writeCValue(writer, operand);2039 try f.writeCValue(writer, operand);
1861 try writer.print("{s}error {s} 0;\n", .{ deref_suffix, op_str });2040 try writer.print("{s}error {s} 0;\n", .{ deref_suffix, op_str });
1862 }2041 }
1863 return local;2042 return local;
1864}2043}
18652044
1866fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue {2045fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
1867 if (o.liveness.isUnused(inst))2046 if (f.liveness.isUnused(inst))
1868 return CValue.none;2047 return CValue.none;
18692048
1870 const inst_ty = o.air.typeOfIndex(inst);2049 const inst_ty = f.air.typeOfIndex(inst);
1871 const local = try o.allocLocal(inst_ty, .Const);2050 const local = try f.allocLocal(inst_ty, .Const);
1872 const ty_op = o.air.instructions.items(.data)[inst].ty_op;2051 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1873 const writer = o.writer();2052 const writer = f.object.writer();
1874 const operand = try o.resolveInst(ty_op.operand);2053 const operand = try f.resolveInst(ty_op.operand);
1875 const array_len = o.air.typeOf(ty_op.operand).elemType().arrayLen();2054 const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();
18762055
1877 try writer.writeAll(" = { .ptr = ");2056 try writer.writeAll(" = { .ptr = ");
1878 try o.writeCValue(writer, operand);2057 try f.writeCValue(writer, operand);
1879 try writer.print(", .len = {d} }};\n", .{array_len});2058 try writer.print(", .len = {d} }};\n", .{array_len});
1880 return local;2059 return local;
1881}2060}
18822061
1883fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {2062/// Emits a local variable with the result type and initializes it
1884 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;2063/// with the operand.
1885 const extra = o.air.extraData(Air.Cmpxchg, ty_pl.payload).data;2064fn airSimpleCast(f: *Function, inst: Air.Inst.Index) !CValue {
1886 const inst_ty = o.air.typeOfIndex(inst);2065 if (f.liveness.isUnused(inst))
1887 const ptr = try o.resolveInst(extra.ptr);2066 return CValue.none;
1888 const expected_value = try o.resolveInst(extra.expected_value);2067
1889 const new_value = try o.resolveInst(extra.new_value);2068 const inst_ty = f.air.typeOfIndex(inst);
1890 const local = try o.allocLocal(inst_ty, .Const);2069 const local = try f.allocLocal(inst_ty, .Const);
1891 const writer = o.writer();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
2080fn 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
2097fn 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();
18922106
1893 try writer.print(" = zig_cmpxchg_{s}(", .{flavor});2107 try writer.print(" = zig_cmpxchg_{s}(", .{flavor});
1894 try o.writeCValue(writer, ptr);2108 try f.writeCValue(writer, ptr);
1895 try writer.writeAll(", ");2109 try writer.writeAll(", ");
1896 try o.writeCValue(writer, expected_value);2110 try f.writeCValue(writer, expected_value);
1897 try writer.writeAll(", ");2111 try writer.writeAll(", ");
1898 try o.writeCValue(writer, new_value);2112 try f.writeCValue(writer, new_value);
1899 try writer.writeAll(", ");2113 try writer.writeAll(", ");
1900 try writeMemoryOrder(writer, extra.successOrder());2114 try writeMemoryOrder(writer, extra.successOrder());
1901 try writer.writeAll(", ");2115 try writer.writeAll(", ");
...@@ -1905,8 +2119,134 @@ fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {...@@ -1905,8 +2119,134 @@ fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
1905 return local;2119 return local;
1906}2120}
19072121
1908fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void {2122fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
1909 const str = switch (order) {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
2142fn 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
2162fn 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
2179fn 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
2198fn 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
2217fn 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
2232fn 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
2248fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
2249 return switch (order) {
1910 .Unordered => "memory_order_relaxed",2250 .Unordered => "memory_order_relaxed",
1911 .Monotonic => "memory_order_consume",2251 .Monotonic => "memory_order_consume",
1912 .Acquire => "memory_order_acquire",2252 .Acquire => "memory_order_acquire",
...@@ -1914,7 +2254,24 @@ fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void {...@@ -1914,7 +2254,24 @@ fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void {
1914 .AcqRel => "memory_order_acq_rel",2254 .AcqRel => "memory_order_acq_rel",
1915 .SeqCst => "memory_order_seq_cst",2255 .SeqCst => "memory_order_seq_cst",
1916 };2256 };
1917 return w.writeAll(str);2257}
2258
2259fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void {
2260 return w.writeAll(toMemoryOrder(order));
2261}
2262
2263fn 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}
19192276
1920fn IndentWriter(comptime UnderlyingWriter: type) type {2277fn IndentWriter(comptime UnderlyingWriter: type) type {
src/codegen/llvm.zig+1032-210
...@@ -158,15 +158,42 @@ pub const Object = struct {...@@ -158,15 +158,42 @@ pub const Object = struct {
158 llvm_module: *const llvm.Module,158 llvm_module: *const llvm.Module,
159 context: *const llvm.Context,159 context: *const llvm.Context,
160 target_machine: *const llvm.TargetMachine,160 target_machine: *const llvm.TargetMachine,
161161 /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function,
162 pub fn create(gpa: *Allocator, options: link.Options) !*Object {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 const obj = try gpa.create(Object);190 const obj = try gpa.create(Object);
164 errdefer gpa.destroy(obj);191 errdefer gpa.destroy(obj);
165 obj.* = try Object.init(gpa, options);192 obj.* = try Object.init(gpa, sub_path, options);
166 return obj;193 return obj;
167 }194 }
168195
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 const context = llvm.Context.create();197 const context = llvm.Context.create();
171 errdefer context.dispose();198 errdefer context.dispose();
172199
...@@ -240,22 +267,34 @@ pub const Object = struct {...@@ -240,22 +267,34 @@ pub const Object = struct {
240 );267 );
241 errdefer target_machine.dispose();268 errdefer target_machine.dispose();
242269
270 const target_data = target_machine.createTargetDataLayout();
271 defer target_data.dispose();
272
273 llvm_module.setModuleDataLayout(target_data);
274
243 return Object{275 return Object{
244 .llvm_module = llvm_module,276 .llvm_module = llvm_module,
245 .context = context,277 .context = context,
246 .target_machine = target_machine,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 }
249285
250 pub fn deinit(self: *Object) void {286 pub fn deinit(self: *Object, gpa: *Allocator) void {
251 self.target_machine.dispose();287 self.target_machine.dispose();
252 self.llvm_module.dispose();288 self.llvm_module.dispose();
253 self.context.dispose();289 self.context.dispose();
290 self.decl_map.deinit(gpa);
291 self.type_map.deinit(gpa);
292 self.type_map_arena.deinit();
254 self.* = undefined;293 self.* = undefined;
255 }294 }
256295
257 pub fn destroy(self: *Object, gpa: *Allocator) void {296 pub fn destroy(self: *Object, gpa: *Allocator) void {
258 self.deinit();297 self.deinit(gpa);
259 gpa.destroy(self);298 gpa.destroy(self);
260 }299 }
261300
...@@ -293,23 +332,18 @@ pub const Object = struct {...@@ -293,23 +332,18 @@ pub const Object = struct {
293 const mod = comp.bin_file.options.module.?;332 const mod = comp.bin_file.options.module.?;
294 const cache_dir = mod.zig_cache_artifact_directory;333 const cache_dir = mod.zig_cache_artifact_directory;
295334
296 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit != null) blk: {335 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit|
297 const obj_basename = try std.zig.binNameAlloc(arena, .{336 try emit.directory.joinZ(arena, &[_][]const u8{self.sub_path})
298 .root_name = comp.bin_file.options.root_name,337 else
299 .target = comp.bin_file.options.target,338 null;
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;
308339
309 const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);340 const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);
310 const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);341 const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);
311 const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir);342 const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir);
312343
344 const debug_emit_path = emit_bin_path orelse "(none)";
345 log.debug("emit LLVM object to {s}", .{debug_emit_path});
346
313 var error_message: [*:0]const u8 = undefined;347 var error_message: [*:0]const u8 = undefined;
314 if (self.target_machine.emitToFile(348 if (self.target_machine.emitToFile(
315 self.llvm_module,349 self.llvm_module,
...@@ -358,18 +392,31 @@ pub const Object = struct {...@@ -358,18 +392,31 @@ pub const Object = struct {
358392
359 const llvm_func = try dg.resolveLlvmFunction(decl);393 const llvm_func = try dg.resolveLlvmFunction(decl);
360394
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 // This gets the LLVM values from the function and stores them in `dg.args`.409 // This gets the LLVM values from the function and stores them in `dg.args`.
362 const fn_param_len = decl.ty.fnParamLen();410 const fn_info = decl.ty.fnInfo();
363 var args = try dg.gpa.alloc(*const llvm.Value, fn_param_len);411 var args = try dg.gpa.alloc(*const llvm.Value, fn_info.param_types.len);
364412
365 for (args) |*arg, i| {413 for (args) |*arg, i| {
366 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));414 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));
367 }415 }
368416
369 // We remove all the basic blocks of a function to support incremental417 // Remove all the basic blocks of a function in order to start over, generating
370 // compilation!418 // LLVM IR from an empty function body.
371 // TODO: remove all basic blocks if functions can have more than one419 while (llvm_func.getFirstBasicBlock()) |bb| {
372 if (llvm_func.getFirstBasicBlock()) |bb| {
373 bb.deleteBasicBlock();420 bb.deleteBasicBlock();
374 }421 }
375422
...@@ -440,30 +487,62 @@ pub const Object = struct {...@@ -440,30 +487,62 @@ pub const Object = struct {
440 ) !void {487 ) !void {
441 // If the module does not already have the function, we ignore this function call488 // If the module does not already have the function, we ignore this function call
442 // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`.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 const is_extern = decl.val.tag() == .extern_fn;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 llvm_fn.setLinkage(.External);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 llvm_fn.setUnnamedAddr(.False);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 } else {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 llvm_fn.setLinkage(.Internal);538 llvm_fn.setLinkage(.Internal);
450 llvm_fn.setUnnamedAddr(.True);539 llvm_fn.setUnnamedAddr(.True);
451 }540 }
452 // TODO LLVM C API does not support deleting aliases. We need to541 }
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);
459542
460 if (self.llvm_module.getNamedGlobalAlias(exp_name_z.ptr, exp_name_z.len)) |alias| {543 pub fn freeDecl(self: *Object, decl: *Module.Decl) void {
461 alias.setAliasee(llvm_fn);544 const llvm_value = self.decl_map.get(decl) orelse return;
462 } else {545 llvm_value.deleteGlobal();
463 const alias = self.llvm_module.addAlias(llvm_fn.typeOf(), llvm_fn, exp_name_z);
464 _ = alias;
465 }
466 }
467 }546 }
468};547};
469548
...@@ -472,9 +551,8 @@ pub const DeclGen = struct {...@@ -472,9 +551,8 @@ pub const DeclGen = struct {
472 object: *Object,551 object: *Object,
473 module: *Module,552 module: *Module,
474 decl: *Module.Decl,553 decl: *Module.Decl,
475 err_msg: ?*Module.ErrorMsg,
476
477 gpa: *Allocator,554 gpa: *Allocator,
555 err_msg: ?*Module.ErrorMsg,
478556
479 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {557 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
480 @setCold(true);558 @setCold(true);
...@@ -515,33 +593,47 @@ pub const DeclGen = struct {...@@ -515,33 +593,47 @@ pub const DeclGen = struct {
515 }593 }
516 }594 }
517595
518 /// If the llvm function does not exist, create it596 /// 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 fn resolveLlvmFunction(self: *DeclGen, decl: *Module.Decl) !*const llvm.Value {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.*;
521602
522 assert(decl.has_tv);603 assert(decl.has_tv);
523 const zig_fn_type = decl.ty;604 const zig_fn_type = decl.ty;
524 const return_type = zig_fn_type.fnReturnType();605 const fn_info = zig_fn_type.fnInfo();
525 const fn_param_len = zig_fn_type.fnParamLen();606 const return_type = fn_info.return_type;
526607
527 const fn_param_types = try self.gpa.alloc(Type, fn_param_len);608 const llvm_param_buffer = try self.gpa.alloc(*const llvm.Type, fn_info.param_types.len);
528 defer self.gpa.free(fn_param_types);609 defer self.gpa.free(llvm_param_buffer);
529 zig_fn_type.fnParamTypes(fn_param_types);
530610
531 const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len);611 var llvm_params_len: c_uint = 0;
532 defer self.gpa.free(llvm_param);612 for (fn_info.param_types) |param_ty| {
533613 if (param_ty.hasCodeGenBits()) {
534 for (fn_param_types) |fn_param, i| {614 llvm_param_buffer[llvm_params_len] = try self.llvmType(param_ty);
535 llvm_param[i] = try self.llvmType(fn_param);615 llvm_params_len += 1;
616 }
536 }617 }
537618
619 const llvm_ret_ty = if (!return_type.hasCodeGenBits())
620 self.context.voidType()
621 else
622 try self.llvmType(return_type);
623
538 const fn_type = llvm.functionType(624 const fn_type = llvm.functionType(
539 try self.llvmType(return_type),625 llvm_ret_ty,
540 llvm_param.ptr,626 llvm_param_buffer.ptr,
541 @intCast(c_uint, fn_param_len),627 llvm_params_len,
542 .False,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;
545637
546 const is_extern = decl.val.tag() == .extern_fn;638 const is_extern = decl.val.tag() == .extern_fn;
547 if (!is_extern) {639 if (!is_extern) {
...@@ -549,8 +641,85 @@ pub const DeclGen = struct {...@@ -549,8 +641,85 @@ pub const DeclGen = struct {
549 llvm_fn.setUnnamedAddr(.True);641 llvm_fn.setUnnamedAddr(.True);
550 }642 }
551643
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 }
553706
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 if (return_type.isNoReturn()) {723 if (return_type.isNoReturn()) {
555 self.addFnAttr(llvm_fn, "noreturn");724 self.addFnAttr(llvm_fn, "noreturn");
556 }725 }
...@@ -563,18 +732,41 @@ pub const DeclGen = struct {...@@ -563,18 +732,41 @@ pub const DeclGen = struct {
563 if (llvm_module.getNamedGlobal(decl.name)) |val| return val;732 if (llvm_module.getNamedGlobal(decl.name)) |val| return val;
564 // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.733 // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.
565 const llvm_type = try self.llvmType(decl.ty);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 }
568754
569 fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {755 fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
756 const gpa = self.gpa;
570 log.debug("llvmType for {}", .{t});757 log.debug("llvmType for {}", .{t});
571 switch (t.zigTypeTag()) {758 switch (t.zigTypeTag()) {
572 .Void => return self.context.voidType(),759 .Void, .NoReturn => return self.context.voidType(),
573 .NoReturn => return self.context.voidType(),
574 .Int => {760 .Int => {
575 const info = t.intInfo(self.module.getTarget());761 const info = t.intInfo(self.module.getTarget());
576 return self.context.intType(info.bits);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 .Float => switch (t.floatBits(self.module.getTarget())) {770 .Float => switch (t.floatBits(self.module.getTarget())) {
579 16 => return self.context.halfType(),771 16 => return self.context.halfType(),
580 32 => return self.context.floatType(),772 32 => return self.context.floatType(),
...@@ -586,7 +778,7 @@ pub const DeclGen = struct {...@@ -586,7 +778,7 @@ pub const DeclGen = struct {
586 .Bool => return self.context.intType(1),778 .Bool => return self.context.intType(1),
587 .Pointer => {779 .Pointer => {
588 if (t.isSlice()) {780 if (t.isSlice()) {
589 var buf: Type.Payload.ElemType = undefined;781 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
590 const ptr_type = t.slicePtrFieldType(&buf);782 const ptr_type = t.slicePtrFieldType(&buf);
591783
592 const fields: [2]*const llvm.Type = .{784 const fields: [2]*const llvm.Type = .{
...@@ -596,7 +788,8 @@ pub const DeclGen = struct {...@@ -596,7 +788,8 @@ pub const DeclGen = struct {
596 return self.context.structType(&fields, fields.len, .False);788 return self.context.structType(&fields, fields.len, .False);
597 } else {789 } else {
598 const elem_type = try self.llvmType(t.elemType());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 .Array => {795 .Array => {
...@@ -605,18 +798,18 @@ pub const DeclGen = struct {...@@ -605,18 +798,18 @@ pub const DeclGen = struct {
605 return elem_type.arrayType(@intCast(c_uint, total_len));798 return elem_type.arrayType(@intCast(c_uint, total_len));
606 },799 },
607 .Optional => {800 .Optional => {
608 if (!t.isPtrLikeOptional()) {801 var buf: Type.Payload.ElemType = undefined;
609 var buf: Type.Payload.ElemType = undefined;802 const child_type = t.optionalChild(&buf);
610 const child_type = t.optionalChild(&buf);803 const payload_llvm_ty = try self.llvmType(child_type);
611804
612 const optional_types: [2]*const llvm.Type = .{805 if (t.isPtrLikeOptional()) {
613 try self.llvmType(child_type),806 return payload_llvm_ty;
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", .{});
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 .ErrorUnion => {814 .ErrorUnion => {
622 const error_type = t.errorUnionSet();815 const error_type = t.errorUnionSet();
...@@ -634,24 +827,67 @@ pub const DeclGen = struct {...@@ -634,24 +827,67 @@ pub const DeclGen = struct {
634 return self.context.intType(16);827 return self.context.intType(16);
635 },828 },
636 .Struct => {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 const struct_obj = t.castTag(.@"struct").?.data;837 const struct_obj = t.castTag(.@"struct").?.data;
638 assert(struct_obj.haveFieldTypes());838 assert(struct_obj.haveFieldTypes());
639 const llvm_fields = try self.gpa.alloc(*const llvm.Type, struct_obj.fields.count());839
640 defer self.gpa.free(llvm_fields);840 const name = try struct_obj.getFullyQualifiedName(gpa);
641 for (struct_obj.fields.values()) |field, i| {841 defer gpa.free(name);
642 llvm_fields[i] = try self.llvmType(field.ty);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(854
645 llvm_fields.ptr,855 llvm_struct_ty.structSetBody(
646 @intCast(c_uint, llvm_fields.len),856 llvm_field_types.items.ptr,
647 .False,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 .Fn => {886 .Fn => {
651 const ret_ty = try self.llvmType(t.fnReturnType());887 const ret_ty = try self.llvmType(t.fnReturnType());
652 const params_len = t.fnParamLen();888 const params_len = t.fnParamLen();
653 const llvm_params = try self.gpa.alloc(*const llvm.Type, params_len);889 const llvm_params = try gpa.alloc(*const llvm.Type, params_len);
654 defer self.gpa.free(llvm_params);890 defer gpa.free(llvm_params);
655 for (llvm_params) |*llvm_param, i| {891 for (llvm_params) |*llvm_param, i| {
656 llvm_param.* = try self.llvmType(t.fnParamType(i));892 llvm_param.* = try self.llvmType(t.fnParamType(i));
657 }893 }
...@@ -662,7 +898,9 @@ pub const DeclGen = struct {...@@ -662,7 +898,9 @@ pub const DeclGen = struct {
662 @intCast(c_uint, llvm_params.len),898 @intCast(c_uint, llvm_params.len),
663 llvm.Bool.fromBool(is_var_args),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 .ComptimeInt => unreachable,905 .ComptimeInt => unreachable,
668 .ComptimeFloat => unreachable,906 .ComptimeFloat => unreachable,
...@@ -673,8 +911,6 @@ pub const DeclGen = struct {...@@ -673,8 +911,6 @@ pub const DeclGen = struct {
673911
674 .BoundFn => @panic("TODO remove BoundFn from the language"),912 .BoundFn => @panic("TODO remove BoundFn from the language"),
675913
676 .Enum,
677 .Union,
678 .Opaque,914 .Opaque,
679 .Frame,915 .Frame,
680 .AnyFrame,916 .AnyFrame,
...@@ -701,13 +937,24 @@ pub const DeclGen = struct {...@@ -701,13 +937,24 @@ pub const DeclGen = struct {
701 const llvm_type = try self.llvmType(tv.ty);937 const llvm_type = try self.llvmType(tv.ty);
702 if (bigint.eqZero()) return llvm_type.constNull();938 if (bigint.eqZero()) return llvm_type.constNull();
703939
704 if (bigint.limbs.len != 1) {940 const unsigned_val = if (bigint.limbs.len == 1)
705 return self.todo("implement bigger bigint", .{});941 llvm_type.constInt(bigint.limbs[0], .False)
706 }942 else
707 const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);943 llvm_type.constIntOfArbitraryPrecision(@intCast(c_uint, bigint.limbs.len), bigint.limbs.ptr);
708 if (!bigint.positive) {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 return llvm_int;958 return llvm_int;
712 },959 },
713 .Float => {960 .Float => {
...@@ -720,7 +967,7 @@ pub const DeclGen = struct {...@@ -720,7 +967,7 @@ pub const DeclGen = struct {
720 .Pointer => switch (tv.val.tag()) {967 .Pointer => switch (tv.val.tag()) {
721 .decl_ref => {968 .decl_ref => {
722 if (tv.ty.isSlice()) {969 if (tv.ty.isSlice()) {
723 var buf: Type.Payload.ElemType = undefined;970 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
724 const ptr_ty = tv.ty.slicePtrFieldType(&buf);971 const ptr_ty = tv.ty.slicePtrFieldType(&buf);
725 var slice_len: Value.Payload.U64 = .{972 var slice_len: Value.Payload.U64 = .{
726 .base = .{ .tag = .int_u64 },973 .base = .{ .tag = .int_u64 },
...@@ -750,12 +997,13 @@ pub const DeclGen = struct {...@@ -750,12 +997,13 @@ pub const DeclGen = struct {
750 decl.alive = true;997 decl.alive = true;
751 const val = try self.resolveGlobalDecl(decl);998 const val = try self.resolveGlobalDecl(decl);
752 const llvm_var_type = try self.llvmType(tv.ty);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 return val.constBitCast(llvm_type);1002 return val.constBitCast(llvm_type);
755 },1003 },
756 .slice => {1004 .slice => {
757 const slice = tv.val.castTag(.slice).?.data;1005 const slice = tv.val.castTag(.slice).?.data;
758 var buf: Type.Payload.ElemType = undefined;1006 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
759 const fields: [2]*const llvm.Value = .{1007 const fields: [2]*const llvm.Value = .{
760 try self.genTypedValue(.{1008 try self.genTypedValue(.{
761 .ty = tv.ty.slicePtrFieldType(&buf),1009 .ty = tv.ty.slicePtrFieldType(&buf),
...@@ -873,42 +1121,80 @@ pub const DeclGen = struct {...@@ -873,42 +1121,80 @@ pub const DeclGen = struct {
873 return self.context.constStruct(&fields, fields.len, .False);1121 return self.context.constStruct(&fields, fields.len, .False);
874 },1122 },
875 .Struct => {1123 .Struct => {
876 const fields_len = tv.ty.structFieldCount();1124 const llvm_struct_ty = try self.llvmType(tv.ty);
877 const field_vals = tv.val.castTag(.@"struct").?.data;1125 const field_vals = tv.val.castTag(.@"struct").?.data;
878 const gpa = self.gpa;1126 const gpa = self.gpa;
879 const llvm_fields = try gpa.alloc(*const llvm.Value, fields_len);1127
880 defer gpa.free(llvm_fields);1128 var llvm_fields: std.ArrayListUnmanaged(*const llvm.Value) = .{};
881 for (llvm_fields) |*llvm_field, i| {1129 try llvm_fields.ensureTotalCapacity(gpa, field_vals.len);
882 llvm_field.* = try self.genTypedValue(.{1130 defer llvm_fields.deinit(gpa);
883 .ty = tv.ty.structFieldType(i),1131
884 .val = field_vals[i],1132 for (field_vals) |field_val, i| {
885 });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(1141 return llvm_struct_ty.constNamedStruct(
888 llvm_fields.ptr,1142 llvm_fields.items.ptr,
889 @intCast(c_uint, llvm_fields.len),1143 @intCast(c_uint, llvm_fields.items.len),
890 .False,
891 );1144 );
892 },1145 },
893 .ComptimeInt => unreachable,1146 .ComptimeInt => unreachable,
894 .ComptimeFloat => unreachable,1147 .ComptimeFloat => unreachable,
895 .Type => unreachable,1148 .Type => unreachable,
896 .EnumLiteral => unreachable,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 }
9001164
901 // Helper functions1165 fn addAttr(dg: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
902 fn addAttr(self: *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 const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);1170 const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);
904 assert(kind_id != 0);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 val.addAttributeAtIndex(index, llvm_attr);1185 val.addAttributeAtIndex(index, llvm_attr);
907 }1186 }
9081187
909 fn addFnAttr(self: *DeclGen, val: *const llvm.Value, attr_name: []const u8) void {1188 fn addFnAttr(dg: *DeclGen, val: *const llvm.Value, name: []const u8) void {
910 // TODO: improve this API, `addAttr(-1, attr_name)`1189 dg.addAttr(val, std.math.maxInt(llvm.AttributeIndex), name);
911 self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_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 }
9131199
914 /// If the operand type of an atomic operation is not byte sized we need to1200 /// If the operand type of an atomic operation is not byte sized we need to
...@@ -920,7 +1206,7 @@ pub const DeclGen = struct {...@@ -920,7 +1206,7 @@ pub const DeclGen = struct {
920 var buffer: Type.Payload.Bits = undefined;1206 var buffer: Type.Payload.Bits = undefined;
921 const int_ty = switch (ty.zigTypeTag()) {1207 const int_ty = switch (ty.zigTypeTag()) {
922 .Int => ty,1208 .Int => ty,
923 .Enum => ty.enumTagType(&buffer),1209 .Enum => ty.intTagType(&buffer),
924 .Float => {1210 .Float => {
925 if (!is_rmw_xchg) return null;1211 if (!is_rmw_xchg) return null;
926 return dg.context.intType(@intCast(c_uint, ty.abiSize(target) * 8));1212 return dg.context.intType(@intCast(c_uint, ty.abiSize(target) * 8));
...@@ -950,7 +1236,7 @@ pub const FuncGen = struct {...@@ -950,7 +1236,7 @@ pub const FuncGen = struct {
950 /// in other instructions. This table is cleared before every function is generated.1236 /// in other instructions. This table is cleared before every function is generated.
951 func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Index, *const llvm.Value),1237 func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Index, *const llvm.Value),
9521238
953 /// These fields are used to refer to the LLVM value of the function paramaters1239 /// These fields are used to refer to the LLVM value of the function parameters
954 /// in an Arg instruction.1240 /// in an Arg instruction.
955 args: []*const llvm.Value,1241 args: []*const llvm.Value,
956 arg_index: usize,1242 arg_index: usize,
...@@ -1003,22 +1289,27 @@ pub const FuncGen = struct {...@@ -1003,22 +1289,27 @@ pub const FuncGen = struct {
1003 for (body) |inst| {1289 for (body) |inst| {
1004 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {1290 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
1005 // zig fmt: off1291 // zig fmt: off
1006 .add => try self.airAdd(inst, false),1292 .add => try self.airAdd(inst),
1007 .addwrap => try self.airAdd(inst, true),1293 .addwrap => try self.airAddWrap(inst),
1008 .sub => try self.airSub(inst, false),1294 .add_sat => try self.airAddSat(inst),
1009 .subwrap => try self.airSub(inst, true),1295 .sub => try self.airSub(inst),
1010 .mul => try self.airMul(inst, false),1296 .subwrap => try self.airSubWrap(inst),
1011 .mulwrap => try self.airMul(inst, true),1297 .sub_sat => try self.airSubSat(inst),
1012 .div => try self.airDiv(inst),1298 .mul => try self.airMul(inst),
1013 .rem => try self.airRem(inst),1299 .mulwrap => try self.airMulWrap(inst),
1014 .ptr_add => try self.airPtrAdd(inst),1300 .mul_sat => try self.airMulSat(inst),
1015 .ptr_sub => try self.airPtrSub(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),
10161309
1017 .bit_and, .bool_and => try self.airAnd(inst),1310 .bit_and, .bool_and => try self.airAnd(inst),
1018 .bit_or, .bool_or => try self.airOr(inst),1311 .bit_or, .bool_or => try self.airOr(inst),
1019 .xor => try self.airXor(inst),1312 .xor => try self.airXor(inst),
1020
1021 .shl => try self.airShl(inst),
1022 .shr => try self.airShr(inst),1313 .shr => try self.airShr(inst),
10231314
1024 .cmp_eq => try self.airCmp(inst, .eq),1315 .cmp_eq => try self.airCmp(inst, .eq),
...@@ -1049,7 +1340,8 @@ pub const FuncGen = struct {...@@ -1049,7 +1340,8 @@ pub const FuncGen = struct {
1049 .cond_br => try self.airCondBr(inst),1340 .cond_br => try self.airCondBr(inst),
1050 .intcast => try self.airIntCast(inst),1341 .intcast => try self.airIntCast(inst),
1051 .trunc => try self.airTrunc(inst),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 .ptrtoint => try self.airPtrToInt(inst),1345 .ptrtoint => try self.airPtrToInt(inst),
1054 .load => try self.airLoad(inst),1346 .load => try self.airLoad(inst),
1055 .loop => try self.airLoop(inst),1347 .loop => try self.airLoop(inst),
...@@ -1060,8 +1352,24 @@ pub const FuncGen = struct {...@@ -1060,8 +1352,24 @@ pub const FuncGen = struct {
1060 .slice_ptr => try self.airSliceField(inst, 0),1352 .slice_ptr => try self.airSliceField(inst, 0),
1061 .slice_len => try self.airSliceField(inst, 1),1353 .slice_len => try self.airSliceField(inst, 1),
1062 .array_to_slice => try self.airArrayToSlice(inst),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 .cmpxchg_weak => try self.airCmpxchg(inst, true),1357 .cmpxchg_weak => try self.airCmpxchg(inst, true),
1064 .cmpxchg_strong => try self.airCmpxchg(inst, false),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),
10651373
1066 .struct_field_ptr => try self.airStructFieldPtr(inst),1374 .struct_field_ptr => try self.airStructFieldPtr(inst),
1067 .struct_field_val => try self.airStructFieldVal(inst),1375 .struct_field_val => try self.airStructFieldVal(inst),
...@@ -1152,21 +1460,15 @@ pub const FuncGen = struct {...@@ -1152,21 +1460,15 @@ pub const FuncGen = struct {
1152 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1460 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1153 const lhs = try self.resolveInst(bin_op.lhs);1461 const lhs = try self.resolveInst(bin_op.lhs);
1154 const rhs = try self.resolveInst(bin_op.rhs);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);
11561464
1157 switch (self.air.typeOf(bin_op.lhs).zigTypeTag()) {1465 const int_ty = switch (operand_ty.zigTypeTag()) {
1158 .Int, .Bool, .Pointer, .ErrorSet => {1466 .Enum => blk: {
1159 const is_signed = inst_ty.isSignedInt();1467 var buffer: Type.Payload.Bits = undefined;
1160 const operation = switch (op) {1468 const int_ty = operand_ty.intTagType(&buffer);
1161 .eq => .EQ,1469 break :blk int_ty;
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, "");
1169 },1470 },
1471 .Int, .Bool, .Pointer, .ErrorSet => operand_ty,
1170 .Float => {1472 .Float => {
1171 const operation: llvm.RealPredicate = switch (op) {1473 const operation: llvm.RealPredicate = switch (op) {
1172 .eq => .OEQ,1474 .eq => .OEQ,
...@@ -1179,7 +1481,17 @@ pub const FuncGen = struct {...@@ -1179,7 +1481,17 @@ pub const FuncGen = struct {
1179 return self.builder.buildFCmp(operation, lhs, rhs, "");1481 return self.builder.buildFCmp(operation, lhs, rhs, "");
1180 },1482 },
1181 else => unreachable,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 }
11841496
1185 fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1497 fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -1285,19 +1597,57 @@ pub const FuncGen = struct {...@@ -1285,19 +1597,57 @@ pub const FuncGen = struct {
1285 return null;1597 return null;
12861598
1287 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1599 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1288 const operand = try self.resolveInst(ty_op.operand);1600 const operand_ty = self.air.typeOf(ty_op.operand);
1289 const array_len = self.air.typeOf(ty_op.operand).elemType().arrayLen();1601 const array_ty = operand_ty.childType();
1290 const usize_llvm_ty = try self.dg.llvmType(Type.initTag(.usize));1602 const llvm_usize = try self.dg.llvmType(Type.usize);
1291 const len = usize_llvm_ty.constInt(array_len, .False);1603 const len = llvm_usize.constInt(array_ty.arrayLen(), .False);
1292 const slice_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));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 const indices: [2]*const llvm.Value = .{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 const ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");1612 const ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
1297 const partial = self.builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr, 0, "");1613 const partial = self.builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr, 0, "");
1298 return self.builder.buildInsertValue(partial, len, 1, "");1614 return self.builder.buildInsertValue(partial, len, 1, "");
1299 }1615 }
13001616
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 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {1651 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {
1302 if (self.liveness.isUnused(inst))1652 if (self.liveness.isUnused(inst))
1303 return null;1653 return null;
...@@ -1398,27 +1748,31 @@ pub const FuncGen = struct {...@@ -1398,27 +1748,31 @@ pub const FuncGen = struct {
1398 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1748 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1399 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;1749 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
1400 const struct_ptr = try self.resolveInst(struct_field.struct_operand);1750 const struct_ptr = try self.resolveInst(struct_field.struct_operand);
1401 const field_index = @intCast(c_uint, struct_field.field_index);1751 const struct_ptr_ty = self.air.typeOf(struct_field.struct_operand);
1402 return self.builder.buildStructGEP(struct_ptr, field_index, "");1752 return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, struct_field.field_index);
1403 }1753 }
14041754
1405 fn airStructFieldPtrIndex(self: *FuncGen, inst: Air.Inst.Index, field_index: c_uint) !?*const llvm.Value {1755 fn airStructFieldPtrIndex(
1406 if (self.liveness.isUnused(inst))1756 self: *FuncGen,
1407 return null;1757 inst: Air.Inst.Index,
1758 field_index: u32,
1759 ) !?*const llvm.Value {
1760 if (self.liveness.isUnused(inst)) return null;
14081761
1409 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1762 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1410 const struct_ptr = try self.resolveInst(ty_op.operand);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 }
14131767
1414 fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1768 fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1415 if (self.liveness.isUnused(inst))1769 if (self.liveness.isUnused(inst)) return null;
1416 return null;
14171770
1418 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1771 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1419 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;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 const struct_byval = try self.resolveInst(struct_field.struct_operand);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 return self.builder.buildExtractValue(struct_byval, field_index, "");1776 return self.builder.buildExtractValue(struct_byval, field_index, "");
1423 }1777 }
14241778
...@@ -1569,6 +1923,13 @@ pub const FuncGen = struct {...@@ -1569,6 +1923,13 @@ pub const FuncGen = struct {
1569 const operand = try self.resolveInst(un_op);1923 const operand = try self.resolveInst(un_op);
15701924
1571 if (operand_is_ptr) {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 const index_type = self.context.intType(32);1933 const index_type = self.context.intType(32);
15731934
1574 var indices: [2]*const llvm.Value = .{1935 var indices: [2]*const llvm.Value = .{
...@@ -1577,9 +1938,15 @@ pub const FuncGen = struct {...@@ -1577,9 +1938,15 @@ pub const FuncGen = struct {
1577 };1938 };
15781939
1579 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""), "");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 }
15841951
1585 fn airIsNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value {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,17 +1999,24 @@ pub const FuncGen = struct {
1632 const operand = try self.resolveInst(ty_op.operand);1999 const operand = try self.resolveInst(ty_op.operand);
16332000
1634 if (operand_is_ptr) {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 }
16362006
2007 const index_type = self.context.intType(32);
1637 var indices: [2]*const llvm.Value = .{2008 var indices: [2]*const llvm.Value = .{
1638 index_type.constNull(),2009 index_type.constNull(), index_type.constNull(),
1639 index_type.constNull(),
1640 };2010 };
1641
1642 return self.builder.buildInBoundsGEP(operand, &indices, 2, "");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 }
16472021
1648 fn airErrUnionPayload(2022 fn airErrUnionPayload(
...@@ -1716,51 +2090,115 @@ pub const FuncGen = struct {...@@ -1716,51 +2090,115 @@ pub const FuncGen = struct {
1716 return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});2090 return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});
1717 }2091 }
17182092
1719 fn airAdd(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {2093 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1720 if (self.liveness.isUnused(inst))2094 if (self.liveness.isUnused(inst)) return null;
1721 return null;
17222095
1723 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2096 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1724 const lhs = try self.resolveInst(bin_op.lhs);2097 const lhs = try self.resolveInst(bin_op.lhs);
1725 const rhs = try self.resolveInst(bin_op.rhs);2098 const rhs = try self.resolveInst(bin_op.rhs);
1726 const inst_ty = self.air.typeOfIndex(inst);2099 const inst_ty = self.air.typeOfIndex(inst);
17272100
1728 if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, "");2101 if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, "");
1729 if (wrap) return self.builder.buildAdd(lhs, rhs, "");
1730 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");2102 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
1731 return self.builder.buildNUWAdd(lhs, rhs, "");2103 return self.builder.buildNUWAdd(lhs, rhs, "");
1732 }2104 }
17332105
1734 fn airSub(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {2106 fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1735 if (self.liveness.isUnused(inst))2107 if (self.liveness.isUnused(inst)) return null;
1736 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;
17372132
1738 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2133 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1739 const lhs = try self.resolveInst(bin_op.lhs);2134 const lhs = try self.resolveInst(bin_op.lhs);
1740 const rhs = try self.resolveInst(bin_op.rhs);2135 const rhs = try self.resolveInst(bin_op.rhs);
1741 const inst_ty = self.air.typeOfIndex(inst);2136 const inst_ty = self.air.typeOfIndex(inst);
17422137
1743 if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, "");2138 if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, "");
1744 if (wrap) return self.builder.buildSub(lhs, rhs, "");
1745 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");2139 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
1746 return self.builder.buildNUWSub(lhs, rhs, "");2140 return self.builder.buildNUWSub(lhs, rhs, "");
1747 }2141 }
17482142
1749 fn airMul(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {2143 fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1750 if (self.liveness.isUnused(inst))2144 if (self.liveness.isUnused(inst)) return null;
1751 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;
17522155
1753 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2156 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1754 const lhs = try self.resolveInst(bin_op.lhs);2157 const lhs = try self.resolveInst(bin_op.lhs);
1755 const rhs = try self.resolveInst(bin_op.rhs);2158 const rhs = try self.resolveInst(bin_op.rhs);
1756 const inst_ty = self.air.typeOfIndex(inst);2159 const inst_ty = self.air.typeOfIndex(inst);
17572160
1758 if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, "");2161 if (inst_ty.isAnyFloat()) return self.todo("saturating float sub", .{});
1759 if (wrap) return self.builder.buildMul(lhs, rhs, "");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 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");2175 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
1761 return self.builder.buildNUWMul(lhs, rhs, "");2176 return self.builder.buildNUWMul(lhs, rhs, "");
1762 }2177 }
17632178
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 fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2202 fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1765 if (self.liveness.isUnused(inst))2203 if (self.liveness.isUnused(inst))
1766 return null;2204 return null;
...@@ -1770,7 +2208,7 @@ pub const FuncGen = struct {...@@ -1770,7 +2208,7 @@ pub const FuncGen = struct {
1770 const rhs = try self.resolveInst(bin_op.rhs);2208 const rhs = try self.resolveInst(bin_op.rhs);
1771 const inst_ty = self.air.typeOfIndex(inst);2209 const inst_ty = self.air.typeOfIndex(inst);
17722210
1773 if (inst_ty.isFloat()) return self.builder.buildFDiv(lhs, rhs, "");2211 if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, "");
1774 if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");2212 if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
1775 return self.builder.buildUDiv(lhs, rhs, "");2213 return self.builder.buildUDiv(lhs, rhs, "");
1776 }2214 }
...@@ -1783,11 +2221,39 @@ pub const FuncGen = struct {...@@ -1783,11 +2221,39 @@ pub const FuncGen = struct {
1783 const rhs = try self.resolveInst(bin_op.rhs);2221 const rhs = try self.resolveInst(bin_op.rhs);
1784 const inst_ty = self.air.typeOfIndex(inst);2222 const inst_ty = self.air.typeOfIndex(inst);
17852223
1786 if (inst_ty.isFloat()) return self.builder.buildFRem(lhs, rhs, "");2224 if (inst_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, "");
1787 if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");2225 if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");
1788 return self.builder.buildURem(lhs, rhs, "");2226 return self.builder.buildURem(lhs, rhs, "");
1789 }2227 }
17902228
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 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2257 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1792 if (self.liveness.isUnused(inst))2258 if (self.liveness.isUnused(inst))
1793 return null;2259 return null;
...@@ -1838,9 +2304,25 @@ pub const FuncGen = struct {...@@ -1838,9 +2304,25 @@ pub const FuncGen = struct {
1838 return self.builder.buildXor(lhs, rhs, "");2304 return self.builder.buildXor(lhs, rhs, "");
1839 }2305 }
18402306
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 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2323 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1842 if (self.liveness.isUnused(inst))2324 if (self.liveness.isUnused(inst)) return null;
1843 return null;2325
1844 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2326 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1845 const lhs = try self.resolveInst(bin_op.lhs);2327 const lhs = try self.resolveInst(bin_op.lhs);
1846 const rhs = try self.resolveInst(bin_op.rhs);2328 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -1853,6 +2335,22 @@ pub const FuncGen = struct {...@@ -1853,6 +2335,22 @@ pub const FuncGen = struct {
1853 return self.builder.buildShl(lhs, casted_rhs, "");2335 return self.builder.buildShl(lhs, casted_rhs, "");
1854 }2336 }
18552337
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 fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2354 fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1857 if (self.liveness.isUnused(inst))2355 if (self.liveness.isUnused(inst))
1858 return null;2356 return null;
...@@ -1877,14 +2375,22 @@ pub const FuncGen = struct {...@@ -1877,14 +2375,22 @@ pub const FuncGen = struct {
1877 if (self.liveness.isUnused(inst))2375 if (self.liveness.isUnused(inst))
1878 return null;2376 return null;
18792377
2378 const target = self.dg.module.getTarget();
1880 const ty_op = self.air.instructions.items(.data)[inst].ty_op;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 const operand = try self.resolveInst(ty_op.operand);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);
18832386
1884 const signed = inst_ty.isSignedInt();2387 if (operand_info.bits < dest_info.bits) {
1885 // TODO: Should we use intcast here or just a simple bitcast?2388 switch (operand_info.signedness) {
1886 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes2389 .signed => return self.builder.buildSExt(operand, dest_llvm_ty, ""),
1887 return self.builder.buildIntCast2(operand, try self.dg.llvmType(inst_ty), llvm.Bool.fromBool(signed), "");2390 .unsigned => return self.builder.buildZExt(operand, dest_llvm_ty, ""),
2391 }
2392 }
2393 return self.builder.buildTrunc(operand, dest_llvm_ty, "");
1888 }2394 }
18892395
1890 fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2396 fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -1897,12 +2403,26 @@ pub const FuncGen = struct {...@@ -1897,12 +2403,26 @@ pub const FuncGen = struct {
1897 return self.builder.buildTrunc(operand, dest_llvm_ty, "");2403 return self.builder.buildTrunc(operand, dest_llvm_ty, "");
1898 }2404 }
18992405
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 if (self.liveness.isUnused(inst))2407 if (self.liveness.isUnused(inst))
1902 return null;2408 return null;
19032409
1904 // TODO split floatcast AIR into float_widen and float_shorten2410 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1905 return self.todo("implement 'airFloatCast'", .{});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 }
19072427
1908 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2428 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -1947,16 +2467,17 @@ pub const FuncGen = struct {...@@ -1947,16 +2467,17 @@ pub const FuncGen = struct {
1947 }2467 }
19482468
1949 fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2469 fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1950 if (self.liveness.isUnused(inst))2470 if (self.liveness.isUnused(inst)) return null;
1951 return null;
1952 // buildAlloca expects the pointee type, not the pointer type, so assert that2471 // buildAlloca expects the pointee type, not the pointer type, so assert that
1953 // a Payload.PointerSimple is passed to the alloc instruction.2472 // a Payload.PointerSimple is passed to the alloc instruction.
1954 const inst_ty = self.air.typeOfIndex(inst);2473 const ptr_ty = self.air.typeOfIndex(inst);
1955 const pointee_type = inst_ty.castPointer().?.data;2474 const pointee_type = ptr_ty.elemType();
19562475 if (!pointee_type.hasCodeGenBits()) return null;
1957 // TODO: figure out a way to get the name of the var decl.2476 const pointee_llvm_ty = try self.dg.llvmType(pointee_type);
1958 // TODO: set alignment and volatile2477 const target = self.dg.module.getTarget();
1959 return self.buildAlloca(try self.dg.llvmType(pointee_type));2478 const alloca_inst = self.buildAlloca(pointee_llvm_ty);
2479 alloca_inst.setAlignment(ptr_ty.ptrAlignment(target));
2480 return alloca_inst;
1960 }2481 }
19612482
1962 /// Use this instead of builder.buildAlloca, because this function makes sure to2483 /// Use this instead of builder.buildAlloca, because this function makes sure to
...@@ -1986,20 +2507,19 @@ pub const FuncGen = struct {...@@ -1986,20 +2507,19 @@ pub const FuncGen = struct {
1986 fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2507 fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1987 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2508 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1988 const dest_ptr = try self.resolveInst(bin_op.lhs);2509 const dest_ptr = try self.resolveInst(bin_op.lhs);
2510 const ptr_ty = self.air.typeOf(bin_op.lhs);
1989 const src_operand = try self.resolveInst(bin_op.rhs);2511 const src_operand = try self.resolveInst(bin_op.rhs);
1990 // TODO set volatile on this store properly2512 _ = self.store(dest_ptr, ptr_ty, src_operand);
1991 _ = self.builder.buildStore(src_operand, dest_ptr);
1992 return null;2513 return null;
1993 }2514 }
19942515
1995 fn airLoad(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2516 fn airLoad(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1996 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2517 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1997 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();2518 const ptr_ty = self.air.typeOf(ty_op.operand);
1998 if (!is_volatile and self.liveness.isUnused(inst))2519 if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst))
1999 return null;2520 return null;
2000 const ptr = try self.resolveInst(ty_op.operand);2521 const ptr = try self.resolveInst(ty_op.operand);
2001 // TODO set volatile on this load properly2522 return self.load(ptr, ptr_ty);
2002 return self.builder.buildLoad(ptr, "");
2003 }2523 }
20042524
2005 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2525 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -2009,6 +2529,14 @@ pub const FuncGen = struct {...@@ -2009,6 +2529,14 @@ pub const FuncGen = struct {
2009 return null;2529 return null;
2010 }2530 }
20112531
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 fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !?*const llvm.Value {2540 fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !?*const llvm.Value {
2013 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2541 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2014 const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data;2542 const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
...@@ -2039,8 +2567,6 @@ pub const FuncGen = struct {...@@ -2039,8 +2567,6 @@ pub const FuncGen = struct {
2039 result.setWeak(llvm.Bool.fromBool(is_weak));2567 result.setWeak(llvm.Bool.fromBool(is_weak));
20402568
2041 const optional_ty = self.air.typeOfIndex(inst);2569 const optional_ty = self.air.typeOfIndex(inst);
2042 var buffer: Type.Payload.ElemType = undefined;
2043 const child_ty = optional_ty.optionalChild(&buffer);
20442570
2045 var payload = self.builder.buildExtractValue(result, 0, "");2571 var payload = self.builder.buildExtractValue(result, 0, "");
2046 if (opt_abi_ty != null) {2572 if (opt_abi_ty != null) {
...@@ -2049,8 +2575,7 @@ pub const FuncGen = struct {...@@ -2049,8 +2575,7 @@ pub const FuncGen = struct {
2049 const success_bit = self.builder.buildExtractValue(result, 1, "");2575 const success_bit = self.builder.buildExtractValue(result, 1, "");
20502576
2051 if (optional_ty.isPtrLikeOptional()) {2577 if (optional_ty.isPtrLikeOptional()) {
2052 const child_llvm_ty = try self.dg.llvmType(child_ty);2578 return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, "");
2053 return self.builder.buildSelect(success_bit, child_llvm_ty.constNull(), payload, "");
2054 }2579 }
20552580
2056 const optional_llvm_ty = try self.dg.llvmType(optional_ty);2581 const optional_llvm_ty = try self.dg.llvmType(optional_ty);
...@@ -2059,6 +2584,252 @@ pub const FuncGen = struct {...@@ -2059,6 +2584,252 @@ pub const FuncGen = struct {
2059 return self.builder.buildInsertValue(partial, non_null_bit, 1, "");2584 return self.builder.buildInsertValue(partial, non_null_bit, 1, "");
2060 }2585 }
20612586
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 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {2833 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
2063 const id = llvm.lookupIntrinsicID(name.ptr, name.len);2834 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
2064 assert(id != 0);2835 assert(id != 0);
...@@ -2067,6 +2838,27 @@ pub const FuncGen = struct {...@@ -2067,6 +2838,27 @@ pub const FuncGen = struct {
2067 // `getIntrinsicDeclaration`2838 // `getIntrinsicDeclaration`
2068 return self.llvmModule().getIntrinsicDeclaration(id, null, 0);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};
20712863
2072fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void {2864fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void {
...@@ -2261,3 +3053,33 @@ fn toLlvmAtomicOrdering(atomic_order: std.builtin.AtomicOrder) llvm.AtomicOrderi...@@ -2261,3 +3053,33 @@ fn toLlvmAtomicOrdering(atomic_order: std.builtin.AtomicOrder) llvm.AtomicOrderi
2261 .SeqCst => .SequentiallyConsistent,3053 .SeqCst => .SequentiallyConsistent,
2262 };3054 };
2263}3055}
3056
3057fn 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.
3076fn 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,6 +85,9 @@ pub const Value = opaque {
85 pub const addAttributeAtIndex = LLVMAddAttributeAtIndex;85 pub const addAttributeAtIndex = LLVMAddAttributeAtIndex;
86 extern fn LLVMAddAttributeAtIndex(*const Value, Idx: AttributeIndex, A: *const Attribute) void;86 extern fn LLVMAddAttributeAtIndex(*const Value, Idx: AttributeIndex, A: *const Attribute) void;
8787
88 pub const removeEnumAttributeAtIndex = LLVMRemoveEnumAttributeAtIndex;
89 extern fn LLVMRemoveEnumAttributeAtIndex(F: *const Value, Idx: AttributeIndex, KindID: c_uint) void;
90
88 pub const getFirstBasicBlock = LLVMGetFirstBasicBlock;91 pub const getFirstBasicBlock = LLVMGetFirstBasicBlock;
89 extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock;92 extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock;
9093
...@@ -136,6 +139,30 @@ pub const Value = opaque {...@@ -136,6 +139,30 @@ pub const Value = opaque {
136139
137 pub const setWeak = LLVMSetWeak;140 pub const setWeak = LLVMSetWeak;
138 extern fn LLVMSetWeak(CmpXchgInst: *const Value, IsWeak: Bool) void;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};
140167
141pub const Type = opaque {168pub const Type = opaque {
...@@ -148,12 +175,22 @@ pub const Type = opaque {...@@ -148,12 +175,22 @@ pub const Type = opaque {
148 pub const constInt = LLVMConstInt;175 pub const constInt = LLVMConstInt;
149 extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value;176 extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value;
150177
178 pub const constIntOfArbitraryPrecision = LLVMConstIntOfArbitraryPrecision;
179 extern fn LLVMConstIntOfArbitraryPrecision(IntTy: *const Type, NumWords: c_uint, Words: [*]const u64) *const Value;
180
151 pub const constReal = LLVMConstReal;181 pub const constReal = LLVMConstReal;
152 extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value;182 extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value;
153183
154 pub const constArray = LLVMConstArray;184 pub const constArray = LLVMConstArray;
155 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]*const Value, Length: c_uint) *const Value;185 extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]*const Value, Length: c_uint) *const Value;
156186
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 pub const getUndef = LLVMGetUndef;194 pub const getUndef = LLVMGetUndef;
158 extern fn LLVMGetUndef(Ty: *const Type) *const Value;195 extern fn LLVMGetUndef(Ty: *const Type) *const Value;
159196
...@@ -170,6 +207,9 @@ pub const Type = opaque {...@@ -170,6 +207,9 @@ pub const Type = opaque {
170 ElementCount: c_uint,207 ElementCount: c_uint,
171 Packed: Bool,208 Packed: Bool,
172 ) void;209 ) void;
210
211 pub const getTypeKind = LLVMGetTypeKind;
212 extern fn LLVMGetTypeKind(Ty: *const Type) TypeKind;
173};213};
174214
175pub const Module = opaque {215pub const Module = opaque {
...@@ -182,9 +222,15 @@ pub const Module = opaque {...@@ -182,9 +222,15 @@ pub const Module = opaque {
182 pub const verify = LLVMVerifyModule;222 pub const verify = LLVMVerifyModule;
183 extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) Bool;223 extern fn LLVMVerifyModule(*const Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) Bool;
184224
225 pub const setModuleDataLayout = LLVMSetModuleDataLayout;
226 extern fn LLVMSetModuleDataLayout(*const Module, *const TargetData) void;
227
185 pub const addFunction = LLVMAddFunction;228 pub const addFunction = LLVMAddFunction;
186 extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;229 extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;
187230
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 pub const getNamedFunction = LLVMGetNamedFunction;234 pub const getNamedFunction = LLVMGetNamedFunction;
189 extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value;235 extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value;
190236
...@@ -197,6 +243,9 @@ pub const Module = opaque {...@@ -197,6 +243,9 @@ pub const Module = opaque {
197 pub const addGlobal = LLVMAddGlobal;243 pub const addGlobal = LLVMAddGlobal;
198 extern fn LLVMAddGlobal(M: *const Module, Ty: *const Type, Name: [*:0]const u8) *const Value;244 extern fn LLVMAddGlobal(M: *const Module, Ty: *const Type, Name: [*:0]const u8) *const Value;
199245
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 pub const getNamedGlobal = LLVMGetNamedGlobal;249 pub const getNamedGlobal = LLVMGetNamedGlobal;
201 extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value;250 extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value;
202251
...@@ -268,7 +317,7 @@ extern fn LLVMGetInlineAsm(...@@ -268,7 +317,7 @@ extern fn LLVMGetInlineAsm(
268pub const functionType = LLVMFunctionType;317pub const functionType = LLVMFunctionType;
269extern fn LLVMFunctionType(318extern fn LLVMFunctionType(
270 ReturnType: *const Type,319 ReturnType: *const Type,
271 ParamTypes: [*]*const Type,320 ParamTypes: [*]const *const Type,
272 ParamCount: c_uint,321 ParamCount: c_uint,
273 IsVarArg: Bool,322 IsVarArg: Bool,
274) *const Type;323) *const Type;
...@@ -314,7 +363,7 @@ pub const Builder = opaque {...@@ -314,7 +363,7 @@ pub const Builder = opaque {
314 extern fn LLVMBuildCall(363 extern fn LLVMBuildCall(
315 *const Builder,364 *const Builder,
316 Fn: *const Value,365 Fn: *const Value,
317 Args: [*]*const Value,366 Args: [*]const *const Value,
318 NumArgs: c_uint,367 NumArgs: c_uint,
319 Name: [*:0]const u8,368 Name: [*:0]const u8,
320 ) *const Value;369 ) *const Value;
...@@ -365,6 +414,12 @@ pub const Builder = opaque {...@@ -365,6 +414,12 @@ pub const Builder = opaque {
365 pub const buildNUWAdd = LLVMBuildNUWAdd;414 pub const buildNUWAdd = LLVMBuildNUWAdd;
366 extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;415 extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
367416
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 pub const buildFSub = LLVMBuildFSub;423 pub const buildFSub = LLVMBuildFSub;
369 extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;424 extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
370425
...@@ -377,6 +432,12 @@ pub const Builder = opaque {...@@ -377,6 +432,12 @@ pub const Builder = opaque {
377 pub const buildNUWSub = LLVMBuildNUWSub;432 pub const buildNUWSub = LLVMBuildNUWSub;
378 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;433 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
379434
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 pub const buildFMul = LLVMBuildFMul;441 pub const buildFMul = LLVMBuildFMul;
381 extern fn LLVMBuildFMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;442 extern fn LLVMBuildFMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
382443
...@@ -389,6 +450,12 @@ pub const Builder = opaque {...@@ -389,6 +450,12 @@ pub const Builder = opaque {
389 pub const buildNUWMul = LLVMBuildNUWMul;450 pub const buildNUWMul = LLVMBuildNUWMul;
390 extern fn LLVMBuildNUWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;451 extern fn LLVMBuildNUWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
391452
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 pub const buildUDiv = LLVMBuildUDiv;459 pub const buildUDiv = LLVMBuildUDiv;
393 extern fn LLVMBuildUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;460 extern fn LLVMBuildUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
394461
...@@ -419,6 +486,18 @@ pub const Builder = opaque {...@@ -419,6 +486,18 @@ pub const Builder = opaque {
419 pub const buildShl = LLVMBuildShl;486 pub const buildShl = LLVMBuildShl;
420 extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;487 extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
421488
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 pub const buildOr = LLVMBuildOr;501 pub const buildOr = LLVMBuildOr;
423 extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;502 extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
424503
...@@ -481,6 +560,14 @@ pub const Builder = opaque {...@@ -481,6 +560,14 @@ pub const Builder = opaque {
481 Name: [*:0]const u8,560 Name: [*:0]const u8,
482 ) *const Value;561 ) *const Value;
483562
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 pub const buildStructGEP = LLVMBuildStructGEP;571 pub const buildStructGEP = LLVMBuildStructGEP;
485 extern fn LLVMBuildStructGEP(572 extern fn LLVMBuildStructGEP(
486 B: *const Builder,573 B: *const Builder,
...@@ -525,6 +612,93 @@ pub const Builder = opaque {...@@ -525,6 +612,93 @@ pub const Builder = opaque {
525 Else: *const Value,612 Else: *const Value,
526 Name: [*:0]const u8,613 Name: [*:0]const u8,
527 ) *const Value;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};
529703
530pub const IntPredicate = enum(c_uint) {704pub const IntPredicate = enum(c_uint) {
...@@ -598,6 +772,14 @@ pub const TargetMachine = opaque {...@@ -598,6 +772,14 @@ pub const TargetMachine = opaque {
598 llvm_ir_filename: ?[*:0]const u8,772 llvm_ir_filename: ?[*:0]const u8,
599 bitcode_filename: ?[*:0]const u8,773 bitcode_filename: ?[*:0]const u8,
600 ) bool;774 ) bool;
775
776 pub const createTargetDataLayout = LLVMCreateTargetDataLayout;
777 extern fn LLVMCreateTargetDataLayout(*const TargetMachine) *const TargetData;
778};
779
780pub const TargetData = opaque {
781 pub const dispose = LLVMDisposeTargetData;
782 extern fn LLVMDisposeTargetData(*const TargetData) void;
601};783};
602784
603pub const CodeModel = enum(c_int) {785pub const CodeModel = enum(c_int) {
...@@ -915,3 +1097,151 @@ pub const AtomicOrdering = enum(c_uint) {...@@ -915,3 +1097,151 @@ pub const AtomicOrdering = enum(c_uint) {
915 AcquireRelease = 6,1097 AcquireRelease = 6,
916 SequentiallyConsistent = 7,1098 SequentiallyConsistent = 7,
917};1099};
1100
1101pub 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
1117pub 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
1140pub 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
1187pub 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 @@
1const std = @import("std");
2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6// TODO: this is only tagged to facilitate the monstrosity.
7// Once packed structs work make it packed.
8pub 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
386pub 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
397pub 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
433pub const callee_preserved_regs = [_]Register{
434 .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
435};
436
437test "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,7 +260,7 @@ pub const DeclGen = struct {
260 };260 };
261 }261 }
262262
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 /// a message is returned by this function. Callee owns the memory. If this function264 /// a message is returned by this function. Callee owns the memory. If this function
265 /// returns such a reportable error, it is valid to be called again for a different decl.265 /// returns such a reportable error, it is valid to be called again for a different decl.
266 pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg {266 pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg {
...@@ -565,7 +565,7 @@ pub const DeclGen = struct {...@@ -565,7 +565,7 @@ pub const DeclGen = struct {
565 }565 }
566 },566 },
567 // When recursively generating a type, we cannot infer the pointer's storage class. See genPointerType.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 .Vector => {569 .Vector => {
570 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations570 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations
571 // which work on them), so simply use those.571 // which work on them), so simply use those.
...@@ -629,7 +629,7 @@ pub const DeclGen = struct {...@@ -629,7 +629,7 @@ pub const DeclGen = struct {
629 const params = decl.ty.fnParamLen();629 const params = decl.ty.fnParamLen();
630 var i: usize = 0;630 var i: usize = 0;
631631
632 try self.args.ensureCapacity(params);632 try self.args.ensureTotalCapacity(params);
633 while (i < params) : (i += 1) {633 while (i < params) : (i += 1) {
634 const param_type_id = self.spv.types.get(decl.ty.fnParamType(i)).?;634 const param_type_id = self.spv.types.get(decl.ty.fnParamType(i)).?;
635 const arg_result_id = self.spv.allocResultId();635 const arg_result_id = self.spv.allocResultId();
src/codegen/wasm.zig+3-3
...@@ -1005,7 +1005,7 @@ pub const Context = struct {...@@ -1005,7 +1005,7 @@ pub const Context = struct {
1005 const rhs = self.resolveInst(bin_op.rhs);1005 const rhs = self.resolveInst(bin_op.rhs);
10061006
1007 // it's possible for both lhs and/or rhs to return an offset as well,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 const offset = blk: {1009 const offset = blk: {
1010 if (lhs == .code_offset) break :blk lhs.code_offset;1010 if (lhs == .code_offset) break :blk lhs.code_offset;
1011 if (rhs == .code_offset) break :blk rhs.code_offset;1011 if (rhs == .code_offset) break :blk rhs.code_offset;
...@@ -1031,7 +1031,7 @@ pub const Context = struct {...@@ -1031,7 +1031,7 @@ pub const Context = struct {
1031 const rhs = self.resolveInst(bin_op.rhs);1031 const rhs = self.resolveInst(bin_op.rhs);
10321032
1033 // it's possible for both lhs and/or rhs to return an offset as well,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 const offset = blk: {1035 const offset = blk: {
1036 if (lhs == .code_offset) break :blk lhs.code_offset;1036 if (lhs == .code_offset) break :blk lhs.code_offset;
1037 if (rhs == .code_offset) break :blk rhs.code_offset;1037 if (rhs == .code_offset) break :blk rhs.code_offset;
...@@ -1395,7 +1395,7 @@ pub const Context = struct {...@@ -1395,7 +1395,7 @@ pub const Context = struct {
1395 }1395 }
13961396
1397 // We map every block to its block index.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 depth1398 // We then determine how far we have to jump to it by subtracting it from current block depth
1399 const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;1399 const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;
1400 const writer = self.code.writer();1400 const writer = self.code.writer();
1401 try writer.writeByte(wasm.opcode(.br));1401 try writer.writeByte(wasm.opcode(.br));
src/codegen/x86.zig deleted-123
...@@ -1,123 +0,0 @@
1const std = @import("std");
2const DW = std.dwarf;
3
4// zig fmt: off
5pub 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
77pub 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 @@
1const std = @import("std");
2const testing = std.testing;
3const mem = std.mem;
4const assert = std.debug.assert;
5const ArrayList = std.ArrayList;
6const Allocator = std.mem.Allocator;
7const 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.
25pub 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.
144pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
145pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
146pub 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.
160pub 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
563test "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 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const debug = std.debug;
4const os = std.os;
5const io = std.io;
6const print_zir = @import("print_zir.zig");
7
8const Module = @import("Module.zig");
9const Sema = @import("Sema.zig");
10const Zir = @import("Zir.zig");
11
12pub 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.
16pub 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.
22pub fn initialize() void {
23 if (is_enabled and debug.have_segfault_handling_support) {
24 attachSegfaultHandler();
25 }
26}
27
28fn En(comptime T: type) type {
29 return if (is_enabled) T else void;
30}
31
32fn en(val: anytype) En(@TypeOf(val)) {
33 return if (is_enabled) val else {};
34}
35
36pub 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
65threadlocal var zir_state: ?*AnalyzeBody = if (is_enabled) null else @compileError("Cannot use zir_state if crash_report is disabled.");
66
67pub 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
83fn 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
137const Scope = Module.Scope;
138const Decl = Module.Decl;
139
140var crash_heap: [16 * 4096]u8 = undefined;
141
142fn 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
150fn 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
156fn 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
165pub 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
184fn 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
247const WindowsSegfaultMessage = union(enum) {
248 literal: []const u8,
249 segfault: void,
250 illegal_instruction: void,
251};
252
253fn 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
263fn 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
301const 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
327const 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,6 +221,7 @@ pub const LibCInstallation = struct {
221 batch.add(&async self.findNativeIncludeDirPosix(args));221 batch.add(&async self.findNativeIncludeDirPosix(args));
222 switch (Target.current.os.tag) {222 switch (Target.current.os.tag) {
223 .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try std.mem.dupeZ(args.allocator, u8, "/usr/lib"),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 .linux => batch.add(&async self.findNativeCrtDirPosix(args)),225 .linux => batch.add(&async self.findNativeCrtDirPosix(args)),
225 else => {},226 else => {},
226 }227 }
src/libcxx.zig+2-2
...@@ -110,7 +110,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {...@@ -110,7 +110,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
110 const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" });110 const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" });
111 const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" });111 const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" });
112 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);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);
114114
115 for (libcxx_files) |cxx_src| {115 for (libcxx_files) |cxx_src| {
116 var cflags = std.ArrayList([]const u8).init(arena);116 var cflags = std.ArrayList([]const u8).init(arena);
...@@ -250,7 +250,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {...@@ -250,7 +250,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
250 const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" });250 const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" });
251 const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" });251 const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" });
252 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);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);
254254
255 for (libcxxabi_files) |cxxabi_src| {255 for (libcxxabi_files) |cxxabi_src| {
256 var cflags = std.ArrayList([]const u8).init(arena);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,7 +34,7 @@ pub fn buildTsan(comp: *Compilation) !void {
34 };34 };
3535
36 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);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);
3838
39 const tsan_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"tsan"});39 const tsan_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"tsan"});
40 for (tsan_sources) |tsan_src| {40 for (tsan_sources) |tsan_src| {
...@@ -58,7 +58,7 @@ pub fn buildTsan(comp: *Compilation) !void {...@@ -58,7 +58,7 @@ pub fn buildTsan(comp: *Compilation) !void {
58 &darwin_tsan_sources58 &darwin_tsan_sources
59 else59 else
60 &unix_tsan_sources;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 for (platform_tsan_sources) |tsan_src| {62 for (platform_tsan_sources) |tsan_src| {
63 var cflags = std.ArrayList([]const u8).init(arena);63 var cflags = std.ArrayList([]const u8).init(arena);
6464
...@@ -96,7 +96,7 @@ pub fn buildTsan(comp: *Compilation) !void {...@@ -96,7 +96,7 @@ pub fn buildTsan(comp: *Compilation) !void {
96 });96 });
97 }97 }
9898
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 const sanitizer_common_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{100 const sanitizer_common_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
101 "tsan", "sanitizer_common",101 "tsan", "sanitizer_common",
102 });102 });
...@@ -123,7 +123,7 @@ pub fn buildTsan(comp: *Compilation) !void {...@@ -123,7 +123,7 @@ pub fn buildTsan(comp: *Compilation) !void {
123 &sanitizer_libcdep_sources123 &sanitizer_libcdep_sources
124 else124 else
125 &sanitizer_nolibc_sources;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 for (to_c_or_not_to_c_sources) |c_src| {127 for (to_c_or_not_to_c_sources) |c_src| {
128 var cflags = std.ArrayList([]const u8).init(arena);128 var cflags = std.ArrayList([]const u8).init(arena);
129129
...@@ -143,7 +143,7 @@ pub fn buildTsan(comp: *Compilation) !void {...@@ -143,7 +143,7 @@ pub fn buildTsan(comp: *Compilation) !void {
143 });143 });
144 }144 }
145145
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 for (sanitizer_symbolizer_sources) |c_src| {147 for (sanitizer_symbolizer_sources) |c_src| {
148 var cflags = std.ArrayList([]const u8).init(arena);148 var cflags = std.ArrayList([]const u8).init(arena);
149149
...@@ -168,7 +168,7 @@ pub fn buildTsan(comp: *Compilation) !void {...@@ -168,7 +168,7 @@ pub fn buildTsan(comp: *Compilation) !void {
168 &[_][]const u8{"interception"},168 &[_][]const u8{"interception"},
169 );169 );
170170
171 try c_source_files.ensureCapacity(c_source_files.items.len + interception_sources.len);171 try c_source_files.ensureUnusedCapacity(interception_sources.len);
172 for (interception_sources) |c_src| {172 for (interception_sources) |c_src| {
173 var cflags = std.ArrayList([]const u8).init(arena);173 var cflags = std.ArrayList([]const u8).init(arena);
174174
src/link.zig+10-5
...@@ -149,7 +149,7 @@ pub const File = struct {...@@ -149,7 +149,7 @@ pub const File = struct {
149 coff: Coff.TextBlock,149 coff: Coff.TextBlock,
150 macho: MachO.TextBlock,150 macho: MachO.TextBlock,
151 plan9: Plan9.DeclBlock,151 plan9: Plan9.DeclBlock,
152 c: C.DeclBlock,152 c: void,
153 wasm: Wasm.DeclBlock,153 wasm: Wasm.DeclBlock,
154 spirv: void,154 spirv: void,
155 };155 };
...@@ -159,7 +159,7 @@ pub const File = struct {...@@ -159,7 +159,7 @@ pub const File = struct {
159 coff: Coff.SrcFn,159 coff: Coff.SrcFn,
160 macho: MachO.SrcFn,160 macho: MachO.SrcFn,
161 plan9: void,161 plan9: void,
162 c: C.FnBlock,162 c: void,
163 wasm: Wasm.FnData,163 wasm: Wasm.FnData,
164 spirv: SpirV.FnData,164 spirv: SpirV.FnData,
165 };165 };
...@@ -245,6 +245,9 @@ pub const File = struct {...@@ -245,6 +245,9 @@ pub const File = struct {
245 };245 };
246246
247 if (use_lld) {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 file.intermediary_basename = sub_path;251 file.intermediary_basename = sub_path;
249 }252 }
250253
...@@ -372,16 +375,18 @@ pub const File = struct {...@@ -372,16 +375,18 @@ pub const File = struct {
372375
373 /// Must be called before any call to updateDecl or updateDeclExports for376 /// Must be called before any call to updateDecl or updateDeclExports for
374 /// any given Decl.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 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {381 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
376 log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name });382 log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name });
377 switch (base.tag) {383 switch (base.tag) {
378 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),384 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
379 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),385 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
380 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),386 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
381 .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),
382 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl),387 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl),
383 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl),388 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl),
384 .spirv => {},389 .c, .spirv => {},
385 }390 }
386 }391 }
387392
...@@ -635,7 +640,7 @@ pub const File = struct {...@@ -635,7 +640,7 @@ pub const File = struct {
635 var object_files = std.ArrayList([*:0]const u8).init(base.allocator);640 var object_files = std.ArrayList([*:0]const u8).init(base.allocator);
636 defer object_files.deinit();641 defer object_files.deinit();
637642
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 for (base.options.objects) |obj_path| {644 for (base.options.objects) |obj_path| {
640 object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj_path));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,30 +21,34 @@ base: link.File,
21/// This linker backend does not try to incrementally link output C source code.21/// This linker backend does not try to incrementally link output C source code.
22/// Instead, it tracks all declarations in this table, and iterates over it22/// Instead, it tracks all declarations in this table, and iterates over it
23/// in the flush function, stitching pre-rendered pieces of C code together.23/// in the flush function, stitching pre-rendered pieces of C code together.
24decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},24decl_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().
27arena: std.heap.ArenaAllocator,
2528
26/// Per-declaration data. For functions this is the body, and29/// Per-declaration data. For functions this is the body, and
27/// the forward declaration is stored in the FnBlock.30/// the forward declaration is stored in the FnBlock.
28pub const DeclBlock = struct {31const DeclBlock = struct {
29 code: std.ArrayListUnmanaged(u8),32 code: std.ArrayListUnmanaged(u8) = .{},
3033 fwd_decl: std.ArrayListUnmanaged(u8) = .{},
31 pub const empty: DeclBlock = .{34 /// Each Decl stores a mapping of Zig Types to corresponding C types, for every
32 .code = .{},35 /// Zig Type used by the Decl. In flush(), we iterate over each Decl
33 };36 /// and emit the typedef code for all types, making sure to not emit the same thing twice.
34};37 /// Any arena memory the Type points to lives in the `arena` field of `C`.
3538 typedefs: codegen.TypedefMap.Unmanaged = .{},
36/// Per-function data.39
37pub const FnBlock = struct {40 fn deinit(db: *DeclBlock, gpa: *Allocator) void {
38 fwd_decl: std.ArrayListUnmanaged(u8),41 db.code.deinit(gpa);
39 typedefs: codegen.TypedefMap.Unmanaged,42 db.fwd_decl.deinit(gpa);
4043 for (db.typedefs.values()) |typedef| {
41 pub const empty: FnBlock = .{44 gpa.free(typedef.rendered);
42 .fwd_decl = .{},45 }
43 .typedefs = .{},46 db.typedefs.deinit(gpa);
44 };47 db.* = undefined;
48 }
45};49};
4650
47pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {51pub fn openPath(gpa: *Allocator, sub_path: []const u8, options: link.Options) !*C {
48 assert(options.object_format == .c);52 assert(options.object_format == .c);
4953
50 if (options.use_llvm) return error.LLVMHasNoCBackend;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,15 +61,16 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
57 });61 });
58 errdefer file.close();62 errdefer file.close();
5963
60 var c_file = try allocator.create(C);64 var c_file = try gpa.create(C);
61 errdefer allocator.destroy(c_file);65 errdefer gpa.destroy(c_file);
6266
63 c_file.* = C{67 c_file.* = C{
68 .arena = std.heap.ArenaAllocator.init(gpa),
64 .base = .{69 .base = .{
65 .tag = .c,70 .tag = .c,
66 .options = options,71 .options = options,
67 .file = file,72 .file = file,
68 .allocator = allocator,73 .allocator = gpa,
69 },74 },
70 };75 };
7176
...@@ -73,38 +78,105 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -73,38 +78,105 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
73}78}
7479
75pub fn deinit(self: *C) void {80pub fn deinit(self: *C) void {
76 for (self.decl_table.keys()) |key| {81 const gpa = self.base.allocator;
77 deinitDecl(self.base.allocator, key);82
83 for (self.decl_table.values()) |*db| {
84 db.deinit(gpa);
78 }85 }
79 self.decl_table.deinit(self.base.allocator);86 self.decl_table.deinit(gpa);
80}
8187
82pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {88 self.arena.deinit();
83 _ = self;
84 _ = decl;
85}89}
8690
87pub fn freeDecl(self: *C, decl: *Module.Decl) void {91pub fn freeDecl(self: *C, decl: *Module.Decl) void {
88 _ = self.decl_table.swapRemove(decl);92 const gpa = self.base.allocator;
89 deinitDecl(self.base.allocator, decl);93 if (self.decl_table.fetchSwapRemove(decl)) |*kv| {
94 kv.value.deinit(gpa);
95 }
90}96}
9197
92fn deinitDecl(gpa: *Allocator, decl: *Module.Decl) void {98pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
93 decl.link.c.code.deinit(gpa);99 const tracy = trace(@src());
94 decl.fn_link.c.fwd_decl.deinit(gpa);100 defer tracy.end();
95 for (decl.fn_link.c.typedefs.values()) |value| {101
96 gpa.free(value.rendered);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}
100168
101pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air, liveness: Liveness) !void {169pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
102 // Keep track of all decls so we can iterate over them on flush().170 const tracy = trace(@src());
103 _ = try self.decl_table.getOrPut(self.base.allocator, decl);171 defer tracy.end();
104172
105 const fwd_decl = &decl.fn_link.c.fwd_decl;173 const gop = try self.decl_table.getOrPut(self.base.allocator, decl);
106 const typedefs = &decl.fn_link.c.typedefs;174 if (!gop.found_existing) {
107 const code = &decl.link.c.code;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 fwd_decl.shrinkRetainingCapacity(0);180 fwd_decl.shrinkRetainingCapacity(0);
109 {181 {
110 for (typedefs.values()) |value| {182 for (typedefs.values()) |value| {
...@@ -116,23 +188,19 @@ pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air,...@@ -116,23 +188,19 @@ pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air,
116188
117 var object: codegen.Object = .{189 var object: codegen.Object = .{
118 .dg = .{190 .dg = .{
191 .gpa = module.gpa,
119 .module = module,192 .module = module,
120 .error_msg = null,193 .error_msg = null,
121 .decl = decl,194 .decl = decl,
122 .fwd_decl = fwd_decl.toManaged(module.gpa),195 .fwd_decl = fwd_decl.toManaged(module.gpa),
123 .typedefs = typedefs.promote(module.gpa),196 .typedefs = typedefs.promote(module.gpa),
197 .typedefs_arena = &self.arena.allocator,
124 },198 },
125 .gpa = module.gpa,
126 .code = code.toManaged(module.gpa),199 .code = code.toManaged(module.gpa),
127 .value_map = codegen.CValueMap.init(module.gpa),
128 .indent_writer = undefined, // set later so we can get a pointer to object.code200 .indent_writer = undefined, // set later so we can get a pointer to object.code
129 .air = air,
130 .liveness = liveness,
131 };201 };
132 object.indent_writer = .{ .underlying_writer = object.code.writer() };202 object.indent_writer = .{ .underlying_writer = object.code.writer() };
133 defer {203 defer {
134 object.value_map.deinit();
135 object.blocks.deinit(module.gpa);
136 object.code.deinit();204 object.code.deinit();
137 object.dg.fwd_decl.deinit();205 object.dg.fwd_decl.deinit();
138 for (object.dg.typedefs.values()) |value| {206 for (object.dg.typedefs.values()) |value| {
...@@ -159,24 +227,12 @@ pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air,...@@ -159,24 +227,12 @@ pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air,
159 code.shrinkAndFree(module.gpa, code.items.len);227 code.shrinkAndFree(module.gpa, code.items.len);
160}228}
161229
162pub 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
169pub 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
176pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {230pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {
177 // The C backend does not have the ability to fix line numbers without re-generating231 // The C backend does not have the ability to fix line numbers without re-generating
178 // the entire Decl.232 // the entire Decl.
179 return self.updateDecl(module, decl);233 _ = self;
234 _ = module;
235 _ = decl;
180}236}
181237
182pub fn flush(self: *C, comp: *Compilation) !void {238pub fn flush(self: *C, comp: *Compilation) !void {
...@@ -197,7 +253,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -197,7 +253,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
197 defer all_buffers.deinit();253 defer all_buffers.deinit();
198254
199 // This is at least enough until we get to the function bodies without error handling.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);
201257
202 var file_size: u64 = zig_h.len;258 var file_size: u64 = zig_h.len;
203 all_buffers.appendAssumeCapacity(.{259 all_buffers.appendAssumeCapacity(.{
...@@ -223,32 +279,42 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -223,32 +279,42 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
223 var typedefs = std.HashMap(Type, void, Type.HashContext64, std.hash_map.default_max_load_percentage).init(comp.gpa);279 var typedefs = std.HashMap(Type, void, Type.HashContext64, std.hash_map.default_max_load_percentage).init(comp.gpa);
224 defer typedefs.deinit();280 defer typedefs.deinit();
225281
226 // Typedefs, forward decls and non-functions first.282 // Typedefs, forward decls, and non-functions first.
227 // TODO: performance investigation: would keeping a list of Decls that we should283 // TODO: performance investigation: would keeping a list of Decls that we should
228 // generate, rather than querying here, be faster?284 // generate, rather than querying here, be faster?
229 for (self.decl_table.keys()) |decl| {285 const decl_keys = self.decl_table.keys();
230 if (!decl.has_tv) continue;286 const decl_values = self.decl_table.values();
231 const buf = buf: {287 for (decl_keys) |decl, i| {
232 if (decl.val.castTag(.function)) |_| {288 if (!decl.has_tv) continue; // TODO do we really need this branch?
233 try typedefs.ensureUnusedCapacity(@intCast(u32, decl.fn_link.c.typedefs.count()));289
234 var it = decl.fn_link.c.typedefs.iterator();290 const decl_block = &decl_values[i];
235 while (it.next()) |new| {291
236 const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*);292 if (decl_block.fwd_decl.items.len != 0) {
237 if (!gop.found_existing) {293 try typedefs.ensureUnusedCapacity(@intCast(u32, decl_block.typedefs.count()));
238 try err_typedef_writer.writeAll(new.value_ptr.rendered);294 var it = decl_block.typedefs.iterator();
239 }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 };301 const buf = decl_block.fwd_decl.items;
247 all_buffers.appendAssumeCapacity(.{302 all_buffers.appendAssumeCapacity(.{
248 .iov_base = buf.ptr,303 .iov_base = buf.ptr,
249 .iov_len = buf.len,304 .iov_len = buf.len,
250 });305 });
251 file_size += buf.len;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 }
253319
254 err_typedef_item.* = .{320 err_typedef_item.* = .{
...@@ -258,16 +324,18 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -258,16 +324,18 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
258 file_size += err_typedef_buf.items.len;324 file_size += err_typedef_buf.items.len;
259325
260 // Now the function bodies.326 // Now the function bodies.
261 try all_buffers.ensureCapacity(all_buffers.items.len + fn_count);327 try all_buffers.ensureUnusedCapacity(fn_count);
262 for (self.decl_table.keys()) |decl| {328 for (decl_keys) |decl, i| {
263 if (!decl.has_tv) continue;329 if (decl.getFunction() != null) {
264 if (decl.val.castTag(.function)) |_| {330 const decl_block = &decl_values[i];
265 const buf = decl.link.c.code.items;331 const buf = decl_block.code.items;
266 all_buffers.appendAssumeCapacity(.{332 if (buf.len != 0) {
267 .iov_base = buf.ptr,333 all_buffers.appendAssumeCapacity(.{
268 .iov_len = buf.len,334 .iov_base = buf.ptr,
269 });335 .iov_len = buf.len,
270 file_size += buf.len;336 });
337 file_size += buf.len;
338 }
271 }339 }
272 }340 }
273341
...@@ -286,7 +354,7 @@ pub fn flushEmitH(module: *Module) !void {...@@ -286,7 +354,7 @@ pub fn flushEmitH(module: *Module) !void {
286 var all_buffers = std.ArrayList(std.os.iovec_const).init(module.gpa);354 var all_buffers = std.ArrayList(std.os.iovec_const).init(module.gpa);
287 defer all_buffers.deinit();355 defer all_buffers.deinit();
288356
289 try all_buffers.ensureCapacity(emit_h.decl_table.count() + 1);357 try all_buffers.ensureTotalCapacity(emit_h.decl_table.count() + 1);
290358
291 var file_size: u64 = zig_h.len;359 var file_size: u64 = zig_h.len;
292 all_buffers.appendAssumeCapacity(.{360 all_buffers.appendAssumeCapacity(.{
src/link/C/zig.h+148-5
...@@ -62,14 +62,62 @@...@@ -62,14 +62,62 @@
6262
63#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)63#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
64#include <stdatomic.h>64#include <stdatomic.h>
65#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_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)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#elif __GNUC__79#elif __GNUC__
68#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired)80#define memory_order_relaxed __ATOMIC_RELAXED
69#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired)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#else100#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#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) zig_unimplemented()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#endif121#endif
74122
75#include <stdint.h>123#include <stdint.h>
...@@ -78,6 +126,7 @@...@@ -78,6 +126,7 @@
78#define int128_t __int128126#define int128_t __int128
79#define uint128_t unsigned __int128127#define uint128_t unsigned __int128
80ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);128ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);
129ZIG_EXTERN_C void *memset (void *, int, size_t);
81130
82static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {131static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
83 uint8_t thresh = max - rhs;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,3 +356,97 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon
307 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));356 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));
308}357}
309358
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
368zig_add_sat_u( u8, uint8_t)
369zig_add_sat_s( i8, int8_t, int16_t)
370zig_add_sat_u(u16, uint16_t)
371zig_add_sat_s(i16, int16_t, int32_t)
372zig_add_sat_u(u32, uint32_t)
373zig_add_sat_s(i32, int32_t, int64_t)
374zig_add_sat_u(u64, uint64_t)
375zig_add_sat_s(i64, int64_t, int128_t)
376zig_add_sat_s(isize, intptr_t, int128_t)
377zig_add_sat_s(short, short, int)
378zig_add_sat_s(int, int, long)
379zig_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
390zig_sub_sat_u( u8, uint8_t)
391zig_sub_sat_s( i8, int8_t, int16_t)
392zig_sub_sat_u(u16, uint16_t)
393zig_sub_sat_s(i16, int16_t, int32_t)
394zig_sub_sat_u(u32, uint32_t)
395zig_sub_sat_s(i32, int32_t, int64_t)
396zig_sub_sat_u(u64, uint64_t)
397zig_sub_sat_s(i64, int64_t, int128_t)
398zig_sub_sat_s(isize, intptr_t, int128_t)
399zig_sub_sat_s(short, short, int)
400zig_sub_sat_s(int, int, long)
401zig_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
414zig_mul_sat_u(u8, uint8_t, uint16_t)
415zig_mul_sat_s(i8, int8_t, int16_t)
416zig_mul_sat_u(u16, uint16_t, uint32_t)
417zig_mul_sat_s(i16, int16_t, int32_t)
418zig_mul_sat_u(u32, uint32_t, uint64_t)
419zig_mul_sat_s(i32, int32_t, int64_t)
420zig_mul_sat_u(u64, uint64_t, uint128_t)
421zig_mul_sat_s(i64, int64_t, int128_t)
422zig_mul_sat_s(isize, intptr_t, int128_t)
423zig_mul_sat_s(short, short, int)
424zig_mul_sat_s(int, int, long)
425zig_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
441zig_shl_sat_u(u8, uint8_t, 8)
442zig_shl_sat_s(i8, int8_t, 7)
443zig_shl_sat_u(u16, uint16_t, 16)
444zig_shl_sat_s(i16, int16_t, 15)
445zig_shl_sat_u(u32, uint32_t, 32)
446zig_shl_sat_s(i32, int32_t, 31)
447zig_shl_sat_u(u64, uint64_t, 64)
448zig_shl_sat_s(i64, int64_t, 63)
449zig_shl_sat_s(isize, intptr_t, ((sizeof(intptr_t)) * CHAR_BIT - 1))
450zig_shl_sat_s(short, short, ((sizeof(short )) * CHAR_BIT - 1))
451zig_shl_sat_s(int, int, ((sizeof(int )) * CHAR_BIT - 1))
452zig_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,7 +50,7 @@ last_text_block: ?*TextBlock = null,
50section_table_offset: u32 = 0,50section_table_offset: u32 = 0,
51/// Section data file pointer.51/// Section data file pointer.
52section_data_offset: u32 = 0,52section_data_offset: u32 = 0,
53/// Optiona header file pointer.53/// Optional header file pointer.
54optional_header_offset: u32 = 0,54optional_header_offset: u32 = 0,
5555
56/// Absolute virtual address of the offset table when the executable is loaded in memory.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,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
132 const self = try createEmpty(allocator, options);132 const self = try createEmpty(allocator, options);
133 errdefer self.base.destroy();133 errdefer self.base.destroy();
134134
135 self.llvm_object = try LlvmObject.create(allocator, options);135 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
136 return self;136 return self;
137 }137 }
138138
...@@ -418,7 +418,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff {...@@ -418,7 +418,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff {
418pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {418pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
419 if (self.llvm_object) |_| return;419 if (self.llvm_object) |_| return;
420420
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);
422422
423 if (self.offset_table_free_list.popOrNull()) |i| {423 if (self.offset_table_free_list.popOrNull()) |i| {
424 decl.link.coff.offset_table_index = i;424 decl.link.coff.offset_table_index = i;
...@@ -602,7 +602,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {...@@ -602,7 +602,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
602 const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);602 const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
603 const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment);603 const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
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` section604 // 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` section605 // and the virtual size of the `.got` section
606606
607 if (new_virtual_size != current_virtual_size) {607 if (new_virtual_size != current_virtual_size) {
608 log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size });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,7 +770,9 @@ fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []co
770}770}
771771
772pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {772pub 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 }
774776
775 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.777 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
776 self.freeTextBlock(&decl.link.coff);778 self.freeTextBlock(&decl.link.coff);
...@@ -793,7 +795,7 @@ pub fn updateDeclExports(...@@ -793,7 +795,7 @@ pub fn updateDeclExports(
793 for (exports) |exp| {795 for (exports) |exp| {
794 if (exp.options.section) |section_name| {796 if (exp.options.section) |section_name| {
795 if (!mem.eql(u8, section_name, ".text")) {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 module.failed_exports.putAssumeCapacityNoClobber(799 module.failed_exports.putAssumeCapacityNoClobber(
798 exp,800 exp,
799 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),801 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
...@@ -804,7 +806,7 @@ pub fn updateDeclExports(...@@ -804,7 +806,7 @@ pub fn updateDeclExports(
804 if (mem.eql(u8, exp.options.name, "_start")) {806 if (mem.eql(u8, exp.options.name, "_start")) {
805 self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base;807 self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base;
806 } else {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 module.failed_exports.putAssumeCapacityNoClobber(810 module.failed_exports.putAssumeCapacityNoClobber(
809 exp,811 exp,
810 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: Exports other than '_start'", .{}),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,11 +884,8 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
882 // If there is no Zig code to compile, then we should skip flushing the output file because it884 // If there is no Zig code to compile, then we should skip flushing the output file because it
883 // will not be part of the linker line anyway.885 // will not be part of the linker line anyway.
884 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {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.887 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
886 if (self.base.options.use_llvm) {888 if (use_stage1) {
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
890 const obj_basename = try std.zig.binNameAlloc(arena, .{889 const obj_basename = try std.zig.binNameAlloc(arena, .{
891 .root_name = self.base.options.root_name,890 .root_name = self.base.options.root_name,
892 .target = self.base.options.target,891 .target = self.base.options.target,
...@@ -981,7 +980,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -981,7 +980,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
981 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});980 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
982981
983 if (self.base.options.output_mode == .Obj) {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 copy983 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
985 // here. TODO: think carefully about how we can avoid this redundant operation when doing984 // here. TODO: think carefully about how we can avoid this redundant operation when doing
986 // build-obj. See also the corresponding TODO in linkAsArchive.985 // build-obj. See also the corresponding TODO in linkAsArchive.
987 const the_object_path = blk: {986 const the_object_path = blk: {
...@@ -1267,22 +1266,23 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -1267,22 +1266,23 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
1267 try argv.append(comp.libunwind_static_lib.?.full_object_path);1266 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1268 }1267 }
12691268
1270 // TODO: remove when stage2 can build compiler_rt.zig, c.zig and ssp.zig1269 if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
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 {
1276 if (!self.base.options.link_libc) {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 // MinGW doesn't provide libssp symbols1275 // MinGW doesn't provide libssp symbols
1280 if (target.abi.isGnu()) {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 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but1281 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
1284 // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.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 }
12871287
1288 try argv.ensureUnusedCapacity(self.base.options.system_libs.count());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,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
235 const self = try createEmpty(allocator, options);235 const self = try createEmpty(allocator, options);
236 errdefer self.base.destroy();236 errdefer self.base.destroy();
237237
238 self.llvm_object = try LlvmObject.create(allocator, options);238 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
239 return self;239 return self;
240 }240 }
241241
...@@ -411,7 +411,7 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {...@@ -411,7 +411,7 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
411411
412/// TODO Improve this to use a table.412/// TODO Improve this to use a table.
413fn makeString(self: *Elf, bytes: []const u8) !u32 {413fn 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 const result = self.shstrtab.items.len;415 const result = self.shstrtab.items.len;
416 self.shstrtab.appendSliceAssumeCapacity(bytes);416 self.shstrtab.appendSliceAssumeCapacity(bytes);
417 self.shstrtab.appendAssumeCapacity(0);417 self.shstrtab.appendAssumeCapacity(0);
...@@ -420,7 +420,7 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 {...@@ -420,7 +420,7 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 {
420420
421/// TODO Improve this to use a table.421/// TODO Improve this to use a table.
422fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {422fn 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 const result = self.debug_strtab.items.len;424 const result = self.debug_strtab.items.len;
425 self.debug_strtab.appendSliceAssumeCapacity(bytes);425 self.debug_strtab.appendSliceAssumeCapacity(bytes);
426 self.debug_strtab.appendAssumeCapacity(0);426 self.debug_strtab.appendAssumeCapacity(0);
...@@ -856,7 +856,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -856,7 +856,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
856856
857 // We have a function to compute the upper bound size, because it's needed857 // We have a function to compute the upper bound size, because it's needed
858 // for determining where to put the offset of the first `LinkBlock`.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());
860860
861 // initial length - length of the .debug_info contribution for this compilation unit,861 // initial length - length of the .debug_info contribution for this compilation unit,
862 // not including the initial length itself.862 // not including the initial length itself.
...@@ -925,7 +925,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -925,7 +925,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
925925
926 // Enough for all the data without resizing. When support for more compilation units926 // Enough for all the data without resizing. When support for more compilation units
927 // is added, the size of this section will become more variable.927 // is added, the size of this section will become more variable.
928 try di_buf.ensureCapacity(100);928 try di_buf.ensureTotalCapacity(100);
929929
930 // initial length - length of the .debug_aranges contribution for this compilation unit,930 // initial length - length of the .debug_aranges contribution for this compilation unit,
931 // not including the initial length itself.931 // not including the initial length itself.
...@@ -1004,7 +1004,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -1004,7 +1004,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
1004 // The size of this header is variable, depending on the number of directories,1004 // The size of this header is variable, depending on the number of directories,
1005 // files, and padding. We have a function to compute the upper bound size, however,1005 // files, and padding. We have a function to compute the upper bound size, however,
1006 // because it's needed for determining where to put the offset of the first `SrcFn`.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());
10081008
1009 // initial length - length of the .debug_line contribution for this compilation unit,1009 // initial length - length of the .debug_line contribution for this compilation unit,
1010 // not including the initial length itself.1010 // not including the initial length itself.
...@@ -1254,11 +1254,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1254,11 +1254,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1254 // If there is no Zig code to compile, then we should skip flushing the output file because it1254 // If there is no Zig code to compile, then we should skip flushing the output file because it
1255 // will not be part of the linker line anyway.1255 // will not be part of the linker line anyway.
1256 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {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.1257 // stage1 puts the object file in the cache directory.
1258 if (self.base.options.use_llvm) {1258 if (self.base.options.use_stage1) {
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
1262 const obj_basename = try std.zig.binNameAlloc(arena, .{1259 const obj_basename = try std.zig.binNameAlloc(arena, .{
1263 .root_name = self.base.options.root_name,1260 .root_name = self.base.options.root_name,
1264 .target = self.base.options.target,1261 .target = self.base.options.target,
...@@ -1285,20 +1282,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1285,20 +1282,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1285 const gc_sections = self.base.options.gc_sections orelse !is_obj;1282 const gc_sections = self.base.options.gc_sections orelse !is_obj;
1286 const stack_size = self.base.options.stack_size_override orelse 16777216;1283 const stack_size = self.base.options.stack_size_override orelse 16777216;
1287 const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;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: {1285 const compiler_rt_path: ?[]const u8 = blk: {
1289 // TODO: remove when stage2 can build compiler_rt.zig1286 if (comp.compiler_rt_static_lib) |x| break :blk x.full_object_path;
1290 if (!build_options.is_stage1 or !self.base.options.use_stage1) break :blk null;1287 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
12911288 break :blk null;
1292 // In the case of build-obj we include the compiler-rt symbols directly alongside1289 };
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;
13021290
1303 // Here we want to determine whether we can save time by not invoking LLD when the1291 // Here we want to determine whether we can save time by not invoking LLD when the
1304 // output is unchanged. None of the linker options or the object files that are being1292 // 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,14 +1609,13 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1621 }1609 }
16221610
1623 // libc1611 // libc
1624 // TODO: enable when stage2 can build c.zig
1625 if (is_exe_or_dyn_lib and1612 if (is_exe_or_dyn_lib and
1626 !self.base.options.skip_linker_dependencies and1613 !self.base.options.skip_linker_dependencies and
1627 !self.base.options.link_libc and1614 !self.base.options.link_libc)
1628 build_options.is_stage1 and
1629 self.base.options.use_stage1)
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 }
16331620
1634 // compiler-rt1621 // compiler-rt
...@@ -1639,7 +1626,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1639,7 +1626,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1639 // Shared libraries.1626 // Shared libraries.
1640 if (is_exe_or_dyn_lib) {1627 if (is_exe_or_dyn_lib) {
1641 const system_libs = self.base.options.system_libs.keys();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 for (system_libs) |link_lib| {1630 for (system_libs) |link_lib| {
1644 // By this time, we depend on these libs being dynamically linked libraries and not static libraries1631 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
1645 // (the check for that needs to be earlier), but they could be full paths to .so files, in which1632 // (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,8 +2100,8 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
21132100
2114 if (decl.link.elf.local_sym_index != 0) return;2101 if (decl.link.elf.local_sym_index != 0) return;
21152102
2116 try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);2103 try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1);
2117 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);2104 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
21182105
2119 if (self.local_symbol_free_list.popOrNull()) |i| {2106 if (self.local_symbol_free_list.popOrNull()) |i| {
2120 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });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,7 +2134,9 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
2147}2134}
21482135
2149pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {2136pub 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 }
21512140
2152 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.2141 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2153 self.freeTextBlock(&decl.link.elf);2142 self.freeTextBlock(&decl.link.elf);
...@@ -2316,7 +2305,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven...@@ -2316,7 +2305,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
2316 defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs);2305 defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs);
23172306
2318 // For functions we need to add a prologue to the debug line program.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);
23202309
2321 const decl = func.owner_decl;2310 const decl = func.owner_decl;
2322 const line_off = @intCast(u28, decl.src_line + func.lbrace_line);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,7 +2340,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
23512340
2352 // .debug_info subprogram2341 // .debug_info subprogram
2353 const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];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);
23552344
2356 const fn_ret_type = decl.ty.fnReturnType();2345 const fn_ret_type = decl.ty.fnReturnType();
2357 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();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,7 +2582,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
2593 },2582 },
2594 .Int => {2583 .Int => {
2595 const info = ty.intInfo(self.base.options.target);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 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);2586 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
2598 // DW.AT.encoding, DW.FORM.data12587 // DW.AT.encoding, DW.FORM.data1
2599 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {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,7 +2596,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
2607 },2596 },
2608 .Optional => {2597 .Optional => {
2609 if (ty.isPtrLikeOptional()) {2598 if (ty.isPtrLikeOptional()) {
2610 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);2599 try dbg_info_buffer.ensureUnusedCapacity(12);
2611 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);2600 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
2612 // DW.AT.encoding, DW.FORM.data12601 // DW.AT.encoding, DW.FORM.data1
2613 dbg_info_buffer.appendAssumeCapacity(DW.ATE.address);2602 dbg_info_buffer.appendAssumeCapacity(DW.ATE.address);
...@@ -2747,14 +2736,14 @@ pub fn updateDeclExports(...@@ -2747,14 +2736,14 @@ pub fn updateDeclExports(
2747 const tracy = trace(@src());2736 const tracy = trace(@src());
2748 defer tracy.end();2737 defer tracy.end();
27492738
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 if (decl.link.elf.local_sym_index == 0) return;2740 if (decl.link.elf.local_sym_index == 0) return;
2752 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];2741 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
27532742
2754 for (exports) |exp| {2743 for (exports) |exp| {
2755 if (exp.options.section) |section_name| {2744 if (exp.options.section) |section_name| {
2756 if (!mem.eql(u8, section_name, ".text")) {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 module.failed_exports.putAssumeCapacityNoClobber(2747 module.failed_exports.putAssumeCapacityNoClobber(
2759 exp,2748 exp,
2760 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),2749 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
...@@ -2772,7 +2761,7 @@ pub fn updateDeclExports(...@@ -2772,7 +2761,7 @@ pub fn updateDeclExports(
2772 },2761 },
2773 .Weak => elf.STB_WEAK,2762 .Weak => elf.STB_WEAK,
2774 .LinkOnce => {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 module.failed_exports.putAssumeCapacityNoClobber(2765 module.failed_exports.putAssumeCapacityNoClobber(
2777 exp,2766 exp,
2778 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}),2767 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}),
...@@ -3448,6 +3437,15 @@ const CsuObjects = struct {...@@ -3448,6 +3437,15 @@ const CsuObjects = struct {
3448 .static_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),3437 .static_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
3449 // zig fmt: on3438 // 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 else => {},3449 else => {},
3452 }3450 }
3453 }3451 }
src/link/MachO.zig+270-118
...@@ -12,7 +12,7 @@ const math = std.math;...@@ -12,7 +12,7 @@ const math = std.math;
12const mem = std.mem;12const mem = std.mem;
13const meta = std.meta;13const meta = std.meta;
1414
15const aarch64 = @import("../codegen/aarch64.zig");15const aarch64 = @import("../arch/aarch64/bits.zig");
16const bind = @import("MachO/bind.zig");16const bind = @import("MachO/bind.zig");
17const codegen = @import("../codegen.zig");17const codegen = @import("../codegen.zig");
18const commands = @import("MachO/commands.zig");18const commands = @import("MachO/commands.zig");
...@@ -200,7 +200,7 @@ atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},...@@ -200,7 +200,7 @@ atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},
200200
201/// List of atoms that are owned directly by the linker.201/// List of atoms that are owned directly by the linker.
202/// Currently these are only atoms that are the result of linking202/// 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/// at present owned by Module.Decl.204/// at present owned by Module.Decl.
205/// TODO consolidate this.205/// TODO consolidate this.
206managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},206managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
...@@ -213,7 +213,7 @@ decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},...@@ -213,7 +213,7 @@ decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
213213
214/// Currently active Module.Decl.214/// Currently active Module.Decl.
215/// TODO this might not be necessary if we figure out how to pass Module.Decl instance215/// 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/// somewhere else in the codegen.217/// somewhere else in the codegen.
218active_decl: ?*Module.Decl = null,218active_decl: ?*Module.Decl = null,
219219
...@@ -231,7 +231,7 @@ const SymbolWithLoc = struct {...@@ -231,7 +231,7 @@ const SymbolWithLoc = struct {
231 },231 },
232 where_index: u32,232 where_index: u32,
233 local_sym_index: u32 = 0,233 local_sym_index: u32 = 0,
234 file: u16 = 0,234 file: ?u16 = null, // null means Zig module
235};235};
236236
237pub const GotIndirectionKey = struct {237pub const GotIndirectionKey = struct {
...@@ -290,7 +290,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -290,7 +290,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
290 const self = try createEmpty(allocator, options);290 const self = try createEmpty(allocator, options);
291 errdefer self.base.destroy();291 errdefer self.base.destroy();
292292
293 self.llvm_object = try LlvmObject.create(allocator, options);293 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
294 return self;294 return self;
295 }295 }
296296
...@@ -512,7 +512,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -512,7 +512,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
512 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});512 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
513513
514 if (self.base.options.output_mode == .Obj) {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 copy515 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
516 // here. TODO: think carefully about how we can avoid this redundant operation when doing516 // here. TODO: think carefully about how we can avoid this redundant operation when doing
517 // build-obj. See also the corresponding TODO in linkAsArchive.517 // build-obj. See also the corresponding TODO in linkAsArchive.
518 const the_object_path = blk: {518 const the_object_path = blk: {
...@@ -543,9 +543,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -543,9 +543,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
543 .mode = link.determineMode(self.base.options),543 .mode = link.determineMode(self.base.options),
544 });544 });
545 try self.populateMissingMetadata();545 try self.populateMissingMetadata();
546
547 // TODO mimicking insertion of null symbol from incremental linker.
548 // This will need to moved.
549 try self.locals.append(self.base.allocator, .{546 try self.locals.append(self.base.allocator, .{
550 .n_strx = 0,547 .n_strx = 0,
551 .n_type = macho.N_UNDF,548 .n_type = macho.N_UNDF,
...@@ -557,13 +554,56 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -557,13 +554,56 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
557 }554 }
558555
559 if (needs_full_relink) {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 self.objects.clearRetainingCapacity();561 self.objects.clearRetainingCapacity();
562
563 for (self.archives.items) |*archive| {
564 archive.deinit(self.base.allocator);
565 }
561 self.archives.clearRetainingCapacity();566 self.archives.clearRetainingCapacity();
567
568 for (self.dylibs.items) |*dylib| {
569 dylib.deinit(self.base.allocator);
570 }
562 self.dylibs.clearRetainingCapacity();571 self.dylibs.clearRetainingCapacity();
563 self.dylibs_map.clearRetainingCapacity();572 self.dylibs_map.clearRetainingCapacity();
564 self.referenced_dylibs.clearRetainingCapacity();573 self.referenced_dylibs.clearRetainingCapacity();
565574
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 }
567607
568 // Positional arguments to the linker such as object files and static archives.608 // Positional arguments to the linker such as object files and static archives.
569 var positionals = std.ArrayList([]const u8).init(arena);609 var positionals = std.ArrayList([]const u8).init(arena);
...@@ -802,13 +842,35 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {...@@ -802,13 +842,35 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
802 try self.createDsoHandleAtom();842 try self.createDsoHandleAtom();
803 try self.addCodeSignatureLC();843 try self.addCodeSignatureLC();
804844
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 for (self.unresolved.keys()) |index| {865 for (self.unresolved.keys()) |index| {
806 const sym = self.undefs.items[index];866 const sym = self.undefs.items[index];
807 const sym_name = self.getString(sym.n_strx);867 const sym_name = self.getString(sym.n_strx);
808 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;868 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;
809869
810 log.err("undefined reference to symbol '{s}'", .{sym_name});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 if (self.unresolved.count() > 0) {875 if (self.unresolved.count() > 0) {
814 return error.UndefinedSymbolReference;876 return error.UndefinedSymbolReference;
...@@ -1807,7 +1869,7 @@ fn writeAtoms(self: *MachO) !void {...@@ -1807,7 +1869,7 @@ fn writeAtoms(self: *MachO) !void {
1807pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom {1869pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom {
1808 const local_sym_index = @intCast(u32, self.locals.items.len);1870 const local_sym_index = @intCast(u32, self.locals.items.len);
1809 try self.locals.append(self.base.allocator, .{1871 try self.locals.append(self.base.allocator, .{
1810 .n_strx = try self.makeString("l_zld_got_entry"),1872 .n_strx = 0,
1811 .n_type = macho.N_SECT,1873 .n_type = macho.N_SECT,
1812 .n_sect = 0,1874 .n_sect = 0,
1813 .n_desc = 0,1875 .n_desc = 0,
...@@ -1845,7 +1907,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {...@@ -1845,7 +1907,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {
1845 const local_sym_index = @intCast(u32, self.locals.items.len);1907 const local_sym_index = @intCast(u32, self.locals.items.len);
1846 const sym = try self.locals.addOne(self.base.allocator);1908 const sym = try self.locals.addOne(self.base.allocator);
1847 sym.* = .{1909 sym.* = .{
1848 .n_strx = try self.makeString("l_zld_dyld_private"),1910 .n_strx = 0,
1849 .n_type = macho.N_SECT,1911 .n_type = macho.N_SECT,
1850 .n_sect = 0,1912 .n_sect = 0,
1851 .n_desc = 0,1913 .n_desc = 0,
...@@ -1879,7 +1941,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1879,7 +1941,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
1879 const local_sym_index = @intCast(u32, self.locals.items.len);1941 const local_sym_index = @intCast(u32, self.locals.items.len);
1880 const sym = try self.locals.addOne(self.base.allocator);1942 const sym = try self.locals.addOne(self.base.allocator);
1881 sym.* = .{1943 sym.* = .{
1882 .n_strx = try self.makeString("l_zld_stub_preamble"),1944 .n_strx = 0,
1883 .n_type = macho.N_SECT,1945 .n_type = macho.N_SECT,
1884 .n_sect = 0,1946 .n_sect = 0,
1885 .n_desc = 0,1947 .n_desc = 0,
...@@ -2021,7 +2083,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2021,7 +2083,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2021 };2083 };
2022 const local_sym_index = @intCast(u32, self.locals.items.len);2084 const local_sym_index = @intCast(u32, self.locals.items.len);
2023 try self.locals.append(self.base.allocator, .{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 .n_type = macho.N_SECT,2087 .n_type = macho.N_SECT,
2026 .n_sect = 0,2088 .n_sect = 0,
2027 .n_desc = 0,2089 .n_desc = 0,
...@@ -2076,7 +2138,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2076,7 +2138,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2076pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym_index: u32) !*Atom {2138pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym_index: u32) !*Atom {
2077 const local_sym_index = @intCast(u32, self.locals.items.len);2139 const local_sym_index = @intCast(u32, self.locals.items.len);
2078 try self.locals.append(self.base.allocator, .{2140 try self.locals.append(self.base.allocator, .{
2079 .n_strx = try self.makeString("l_zld_lazy_ptr"),2141 .n_strx = 0,
2080 .n_type = macho.N_SECT,2142 .n_type = macho.N_SECT,
2081 .n_sect = 0,2143 .n_sect = 0,
2082 .n_desc = 0,2144 .n_desc = 0,
...@@ -2117,7 +2179,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2117,7 +2179,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2117 };2179 };
2118 const local_sym_index = @intCast(u32, self.locals.items.len);2180 const local_sym_index = @intCast(u32, self.locals.items.len);
2119 try self.locals.append(self.base.allocator, .{2181 try self.locals.append(self.base.allocator, .{
2120 .n_strx = try self.makeString("l_zld_stub"),2182 .n_strx = 0,
2121 .n_type = macho.N_SECT,2183 .n_type = macho.N_SECT,
2122 .n_sect = 0,2184 .n_sect = 0,
2123 .n_desc = 0,2185 .n_desc = 0,
...@@ -2183,7 +2245,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2183,7 +2245,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2183fn createTentativeDefAtoms(self: *MachO) !void {2245fn createTentativeDefAtoms(self: *MachO) !void {
2184 if (self.tentatives.count() == 0) return;2246 if (self.tentatives.count() == 0) return;
2185 // Convert any tentative definition into a regular symbol and allocate2247 // 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 while (self.tentatives.popOrNull()) |entry| {2249 while (self.tentatives.popOrNull()) |entry| {
2188 const match = MatchingSection{2250 const match = MatchingSection{
2189 .seg = self.data_segment_cmd_index.?,2251 .seg = self.data_segment_cmd_index.?,
...@@ -2349,7 +2411,9 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2349,7 +2411,9 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2349 !(symbolIsWeakDef(global.*) or symbolIsPext(global.*)))2411 !(symbolIsWeakDef(global.*) or symbolIsPext(global.*)))
2350 {2412 {
2351 log.err("symbol '{s}' defined multiple times", .{sym_name});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 log.err(" next definition in '{s}'", .{object.name});2417 log.err(" next definition in '{s}'", .{object.name});
2354 return error.MultipleSymbolDefinitions;2418 return error.MultipleSymbolDefinitions;
2355 } else if (symbolIsWeakDef(sym) or symbolIsPext(sym)) continue; // Current symbol is weak, so skip it.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,10 +2696,10 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
2632 const tracy = trace(@src());2696 const tracy = trace(@src());
2633 defer tracy.end();2697 defer tracy.end();
26342698
2635 var parsed_atoms = Object.ParsedAtoms.init(self.base.allocator);2699 var parsed_atoms = std.AutoArrayHashMap(MatchingSection, *Atom).init(self.base.allocator);
2636 defer parsed_atoms.deinit();2700 defer parsed_atoms.deinit();
26372701
2638 var first_atoms = Object.ParsedAtoms.init(self.base.allocator);2702 var first_atoms = std.AutoArrayHashMap(MatchingSection, *Atom).init(self.base.allocator);
2639 defer first_atoms.deinit();2703 defer first_atoms.deinit();
26402704
2641 var section_metadata = std.AutoHashMap(MatchingSection, struct {2705 var section_metadata = std.AutoHashMap(MatchingSection, struct {
...@@ -2644,13 +2708,12 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {...@@ -2644,13 +2708,12 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
2644 }).init(self.base.allocator);2708 }).init(self.base.allocator);
2645 defer section_metadata.deinit();2709 defer section_metadata.deinit();
26462710
2647 for (self.objects.items) |*object, object_id| {2711 for (self.objects.items) |*object| {
2648 if (object.analyzed) continue;2712 if (object.analyzed) continue;
26492713
2650 var atoms_in_objects = try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_id), self);2714 try object.parseIntoAtoms(self.base.allocator, self);
2651 defer atoms_in_objects.deinit();
26522715
2653 var it = atoms_in_objects.iterator();2716 var it = object.end_atoms.iterator();
2654 while (it.next()) |entry| {2717 while (it.next()) |entry| {
2655 const match = entry.key_ptr.*;2718 const match = entry.key_ptr.*;
2656 const last_atom = entry.value_ptr.*;2719 const last_atom = entry.value_ptr.*;
...@@ -3292,8 +3355,6 @@ pub fn updateDeclExports(...@@ -3292,8 +3355,6 @@ pub fn updateDeclExports(
3292 decl: *Module.Decl,3355 decl: *Module.Decl,
3293 exports: []const *Module.Export,3356 exports: []const *Module.Export,
3294) !void {3357) !void {
3295 // TODO If we are exporting with global linkage, check for already defined globals and flag
3296 // symbol duplicate/collision!
3297 if (build_options.skip_non_native and builtin.object_format != .macho) {3358 if (build_options.skip_non_native and builtin.object_format != .macho) {
3298 @panic("Attempted to compile for object format that was disabled by build configuration");3359 @panic("Attempted to compile for object format that was disabled by build configuration");
3299 }3360 }
...@@ -3303,7 +3364,7 @@ pub fn updateDeclExports(...@@ -3303,7 +3364,7 @@ pub fn updateDeclExports(
3303 const tracy = trace(@src());3364 const tracy = trace(@src());
3304 defer tracy.end();3365 defer tracy.end();
33053366
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 if (decl.link.macho.local_sym_index == 0) return;3368 if (decl.link.macho.local_sym_index == 0) return;
3308 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];3369 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];
33093370
...@@ -3313,15 +3374,76 @@ pub fn updateDeclExports(...@@ -3313,15 +3374,76 @@ pub fn updateDeclExports(
33133374
3314 if (exp.options.section) |section_name| {3375 if (exp.options.section) |section_name| {
3315 if (!mem.eql(u8, section_name, "__text")) {3376 if (!mem.eql(u8, section_name, "__text")) {
3316 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);3377 try module.failed_exports.putNoClobber(
3317 module.failed_exports.putAssumeCapacityNoClobber(3378 module.gpa,
3318 exp,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 continue;3387 continue;
3322 }3388 }
3323 }3389 }
33243390
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 var n_type: u8 = macho.N_SECT | macho.N_EXT;3447 var n_type: u8 = macho.N_SECT | macho.N_EXT;
3326 var n_desc: u16 = 0;3448 var n_desc: u16 = 0;
33273449
...@@ -3339,14 +3461,7 @@ pub fn updateDeclExports(...@@ -3339,14 +3461,7 @@ pub fn updateDeclExports(
3339 // Symbol's n_type is like for a symbol with strong linkage.3461 // Symbol's n_type is like for a symbol with strong linkage.
3340 n_desc |= macho.N_WEAK_DEF;3462 n_desc |= macho.N_WEAK_DEF;
3341 },3463 },
3342 .LinkOnce => {3464 else => unreachable,
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 },
3350 }3465 }
33513466
3352 const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {3467 const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {
...@@ -3356,8 +3471,6 @@ pub fn updateDeclExports(...@@ -3356,8 +3471,6 @@ pub fn updateDeclExports(
3356 };3471 };
3357 break :blk i;3472 break :blk i;
3358 };3473 };
3359
3360 const n_strx = try self.makeString(exp_name);
3361 const sym = &self.globals.items[global_sym_index];3474 const sym = &self.globals.items[global_sym_index];
3362 sym.* = .{3475 sym.* = .{
3363 .n_strx = try self.makeString(exp_name),3476 .n_strx = try self.makeString(exp_name),
...@@ -3368,12 +3481,11 @@ pub fn updateDeclExports(...@@ -3368,12 +3481,11 @@ pub fn updateDeclExports(
3368 };3481 };
3369 exp.link.macho.sym_index = global_sym_index;3482 exp.link.macho.sym_index = global_sym_index;
33703483
3371 const resolv = try self.symbol_resolver.getOrPut(self.base.allocator, n_strx);3484 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
3372 resolv.value_ptr.* = .{
3373 .where = .global,3485 .where = .global,
3374 .where_index = global_sym_index,3486 .where_index = global_sym_index,
3375 .local_sym_index = decl.link.macho.local_sym_index,3487 .local_sym_index = decl.link.macho.local_sym_index,
3376 };3488 });
3377 }3489 }
3378}3490}
33793491
...@@ -3381,11 +3493,17 @@ pub fn deleteExport(self: *MachO, exp: Export) void {...@@ -3381,11 +3493,17 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
3381 const sym_index = exp.sym_index orelse return;3493 const sym_index = exp.sym_index orelse return;
3382 self.globals_free_list.append(self.base.allocator, sym_index) catch {};3494 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
3383 const global = &self.globals.items[sym_index];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 assert(self.symbol_resolver.remove(global.n_strx));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}
33873502
3388pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {3503pub 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 log.debug("freeDecl {*}", .{decl});3507 log.debug("freeDecl {*}", .{decl});
3390 _ = self.decls.swapRemove(decl);3508 _ = self.decls.swapRemove(decl);
3391 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.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,6 +4088,70 @@ fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64
3970 return mem.alignForwardGeneric(u64, final_off, alignment);4088 return mem.alignForwardGeneric(u64, final_off, alignment);
3971}4089}
39724090
4091fn 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
3973fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {4155fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
3974 const tracy = trace(@src());4156 const tracy = trace(@src());
3975 defer tracy.end();4157 defer tracy.end();
...@@ -3983,7 +4165,14 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {...@@ -3983,7 +4165,14 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
3983 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);4165 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
39844166
3985 if (needed_size > max_size) blk: {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 // Need to move all sections below in file and address spaces.4176 // Need to move all sections below in file and address spaces.
3988 const offset_amt = offset: {4177 const offset_amt = offset: {
3989 const max_alignment = try self.getSectionMaxAlignment(match.seg, match.sect + 1);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,70 +4188,10 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
39994188
4000 if (last_sect_off + offset_amt > seg_off) {4189 if (last_sect_off + offset_amt > seg_off) {
4001 // Need to grow segment first.4190 // Need to grow segment first.
4002 log.debug(" (need to grow segment first)", .{});
4003 const spill_size = (last_sect_off + offset_amt) - seg_off;4191 const spill_size = (last_sect_off + offset_amt) - seg_off;
4004 const seg_offset_amt = mem.alignForwardGeneric(u64, spill_size, self.page_size);4192 try self.growSegment(match.seg, seg.inner.filesize + spill_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 }
4062 }4193 }
40634194
4064 if (match.sect + 1 >= seg.sections.items.len) break :blk;
4065
4066 // We have enough space to expand within the segment, so move all sections by4195 // We have enough space to expand within the segment, so move all sections by
4067 // the required amount and update their header offsets.4196 // the required amount and update their header offsets.
4068 const next_sect = seg.sections.items[match.sect + 1];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,20 +4357,34 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
4228 return vaddr;4357 return vaddr;
4229}4358}
42304359
4231pub fn addExternFn(self: *MachO, name: []const u8) !u32 {4360const AddExternFnRes = struct {
4361 where: enum {
4362 local,
4363 undef,
4364 },
4365 where_index: u32,
4366};
4367
4368pub fn addExternFn(self: *MachO, name: []const u8) !AddExternFnRes {
4232 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});4369 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
4233 defer self.base.allocator.free(sym_name);4370 defer self.base.allocator.free(sym_name);
4371 const n_strx = try self.makeString(sym_name);
42344372
4235 if (self.strtab_dir.getKeyAdapted(@as([]const u8, sym_name), StringIndexAdapter{4373 if (self.symbol_resolver.get(n_strx)) |resolv| {
4236 .bytes = &self.strtab,4374 return switch (resolv.where) {
4237 })) |n_strx| {4375 .global => AddExternFnRes{
4238 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;4376 .where = .local,
4239 return resolv.where_index;4377 .where_index = resolv.local_sym_index,
4378 },
4379 .undef => AddExternFnRes{
4380 .where = .undef,
4381 .where_index = resolv.where_index,
4382 },
4383 };
4240 }4384 }
42414385
4242 log.debug("adding new extern function '{s}'", .{sym_name});4386 log.debug("adding new extern function '{s}'", .{sym_name});
4243 const sym_index = @intCast(u32, self.undefs.items.len);4387 const sym_index = @intCast(u32, self.undefs.items.len);
4244 const n_strx = try self.makeString(sym_name);
4245 try self.undefs.append(self.base.allocator, .{4388 try self.undefs.append(self.base.allocator, .{
4246 .n_strx = n_strx,4389 .n_strx = n_strx,
4247 .n_type = macho.N_UNDF,4390 .n_type = macho.N_UNDF,
...@@ -4255,7 +4398,10 @@ pub fn addExternFn(self: *MachO, name: []const u8) !u32 {...@@ -4255,7 +4398,10 @@ pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
4255 });4398 });
4256 try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub);4399 try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub);
42574400
4258 return sym_index;4401 return AddExternFnRes{
4402 .where = .undef,
4403 .where_index = sym_index,
4404 };
4259}4405}
42604406
4261const NextSegmentAddressAndOffset = struct {4407const NextSegmentAddressAndOffset = struct {
...@@ -4386,6 +4532,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -4386,6 +4532,7 @@ fn writeDyldInfoData(self: *MachO) !void {
4386 const base_address = text_segment.inner.vmaddr;4532 const base_address = text_segment.inner.vmaddr;
43874533
4388 for (self.globals.items) |sym| {4534 for (self.globals.items) |sym| {
4535 if (sym.n_type == 0) continue;
4389 const sym_name = self.getString(sym.n_strx);4536 const sym_name = self.getString(sym.n_strx);
4390 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });4537 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
43914538
...@@ -4462,7 +4609,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -4462,7 +4609,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
44624609
4463 // Because we insert lazy binding opcodes in reverse order (from last to the first atom),4610 // Because we insert lazy binding opcodes in reverse order (from last to the first atom),
4464 // we need reverse the order of atom traversal here as well.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 var atom = last_atom;4613 var atom = last_atom;
4467 while (atom.prev) |prev| {4614 while (atom.prev) |prev| {
4468 atom = prev;4615 atom = prev;
...@@ -4608,7 +4755,12 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -4608,7 +4755,12 @@ fn writeSymbolTable(self: *MachO) !void {
46084755
4609 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);4756 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
4610 defer locals.deinit();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 }
46124764
4613 if (self.has_stabs) {4765 if (self.has_stabs) {
4614 for (self.objects.items) |object| {4766 for (self.objects.items) |object| {
...@@ -4638,7 +4790,7 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -4638,7 +4790,7 @@ fn writeSymbolTable(self: *MachO) !void {
4638 .n_value = object.mtime orelse 0,4790 .n_value = object.mtime orelse 0,
4639 });4791 });
46404792
4641 for (object.atoms.items) |atom| {4793 for (object.contained_atoms.items) |atom| {
4642 if (atom.stab) |stab| {4794 if (atom.stab) |stab| {
4643 const nlists = try stab.asNlists(atom.local_sym_index, self);4795 const nlists = try stab.asNlists(atom.local_sym_index, self);
4644 defer self.base.allocator.free(nlists);4796 defer self.base.allocator.free(nlists);
src/link/MachO/Atom.zig+61-54
...@@ -2,7 +2,7 @@ const Atom = @This();...@@ -2,7 +2,7 @@ const Atom = @This();
22
3const std = @import("std");3const std = @import("std");
4const build_options = @import("build_options");4const build_options = @import("build_options");
5const aarch64 = @import("../../codegen/aarch64.zig");5const aarch64 = @import("../../arch/aarch64/bits.zig");
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const commands = @import("commands.zig");7const commands = @import("commands.zig");
8const log = std.log.scoped(.text_block);8const log = std.log.scoped(.text_block);
...@@ -41,7 +41,7 @@ code: std.ArrayListUnmanaged(u8) = .{},...@@ -41,7 +41,7 @@ code: std.ArrayListUnmanaged(u8) = .{},
41size: u64,41size: u64,
4242
43/// Alignment of this atom as a power of 2.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.
45alignment: u32,45alignment: u32,
4646
47/// List of relocations belonging to this atom.47/// List of relocations belonging to this atom.
...@@ -645,7 +645,6 @@ const RelocContext = struct {...@@ -645,7 +645,6 @@ const RelocContext = struct {
645 allocator: *Allocator,645 allocator: *Allocator,
646 object: *Object,646 object: *Object,
647 macho_file: *MachO,647 macho_file: *MachO,
648 parsed_atoms: *Object.ParsedAtoms,
649};648};
650649
651fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Relocation {650fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Relocation {
...@@ -664,15 +663,8 @@ fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Reloc...@@ -664,15 +663,8 @@ fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Reloc
664 const sect = seg.sections.items[sect_id];663 const sect = seg.sections.items[sect_id];
665 const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable;664 const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable;
666 const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);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 try context.macho_file.locals.append(context.allocator, .{666 try context.macho_file.locals.append(context.allocator, .{
675 .n_strx = try context.macho_file.makeString(sym_name),667 .n_strx = 0,
676 .n_type = macho.N_SECT,668 .n_type = macho.N_SECT,
677 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),669 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),
678 .n_desc = 0,670 .n_desc = 0,
...@@ -877,12 +869,16 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -877,12 +869,16 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
877 .sect = context.macho_file.got_section_index.?,869 .sect = context.macho_file.got_section_index.?,
878 };870 };
879871
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 last.*.next = atom;877 last.*.next = atom;
882 atom.prev = last.*;878 atom.prev = last.*;
883 last.* = atom;879 last.* = atom;
884 } else {880 } else {
885 try context.parsed_atoms.putNoClobber(match, atom);881 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
886 }882 }
887 } else if (parsed_rel.payload == .unsigned) {883 } else if (parsed_rel.payload == .unsigned) {
888 switch (parsed_rel.where) {884 switch (parsed_rel.where) {
...@@ -939,52 +935,63 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -939,52 +935,63 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
939 if (parsed_rel.where != .undef) break :blk;935 if (parsed_rel.where != .undef) break :blk;
940 if (context.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk;936 if (context.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk;
941937
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 // TODO clean this up!938 // TODO clean this up!
950 if (context.parsed_atoms.getPtr(.{939 const stub_helper_atom = atom: {
951 .seg = context.macho_file.text_segment_cmd_index.?,940 const atom = try context.macho_file.createStubHelperAtom();
952 .sect = context.macho_file.stub_helper_section_index.?,941 const match = MachO.MatchingSection{
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(.{
959 .seg = context.macho_file.text_segment_cmd_index.?,942 .seg = context.macho_file.text_segment_cmd_index.?,
960 .sect = context.macho_file.stub_helper_section_index.?,943 .sect = context.macho_file.stub_helper_section_index.?,
961 }, stub_helper_atom);944 };
962 }945 if (!context.object.start_atoms.contains(match)) {
963 if (context.parsed_atoms.getPtr(.{946 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
964 .seg = context.macho_file.text_segment_cmd_index.?,947 }
965 .sect = context.macho_file.stubs_section_index.?,948 if (context.object.end_atoms.getPtr(match)) |last| {
966 })) |last| {949 last.*.next = atom;
967 last.*.next = stub_atom;950 atom.prev = last.*;
968 stub_atom.prev = last.*;951 last.* = atom;
969 last.* = stub_atom;952 } else {
970 } else {953 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
971 try context.parsed_atoms.putNoClobber(.{954 }
972 .seg = context.macho_file.text_segment_cmd_index.?,955 break :atom atom;
973 .sect = context.macho_file.stubs_section_index.?,956 };
974 }, stub_atom);957 const laptr_atom = atom: {
975 }958 const atom = try context.macho_file.createLazyPointerAtom(
976 if (context.parsed_atoms.getPtr(.{959 stub_helper_atom.local_sym_index,
977 .seg = context.macho_file.data_segment_cmd_index.?,960 parsed_rel.where_index,
978 .sect = context.macho_file.la_symbol_ptr_section_index.?,961 );
979 })) |last| {962 const match = MachO.MatchingSection{
980 last.*.next = laptr_atom;
981 laptr_atom.prev = last.*;
982 last.* = laptr_atom;
983 } else {
984 try context.parsed_atoms.putNoClobber(.{
985 .seg = context.macho_file.data_segment_cmd_index.?,963 .seg = context.macho_file.data_segment_cmd_index.?,
986 .sect = context.macho_file.la_symbol_ptr_section_index.?,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,7 +102,7 @@ pub fn calcAdhocSignature(
102 var buffer = try allocator.alloc(u8, page_size);102 var buffer = try allocator.alloc(u8, page_size);
103 defer allocator.free(buffer);103 defer allocator.free(buffer);
104104
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);
106106
107 // 1. Save the identifier and update offsets107 // 1. Save the identifier and update offsets
108 cdir.inner.identOffset = cdir.inner.length;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,7 +353,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
353353
354 // We have a function to compute the upper bound size, because it's needed354 // We have a function to compute the upper bound size, because it's needed
355 // for determining where to put the offset of the first `LinkBlock`.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());
357357
358 // initial length - length of the .debug_info contribution for this compilation unit,358 // initial length - length of the .debug_info contribution for this compilation unit,
359 // not including the initial length itself.359 // not including the initial length itself.
...@@ -408,7 +408,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt...@@ -408,7 +408,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
408408
409 // Enough for all the data without resizing. When support for more compilation units409 // Enough for all the data without resizing. When support for more compilation units
410 // is added, the size of this section will become more variable.410 // is added, the size of this section will become more variable.
411 try di_buf.ensureCapacity(100);411 try di_buf.ensureTotalCapacity(100);
412412
413 // initial length - length of the .debug_aranges contribution for this compilation unit,413 // initial length - length of the .debug_aranges contribution for this compilation unit,
414 // not including the initial length itself.414 // not including the initial length itself.
...@@ -479,7 +479,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt...@@ -479,7 +479,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
479 // The size of this header is variable, depending on the number of directories,479 // The size of this header is variable, depending on the number of directories,
480 // files, and padding. We have a function to compute the upper bound size, however,480 // files, and padding. We have a function to compute the upper bound size, however,
481 // because it's needed for determining where to put the offset of the first `SrcFn`.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));
483483
484 // initial length - length of the .debug_line contribution for this compilation unit,484 // initial length - length of the .debug_line contribution for this compilation unit,
485 // not including the initial length itself.485 // not including the initial length itself.
...@@ -607,7 +607,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: Segm...@@ -607,7 +607,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: Segm
607 };607 };
608 mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);608 mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);
609609
610 try cmd.sections.ensureCapacity(allocator, cmd.inner.nsects);610 try cmd.sections.ensureTotalCapacity(allocator, cmd.inner.nsects);
611 for (base_cmd.sections.items) |base_sect, i| {611 for (base_cmd.sections.items) |base_sect, i| {
612 var sect = macho.section_64{612 var sect = macho.section_64{
613 .sectname = undefined,613 .sectname = undefined,
...@@ -855,7 +855,7 @@ pub fn initDeclDebugBuffers(...@@ -855,7 +855,7 @@ pub fn initDeclDebugBuffers(
855 switch (decl.ty.zigTypeTag()) {855 switch (decl.ty.zigTypeTag()) {
856 .Fn => {856 .Fn => {
857 // For functions we need to add a prologue to the debug line program.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);
859859
860 const func = decl.val.castTag(.function).?.data;860 const func = decl.val.castTag(.function).?.data;
861 const line_off = @intCast(u28, decl.src_line + func.lbrace_line);861 const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
...@@ -889,7 +889,7 @@ pub fn initDeclDebugBuffers(...@@ -889,7 +889,7 @@ pub fn initDeclDebugBuffers(
889889
890 // .debug_info subprogram890 // .debug_info subprogram
891 const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];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);
893893
894 const fn_ret_type = decl.ty.fnReturnType();894 const fn_ret_type = decl.ty.fnReturnType();
895 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();895 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
...@@ -1124,7 +1124,7 @@ fn addDbgInfoType(...@@ -1124,7 +1124,7 @@ fn addDbgInfoType(
1124 },1124 },
1125 .Int => {1125 .Int => {
1126 const info = ty.intInfo(target);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 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);1128 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
1129 // DW.AT.encoding, DW.FORM.data11129 // DW.AT.encoding, DW.FORM.data1
1130 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {1130 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {
...@@ -1261,7 +1261,7 @@ fn getDebugLineProgramEnd(self: DebugSymbols) u32 {...@@ -1261,7 +1261,7 @@ fn getDebugLineProgramEnd(self: DebugSymbols) u32 {
12611261
1262/// TODO Improve this to use a table.1262/// TODO Improve this to use a table.
1263fn makeDebugString(self: *DebugSymbols, allocator: *Allocator, bytes: []const u8) !u32 {1263fn 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 const result = self.debug_string_table.items.len;1265 const result = self.debug_string_table.items.len;
1266 self.debug_string_table.appendSliceAssumeCapacity(bytes);1266 self.debug_string_table.appendSliceAssumeCapacity(bytes);
1267 self.debug_string_table.appendAssumeCapacity(0);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,7 +180,7 @@ pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void {
180fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void {180fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void {
181 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;181 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
182182
183 try self.load_commands.ensureCapacity(allocator, self.header.?.ncmds);183 try self.load_commands.ensureTotalCapacity(allocator, self.header.?.ncmds);
184184
185 var i: u16 = 0;185 var i: u16 = 0;
186 while (i < self.header.?.ncmds) : (i += 1) {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,14 +31,12 @@ header: ?macho.mach_header_64 = null,
31load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},31load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
3232
33segment_cmd_index: ?u16 = null,33segment_cmd_index: ?u16 = null,
34text_section_index: ?u16 = null,
34symtab_cmd_index: ?u16 = null,35symtab_cmd_index: ?u16 = null,
35dysymtab_cmd_index: ?u16 = null,36dysymtab_cmd_index: ?u16 = null,
36build_version_cmd_index: ?u16 = null,37build_version_cmd_index: ?u16 = null,
37data_in_code_cmd_index: ?u16 = null,38data_in_code_cmd_index: ?u16 = null,
3839
39text_section_index: ?u16 = null,
40mod_init_func_section_index: ?u16 = null,
41
42// __DWARF segment sections40// __DWARF segment sections
43dwarf_debug_info_index: ?u16 = null,41dwarf_debug_info_index: ?u16 = null,
44dwarf_debug_abbrev_index: ?u16 = null,42dwarf_debug_abbrev_index: ?u16 = null,
...@@ -56,7 +54,9 @@ tu_name: ?[]const u8 = null,...@@ -56,7 +54,9 @@ tu_name: ?[]const u8 = null,
56tu_comp_dir: ?[]const u8 = null,54tu_comp_dir: ?[]const u8 = null,
57mtime: ?u64 = null,55mtime: ?u64 = null,
5856
59atoms: std.ArrayListUnmanaged(*Atom) = .{},57contained_atoms: std.ArrayListUnmanaged(*Atom) = .{},
58start_atoms: std.AutoHashMapUnmanaged(MachO.MatchingSection, *Atom) = .{},
59end_atoms: std.AutoHashMapUnmanaged(MachO.MatchingSection, *Atom) = .{},
60sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},60sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
6161
62// TODO symbol mapping and its inverse can probably be simple arrays62// TODO symbol mapping and its inverse can probably be simple arrays
...@@ -138,12 +138,15 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {...@@ -138,12 +138,15 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {
138 self.data_in_code_entries.deinit(allocator);138 self.data_in_code_entries.deinit(allocator);
139 self.symtab.deinit(allocator);139 self.symtab.deinit(allocator);
140 self.strtab.deinit(allocator);140 self.strtab.deinit(allocator);
141 self.atoms.deinit(allocator);
142 self.sections_as_symbols.deinit(allocator);141 self.sections_as_symbols.deinit(allocator);
143 self.symbol_mapping.deinit(allocator);142 self.symbol_mapping.deinit(allocator);
144 self.reverse_symbol_mapping.deinit(allocator);143 self.reverse_symbol_mapping.deinit(allocator);
145 allocator.free(self.name);144 allocator.free(self.name);
146145
146 self.contained_atoms.deinit(allocator);
147 self.start_atoms.deinit(allocator);
148 self.end_atoms.deinit(allocator);
149
147 if (self.debug_info) |*db| {150 if (self.debug_info) |*db| {
148 db.deinit(allocator);151 db.deinit(allocator);
149 }152 }
...@@ -157,6 +160,67 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {...@@ -157,6 +160,67 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {
157 }160 }
158}161}
159162
163pub 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
192fn 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
160pub fn parse(self: *Object, allocator: *Allocator, target: std.Target) !void {224pub fn parse(self: *Object, allocator: *Allocator, target: std.Target) !void {
161 const reader = self.file.reader();225 const reader = self.file.reader();
162 if (self.file_offset) |offset| {226 if (self.file_offset) |offset| {
...@@ -197,7 +261,7 @@ pub fn readLoadCommands(self: *Object, allocator: *Allocator, reader: anytype) !...@@ -197,7 +261,7 @@ pub fn readLoadCommands(self: *Object, allocator: *Allocator, reader: anytype) !
197 const header = self.header orelse unreachable; // Unreachable here signifies a fatal unexplored condition.261 const header = self.header orelse unreachable; // Unreachable here signifies a fatal unexplored condition.
198 const offset = self.file_offset orelse 0;262 const offset = self.file_offset orelse 0;
199263
200 try self.load_commands.ensureCapacity(allocator, header.ncmds);264 try self.load_commands.ensureTotalCapacity(allocator, header.ncmds);
201265
202 var i: u16 = 0;266 var i: u16 = 0;
203 while (i < header.ncmds) : (i += 1) {267 while (i < header.ncmds) : (i += 1) {
...@@ -226,10 +290,6 @@ pub fn readLoadCommands(self: *Object, allocator: *Allocator, reader: anytype) !...@@ -226,10 +290,6 @@ pub fn readLoadCommands(self: *Object, allocator: *Allocator, reader: anytype) !
226 if (mem.eql(u8, sectname, "__text")) {290 if (mem.eql(u8, sectname, "__text")) {
227 self.text_section_index = index;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 }
234294
235 sect.offset += offset;295 sect.offset += offset;
...@@ -315,166 +375,10 @@ fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64)...@@ -315,166 +375,10 @@ fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64)
315 return dices[start..end];375 return dices[start..end];
316}376}
317377
318const Context = struct {378pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO) !void {
319 allocator: *Allocator,
320 object: *Object,
321 macho_file: *MachO,
322 match: MachO.MatchingSection,
323 parsed_atoms: *ParsedAtoms,
324};
325
326const 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
466pub const ParsedAtoms = std.AutoHashMap(MachO.MatchingSection, *Atom);
467
468pub fn parseIntoAtoms(
469 self: *Object,
470 allocator: *Allocator,
471 object_id: u16,
472 macho_file: *MachO,
473) !ParsedAtoms {
474 const tracy = trace(@src());379 const tracy = trace(@src());
475 defer tracy.end();380 defer tracy.end();
476381
477 var parsed_atoms = ParsedAtoms.init(allocator);
478 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;382 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
479383
480 log.debug("analysing {s}", .{self.name});384 log.debug("analysing {s}", .{self.name});
...@@ -540,16 +444,6 @@ pub fn parseIntoAtoms(...@@ -540,16 +444,6 @@ pub fn parseIntoAtoms(
540 // Symbols within this section only.444 // Symbols within this section only.
541 const filtered_nlists = NlistWithIndex.filterInSection(sorted_nlists, sect);445 const filtered_nlists = NlistWithIndex.filterInSection(sorted_nlists, sect);
542446
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 macho_file.has_dices = macho_file.has_dices or blk: {447 macho_file.has_dices = macho_file.has_dices or blk: {
554 if (self.text_section_index) |index| {448 if (self.text_section_index) |index| {
555 if (index != id) break :blk false;449 if (index != id) break :blk false;
...@@ -560,237 +454,101 @@ pub fn parseIntoAtoms(...@@ -560,237 +454,101 @@ pub fn parseIntoAtoms(
560 };454 };
561 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;455 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
562456
563 next: {457 // Since there is no symbol to refer to this atom, we create
564 if (is_splittable) atoms: {458 // a temp one, unless we already did that when working out the relocations
565 if (filtered_nlists.len == 0) break :atoms;459 // of other atoms.
566460 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
567 // If the first nlist does not match the start of the section,461 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
568 // then we need to encapsulate the memory range [section start, first symbol)462 try macho_file.locals.append(allocator, .{
569 // as a temporary symbol and insert the matching Atom.463 .n_strx = 0,
570 const first_nlist = filtered_nlists[0].nlist;464 .n_type = macho.N_SECT,
571 if (first_nlist.n_value > sect.addr) {465 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
572 const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{466 .n_desc = 0,
573 self.name,467 .n_value = 0,
574 segmentName(sect),468 });
575 sectionName(sect),469 try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index);
576 });470 break :blk atom_local_sym_index;
577 defer allocator.free(sym_name);471 };
578472 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, sect.size, sect.@"align");
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 }
669473
670 if (sym.n_value == sect.addr) {474 const is_zerofill = blk: {
671 if (self.sections_as_symbols.get(sect_id)) |alias| {475 const section_type = commands.sectionType(sect);
672 // In x86_64 relocs, it can so happen that the compiler refers to the same476 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
673 // atom by both the actual assigned symbol and the start of the section. In this477 };
674 // case, we need to link the two together so add an alias.478 if (!is_zerofill) {
675 try atom.aliases.append(allocator, alias);479 mem.copy(u8, atom.code.items, code);
676 }480 }
677 }
678481
679 if (parsed_atoms.getPtr(match)) |last| {482 try atom.parseRelocs(relocs, .{
680 last.*.next = atom;483 .base_addr = sect.addr,
681 atom.prev = last.*;484 .base_offset = 0,
682 last.* = atom;485 .allocator = allocator,
683 } else {486 .object = self,
684 try parsed_atoms.putNoClobber(match, atom);487 .macho_file = macho_file,
685 }488 });
686 try self.atoms.append(allocator, atom);
687 }
688489
689 break :next;490 if (macho_file.has_dices) {
690 }491 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);
492 try atom.dices.ensureTotalCapacity(allocator, dices.len);
691493
692 // Since there is no symbol to refer to this atom, we create494 for (dices) |dice| {
693 // a temp one, unless we already did that when working out the relocations495 atom.dices.appendAssumeCapacity(.{
694 // of other atoms.496 .offset = dice.offset - try math.cast(u32, sect.addr),
695 const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{497 .length = dice.length,
696 self.name,498 .kind = dice.kind,
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,
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 }
745502
746 // Since this is atom gets a helper local temporary symbol that didn't exist503 // 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 traverse504 // in the object file which encompasses the entire section, we need traverse
748 // the filtered symbols and note which symbol is contained within so that505 // the filtered symbols and note which symbol is contained within so that
749 // we can properly allocate addresses down the line.506 // 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.507 // 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);508 try atom.contained.ensureTotalCapacity(allocator, filtered_nlists.len);
752509
753 for (filtered_nlists) |nlist_with_index| {510 for (filtered_nlists) |nlist_with_index| {
754 const nlist = nlist_with_index.nlist;511 const nlist = nlist_with_index.nlist;
755 const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;512 const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
756 const local = &macho_file.locals.items[local_sym_index];513 const local = &macho_file.locals.items[local_sym_index];
757 local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);514 local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);
758515
759 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {516 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {
760 // TODO there has to be a better to handle this.517 // TODO there has to be a better to handle this.
761 for (di.inner.func_list.items) |func| {518 for (di.inner.func_list.items) |func| {
762 if (func.pc_range) |range| {519 if (func.pc_range) |range| {
763 if (nlist.n_value >= range.start and nlist.n_value < range.end) {520 if (nlist.n_value >= range.start and nlist.n_value < range.end) {
764 break :blk Atom.Stab{521 break :blk Atom.Stab{
765 .function = range.end - range.start,522 .function = range.end - range.start,
766 };523 };
767 }
768 }524 }
769 }525 }
770 // TODO526 }
771 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;527 // TODO
772 break :blk .static;528 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
773 } else null;529 break :blk .static;
774530 } else null;
775 atom.contained.appendAssumeCapacity(.{531
776 .local_sym_index = local_sym_index,532 atom.contained.appendAssumeCapacity(.{
777 .offset = nlist.n_value - sect.addr,533 .local_sym_index = local_sym_index,
778 .stab = stab,534 .offset = nlist.n_value - sect.addr,
779 });535 .stab = stab,
780 }536 });
537 }
781538
782 if (parsed_atoms.getPtr(match)) |last| {539 if (!self.start_atoms.contains(match)) {
783 last.*.next = atom;540 try self.start_atoms.putNoClobber(allocator, match, 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);
790 }541 }
791 }
792542
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}
795553
796fn parseSymtab(self: *Object, allocator: *Allocator) !void {554fn 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,7 +326,7 @@ pub fn finalize(self: *Trie, allocator: *Allocator) !void {
326 if (!self.trie_dirty) return;326 if (!self.trie_dirty) return;
327327
328 self.ordered_nodes.shrinkRetainingCapacity(0);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);
330330
331 var fifo = std.fifo.LinearFifo(*Node, .Dynamic).init(allocator);331 var fifo = std.fifo.LinearFifo(*Node, .Dynamic).init(allocator);
332 defer fifo.deinit();332 defer fifo.deinit();
...@@ -506,7 +506,7 @@ test "write Trie to a byte stream" {...@@ -506,7 +506,7 @@ test "write Trie to a byte stream" {
506 });506 });
507507
508 try trie.finalize(gpa);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.
510510
511 const exp_buffer = [_]u8{511 const exp_buffer = [_]u8{
512 0x0, 0x1, // node root512 0x0, 0x1, // node root
src/link/MachO/commands.zig+1-1
...@@ -223,7 +223,7 @@ pub const SegmentCommand = struct {...@@ -223,7 +223,7 @@ pub const SegmentCommand = struct {
223 var segment = SegmentCommand{223 var segment = SegmentCommand{
224 .inner = inner,224 .inner = inner,
225 };225 };
226 try segment.sections.ensureCapacity(alloc, inner.nsects);226 try segment.sections.ensureTotalCapacity(alloc, inner.nsects);
227227
228 var i: usize = 0;228 var i: usize = 0;
229 while (i < inner.nsects) : (i += 1) {229 while (i < inner.nsects) : (i += 1) {
src/link/Plan9.zig+332-64
...@@ -20,6 +20,14 @@ const Allocator = std.mem.Allocator;...@@ -20,6 +20,14 @@ const Allocator = std.mem.Allocator;
20const log = std.log.scoped(.link);20const log = std.log.scoped(.link);
21const assert = std.debug.assert;21const assert = std.debug.assert;
2222
23const 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
23base: link.File,31base: link.File,
24sixtyfour_bit: bool,32sixtyfour_bit: bool,
25error_flags: File.ErrorFlags = File.ErrorFlags{},33error_flags: File.ErrorFlags = File.ErrorFlags{},
...@@ -27,16 +35,45 @@ bases: Bases,...@@ -27,16 +35,45 @@ bases: Bases,
2735
28/// A symbol's value is just casted down when compiling36/// A symbol's value is just casted down when compiling
29/// for a 32 bit target.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.
30syms: std.ArrayListUnmanaged(aout.Sym) = .{},41syms: std.ArrayListUnmanaged(aout.Sym) = .{},
3142
32fn_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
47file_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.
50file_segments_i: u16 = 1,
51
52path_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)
59fn_decl_table: std.AutoArrayHashMapUnmanaged(
60 *Module.Scope.File,
61 struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(*Module.Decl, FnDeclOutput) = .{} },
62) = .{},
33data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},63data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
3464
35hdr: aout.ExecHdr = undefined,65hdr: aout.ExecHdr = undefined,
3666
67magic: u32,
68
37entry_val: ?u64 = null,69entry_val: ?u64 = null,
3870
39got_len: usize = 0,71got_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.
74got_index_free_list: std.ArrayListUnmanaged(u64) = .{},
75
76syms_index_free_list: std.ArrayListUnmanaged(u64) = .{},
4077
41const Bases = struct {78const Bases = struct {
42 text: u64,79 text: u64,
...@@ -103,8 +140,12 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {...@@ -103,8 +140,12 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
103 33...64 => true,140 33...64 => true,
104 else => return error.UnsupportedP9Architecture,141 else => return error.UnsupportedP9Architecture,
105 };142 };
143
144 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
145
106 const self = try gpa.create(Plan9);146 const self = try gpa.create(Plan9);
107 self.* = .{147 self.* = .{
148 .path_arena = arena_allocator,
108 .base = .{149 .base = .{
109 .tag = .plan9,150 .tag = .plan9,
110 .options = options,151 .options = options,
...@@ -113,21 +154,102 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {...@@ -113,21 +154,102 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
113 },154 },
114 .sixtyfour_bit = sixtyfour_bit,155 .sixtyfour_bit = sixtyfour_bit,
115 .bases = undefined,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 return self;161 return self;
118}162}
119163
164fn 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
205fn 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
120pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {219pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
121 if (build_options.skip_non_native and builtin.object_format != .plan9) {220 if (build_options.skip_non_native and builtin.object_format != .plan9) {
122 @panic("Attempted to compile for object format that was disabled by build configuration");221 @panic("Attempted to compile for object format that was disabled by build configuration");
123 }222 }
124223
125 const decl = func.owner_decl;224 const decl = func.owner_decl;
225
226 try self.seeDecl(decl);
126 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });227 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
127228
128 var code_buffer = std.ArrayList(u8).init(self.base.allocator);229 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
129 defer code_buffer.deinit();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 const code = switch (res) {253 const code = switch (res) {
132 .appended => code_buffer.toOwnedSlice(),254 .appended => code_buffer.toOwnedSlice(),
133 .fail => |em| {255 .fail => |em| {
...@@ -136,7 +258,13 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv...@@ -136,7 +258,13 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
136 return;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 return self.updateFinish(decl);268 return self.updateFinish(decl);
141}269}
142270
...@@ -151,6 +279,8 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {...@@ -151,6 +279,8 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
151 }279 }
152 }280 }
153281
282 try self.seeDecl(decl);
283
154 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });284 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
155285
156 var code_buffer = std.ArrayList(u8).init(self.base.allocator);286 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
...@@ -192,8 +322,12 @@ fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {...@@ -192,8 +322,12 @@ fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {
192 if (decl.link.plan9.sym_index) |s| {322 if (decl.link.plan9.sym_index) |s| {
193 self.syms.items[s] = sym;323 self.syms.items[s] = sym;
194 } else {324 } else {
195 try self.syms.append(self.base.allocator, sym);325 if (self.syms_index_free_list.popOrNull()) |i| {
196 decl.link.plan9.sym_index = self.syms.items.len - 1;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}
199333
...@@ -209,6 +343,30 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {...@@ -209,6 +343,30 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {
209 return self.flushModule(comp);343 return self.flushModule(comp);
210}344}
211345
346pub 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
359fn 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
212pub fn flushModule(self: *Plan9, comp: *Compilation) !void {370pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
213 if (build_options.skip_non_native and builtin.object_format != .plan9) {371 if (build_options.skip_non_native and builtin.object_format != .plan9) {
214 @panic("Attempted to compile for object format that was disabled by build configuration");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,15 +382,13 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
224382
225 const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;383 const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
226384
227 // TODO I changed this assert from == to >= but this code all needs to be audited; see385 assert(self.got_len == self.declCount() + self.got_index_free_list.items.len);
228 // the comment in `freeDecl`.
229 assert(self.got_len >= self.fn_decl_table.count() + self.data_decl_table.count());
230 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;386 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
231 var got_table = try self.base.allocator.alloc(u8, got_size);387 var got_table = try self.base.allocator.alloc(u8, got_size);
232 defer self.base.allocator.free(got_table);388 defer self.base.allocator.free(got_table);
233389
234 // + 2 for header, got, symbols390 // + 4 for header, got, symbols, linecountinfo
235 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.fn_decl_table.count() + self.data_decl_table.count() + 3);391 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.declCount() + 4);
236 defer self.base.allocator.free(iovecs);392 defer self.base.allocator.free(iovecs);
237393
238 const file = self.base.file.?;394 const file = self.base.file.?;
...@@ -245,30 +401,52 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {...@@ -245,30 +401,52 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
245 iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len };401 iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len };
246 var iovecs_i: usize = 1;402 var iovecs_i: usize = 1;
247 var text_i: u64 = 0;403 var text_i: u64 = 0;
404
405 var linecountinfo = std.ArrayList(u8).init(self.base.allocator);
406 defer linecountinfo.deinit();
248 // text407 // text
249 {408 {
250 var it = self.fn_decl_table.iterator();409 var linecount: u32 = 0;
251 while (it.next()) |entry| {410 var it_file = self.fn_decl_table.iterator();
252 const decl = entry.key_ptr.*;411 while (it_file.next()) |fentry| {
253 const code = entry.value_ptr.*;412 var it = fentry.value_ptr.functions.iterator();
254 log.debug("write text decl {*} ({s})", .{ decl, decl.name });413 while (it.next()) |entry| {
255 foff += code.len;414 const decl = entry.key_ptr.*;
256 iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len };415 const out = entry.value_ptr.*;
257 iovecs_i += 1;416 log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line, out.end_line });
258 const off = self.getAddr(text_i, .t);417 {
259 text_i += code.len;418 // connect the previous decl to the next
260 decl.link.plan9.offset = off;419 const delta_line = @intCast(i32, out.start_line) - @intCast(i32, linecount);
261 if (!self.sixtyfour_bit) {420
262 mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));421 try changeLine(&linecountinfo, delta_line);
263 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());422 // TODO change the pc too (maybe?)
264 } else {423
265 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());424 // write out the actual info that was generated in codegen now
266 }425 try linecountinfo.appendSlice(out.lineinfo);
267 self.syms.items[decl.link.plan9.sym_index.?].value = off;426 linecount = out.end_line;
268 if (mod.decl_exports.get(decl)) |exports| {427 }
269 try self.addDeclExports(mod, decl, exports);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 // etext symbol450 // etext symbol
273 self.syms.items[2].value = self.getAddr(text_i, .t);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,20 +484,23 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
306 // edata484 // edata
307 self.syms.items[1].value = self.getAddr(0x0, .b);485 self.syms.items[1].value = self.getAddr(0x0, .b);
308 var sym_buf = std.ArrayList(u8).init(self.base.allocator);486 var sym_buf = std.ArrayList(u8).init(self.base.allocator);
309 defer sym_buf.deinit();
310 try self.writeSyms(&sym_buf);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 decls488 const syms = sym_buf.toOwnedSlice();
312 iovecs[iovecs_i] = .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len };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 iovecs_i += 1;494 iovecs_i += 1;
314 // generate the header495 // generate the header
315 self.hdr = .{496 self.hdr = .{
316 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),497 .magic = self.magic,
317 .text = @intCast(u32, text_i),498 .text = @intCast(u32, text_i),
318 .data = @intCast(u32, data_i),499 .data = @intCast(u32, data_i),
319 .syms = @intCast(u32, sym_buf.items.len),500 .syms = @intCast(u32, syms.len),
320 .bss = 0,501 .bss = 0,
321 .pcsz = 0,
322 .spsz = 0,502 .spsz = 0,
503 .pcsz = @intCast(u32, linecountinfo.items.len),
323 .entry = @intCast(u32, self.entry_val.?),504 .entry = @intCast(u32, self.entry_val.?),
324 };505 };
325 std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]);506 std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]);
...@@ -360,18 +541,43 @@ fn addDeclExports(...@@ -360,18 +541,43 @@ fn addDeclExports(
360}541}
361542
362pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {543pub 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 // TODO audit the lifetimes of decls table entries. It's possible to get544 // TODO audit the lifetimes of decls table entries. It's possible to get
366 // allocateDeclIndexes and then freeDecl without any updateDecl in between.545 // allocateDeclIndexes and then freeDecl without any updateDecl in between.
367 // However that is planned to change, see the TODO comment in Module.zig546 // However that is planned to change, see the TODO comment in Module.zig
368 // in the deleteUnusedDecl function.547 // in the deleteUnusedDecl function.
369 const is_fn = (decl.ty.zigTypeTag() == .Fn);548 const is_fn = (decl.val.tag() == .function);
370 if (is_fn) {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 } else {559 } else {
373 _ = self.data_decl_table.swapRemove(decl);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
572pub 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}
376582
377pub fn updateDeclExports(583pub fn updateDeclExports(
...@@ -380,6 +586,7 @@ pub fn updateDeclExports(...@@ -380,6 +586,7 @@ pub fn updateDeclExports(
380 decl: *Module.Decl,586 decl: *Module.Decl,
381 exports: []const *Module.Export,587 exports: []const *Module.Export,
382) !void {588) !void {
589 try self.seeDecl(decl);
383 // we do all the things in flush590 // we do all the things in flush
384 _ = self;591 _ = self;
385 _ = module;592 _ = module;
...@@ -387,17 +594,29 @@ pub fn updateDeclExports(...@@ -387,17 +594,29 @@ pub fn updateDeclExports(
387 _ = exports;594 _ = exports;
388}595}
389pub fn deinit(self: *Plan9) void {596pub fn deinit(self: *Plan9) void {
390 var itf = self.fn_decl_table.iterator();597 const gpa = self.base.allocator;
391 while (itf.next()) |entry| {598 var itf_files = self.fn_decl_table.iterator();
392 self.base.allocator.free(entry.value_ptr.*);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 var itd = self.data_decl_table.iterator();610 var itd = self.data_decl_table.iterator();
396 while (itd.next()) |entry| {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);614 self.data_decl_table.deinit(gpa);
400 self.syms.deinit(self.base.allocator);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}
402621
403pub const Export = ?usize;622pub const Export = ?usize;
...@@ -407,7 +626,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -407,7 +626,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
407 return error.LLVMBackendDoesNotSupportPlan9;626 return error.LLVMBackendDoesNotSupportPlan9;
408 assert(options.object_format == .plan9);627 assert(options.object_format == .plan9);
409 const file = try options.emit.?.directory.handle.createFile(sub_path, .{628 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
410 .truncate = false,
411 .read = true,629 .read = true,
412 .mode = link.determineMode(options),630 .mode = link.determineMode(options),
413 });631 });
...@@ -441,27 +659,77 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -441,27 +659,77 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
441 return self;659 return self;
442}660}
443661
662pub 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}
444pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {675pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
445 const writer = buf.writer();676 const writer = buf.writer();
446 for (self.syms.items) |sym| {677 // write the f symbols
447 log.debug("sym.name: {s}", .{sym.name});678 {
448 log.debug("sym.value: {x}", .{sym.value});679 var it = self.file_segments.iterator();
449 if (mem.eql(u8, sym.name, "_start"))680 while (it.next()) |entry| {
450 self.entry_val = sym.value;681 try self.writeSym(writer, .{
451 if (!self.sixtyfour_bit) {682 .type = .f,
452 try writer.writeIntBig(u32, @intCast(u32, sym.value));683 .value = entry.value_ptr.*,
453 } else {684 .name = entry.key_ptr.*,
454 try writer.writeIntBig(u64, sym.value);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}
461730
731/// this will be removed, moved to updateFinish
462pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {732pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
463 if (decl.link.plan9.got_index == null) {733 _ = self;
464 self.got_len += 1;734 _ = decl;
465 decl.link.plan9.got_index = self.got_len - 1;
466 }
467}735}
src/link/Plan9/aout.zig+18-1
...@@ -16,7 +16,7 @@ pub const ExecHdr = extern struct {...@@ -16,7 +16,7 @@ pub const ExecHdr = extern struct {
16 comptime {16 comptime {
17 assert(@sizeOf(@This()) == 32);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 pub fn toU8s(self: *@This()) [40]u8 {20 pub fn toU8s(self: *@This()) [40]u8 {
21 var buf: [40]u8 = undefined;21 var buf: [40]u8 = undefined;
22 var i: u8 = 0;22 var i: u8 = 0;
...@@ -34,6 +34,12 @@ pub const Sym = struct {...@@ -34,6 +34,12 @@ pub const Sym = struct {
34 type: Type,34 type: Type,
35 name: []const u8,35 name: []const u8,
3636
37 pub const undefined_symbol: Sym = .{
38 .value = undefined,
39 .type = .bad,
40 .name = "undefined_symbol",
41 };
42
37 /// The type field is one of the following characters with the43 /// The type field is one of the following characters with the
38 /// high bit set:44 /// high bit set:
39 /// T text segment symbol45 /// T text segment symbol
...@@ -65,6 +71,8 @@ pub const Sym = struct {...@@ -65,6 +71,8 @@ pub const Sym = struct {
65 z = 0x80 | 'z',71 z = 0x80 | 'z',
66 Z = 0x80 | 'Z',72 Z = 0x80 | 'Z',
67 m = 0x80 | 'm',73 m = 0x80 | 'm',
74 /// represents an undefined symbol, to be removed in flush
75 bad = 0,
6876
69 pub fn toGlobal(self: Type) Type {77 pub fn toGlobal(self: Type) Type {
70 return switch (self) {78 return switch (self) {
...@@ -112,3 +120,12 @@ pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 {...@@ -112,3 +120,12 @@ pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 {
112 else => error.ArchNotSupportedByPlan9,120 else => error.ArchNotSupportedByPlan9,
113 };121 };
114}122}
123
124/// gets the quantization of pc for the arch
125pub 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,7 +12,7 @@
12//! - OpName and OpMemberName instructions.12//! - OpName and OpMemberName instructions.
13//! - OpModuleProcessed instructions.13//! - OpModuleProcessed instructions.
14//! All annotation (decoration) instructions.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//! All function declarations without a body (extern functions presumably).16//! All function declarations without a body (extern functions presumably).
17//! All regular functions.17//! All regular functions.
1818
...@@ -93,7 +93,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio...@@ -93,7 +93,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
93 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForSpirV; // TODO: LLVM Doesn't support SpirV at all.93 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForSpirV; // TODO: LLVM Doesn't support SpirV at all.
94 if (options.use_lld) return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all.94 if (options.use_lld) return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all.
9595
96 // TODO: read the file and keep vaild parts instead of truncating96 // TODO: read the file and keep valid parts instead of truncating
97 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });97 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
98 errdefer file.close();98 errdefer file.close();
9999
src/link/Wasm.zig+10-6
...@@ -35,7 +35,7 @@ llvm_object: ?*LlvmObject = null,...@@ -35,7 +35,7 @@ llvm_object: ?*LlvmObject = null,
35/// TODO: can/should we access some data structure in Module directly?35/// TODO: can/should we access some data structure in Module directly?
36funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},36funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
37/// List of all extern function Decls to be written to the `import` section of the37/// 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 index38/// wasm binary. The position in the list defines the function index
39ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},39ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
40/// When importing objects from the host environment, a name must be supplied.40/// When importing objects from the host environment, a name must be supplied.
41/// LLVM uses "env" by default when none is given. This would be a good default for Zig41/// 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,7 +121,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
121 const self = try createEmpty(allocator, options);121 const self = try createEmpty(allocator, options);
122 errdefer self.base.destroy();122 errdefer self.base.destroy();
123123
124 self.llvm_object = try LlvmObject.create(allocator, options);124 self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
125 return self;125 return self;
126 }126 }
127127
...@@ -172,8 +172,8 @@ pub fn deinit(self: *Wasm) void {...@@ -172,8 +172,8 @@ pub fn deinit(self: *Wasm) void {
172pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {172pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
173 if (decl.link.wasm.init) return;173 if (decl.link.wasm.init) return;
174174
175 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);175 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
176 try self.symbols.ensureCapacity(self.base.allocator, self.symbols.items.len + 1);176 try self.symbols.ensureUnusedCapacity(self.base.allocator, 1);
177177
178 const block = &decl.link.wasm;178 const block = &decl.link.wasm;
179 block.init = true;179 block.init = true;
...@@ -339,6 +339,10 @@ pub fn updateDeclExports(...@@ -339,6 +339,10 @@ pub fn updateDeclExports(
339}339}
340340
341pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {341pub 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 if (self.getFuncidx(decl)) |func_idx| {346 if (self.getFuncidx(decl)) |func_idx| {
343 switch (decl.val.tag()) {347 switch (decl.val.tag()) {
344 .function => _ = self.funcs.swapRemove(func_idx),348 .function => _ = self.funcs.swapRemove(func_idx),
...@@ -710,7 +714,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -710,7 +714,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
710 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});714 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
711715
712 if (self.base.options.output_mode == .Obj) {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 copy717 // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy
714 // here. TODO: think carefully about how we can avoid this redundant operation when doing718 // here. TODO: think carefully about how we can avoid this redundant operation when doing
715 // build-obj. See also the corresponding TODO in linkAsArchive.719 // build-obj. See also the corresponding TODO in linkAsArchive.
716 const the_object_path = blk: {720 const the_object_path = blk: {
...@@ -752,7 +756,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -752,7 +756,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
752756
753 if (self.base.options.output_mode == .Exe) {757 if (self.base.options.output_mode == .Exe) {
754 // Increase the default stack size to a more reasonable value of 1MB instead of758 // 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 try argv.append("-z");760 try argv.append("-z");
757 const stack_size = self.base.options.stack_size_override orelse 1048576;761 const stack_size = self.base.options.stack_size_override orelse 1048576;
758 const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size});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,6 +20,10 @@ const translate_c = @import("translate_c.zig");
20const Cache = @import("Cache.zig");20const Cache = @import("Cache.zig");
21const target_util = @import("target.zig");21const target_util = @import("target.zig");
22const ThreadPool = @import("ThreadPool.zig");22const ThreadPool = @import("ThreadPool.zig");
23const crash_report = @import("crash_report.zig");
24
25// Crash report needs to override the panic handler and other root decls
26pub usingnamespace crash_report.root_decls;
2327
24pub fn fatal(comptime format: []const u8, args: anytype) noreturn {28pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
25 std.log.emerg(format, args);29 std.log.emerg(format, args);
...@@ -134,6 +138,8 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{...@@ -134,6 +138,8 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{
134}){};138}){};
135139
136pub fn main() anyerror!void {140pub fn main() anyerror!void {
141 crash_report.initialize();
142
137 var gpa_need_deinit = false;143 var gpa_need_deinit = false;
138 const gpa = gpa: {144 const gpa = gpa: {
139 if (!std.builtin.link_libc) {145 if (!std.builtin.link_libc) {
...@@ -1151,6 +1157,7 @@ fn buildOutputType(...@@ -1151,6 +1157,7 @@ fn buildOutputType(
1151 var is_shared_lib = false;1157 var is_shared_lib = false;
1152 var linker_args = std.ArrayList([]const u8).init(arena);1158 var linker_args = std.ArrayList([]const u8).init(arena);
1153 var it = ClangArgIterator.init(arena, all_args);1159 var it = ClangArgIterator.init(arena, all_args);
1160 var emit_llvm = false;
1154 while (it.has_next) {1161 while (it.has_next) {
1155 it.next() catch |err| {1162 it.next() catch |err| {
1156 fatal("unable to parse command line parameters: {s}", .{@errorName(err)});1163 fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
...@@ -1161,6 +1168,7 @@ fn buildOutputType(...@@ -1161,6 +1168,7 @@ fn buildOutputType(
1161 .c => c_out_mode = .object, // -c1168 .c => c_out_mode = .object, // -c
1162 .asm_only => c_out_mode = .assembly, // -S1169 .asm_only => c_out_mode = .assembly, // -S
1163 .preprocess_only => c_out_mode = .preprocessor, // -E1170 .preprocess_only => c_out_mode = .preprocessor, // -E
1171 .emit_llvm => emit_llvm = true,
1164 .other => {1172 .other => {
1165 try clang_argv.appendSlice(it.other_args);1173 try clang_argv.appendSlice(it.other_args);
1166 },1174 },
...@@ -1518,22 +1526,42 @@ fn buildOutputType(...@@ -1518,22 +1526,42 @@ fn buildOutputType(
1518 output_mode = if (is_shared_lib) .Lib else .Exe;1526 output_mode = if (is_shared_lib) .Lib else .Exe;
1519 emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;1527 emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
1520 enable_cache = true;1528 enable_cache = true;
1529 if (emit_llvm) {
1530 fatal("-emit-llvm cannot be used when linking", .{});
1531 }
1521 },1532 },
1522 .object => {1533 .object => {
1523 output_mode = .Obj;1534 output_mode = .Obj;
1524 if (out_path) |p| {1535 if (emit_llvm) {
1525 emit_bin = .{ .yes = p };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 } else {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 .assembly => {1550 .assembly => {
1531 output_mode = .Obj;1551 output_mode = .Obj;
1532 emit_bin = .no;1552 emit_bin = .no;
1533 if (out_path) |p| {1553 if (emit_llvm) {
1534 emit_asm = .{ .yes = p };1554 if (out_path) |p| {
1555 emit_llvm_ir = .{ .yes = p };
1556 } else {
1557 emit_llvm_ir = .yes_default_path;
1558 }
1535 } else {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 .preprocessor => {1567 .preprocessor => {
...@@ -1682,22 +1710,22 @@ fn buildOutputType(...@@ -1682,22 +1710,22 @@ fn buildOutputType(
1682 } else true;1710 } else true;
1683 if (!should_get_sdk_path) break :outer false;1711 if (!should_get_sdk_path) break :outer false;
1684 if (try std.zig.system.darwin.getSDKPath(arena, target_info.target)) |sdk_path| {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 clang_argv.appendAssumeCapacity("-isysroot");1714 clang_argv.appendAssumeCapacity("-isysroot");
1687 clang_argv.appendAssumeCapacity(sdk_path);1715 clang_argv.appendAssumeCapacity(sdk_path);
1688 break :outer true;1716 break :outer true;
1689 } else break :outer false;1717 } else break :outer false;
1690 } else false;1718 } else false;
16911719
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 const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem";1721 const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem";
1694 for (paths.include_dirs.items) |include_dir| {1722 for (paths.include_dirs.items) |include_dir| {
1695 clang_argv.appendAssumeCapacity(isystem_flag);1723 clang_argv.appendAssumeCapacity(isystem_flag);
1696 clang_argv.appendAssumeCapacity(include_dir);1724 clang_argv.appendAssumeCapacity(include_dir);
1697 }1725 }
16981726
1699 try clang_argv.ensureCapacity(clang_argv.items.len + paths.framework_dirs.items.len * 2);1727 try clang_argv.ensureUnusedCapacity(paths.framework_dirs.items.len * 2);
1700 try framework_dirs.ensureCapacity(framework_dirs.items.len + paths.framework_dirs.items.len);1728 try framework_dirs.ensureUnusedCapacity(paths.framework_dirs.items.len);
1701 const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework";1729 const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework";
1702 for (paths.framework_dirs.items) |framework_dir| {1730 for (paths.framework_dirs.items) |framework_dir| {
1703 clang_argv.appendAssumeCapacity(iframework_flag);1731 clang_argv.appendAssumeCapacity(iframework_flag);
...@@ -2761,7 +2789,7 @@ pub fn cmdInit(...@@ -2761,7 +2789,7 @@ pub fn cmdInit(
2761 fatal("unable to read template file 'build.zig': {s}", .{@errorName(err)});2789 fatal("unable to read template file 'build.zig': {s}", .{@errorName(err)});
2762 };2790 };
2763 var modified_build_zig_contents = std.ArrayList(u8).init(arena);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 for (build_zig_contents) |c| {2793 for (build_zig_contents) |c| {
2766 if (c == '$') {2794 if (c == '$') {
2767 try modified_build_zig_contents.appendSlice(cwd_basename);2795 try modified_build_zig_contents.appendSlice(cwd_basename);
...@@ -2778,6 +2806,12 @@ pub fn cmdInit(...@@ -2778,6 +2806,12 @@ pub fn cmdInit(
2778 error.FileNotFound => {},2806 error.FileNotFound => {},
2779 else => fatal("unable to test existence of build.zig: {s}\n", .{@errorName(err)}),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 var src_dir = try fs.cwd().makeOpenPath("src", .{});2815 var src_dir = try fs.cwd().makeOpenPath("src", .{});
2782 defer src_dir.close();2816 defer src_dir.close();
27832817
...@@ -3442,7 +3476,7 @@ fn fmtPathFile(...@@ -3442,7 +3476,7 @@ fn fmtPathFile(
34423476
3443 // As a heuristic, we make enough capacity for the same as the input source.3477 // As a heuristic, we make enough capacity for the same as the input source.
3444 fmt.out_buffer.shrinkRetainingCapacity(0);3478 fmt.out_buffer.shrinkRetainingCapacity(0);
3445 try fmt.out_buffer.ensureCapacity(source_code.len);3479 try fmt.out_buffer.ensureTotalCapacity(source_code.len);
34463480
3447 try tree.renderToArrayList(&fmt.out_buffer);3481 try tree.renderToArrayList(&fmt.out_buffer);
3448 if (mem.eql(u8, fmt.out_buffer.items, source_code))3482 if (mem.eql(u8, fmt.out_buffer.items, source_code))
...@@ -3663,6 +3697,7 @@ pub const ClangArgIterator = struct {...@@ -3663,6 +3697,7 @@ pub const ClangArgIterator = struct {
3663 no_red_zone,3697 no_red_zone,
3664 strip,3698 strip,
3665 exec_model,3699 exec_model,
3700 emit_llvm,
3666 };3701 };
36673702
3668 const Args = struct {3703 const Args = struct {
...@@ -4109,7 +4144,7 @@ pub fn cmdAstCheck(...@@ -4109,7 +4144,7 @@ pub fn cmdAstCheck(
4109 // zig fmt: on4144 // zig fmt: on
4110 }4145 }
41114146
4112 return Zir.renderAsTextToFile(gpa, &file, io.getStdOut());4147 return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
4113}4148}
41144149
4115/// This is only enabled for debug builds.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,7 +312,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
312 if (try man.hit()) {312 if (try man.hit()) {
313 const digest = man.final();313 const digest = man.final();
314314
315 try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);315 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
316 comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{316 comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{
317 .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{317 .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
318 "o", &digest, final_lib_basename,318 "o", &digest, final_lib_basename,
...@@ -857,6 +857,7 @@ const mingwex_generic_src = [_][]const u8{...@@ -857,6 +857,7 @@ const mingwex_generic_src = [_][]const u8{
857 "stdio" ++ path.sep_str ++ "fopen64.c",857 "stdio" ++ path.sep_str ++ "fopen64.c",
858 "stdio" ++ path.sep_str ++ "fseeko32.c",858 "stdio" ++ path.sep_str ++ "fseeko32.c",
859 "stdio" ++ path.sep_str ++ "fseeko64.c",859 "stdio" ++ path.sep_str ++ "fseeko64.c",
860 "stdio" ++ path.sep_str ++ "fseeki64.c",
860 "stdio" ++ path.sep_str ++ "fsetpos64.c",861 "stdio" ++ path.sep_str ++ "fsetpos64.c",
861 "stdio" ++ path.sep_str ++ "ftello.c",862 "stdio" ++ path.sep_str ++ "ftello.c",
862 "stdio" ++ path.sep_str ++ "ftello64.c",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,7 +112,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
112 var source_table = std.StringArrayHashMap(Ext).init(comp.gpa);112 var source_table = std.StringArrayHashMap(Ext).init(comp.gpa);
113 defer source_table.deinit();113 defer source_table.deinit();
114114
115 try source_table.ensureCapacity(compat_time32_files.len + src_files.len);115 try source_table.ensureTotalCapacity(compat_time32_files.len + src_files.len);
116116
117 for (src_files) |src_file| {117 for (src_files) |src_file| {
118 try addSrcFile(arena, &source_table, src_file);118 try addSrcFile(arena, &source_table, src_file);
...@@ -231,7 +231,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -231,7 +231,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
231231
232 try sub_compilation.updateSubCompilation();232 try sub_compilation.updateSubCompilation();
233233
234 try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);234 try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
235235
236 const basename = try comp.gpa.dupe(u8, "libc.so");236 const basename = try comp.gpa.dupe(u8, "libc.so");
237 errdefer comp.gpa.free(basename);237 errdefer comp.gpa.free(basename);
src/print_air.zig+81-1
...@@ -104,12 +104,16 @@ const Writer = struct {...@@ -104,12 +104,16 @@ const Writer = struct {
104104
105 .add,105 .add,
106 .addwrap,106 .addwrap,
107 .add_sat,
107 .sub,108 .sub,
108 .subwrap,109 .subwrap,
110 .sub_sat,
109 .mul,111 .mul,
110 .mulwrap,112 .mulwrap,
113 .mul_sat,
111 .div,114 .div,
112 .rem,115 .rem,
116 .mod,
113 .ptr_add,117 .ptr_add,
114 .ptr_sub,118 .ptr_sub,
115 .bit_and,119 .bit_and,
...@@ -129,7 +133,10 @@ const Writer = struct {...@@ -129,7 +133,10 @@ const Writer = struct {
129 .ptr_elem_val,133 .ptr_elem_val,
130 .ptr_ptr_elem_val,134 .ptr_ptr_elem_val,
131 .shl,135 .shl,
136 .shl_exact,
137 .shl_sat,
132 .shr,138 .shr,
139 .set_union_tag,
133 => try w.writeBinOp(s, inst),140 => try w.writeBinOp(s, inst),
134141
135 .is_null,142 .is_null,
...@@ -156,7 +163,8 @@ const Writer = struct {...@@ -156,7 +163,8 @@ const Writer = struct {
156 .not,163 .not,
157 .bitcast,164 .bitcast,
158 .load,165 .load,
159 .floatcast,166 .fptrunc,
167 .fpext,
160 .intcast,168 .intcast,
161 .trunc,169 .trunc,
162 .optional_payload,170 .optional_payload,
...@@ -175,6 +183,11 @@ const Writer = struct {...@@ -175,6 +183,11 @@ const Writer = struct {
175 .struct_field_ptr_index_2,183 .struct_field_ptr_index_2,
176 .struct_field_ptr_index_3,184 .struct_field_ptr_index_3,
177 .array_to_slice,185 .array_to_slice,
186 .int_to_float,
187 .float_to_int,
188 .get_union_tag,
189 .clz,
190 .ctz,
178 => try w.writeTyOp(s, inst),191 => try w.writeTyOp(s, inst),
179192
180 .block,193 .block,
...@@ -192,6 +205,15 @@ const Writer = struct {...@@ -192,6 +205,15 @@ const Writer = struct {
192 .cond_br => try w.writeCondBr(s, inst),205 .cond_br => try w.writeCondBr(s, inst),
193 .switch_br => try w.writeSwitchBr(s, inst),206 .switch_br => try w.writeSwitchBr(s, inst),
194 .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),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 }
197219
...@@ -276,6 +298,64 @@ const Writer = struct {...@@ -276,6 +298,64 @@ const Writer = struct {
276 });298 });
277 }299 }
278300
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 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {359 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
280 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;360 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
281 const val = w.air.values[ty_pl.payload];361 const val = w.air.values[ty_pl.payload];
src/print_zir.zig created+2040
...@@ -0,0 +1,2040 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;
5const Ast = std.zig.Ast;
6
7const Zir = @import("Zir.zig");
8const Module = @import("Module.zig");
9const LazySrcLoc = Module.LazySrcLoc;
10
11/// Write human-readable, debug formatted ZIR code to a file.
12pub 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
63pub 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
96pub 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
122const 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,6 +86,14 @@ enum CallingConvention {
86 CallingConventionSysV86 CallingConventionSysV
87};87};
8888
89// Stage 1 supports only the generic address space
90enum AddressSpace {
91 AddressSpaceGeneric,
92 AddressSpaceGS,
93 AddressSpaceFS,
94 AddressSpaceSS,
95};
96
89// This one corresponds to the builtin.zig enum.97// This one corresponds to the builtin.zig enum.
90enum BuiltinPtrSize {98enum BuiltinPtrSize {
91 BuiltinPtrSizeOne,99 BuiltinPtrSizeOne,
...@@ -804,14 +812,18 @@ enum BinOpType {...@@ -804,14 +812,18 @@ enum BinOpType {
804 BinOpTypeInvalid,812 BinOpTypeInvalid,
805 BinOpTypeAssign,813 BinOpTypeAssign,
806 BinOpTypeAssignTimes,814 BinOpTypeAssignTimes,
815 BinOpTypeAssignTimesSat,
807 BinOpTypeAssignTimesWrap,816 BinOpTypeAssignTimesWrap,
808 BinOpTypeAssignDiv,817 BinOpTypeAssignDiv,
809 BinOpTypeAssignMod,818 BinOpTypeAssignMod,
810 BinOpTypeAssignPlus,819 BinOpTypeAssignPlus,
820 BinOpTypeAssignPlusSat,
811 BinOpTypeAssignPlusWrap,821 BinOpTypeAssignPlusWrap,
812 BinOpTypeAssignMinus,822 BinOpTypeAssignMinus,
823 BinOpTypeAssignMinusSat,
813 BinOpTypeAssignMinusWrap,824 BinOpTypeAssignMinusWrap,
814 BinOpTypeAssignBitShiftLeft,825 BinOpTypeAssignBitShiftLeft,
826 BinOpTypeAssignBitShiftLeftSat,
815 BinOpTypeAssignBitShiftRight,827 BinOpTypeAssignBitShiftRight,
816 BinOpTypeAssignBitAnd,828 BinOpTypeAssignBitAnd,
817 BinOpTypeAssignBitXor,829 BinOpTypeAssignBitXor,
...@@ -828,12 +840,16 @@ enum BinOpType {...@@ -828,12 +840,16 @@ enum BinOpType {
828 BinOpTypeBinXor,840 BinOpTypeBinXor,
829 BinOpTypeBinAnd,841 BinOpTypeBinAnd,
830 BinOpTypeBitShiftLeft,842 BinOpTypeBitShiftLeft,
843 BinOpTypeBitShiftLeftSat,
831 BinOpTypeBitShiftRight,844 BinOpTypeBitShiftRight,
832 BinOpTypeAdd,845 BinOpTypeAdd,
846 BinOpTypeAddSat,
833 BinOpTypeAddWrap,847 BinOpTypeAddWrap,
834 BinOpTypeSub,848 BinOpTypeSub,
849 BinOpTypeSubSat,
835 BinOpTypeSubWrap,850 BinOpTypeSubWrap,
836 BinOpTypeMult,851 BinOpTypeMult,
852 BinOpTypeMultSat,
837 BinOpTypeMultWrap,853 BinOpTypeMultWrap,
838 BinOpTypeDiv,854 BinOpTypeDiv,
839 BinOpTypeMod,855 BinOpTypeMod,
...@@ -1802,10 +1818,6 @@ enum BuiltinFnId {...@@ -1802,10 +1818,6 @@ enum BuiltinFnId {
1802 BuiltinFnIdReduce,1818 BuiltinFnIdReduce,
1803 BuiltinFnIdMaximum,1819 BuiltinFnIdMaximum,
1804 BuiltinFnIdMinimum,1820 BuiltinFnIdMinimum,
1805 BuiltinFnIdSatAdd,
1806 BuiltinFnIdSatSub,
1807 BuiltinFnIdSatMul,
1808 BuiltinFnIdSatShl,
1809};1821};
18101822
1811struct BuiltinFnEntry {1823struct BuiltinFnEntry {
...@@ -2950,10 +2962,10 @@ enum IrBinOp {...@@ -2950,10 +2962,10 @@ enum IrBinOp {
2950 IrBinOpArrayMult,2962 IrBinOpArrayMult,
2951 IrBinOpMaximum,2963 IrBinOpMaximum,
2952 IrBinOpMinimum,2964 IrBinOpMinimum,
2953 IrBinOpSatAdd,2965 IrBinOpAddSat,
2954 IrBinOpSatSub,2966 IrBinOpSubSat,
2955 IrBinOpSatMul,2967 IrBinOpMultSat,
2956 IrBinOpSatShl,2968 IrBinOpShlSat,
2957};2969};
29582970
2959struct Stage1ZirInstBinOp {2971struct Stage1ZirInstBinOp {
src/stage1/analyze.cpp+11-1
...@@ -1019,6 +1019,16 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {...@@ -1019,6 +1019,16 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
1019 zig_unreachable();1019 zig_unreachable();
1020}1020}
10211021
1022const 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
1022ZigType *get_stack_trace_type(CodeGen *g) {1032ZigType *get_stack_trace_type(CodeGen *g) {
1023 if (g->stack_trace_type == nullptr) {1033 if (g->stack_trace_type == nullptr) {
1024 g->stack_trace_type = get_builtin_type(g, "StackTrace");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,7 +6804,7 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
6794 // Since this frame is async, an await might represent a suspend point, and6804 // Since this frame is async, an await might represent a suspend point, and
6795 // therefore need to spill. It also needs to mark expr scopes as having to spill.6805 // therefore need to spill. It also needs to mark expr scopes as having to spill.
6796 // For example: foo() + await z6806 // 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 for (size_t i = 0; i < fn->await_list.length; i += 1) {6808 for (size_t i = 0; i < fn->await_list.length; i += 1) {
6799 Stage1AirInstAwait *await = fn->await_list.at(i);6809 Stage1AirInstAwait *await = fn->await_list.at(i);
6800 if (await->is_nosuspend) {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,6 +242,8 @@ Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result);
242bool calling_convention_allows_zig_types(CallingConvention cc);242bool calling_convention_allows_zig_types(CallingConvention cc);
243const char *calling_convention_name(CallingConvention cc);243const char *calling_convention_name(CallingConvention cc);
244244
245const char *address_space_name(AddressSpace as);
246
245Error ATTRIBUTE_MUST_USE file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents);247Error ATTRIBUTE_MUST_USE file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents);
246248
247void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);249void 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,6 +3672,8 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
3672 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMult), lval, result_loc);3672 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMult), lval, result_loc);
3673 case BinOpTypeAssignTimesWrap:3673 case BinOpTypeAssignTimesWrap:
3674 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultWrap), lval, result_loc);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 case BinOpTypeAssignDiv:3677 case BinOpTypeAssignDiv:
3676 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);3678 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
3677 case BinOpTypeAssignMod:3679 case BinOpTypeAssignMod:
...@@ -3680,12 +3682,18 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod...@@ -3680,12 +3682,18 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
3680 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAdd), lval, result_loc);3682 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAdd), lval, result_loc);
3681 case BinOpTypeAssignPlusWrap:3683 case BinOpTypeAssignPlusWrap:
3682 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddWrap), lval, result_loc);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 case BinOpTypeAssignMinus:3687 case BinOpTypeAssignMinus:
3684 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSub), lval, result_loc);3688 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSub), lval, result_loc);
3685 case BinOpTypeAssignMinusWrap:3689 case BinOpTypeAssignMinusWrap:
3686 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubWrap), lval, result_loc);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 case BinOpTypeAssignBitShiftLeft:3693 case BinOpTypeAssignBitShiftLeft:
3688 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);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 case BinOpTypeAssignBitShiftRight:3697 case BinOpTypeAssignBitShiftRight:
3690 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);3698 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
3691 case BinOpTypeAssignBitAnd:3699 case BinOpTypeAssignBitAnd:
...@@ -3718,20 +3726,28 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod...@@ -3718,20 +3726,28 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
3718 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBinAnd), lval, result_loc);3726 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBinAnd), lval, result_loc);
3719 case BinOpTypeBitShiftLeft:3727 case BinOpTypeBitShiftLeft:
3720 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);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 case BinOpTypeBitShiftRight:3731 case BinOpTypeBitShiftRight:
3722 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);3732 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
3723 case BinOpTypeAdd:3733 case BinOpTypeAdd:
3724 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAdd), lval, result_loc);3734 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAdd), lval, result_loc);
3725 case BinOpTypeAddWrap:3735 case BinOpTypeAddWrap:
3726 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddWrap), lval, result_loc);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 case BinOpTypeSub:3739 case BinOpTypeSub:
3728 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSub), lval, result_loc);3740 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSub), lval, result_loc);
3729 case BinOpTypeSubWrap:3741 case BinOpTypeSubWrap:
3730 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubWrap), lval, result_loc);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 case BinOpTypeMult:3745 case BinOpTypeMult:
3732 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMult), lval, result_loc);3746 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMult), lval, result_loc);
3733 case BinOpTypeMultWrap:3747 case BinOpTypeMultWrap:
3734 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultWrap), lval, result_loc);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 case BinOpTypeDiv:3751 case BinOpTypeDiv:
3736 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);3752 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
3737 case BinOpTypeMod:3753 case BinOpTypeMod:
...@@ -4704,66 +4720,6 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast...@@ -4704,66 +4720,6 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
4704 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);4720 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);
4705 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);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 case BuiltinFnIdMemcpy:4723 case BuiltinFnIdMemcpy:
4768 {4724 {
4769 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);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,9 +487,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
487 if (mangled_symbol_buf) buf_destroy(mangled_symbol_buf);487 if (mangled_symbol_buf) buf_destroy(mangled_symbol_buf);
488 }488 }
489 } else {489 } else {
490 if (llvm_fn == nullptr) {490 llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type);
491 llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type);
492 }
493491
494 for (size_t i = 1; i < fn->export_list.length; i += 1) {492 for (size_t i = 1; i < fn->export_list.length; i += 1) {
495 GlobalExport *fn_export = &fn->export_list.items[i];493 GlobalExport *fn_export = &fn->export_list.items[i];
...@@ -3335,7 +3333,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3335,7 +3333,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
3335 } else {3333 } else {
3336 zig_unreachable();3334 zig_unreachable();
3337 }3335 }
3338 case IrBinOpSatAdd:3336 case IrBinOpAddSat:
3339 if (scalar_type->id == ZigTypeIdInt) {3337 if (scalar_type->id == ZigTypeIdInt) {
3340 if (scalar_type->data.integral.is_signed) {3338 if (scalar_type->data.integral.is_signed) {
3341 return ZigLLVMBuildSAddSat(g->builder, op1_value, op2_value, "");3339 return ZigLLVMBuildSAddSat(g->builder, op1_value, op2_value, "");
...@@ -3345,7 +3343,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3345,7 +3343,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
3345 } else {3343 } else {
3346 zig_unreachable();3344 zig_unreachable();
3347 }3345 }
3348 case IrBinOpSatSub:3346 case IrBinOpSubSat:
3349 if (scalar_type->id == ZigTypeIdInt) {3347 if (scalar_type->id == ZigTypeIdInt) {
3350 if (scalar_type->data.integral.is_signed) {3348 if (scalar_type->data.integral.is_signed) {
3351 return ZigLLVMBuildSSubSat(g->builder, op1_value, op2_value, "");3349 return ZigLLVMBuildSSubSat(g->builder, op1_value, op2_value, "");
...@@ -3355,7 +3353,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3355,7 +3353,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
3355 } else {3353 } else {
3356 zig_unreachable();3354 zig_unreachable();
3357 }3355 }
3358 case IrBinOpSatMul:3356 case IrBinOpMultSat:
3359 if (scalar_type->id == ZigTypeIdInt) {3357 if (scalar_type->id == ZigTypeIdInt) {
3360 if (scalar_type->data.integral.is_signed) {3358 if (scalar_type->data.integral.is_signed) {
3361 return ZigLLVMBuildSMulFixSat(g->builder, op1_value, op2_value, "");3359 return ZigLLVMBuildSMulFixSat(g->builder, op1_value, op2_value, "");
...@@ -3365,7 +3363,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3365,7 +3363,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
3365 } else {3363 } else {
3366 zig_unreachable();3364 zig_unreachable();
3367 }3365 }
3368 case IrBinOpSatShl:3366 case IrBinOpShlSat:
3369 if (scalar_type->id == ZigTypeIdInt) {3367 if (scalar_type->id == ZigTypeIdInt) {
3370 if (scalar_type->data.integral.is_signed) {3368 if (scalar_type->data.integral.is_signed) {
3371 return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, "");3369 return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, "");
...@@ -9134,10 +9132,6 @@ static void define_builtin_fns(CodeGen *g) {...@@ -9134,10 +9132,6 @@ static void define_builtin_fns(CodeGen *g) {
9134 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);9132 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
9135 create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);9133 create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
9136 create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);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}
91429136
9143static const char *bool_to_str(bool b) {9137static const char *bool_to_str(bool b) {
...@@ -9323,6 +9317,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {...@@ -9323,6 +9317,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
9323 buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));9317 buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
9324 buf_appendf(contents, "pub const abi = std.Target.Abi.%s;\n", cur_abi);9318 buf_appendf(contents, "pub const abi = std.Target.Abi.%s;\n", cur_abi);
9325 buf_appendf(contents, "pub const cpu = std.Target.Cpu.baseline(.%s);\n", cur_arch);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 buf_appendf(contents, "pub const os = std.Target.Os.Tag.defaultVersionRange(.%s);\n", cur_os);9321 buf_appendf(contents, "pub const os = std.Target.Os.Tag.defaultVersionRange(.%s);\n", cur_os);
9327 buf_appendf(contents,9322 buf_appendf(contents,
9328 "pub const target = std.Target{\n"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,7 +6374,7 @@ static Stage1AirInst *ir_analyze_enum_to_union(IrAnalyze *ira, Scope *scope, Ast
63746374
6375 if (target->value->type->data.enumeration.non_exhaustive) {6375 if (target->value->type->data.enumeration.non_exhaustive) {
6376 ir_add_error_node(ira, source_node,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 buf_ptr(&wanted_type->name)));6378 buf_ptr(&wanted_type->name)));
6379 return ira->codegen->invalid_inst_gen;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,28 +9820,28 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s
9820 float_min(out_val, op1_val, op2_val);9820 float_min(out_val, op1_val, op2_val);
9821 }9821 }
9822 break;9822 break;
9823 case IrBinOpSatAdd:9823 case IrBinOpAddSat:
9824 if (is_int) {9824 if (is_int) {
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);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 } else {9826 } else {
9827 zig_unreachable();9827 zig_unreachable();
9828 }9828 }
9829 break;9829 break;
9830 case IrBinOpSatSub:9830 case IrBinOpSubSat:
9831 if (is_int) {9831 if (is_int) {
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);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 } else {9833 } else {
9834 zig_unreachable();9834 zig_unreachable();
9835 }9835 }
9836 break;9836 break;
9837 case IrBinOpSatMul:9837 case IrBinOpMultSat:
9838 if (is_int) {9838 if (is_int) {
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);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 } else {9840 } else {
9841 zig_unreachable();9841 zig_unreachable();
9842 }9842 }
9843 break;9843 break;
9844 case IrBinOpSatShl:9844 case IrBinOpShlSat:
9845 if (is_int) {9845 if (is_int) {
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);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 } else {9847 } else {
...@@ -10069,10 +10069,10 @@ static bool ok_float_op(IrBinOp op) {...@@ -10069,10 +10069,10 @@ static bool ok_float_op(IrBinOp op) {
10069 case IrBinOpBitShiftRightExact:10069 case IrBinOpBitShiftRightExact:
10070 case IrBinOpAddWrap:10070 case IrBinOpAddWrap:
10071 case IrBinOpSubWrap:10071 case IrBinOpSubWrap:
10072 case IrBinOpSatAdd:10072 case IrBinOpAddSat:
10073 case IrBinOpSatSub:10073 case IrBinOpSubSat:
10074 case IrBinOpSatMul:10074 case IrBinOpMultSat:
10075 case IrBinOpSatShl:10075 case IrBinOpShlSat:
10076 case IrBinOpMultWrap:10076 case IrBinOpMultWrap:
10077 case IrBinOpArrayCat:10077 case IrBinOpArrayCat:
10078 case IrBinOpArrayMult:10078 case IrBinOpArrayMult:
...@@ -11046,10 +11046,10 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns...@@ -11046,10 +11046,10 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns
11046 case IrBinOpRemMod:11046 case IrBinOpRemMod:
11047 case IrBinOpMaximum:11047 case IrBinOpMaximum:
11048 case IrBinOpMinimum:11048 case IrBinOpMinimum:
11049 case IrBinOpSatAdd:11049 case IrBinOpAddSat:
11050 case IrBinOpSatSub:11050 case IrBinOpSubSat:
11051 case IrBinOpSatMul:11051 case IrBinOpMultSat:
11052 case IrBinOpSatShl:11052 case IrBinOpShlSat:
11053 return ir_analyze_bin_op_math(ira, bin_op_instruction);11053 return ir_analyze_bin_op_math(ira, bin_op_instruction);
11054 case IrBinOpArrayCat:11054 case IrBinOpArrayCat:
11055 return ir_analyze_array_cat(ira, bin_op_instruction);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,7 +15189,7 @@ static Stage1AirInst *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
15189 return ir_analyze_inferred_field_ptr(ira, field_name, scope, source_node, container_ptr, bare_type);15189 return ir_analyze_inferred_field_ptr(ira, field_name, scope, source_node, container_ptr, bare_type);
15190 }15190 }
1519115191
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 // We do this if the container pointer is undefined and we are in a TypeOf call.15193 // We do this if the container pointer is undefined and we are in a TypeOf call.
15194 bool return_undef = container_ptr->value->special == ConstValSpecialUndef && \15194 bool return_undef = container_ptr->value->special == ConstValSpecialUndef && \
15195 get_scope_typeof(scope) != nullptr;15195 get_scope_typeof(scope) != nullptr;
...@@ -15248,7 +15248,7 @@ static Stage1AirInst *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_...@@ -15248,7 +15248,7 @@ static Stage1AirInst *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
15248 if (type_is_invalid(union_val->type))15248 if (type_is_invalid(union_val->type))
15249 return ira->codegen->invalid_inst_gen;15249 return ira->codegen->invalid_inst_gen;
1525015250
15251 // Reject undefined values unless we're intializing the union:15251 // Reject undefined values unless we're initializing the union:
15252 // a undefined union means also the tag is undefined, accessing15252 // a undefined union means also the tag is undefined, accessing
15253 // its payload slot is UB.15253 // its payload slot is UB.
15254 const UndefAllowed allow_undef = initializing ? UndefOk : UndefBad;15254 const UndefAllowed allow_undef = initializing ? UndefOk : UndefBad;
...@@ -16124,7 +16124,7 @@ static Stage1AirInst *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira,...@@ -16124,7 +16124,7 @@ static Stage1AirInst *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira,
1612416124
16125static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCtz *instruction) {16125static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCtz *instruction) {
16126 Error err;16126 Error err;
16127 16127
16128 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);16128 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
16129 if (type_is_invalid(int_type))16129 if (type_is_invalid(int_type))
16130 return ira->codegen->invalid_inst_gen;16130 return ira->codegen->invalid_inst_gen;
...@@ -16166,7 +16166,7 @@ static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCt...@@ -16166,7 +16166,7 @@ static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCt
16166 return ira->codegen->invalid_inst_gen;16166 return ira->codegen->invalid_inst_gen;
16167 if (val->special == ConstValSpecialUndef)16167 if (val->special == ConstValSpecialUndef)
16168 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);16168 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);
16169 16169
16170 if (is_vector) {16170 if (is_vector) {
16171 ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);16171 ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);
16172 Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, smallest_vec_type);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,7 +16200,7 @@ static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCt
1620016200
16201static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstClz *instruction) {16201static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstClz *instruction) {
16202 Error err;16202 Error err;
16203 16203
16204 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);16204 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
16205 if (type_is_invalid(int_type))16205 if (type_is_invalid(int_type))
16206 return ira->codegen->invalid_inst_gen;16206 return ira->codegen->invalid_inst_gen;
...@@ -16242,7 +16242,7 @@ static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstCl...@@ -16242,7 +16242,7 @@ static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstCl
16242 return ira->codegen->invalid_inst_gen;16242 return ira->codegen->invalid_inst_gen;
16243 if (val->special == ConstValSpecialUndef)16243 if (val->special == ConstValSpecialUndef)
16244 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);16244 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);
16245 16245
16246 if (is_vector) {16246 if (is_vector) {
16247 ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);16247 ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);
16248 Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, smallest_vec_type);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,7 +16276,7 @@ static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstCl
1627616276
16277static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1ZirInstPopCount *instruction) {16277static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1ZirInstPopCount *instruction) {
16278 Error err;16278 Error err;
16279 16279
16280 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);16280 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
16281 if (type_is_invalid(int_type))16281 if (type_is_invalid(int_type))
16282 return ira->codegen->invalid_inst_gen;16282 return ira->codegen->invalid_inst_gen;
...@@ -16318,7 +16318,7 @@ static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1Zir...@@ -16318,7 +16318,7 @@ static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1Zir
16318 return ira->codegen->invalid_inst_gen;16318 return ira->codegen->invalid_inst_gen;
16319 if (val->special == ConstValSpecialUndef)16319 if (val->special == ConstValSpecialUndef)
16320 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);16320 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);
16321 16321
16322 if (is_vector) {16322 if (is_vector) {
16323 ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);16323 ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);
16324 Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, smallest_vec_type);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,7 +17904,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, Scope *scope, AstNode
17904 result->special = ConstValSpecialStatic;17904 result->special = ConstValSpecialStatic;
17905 result->type = type_info_pointer_type;17905 result->type = type_info_pointer_type;
1790617906
17907 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 7);17907 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 8);
17908 result->data.x_struct.fields = fields;17908 result->data.x_struct.fields = fields;
1790917909
17910 // size: Size17910 // size: Size
...@@ -17939,24 +17939,29 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, Scope *scope, AstNode...@@ -17939,24 +17939,29 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, Scope *scope, AstNode
17939 lazy_align_of->base.id = LazyValueIdAlignOf;17939 lazy_align_of->base.id = LazyValueIdAlignOf;
17940 lazy_align_of->target_type = ir_const_type(ira, scope, source_node, attrs_type->data.pointer.child_type);17940 lazy_align_of->target_type = ir_const_type(ira, scope, source_node, attrs_type->data.pointer.child_type);
17941 }17941 }
17942 // child: type17942 // address_space: AddressSpace,
17943 ensure_field_index(result->type, "child", 4);17943 ensure_field_index(result->type, "address_space", 4);
17944 fields[4]->special = ConstValSpecialStatic;17944 fields[4]->special = ConstValSpecialStatic;
17945 fields[4]->type = ira->codegen->builtin_types.entry_type;17945 fields[4]->type = get_builtin_type(ira->codegen, "AddressSpace");
17946 fields[4]->data.x_type = attrs_type->data.pointer.child_type;17946 bigint_init_unsigned(&fields[4]->data.x_enum_tag, AddressSpaceGeneric);
17947 // is_allowzero: bool17947 // child: type
17948 ensure_field_index(result->type, "is_allowzero", 5);17948 ensure_field_index(result->type, "child", 5);
17949 fields[5]->special = ConstValSpecialStatic;17949 fields[5]->special = ConstValSpecialStatic;
17950 fields[5]->type = ira->codegen->builtin_types.entry_bool;17950 fields[5]->type = ira->codegen->builtin_types.entry_type;
17951 fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;17951 fields[5]->data.x_type = attrs_type->data.pointer.child_type;
17952 // sentinel: anytype17952 // is_allowzero: bool
17953 ensure_field_index(result->type, "sentinel", 6);17953 ensure_field_index(result->type, "is_allowzero", 6);
17954 fields[6]->special = ConstValSpecialStatic;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 if (attrs_type->data.pointer.sentinel != nullptr) {17960 if (attrs_type->data.pointer.sentinel != nullptr) {
17956 fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);17961 fields[7]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
17957 set_optional_payload(fields[6], attrs_type->data.pointer.sentinel);17962 set_optional_payload(fields[7], attrs_type->data.pointer.sentinel);
17958 } else {17963 } else {
17959 fields[6]->type = ira->codegen->builtin_types.entry_null;17964 fields[7]->type = ira->codegen->builtin_types.entry_null;
17960 }17965 }
1796117966
17962 return result;17967 return result;
...@@ -18465,7 +18470,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour...@@ -18465,7 +18470,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour
18465 result->special = ConstValSpecialStatic;18470 result->special = ConstValSpecialStatic;
18466 result->type = ir_type_info_get_type(ira, "Fn", nullptr);18471 result->type = ir_type_info_get_type(ira, "Fn", nullptr);
1846718472
18468 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 6);18473 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 7);
18469 result->data.x_struct.fields = fields;18474 result->data.x_struct.fields = fields;
1847018475
18471 // calling_convention: TypeInfo.CallingConvention18476 // calling_convention: TypeInfo.CallingConvention
...@@ -18826,11 +18831,11 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_...@@ -18826,11 +18831,11 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
18826 assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type));18831 assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type));
18827 BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag);18832 BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag);
18828 PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index);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 if (type_is_invalid(elem_type))18835 if (type_is_invalid(elem_type))
18831 return ira->codegen->invalid_inst_gen->value->type;18836 return ira->codegen->invalid_inst_gen->value->type;
18832 ZigValue *sentinel;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 elem_type, &sentinel)))18839 elem_type, &sentinel)))
18835 {18840 {
18836 return ira->codegen->invalid_inst_gen->value->type;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,6 +18850,19 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
18845 if (alignment == nullptr)18850 if (alignment == nullptr)
18846 return ira->codegen->invalid_inst_gen->value->type;18851 return ira->codegen->invalid_inst_gen->value->type;
1884718852
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 bool is_const;18866 bool is_const;
18849 if ((err = get_const_field_bool(ira, source_node, payload, "is_const", 1, &is_const)))18867 if ((err = get_const_field_bool(ira, source_node, payload, "is_const", 1, &is_const)))
18850 return ira->codegen->invalid_inst_gen->value->type;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,13 +18875,12 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
18857 }18875 }
1885818876
18859 bool is_allowzero;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 &is_allowzero)))18879 &is_allowzero)))
18862 {18880 {
18863 return ira->codegen->invalid_inst_gen->value->type;18881 return ira->codegen->invalid_inst_gen->value->type;
18864 }18882 }
1886518883
18866
18867 ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen,18884 ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen,
18868 elem_type,18885 elem_type,
18869 is_const,18886 is_const,
src/stage1/ir_print.cpp+6-6
...@@ -558,7 +558,7 @@ const char* ir_inst_gen_type_str(Stage1AirInstId id) {...@@ -558,7 +558,7 @@ const char* ir_inst_gen_type_str(Stage1AirInstId id) {
558 case Stage1AirInstIdWasmMemoryGrow:558 case Stage1AirInstIdWasmMemoryGrow:
559 return "GenWasmMemoryGrow";559 return "GenWasmMemoryGrow";
560 case Stage1AirInstIdExtern:560 case Stage1AirInstIdExtern:
561 return "GenExtrern";561 return "GenExtern";
562 }562 }
563 zig_unreachable();563 zig_unreachable();
564}564}
...@@ -737,13 +737,13 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {...@@ -737,13 +737,13 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
737 return "@maximum";737 return "@maximum";
738 case IrBinOpMinimum:738 case IrBinOpMinimum:
739 return "@minimum";739 return "@minimum";
740 case IrBinOpSatAdd:740 case IrBinOpAddSat:
741 return "@addWithSaturation";741 return "@addWithSaturation";
742 case IrBinOpSatSub:742 case IrBinOpSubSat:
743 return "@subWithSaturation";743 return "@subWithSaturation";
744 case IrBinOpSatMul:744 case IrBinOpMultSat:
745 return "@mulWithSaturation";745 return "@mulWithSaturation";
746 case IrBinOpSatShl:746 case IrBinOpShlSat:
747 return "@shlWithSaturation";747 return "@shlWithSaturation";
748 }748 }
749 zig_unreachable();749 zig_unreachable();
...@@ -829,7 +829,7 @@ static const char *cast_op_str(CastOp op) {...@@ -829,7 +829,7 @@ static const char *cast_op_str(CastOp op) {
829 case CastOpIntToFloat: return "IntToFloat";829 case CastOpIntToFloat: return "IntToFloat";
830 case CastOpFloatToInt: return "FloatToInt";830 case CastOpFloatToInt: return "FloatToInt";
831 case CastOpBoolToInt: return "BoolToInt";831 case CastOpBoolToInt: return "BoolToInt";
832 case CastOpNumLitToConcrete: return "NumLitToConcrate";832 case CastOpNumLitToConcrete: return "NumLitToConcrete";
833 case CastOpErrSet: return "ErrSet";833 case CastOpErrSet: return "ErrSet";
834 case CastOpBitCast: return "BitCast";834 case CastOpBitCast: return "BitCast";
835 }835 }
src/stage1/os.hpp+2
...@@ -33,6 +33,8 @@...@@ -33,6 +33,8 @@
33#define ZIG_OS_OPENBSD33#define ZIG_OS_OPENBSD
34#elif defined(__HAIKU__)34#elif defined(__HAIKU__)
35#define ZIG_OS_HAIKU35#define ZIG_OS_HAIKU
36#elif defined(__sun)
37#define ZIG_OS_SOLARIS
36#else38#else
37#define ZIG_OS_UNKNOWN39#define ZIG_OS_UNKNOWN
38#endif40#endif
src/stage1/parser.cpp+17-1
...@@ -2073,7 +2073,7 @@ static AstNode *ast_parse_field_init(ParseContext *pc) {...@@ -2073,7 +2073,7 @@ static AstNode *ast_parse_field_init(ParseContext *pc) {
2073 return nullptr;2073 return nullptr;
2074 }2074 }
2075 if (eat_token_if(pc, TokenIdEq) == 0) {2075 if (eat_token_if(pc, TokenIdEq) == 0) {
2076 // Because ".Name" can also be intepreted as an enum literal, we should put back2076 // Because ".Name" can also be interpreted as an enum literal, we should put back
2077 // those two tokens again so that the parser can try to parse them as the enum2077 // those two tokens again so that the parser can try to parse them as the enum
2078 // literal later.2078 // literal later.
2079 put_back_token(pc);2079 put_back_token(pc);
...@@ -2381,6 +2381,7 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {...@@ -2381,6 +2381,7 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
2381// / PLUSEQUAL2381// / PLUSEQUAL
2382// / MINUSEQUAL2382// / MINUSEQUAL
2383// / LARROW2EQUAL2383// / LARROW2EQUAL
2384// / LARROW2PIPEEQUAL
2384// / RARROW2EQUAL2385// / RARROW2EQUAL
2385// / AMPERSANDEQUAL2386// / AMPERSANDEQUAL
2386// / CARETEQUAL2387// / CARETEQUAL
...@@ -2388,6 +2389,9 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {...@@ -2388,6 +2389,9 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
2388// / ASTERISKPERCENTEQUAL2389// / ASTERISKPERCENTEQUAL
2389// / PLUSPERCENTEQUAL2390// / PLUSPERCENTEQUAL
2390// / MINUSPERCENTEQUAL2391// / MINUSPERCENTEQUAL
2392// / ASTERISKPIPEEQUAL
2393// / PLUSPIPEEQUAL
2394// / MINUSPIPEEQUAL
2391// / EQUAL2395// / EQUAL
2392static AstNode *ast_parse_assign_op(ParseContext *pc) {2396static AstNode *ast_parse_assign_op(ParseContext *pc) {
2393 // In C, we have `T arr[N] = {[i] = T{}};` but it doesn't2397 // 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,17 +2400,21 @@ static AstNode *ast_parse_assign_op(ParseContext *pc) {
2396 table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd;2400 table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd;
2397 table[TokenIdBitOrEq] = BinOpTypeAssignBitOr;2401 table[TokenIdBitOrEq] = BinOpTypeAssignBitOr;
2398 table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft;2402 table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft;
2403 table[TokenIdBitShiftLeftPipeEq] = BinOpTypeAssignBitShiftLeftSat;
2399 table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight;2404 table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight;
2400 table[TokenIdBitXorEq] = BinOpTypeAssignBitXor;2405 table[TokenIdBitXorEq] = BinOpTypeAssignBitXor;
2401 table[TokenIdDivEq] = BinOpTypeAssignDiv;2406 table[TokenIdDivEq] = BinOpTypeAssignDiv;
2402 table[TokenIdEq] = BinOpTypeAssign;2407 table[TokenIdEq] = BinOpTypeAssign;
2403 table[TokenIdMinusEq] = BinOpTypeAssignMinus;2408 table[TokenIdMinusEq] = BinOpTypeAssignMinus;
2404 table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap;2409 table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap;
2410 table[TokenIdMinusPipeEq] = BinOpTypeAssignMinusSat;
2405 table[TokenIdModEq] = BinOpTypeAssignMod;2411 table[TokenIdModEq] = BinOpTypeAssignMod;
2406 table[TokenIdPlusEq] = BinOpTypeAssignPlus;2412 table[TokenIdPlusEq] = BinOpTypeAssignPlus;
2407 table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap;2413 table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap;
2414 table[TokenIdPlusPipeEq] = BinOpTypeAssignPlusSat;
2408 table[TokenIdTimesEq] = BinOpTypeAssignTimes;2415 table[TokenIdTimesEq] = BinOpTypeAssignTimes;
2409 table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap;2416 table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap;
2417 table[TokenIdTimesPipeEq] = BinOpTypeAssignTimesSat;
24102418
2411 BinOpType op = table[pc->token_ids[pc->current_token]];2419 BinOpType op = table[pc->token_ids[pc->current_token]];
2412 if (op != BinOpTypeInvalid) {2420 if (op != BinOpTypeInvalid) {
...@@ -2483,10 +2491,12 @@ static AstNode *ast_parse_bitwise_op(ParseContext *pc) {...@@ -2483,10 +2491,12 @@ static AstNode *ast_parse_bitwise_op(ParseContext *pc) {
24832491
2484// BitShiftOp2492// BitShiftOp
2485// <- LARROW22493// <- LARROW2
2494// / LARROW2PIPE
2486// / RARROW22495// / RARROW2
2487static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {2496static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
2488 BinOpType table[TokenIdCount] = {};2497 BinOpType table[TokenIdCount] = {};
2489 table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft;2498 table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft;
2499 table[TokenIdBitShiftLeftPipe] = BinOpTypeBitShiftLeftSat;
2490 table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight;2500 table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight;
24912501
2492 BinOpType op = table[pc->token_ids[pc->current_token]];2502 BinOpType op = table[pc->token_ids[pc->current_token]];
...@@ -2506,6 +2516,8 @@ static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {...@@ -2506,6 +2516,8 @@ static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
2506// / PLUS22516// / PLUS2
2507// / PLUSPERCENT2517// / PLUSPERCENT
2508// / MINUSPERCENT2518// / MINUSPERCENT
2519// / PLUSPIPE
2520// / MINUSPIPE
2509static AstNode *ast_parse_addition_op(ParseContext *pc) {2521static AstNode *ast_parse_addition_op(ParseContext *pc) {
2510 BinOpType table[TokenIdCount] = {};2522 BinOpType table[TokenIdCount] = {};
2511 table[TokenIdPlus] = BinOpTypeAdd;2523 table[TokenIdPlus] = BinOpTypeAdd;
...@@ -2513,6 +2525,8 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {...@@ -2513,6 +2525,8 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
2513 table[TokenIdPlusPlus] = BinOpTypeArrayCat;2525 table[TokenIdPlusPlus] = BinOpTypeArrayCat;
2514 table[TokenIdPlusPercent] = BinOpTypeAddWrap;2526 table[TokenIdPlusPercent] = BinOpTypeAddWrap;
2515 table[TokenIdMinusPercent] = BinOpTypeSubWrap;2527 table[TokenIdMinusPercent] = BinOpTypeSubWrap;
2528 table[TokenIdPlusPipe] = BinOpTypeAddSat;
2529 table[TokenIdMinusPipe] = BinOpTypeSubSat;
25162530
2517 BinOpType op = table[pc->token_ids[pc->current_token]];2531 BinOpType op = table[pc->token_ids[pc->current_token]];
2518 if (op != BinOpTypeInvalid) {2532 if (op != BinOpTypeInvalid) {
...@@ -2532,6 +2546,7 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {...@@ -2532,6 +2546,7 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
2532// / PERCENT2546// / PERCENT
2533// / ASTERISK22547// / ASTERISK2
2534// / ASTERISKPERCENT2548// / ASTERISKPERCENT
2549// / ASTERISKPIPE
2535static AstNode *ast_parse_multiply_op(ParseContext *pc) {2550static AstNode *ast_parse_multiply_op(ParseContext *pc) {
2536 BinOpType table[TokenIdCount] = {};2551 BinOpType table[TokenIdCount] = {};
2537 table[TokenIdBarBar] = BinOpTypeMergeErrorSets;2552 table[TokenIdBarBar] = BinOpTypeMergeErrorSets;
...@@ -2540,6 +2555,7 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc) {...@@ -2540,6 +2555,7 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc) {
2540 table[TokenIdPercent] = BinOpTypeMod;2555 table[TokenIdPercent] = BinOpTypeMod;
2541 table[TokenIdStarStar] = BinOpTypeArrayMult;2556 table[TokenIdStarStar] = BinOpTypeArrayMult;
2542 table[TokenIdTimesPercent] = BinOpTypeMultWrap;2557 table[TokenIdTimesPercent] = BinOpTypeMultWrap;
2558 table[TokenIdTimesPipe] = BinOpTypeMultSat;
25432559
2544 BinOpType op = table[pc->token_ids[pc->current_token]];2560 BinOpType op = table[pc->token_ids[pc->current_token]];
2545 if (op != BinOpTypeInvalid) {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,6 +401,9 @@ Error target_parse_os(Os *out_os, const char *os_ptr, size_t os_len) {
401#elif defined(ZIG_OS_HAIKU)401#elif defined(ZIG_OS_HAIKU)
402 *out_os = OsHaiku;402 *out_os = OsHaiku;
403 return ErrorNone;403 return ErrorNone;
404#elif defined(ZIG_OS_SOLARIS)
405 *out_os = OsSolaris;
406 return ErrorNone;
404#else407#else
405 zig_panic("stage1 is unable to detect native target for this OS");408 zig_panic("stage1 is unable to detect native target for this OS");
406#endif409#endif
...@@ -674,6 +677,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {...@@ -674,6 +677,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
674 case OsOpenBSD:677 case OsOpenBSD:
675 case OsWASI:678 case OsWASI:
676 case OsHaiku:679 case OsHaiku:
680 case OsSolaris:
677 case OsEmscripten:681 case OsEmscripten:
678 case OsPlan9:682 case OsPlan9:
679 switch (id) {683 switch (id) {
...@@ -732,7 +736,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {...@@ -732,7 +736,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
732 case OsCloudABI:736 case OsCloudABI:
733 case OsKFreeBSD:737 case OsKFreeBSD:
734 case OsLv2:738 case OsLv2:
735 case OsSolaris:
736 case OsZOS:739 case OsZOS:
737 case OsMinix:740 case OsMinix:
738 case OsRTEMS:741 case OsRTEMS:
src/stage1/tokenizer.cpp+84
...@@ -226,8 +226,10 @@ enum TokenizeState {...@@ -226,8 +226,10 @@ enum TokenizeState {
226 TokenizeState_pipe,226 TokenizeState_pipe,
227 TokenizeState_minus,227 TokenizeState_minus,
228 TokenizeState_minus_percent,228 TokenizeState_minus_percent,
229 TokenizeState_minus_pipe,
229 TokenizeState_asterisk,230 TokenizeState_asterisk,
230 TokenizeState_asterisk_percent,231 TokenizeState_asterisk_percent,
232 TokenizeState_asterisk_pipe,
231 TokenizeState_slash,233 TokenizeState_slash,
232 TokenizeState_line_comment_start,234 TokenizeState_line_comment_start,
233 TokenizeState_line_comment,235 TokenizeState_line_comment,
...@@ -257,8 +259,10 @@ enum TokenizeState {...@@ -257,8 +259,10 @@ enum TokenizeState {
257 TokenizeState_percent,259 TokenizeState_percent,
258 TokenizeState_plus,260 TokenizeState_plus,
259 TokenizeState_plus_percent,261 TokenizeState_plus_percent,
262 TokenizeState_plus_pipe,
260 TokenizeState_angle_bracket_left,263 TokenizeState_angle_bracket_left,
261 TokenizeState_angle_bracket_angle_bracket_left,264 TokenizeState_angle_bracket_angle_bracket_left,
265 TokenizeState_angle_bracket_angle_bracket_left_pipe,
262 TokenizeState_angle_bracket_right,266 TokenizeState_angle_bracket_right,
263 TokenizeState_angle_bracket_angle_bracket_right,267 TokenizeState_angle_bracket_angle_bracket_right,
264 TokenizeState_period,268 TokenizeState_period,
...@@ -548,6 +552,9 @@ void tokenize(const char *source, Tokenization *out) {...@@ -548,6 +552,9 @@ void tokenize(const char *source, Tokenization *out) {
548 case '%':552 case '%':
549 t.state = TokenizeState_asterisk_percent;553 t.state = TokenizeState_asterisk_percent;
550 break;554 break;
555 case '|':
556 t.state = TokenizeState_asterisk_pipe;
557 break;
551 default:558 default:
552 t.state = TokenizeState_start;559 t.state = TokenizeState_start;
553 continue;560 continue;
...@@ -568,6 +575,21 @@ void tokenize(const char *source, Tokenization *out) {...@@ -568,6 +575,21 @@ void tokenize(const char *source, Tokenization *out) {
568 continue;575 continue;
569 }576 }
570 break;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 case TokenizeState_percent:593 case TokenizeState_percent:
572 switch (c) {594 switch (c) {
573 case 0:595 case 0:
...@@ -596,6 +618,9 @@ void tokenize(const char *source, Tokenization *out) {...@@ -596,6 +618,9 @@ void tokenize(const char *source, Tokenization *out) {
596 case '%':618 case '%':
597 t.state = TokenizeState_plus_percent;619 t.state = TokenizeState_plus_percent;
598 break;620 break;
621 case '|':
622 t.state = TokenizeState_plus_pipe;
623 break;
599 default:624 default:
600 t.state = TokenizeState_start;625 t.state = TokenizeState_start;
601 continue;626 continue;
...@@ -616,6 +641,21 @@ void tokenize(const char *source, Tokenization *out) {...@@ -616,6 +641,21 @@ void tokenize(const char *source, Tokenization *out) {
616 continue;641 continue;
617 }642 }
618 break;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 case TokenizeState_caret:659 case TokenizeState_caret:
620 switch (c) {660 switch (c) {
621 case 0:661 case 0:
...@@ -891,6 +931,9 @@ void tokenize(const char *source, Tokenization *out) {...@@ -891,6 +931,9 @@ void tokenize(const char *source, Tokenization *out) {
891 case '%':931 case '%':
892 t.state = TokenizeState_minus_percent;932 t.state = TokenizeState_minus_percent;
893 break;933 break;
934 case '|':
935 t.state = TokenizeState_minus_pipe;
936 break;
894 default:937 default:
895 t.state = TokenizeState_start;938 t.state = TokenizeState_start;
896 continue;939 continue;
...@@ -911,6 +954,21 @@ void tokenize(const char *source, Tokenization *out) {...@@ -911,6 +954,21 @@ void tokenize(const char *source, Tokenization *out) {
911 continue;954 continue;
912 }955 }
913 break;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 case TokenizeState_angle_bracket_left:972 case TokenizeState_angle_bracket_left:
915 switch (c) {973 switch (c) {
916 case 0:974 case 0:
...@@ -936,12 +994,30 @@ void tokenize(const char *source, Tokenization *out) {...@@ -936,12 +994,30 @@ void tokenize(const char *source, Tokenization *out) {
936 t.out->ids.last() = TokenIdBitShiftLeftEq;994 t.out->ids.last() = TokenIdBitShiftLeftEq;
937 t.state = TokenizeState_start;995 t.state = TokenizeState_start;
938 break;996 break;
997 case '|':
998 t.state = TokenizeState_angle_bracket_angle_bracket_left_pipe;
999 break;
939 default:1000 default:
940 t.out->ids.last() = TokenIdBitShiftLeft;1001 t.out->ids.last() = TokenIdBitShiftLeft;
941 t.state = TokenizeState_start;1002 t.state = TokenizeState_start;
942 continue;1003 continue;
943 }1004 }
944 break;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 case TokenizeState_angle_bracket_right:1021 case TokenizeState_angle_bracket_right:
946 switch (c) {1022 switch (c) {
947 case 0:1023 case 0:
...@@ -1437,6 +1513,8 @@ const char * token_name(TokenId id) {...@@ -1437,6 +1513,8 @@ const char * token_name(TokenId id) {
1437 case TokenIdBitOrEq: return "|=";1513 case TokenIdBitOrEq: return "|=";
1438 case TokenIdBitShiftLeft: return "<<";1514 case TokenIdBitShiftLeft: return "<<";
1439 case TokenIdBitShiftLeftEq: return "<<=";1515 case TokenIdBitShiftLeftEq: return "<<=";
1516 case TokenIdBitShiftLeftPipe: return "<<|";
1517 case TokenIdBitShiftLeftPipeEq: return "<<|=";
1440 case TokenIdBitShiftRight: return ">>";1518 case TokenIdBitShiftRight: return ">>";
1441 case TokenIdBitShiftRightEq: return ">>=";1519 case TokenIdBitShiftRightEq: return ">>=";
1442 case TokenIdBitXorEq: return "^=";1520 case TokenIdBitXorEq: return "^=";
...@@ -1521,12 +1599,16 @@ const char * token_name(TokenId id) {...@@ -1521,12 +1599,16 @@ const char * token_name(TokenId id) {
1521 case TokenIdMinusEq: return "-=";1599 case TokenIdMinusEq: return "-=";
1522 case TokenIdMinusPercent: return "-%";1600 case TokenIdMinusPercent: return "-%";
1523 case TokenIdMinusPercentEq: return "-%=";1601 case TokenIdMinusPercentEq: return "-%=";
1602 case TokenIdMinusPipe: return "-|";
1603 case TokenIdMinusPipeEq: return "-|=";
1524 case TokenIdModEq: return "%=";1604 case TokenIdModEq: return "%=";
1525 case TokenIdPercent: return "%";1605 case TokenIdPercent: return "%";
1526 case TokenIdPlus: return "+";1606 case TokenIdPlus: return "+";
1527 case TokenIdPlusEq: return "+=";1607 case TokenIdPlusEq: return "+=";
1528 case TokenIdPlusPercent: return "+%";1608 case TokenIdPlusPercent: return "+%";
1529 case TokenIdPlusPercentEq: return "+%=";1609 case TokenIdPlusPercentEq: return "+%=";
1610 case TokenIdPlusPipe: return "+|";
1611 case TokenIdPlusPipeEq: return "+|=";
1530 case TokenIdPlusPlus: return "++";1612 case TokenIdPlusPlus: return "++";
1531 case TokenIdRBrace: return "}";1613 case TokenIdRBrace: return "}";
1532 case TokenIdRBracket: return "]";1614 case TokenIdRBracket: return "]";
...@@ -1542,6 +1624,8 @@ const char * token_name(TokenId id) {...@@ -1542,6 +1624,8 @@ const char * token_name(TokenId id) {
1542 case TokenIdTimesEq: return "*=";1624 case TokenIdTimesEq: return "*=";
1543 case TokenIdTimesPercent: return "*%";1625 case TokenIdTimesPercent: return "*%";
1544 case TokenIdTimesPercentEq: return "*%=";1626 case TokenIdTimesPercentEq: return "*%=";
1627 case TokenIdTimesPipe: return "*|";
1628 case TokenIdTimesPipeEq: return "*|=";
1545 case TokenIdBuiltin: return "Builtin";1629 case TokenIdBuiltin: return "Builtin";
1546 case TokenIdCount:1630 case TokenIdCount:
1547 zig_unreachable();1631 zig_unreachable();
src/stage1/tokenizer.hpp+8
...@@ -23,6 +23,8 @@ enum TokenId : uint8_t {...@@ -23,6 +23,8 @@ enum TokenId : uint8_t {
23 TokenIdBitOrEq,23 TokenIdBitOrEq,
24 TokenIdBitShiftLeft,24 TokenIdBitShiftLeft,
25 TokenIdBitShiftLeftEq,25 TokenIdBitShiftLeftEq,
26 TokenIdBitShiftLeftPipe,
27 TokenIdBitShiftLeftPipeEq,
26 TokenIdBitShiftRight,28 TokenIdBitShiftRight,
27 TokenIdBitShiftRightEq,29 TokenIdBitShiftRightEq,
28 TokenIdBitXorEq,30 TokenIdBitXorEq,
...@@ -108,12 +110,16 @@ enum TokenId : uint8_t {...@@ -108,12 +110,16 @@ enum TokenId : uint8_t {
108 TokenIdMinusEq,110 TokenIdMinusEq,
109 TokenIdMinusPercent,111 TokenIdMinusPercent,
110 TokenIdMinusPercentEq,112 TokenIdMinusPercentEq,
113 TokenIdMinusPipe,
114 TokenIdMinusPipeEq,
111 TokenIdModEq,115 TokenIdModEq,
112 TokenIdPercent,116 TokenIdPercent,
113 TokenIdPlus,117 TokenIdPlus,
114 TokenIdPlusEq,118 TokenIdPlusEq,
115 TokenIdPlusPercent,119 TokenIdPlusPercent,
116 TokenIdPlusPercentEq,120 TokenIdPlusPercentEq,
121 TokenIdPlusPipe,
122 TokenIdPlusPipeEq,
117 TokenIdPlusPlus,123 TokenIdPlusPlus,
118 TokenIdRBrace,124 TokenIdRBrace,
119 TokenIdRBracket,125 TokenIdRBracket,
...@@ -129,6 +135,8 @@ enum TokenId : uint8_t {...@@ -129,6 +135,8 @@ enum TokenId : uint8_t {
129 TokenIdTimesEq,135 TokenIdTimesEq,
130 TokenIdTimesPercent,136 TokenIdTimesPercent,
131 TokenIdTimesPercentEq,137 TokenIdTimesPercentEq,
138 TokenIdTimesPipe,
139 TokenIdTimesPipeEq,
132140
133 TokenIdCount,141 TokenIdCount,
134};142};
src/target.zig+25
...@@ -438,6 +438,13 @@ pub fn libcFullLinkFlags(target: std.Target) []const []const u8 {...@@ -438,6 +438,13 @@ pub fn libcFullLinkFlags(target: std.Target) []const []const u8 {
438 "-lc",438 "-lc",
439 "-lutil",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 .haiku => &[_][]const u8{448 .haiku => &[_][]const u8{
442 "-lm",449 "-lm",
443 "-lroot",450 "-lroot",
...@@ -550,3 +557,21 @@ pub fn largestAtomicBits(target: std.Target) u32 {...@@ -550,3 +557,21 @@ pub fn largestAtomicBits(target: std.Target) u32 {
550 .x86_64 => 128,557 .x86_64 => 128,
551 };558 };
552}559}
560
561pub 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,7 +4882,7 @@ fn finishTransFnProto(
4882 var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);4882 var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
4883 defer fn_params.deinit();4883 defer fn_params.deinit();
4884 const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0;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);
48864886
4887 var i: usize = 0;4887 var i: usize = 0;
4888 while (i < param_count) : (i += 1) {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,13 +728,13 @@ pub fn render(gpa: *Allocator, nodes: []const Node) !std.zig.Ast {
728728
729 // Estimate that each top level node has 10 child nodes.729 // Estimate that each top level node has 10 child nodes.
730 const estimated_node_count = nodes.len * 10;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 // Estimate that each each node has 2 tokens.732 // Estimate that each each node has 2 tokens.
733 const estimated_tokens_count = estimated_node_count * 2;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 // Estimate that each each token is 3 bytes long.735 // Estimate that each each token is 3 bytes long.
736 const estimated_buf_len = estimated_tokens_count * 3;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);
738738
739 ctx.nodes.appendAssumeCapacity(.{739 ctx.nodes.appendAssumeCapacity(.{
740 .tag = .root,740 .tag = .root,
...@@ -839,7 +839,7 @@ const Context = struct {...@@ -839,7 +839,7 @@ const Context = struct {
839839
840 fn addExtra(c: *Context, extra: anytype) Allocator.Error!NodeIndex {840 fn addExtra(c: *Context, extra: anytype) Allocator.Error!NodeIndex {
841 const fields = std.meta.fields(@TypeOf(extra));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 const result = @intCast(u32, c.extra_data.items.len);843 const result = @intCast(u32, c.extra_data.items.len);
844 inline for (fields) |field| {844 inline for (fields) |field| {
845 comptime std.debug.assert(field.field_type == NodeIndex);845 comptime std.debug.assert(field.field_type == NodeIndex);
...@@ -1462,10 +1462,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1462,10 +1462,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1462 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),1462 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
1463 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),1463 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
1464 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),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, "<<"),1465 .shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"),
1466 .shl_assign => return renderBinOp(c, node, .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal, "<<="),1466 .shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="),
1467 .shr => return renderBinOpGrouped(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"),1467 .shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"),
1468 .shr_assign => return renderBinOp(c, node, .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal, ">>="),1468 .shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="),
1469 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),1469 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
1470 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),1470 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
1471 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),1471 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
...@@ -2614,6 +2614,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex {...@@ -2614,6 +2614,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex {
2614 .type_node = type_node,2614 .type_node = type_node,
2615 .align_node = align_node,2615 .align_node = align_node,
2616 .section_node = section_node,2616 .section_node = section_node,
2617 .addrspace_node = 0,
2617 }),2618 }),
2618 .rhs = init_node,2619 .rhs = init_node,
2619 },2620 },
...@@ -2705,6 +2706,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {...@@ -2705,6 +2706,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
2705 .lhs = try c.addExtra(std.zig.Ast.Node.FnProtoOne{2706 .lhs = try c.addExtra(std.zig.Ast.Node.FnProtoOne{
2706 .param = params.items[0],2707 .param = params.items[0],
2707 .align_expr = align_expr,2708 .align_expr = align_expr,
2709 .addrspace_expr = 0, // TODO
2708 .section_expr = section_expr,2710 .section_expr = section_expr,
2709 .callconv_expr = callconv_expr,2711 .callconv_expr = callconv_expr,
2710 }),2712 }),
...@@ -2720,6 +2722,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {...@@ -2720,6 +2722,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
2720 .params_start = span.start,2722 .params_start = span.start,
2721 .params_end = span.end,2723 .params_end = span.end,
2722 .align_expr = align_expr,2724 .align_expr = align_expr,
2725 .addrspace_expr = 0, // TODO
2723 .section_expr = section_expr,2726 .section_expr = section_expr,
2724 .callconv_expr = callconv_expr,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,7 +2800,7 @@ fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.Ar
2797 _ = try c.addToken(.l_paren, "(");2800 _ = try c.addToken(.l_paren, "(");
2798 var rendered = std.ArrayList(NodeIndex).init(c.gpa);2801 var rendered = std.ArrayList(NodeIndex).init(c.gpa);
2799 errdefer rendered.deinit();2802 errdefer rendered.deinit();
2800 try rendered.ensureCapacity(std.math.max(params.len, 1));2803 try rendered.ensureTotalCapacity(std.math.max(params.len, 1));
28012804
2802 for (params) |param, i| {2805 for (params) |param, i| {
2803 if (i != 0) _ = try c.addToken(.comma, ",");2806 if (i != 0) _ = try c.addToken(.comma, ",");
src/type.zig+449-108
...@@ -124,9 +124,11 @@ pub const Type = extern union {...@@ -124,9 +124,11 @@ pub const Type = extern union {
124 .enum_full,124 .enum_full,
125 .enum_nonexhaustive,125 .enum_nonexhaustive,
126 .enum_simple,126 .enum_simple,
127 .enum_numbered,
127 .atomic_order,128 .atomic_order,
128 .atomic_rmw_op,129 .atomic_rmw_op,
129 .calling_convention,130 .calling_convention,
131 .address_space,
130 .float_mode,132 .float_mode,
131 .reduce_op,133 .reduce_op,
132 => return .Enum,134 => return .Enum,
...@@ -136,6 +138,7 @@ pub const Type = extern union {...@@ -136,6 +138,7 @@ pub const Type = extern union {
136 .type_info,138 .type_info,
137 => return .Union,139 => return .Union,
138140
141 .bound_fn => unreachable,
139 .var_args_param => unreachable, // can be any type142 .var_args_param => unreachable, // can be any type
140 }143 }
141 }144 }
...@@ -288,6 +291,7 @@ pub const Type = extern union {...@@ -288,6 +291,7 @@ pub const Type = extern union {
288 .pointee_type = Type.initTag(.comptime_int),291 .pointee_type = Type.initTag(.comptime_int),
289 .sentinel = null,292 .sentinel = null,
290 .@"align" = 0,293 .@"align" = 0,
294 .@"addrspace" = .generic,
291 .bit_offset = 0,295 .bit_offset = 0,
292 .host_size = 0,296 .host_size = 0,
293 .@"allowzero" = false,297 .@"allowzero" = false,
...@@ -299,6 +303,7 @@ pub const Type = extern union {...@@ -299,6 +303,7 @@ pub const Type = extern union {
299 .pointee_type = Type.initTag(.u8),303 .pointee_type = Type.initTag(.u8),
300 .sentinel = null,304 .sentinel = null,
301 .@"align" = 0,305 .@"align" = 0,
306 .@"addrspace" = .generic,
302 .bit_offset = 0,307 .bit_offset = 0,
303 .host_size = 0,308 .host_size = 0,
304 .@"allowzero" = false,309 .@"allowzero" = false,
...@@ -310,6 +315,7 @@ pub const Type = extern union {...@@ -310,6 +315,7 @@ pub const Type = extern union {
310 .pointee_type = self.castPointer().?.data,315 .pointee_type = self.castPointer().?.data,
311 .sentinel = null,316 .sentinel = null,
312 .@"align" = 0,317 .@"align" = 0,
318 .@"addrspace" = .generic,
313 .bit_offset = 0,319 .bit_offset = 0,
314 .host_size = 0,320 .host_size = 0,
315 .@"allowzero" = false,321 .@"allowzero" = false,
...@@ -321,6 +327,7 @@ pub const Type = extern union {...@@ -321,6 +327,7 @@ pub const Type = extern union {
321 .pointee_type = self.castPointer().?.data,327 .pointee_type = self.castPointer().?.data,
322 .sentinel = null,328 .sentinel = null,
323 .@"align" = 0,329 .@"align" = 0,
330 .@"addrspace" = .generic,
324 .bit_offset = 0,331 .bit_offset = 0,
325 .host_size = 0,332 .host_size = 0,
326 .@"allowzero" = false,333 .@"allowzero" = false,
...@@ -332,6 +339,7 @@ pub const Type = extern union {...@@ -332,6 +339,7 @@ pub const Type = extern union {
332 .pointee_type = self.castPointer().?.data,339 .pointee_type = self.castPointer().?.data,
333 .sentinel = null,340 .sentinel = null,
334 .@"align" = 0,341 .@"align" = 0,
342 .@"addrspace" = .generic,
335 .bit_offset = 0,343 .bit_offset = 0,
336 .host_size = 0,344 .host_size = 0,
337 .@"allowzero" = false,345 .@"allowzero" = false,
...@@ -343,6 +351,7 @@ pub const Type = extern union {...@@ -343,6 +351,7 @@ pub const Type = extern union {
343 .pointee_type = Type.initTag(.u8),351 .pointee_type = Type.initTag(.u8),
344 .sentinel = null,352 .sentinel = null,
345 .@"align" = 0,353 .@"align" = 0,
354 .@"addrspace" = .generic,
346 .bit_offset = 0,355 .bit_offset = 0,
347 .host_size = 0,356 .host_size = 0,
348 .@"allowzero" = false,357 .@"allowzero" = false,
...@@ -354,6 +363,7 @@ pub const Type = extern union {...@@ -354,6 +363,7 @@ pub const Type = extern union {
354 .pointee_type = self.castPointer().?.data,363 .pointee_type = self.castPointer().?.data,
355 .sentinel = null,364 .sentinel = null,
356 .@"align" = 0,365 .@"align" = 0,
366 .@"addrspace" = .generic,
357 .bit_offset = 0,367 .bit_offset = 0,
358 .host_size = 0,368 .host_size = 0,
359 .@"allowzero" = false,369 .@"allowzero" = false,
...@@ -365,6 +375,7 @@ pub const Type = extern union {...@@ -365,6 +375,7 @@ pub const Type = extern union {
365 .pointee_type = Type.initTag(.u8),375 .pointee_type = Type.initTag(.u8),
366 .sentinel = null,376 .sentinel = null,
367 .@"align" = 0,377 .@"align" = 0,
378 .@"addrspace" = .generic,
368 .bit_offset = 0,379 .bit_offset = 0,
369 .host_size = 0,380 .host_size = 0,
370 .@"allowzero" = false,381 .@"allowzero" = false,
...@@ -376,6 +387,7 @@ pub const Type = extern union {...@@ -376,6 +387,7 @@ pub const Type = extern union {
376 .pointee_type = self.castPointer().?.data,387 .pointee_type = self.castPointer().?.data,
377 .sentinel = null,388 .sentinel = null,
378 .@"align" = 0,389 .@"align" = 0,
390 .@"addrspace" = .generic,
379 .bit_offset = 0,391 .bit_offset = 0,
380 .host_size = 0,392 .host_size = 0,
381 .@"allowzero" = false,393 .@"allowzero" = false,
...@@ -387,6 +399,7 @@ pub const Type = extern union {...@@ -387,6 +399,7 @@ pub const Type = extern union {
387 .pointee_type = self.castPointer().?.data,399 .pointee_type = self.castPointer().?.data,
388 .sentinel = null,400 .sentinel = null,
389 .@"align" = 0,401 .@"align" = 0,
402 .@"addrspace" = .generic,
390 .bit_offset = 0,403 .bit_offset = 0,
391 .host_size = 0,404 .host_size = 0,
392 .@"allowzero" = false,405 .@"allowzero" = false,
...@@ -398,6 +411,7 @@ pub const Type = extern union {...@@ -398,6 +411,7 @@ pub const Type = extern union {
398 .pointee_type = self.castPointer().?.data,411 .pointee_type = self.castPointer().?.data,
399 .sentinel = null,412 .sentinel = null,
400 .@"align" = 0,413 .@"align" = 0,
414 .@"addrspace" = .generic,
401 .bit_offset = 0,415 .bit_offset = 0,
402 .host_size = 0,416 .host_size = 0,
403 .@"allowzero" = false,417 .@"allowzero" = false,
...@@ -409,6 +423,7 @@ pub const Type = extern union {...@@ -409,6 +423,7 @@ pub const Type = extern union {
409 .pointee_type = self.castPointer().?.data,423 .pointee_type = self.castPointer().?.data,
410 .sentinel = null,424 .sentinel = null,
411 .@"align" = 0,425 .@"align" = 0,
426 .@"addrspace" = .generic,
412 .bit_offset = 0,427 .bit_offset = 0,
413 .host_size = 0,428 .host_size = 0,
414 .@"allowzero" = false,429 .@"allowzero" = false,
...@@ -461,6 +476,8 @@ pub const Type = extern union {...@@ -461,6 +476,8 @@ pub const Type = extern union {
461 return false;476 return false;
462 if (info_a.host_size != info_b.host_size)477 if (info_a.host_size != info_b.host_size)
463 return false;478 return false;
479 if (info_a.@"addrspace" != info_b.@"addrspace")
480 return false;
464481
465 const sentinel_a = info_a.sentinel;482 const sentinel_a = info_a.sentinel;
466 const sentinel_b = info_b.sentinel;483 const sentinel_b = info_b.sentinel;
...@@ -587,8 +604,8 @@ pub const Type = extern union {...@@ -587,8 +604,8 @@ pub const Type = extern union {
587 }604 }
588 return false;605 return false;
589 },606 },
607 .Float => return a.tag() == b.tag(),
590 .Opaque,608 .Opaque,
591 .Float,
592 .BoundFn,609 .BoundFn,
593 .Frame,610 .Frame,
594 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),611 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
...@@ -746,6 +763,7 @@ pub const Type = extern union {...@@ -746,6 +763,7 @@ pub const Type = extern union {
746 .atomic_order,763 .atomic_order,
747 .atomic_rmw_op,764 .atomic_rmw_op,
748 .calling_convention,765 .calling_convention,
766 .address_space,
749 .float_mode,767 .float_mode,
750 .reduce_op,768 .reduce_op,
751 .call_options,769 .call_options,
...@@ -754,6 +772,7 @@ pub const Type = extern union {...@@ -754,6 +772,7 @@ pub const Type = extern union {
754 .type_info,772 .type_info,
755 .@"anyframe",773 .@"anyframe",
756 .generic_poison,774 .generic_poison,
775 .bound_fn,
757 => unreachable,776 => unreachable,
758777
759 .array_u8,778 .array_u8,
...@@ -835,6 +854,7 @@ pub const Type = extern union {...@@ -835,6 +854,7 @@ pub const Type = extern union {
835 .pointee_type = try payload.pointee_type.copy(allocator),854 .pointee_type = try payload.pointee_type.copy(allocator),
836 .sentinel = sent,855 .sentinel = sent,
837 .@"align" = payload.@"align",856 .@"align" = payload.@"align",
857 .@"addrspace" = payload.@"addrspace",
838 .bit_offset = payload.bit_offset,858 .bit_offset = payload.bit_offset,
839 .host_size = payload.host_size,859 .host_size = payload.host_size,
840 .@"allowzero" = payload.@"allowzero",860 .@"allowzero" = payload.@"allowzero",
...@@ -857,6 +877,7 @@ pub const Type = extern union {...@@ -857,6 +877,7 @@ pub const Type = extern union {
857 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),877 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
858 .@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),878 .@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),
859 .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),879 .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),
880 .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered),
860 .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),881 .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),
861 .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),882 .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
862 }883 }
...@@ -917,6 +938,7 @@ pub const Type = extern union {...@@ -917,6 +938,7 @@ pub const Type = extern union {
917 .comptime_float,938 .comptime_float,
918 .noreturn,939 .noreturn,
919 .var_args_param,940 .var_args_param,
941 .bound_fn,
920 => return writer.writeAll(@tagName(t)),942 => return writer.writeAll(@tagName(t)),
921943
922 .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"),944 .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"),
...@@ -941,6 +963,10 @@ pub const Type = extern union {...@@ -941,6 +963,10 @@ pub const Type = extern union {
941 const enum_simple = ty.castTag(.enum_simple).?.data;963 const enum_simple = ty.castTag(.enum_simple).?.data;
942 return enum_simple.owner_decl.renderFullyQualifiedName(writer);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 .@"opaque" => {970 .@"opaque" => {
945 // TODO use declaration name971 // TODO use declaration name
946 return writer.writeAll("opaque {}");972 return writer.writeAll("opaque {}");
...@@ -958,6 +984,7 @@ pub const Type = extern union {...@@ -958,6 +984,7 @@ pub const Type = extern union {
958 .atomic_order => return writer.writeAll("std.builtin.AtomicOrder"),984 .atomic_order => return writer.writeAll("std.builtin.AtomicOrder"),
959 .atomic_rmw_op => return writer.writeAll("std.builtin.AtomicRmwOp"),985 .atomic_rmw_op => return writer.writeAll("std.builtin.AtomicRmwOp"),
960 .calling_convention => return writer.writeAll("std.builtin.CallingConvention"),986 .calling_convention => return writer.writeAll("std.builtin.CallingConvention"),
987 .address_space => return writer.writeAll("std.builtin.AddressSpace"),
961 .float_mode => return writer.writeAll("std.builtin.FloatMode"),988 .float_mode => return writer.writeAll("std.builtin.FloatMode"),
962 .reduce_op => return writer.writeAll("std.builtin.ReduceOp"),989 .reduce_op => return writer.writeAll("std.builtin.ReduceOp"),
963 .call_options => return writer.writeAll("std.builtin.CallOptions"),990 .call_options => return writer.writeAll("std.builtin.CallOptions"),
...@@ -1111,6 +1138,9 @@ pub const Type = extern union {...@@ -1111,6 +1138,9 @@ pub const Type = extern union {
1111 }1138 }
1112 try writer.writeAll(") ");1139 try writer.writeAll(") ");
1113 }1140 }
1141 if (payload.@"addrspace" != .generic) {
1142 try writer.print("addrspace(.{s}) ", .{@tagName(payload.@"addrspace")});
1143 }
1114 if (!payload.mutable) try writer.writeAll("const ");1144 if (!payload.mutable) try writer.writeAll("const ");
1115 if (payload.@"volatile") try writer.writeAll("volatile ");1145 if (payload.@"volatile") try writer.writeAll("volatile ");
1116 if (payload.@"allowzero") try writer.writeAll("allowzero ");1146 if (payload.@"allowzero") try writer.writeAll("allowzero ");
...@@ -1186,6 +1216,7 @@ pub const Type = extern union {...@@ -1186,6 +1216,7 @@ pub const Type = extern union {
1186 .atomic_order,1216 .atomic_order,
1187 .atomic_rmw_op,1217 .atomic_rmw_op,
1188 .calling_convention,1218 .calling_convention,
1219 .address_space,
1189 .float_mode,1220 .float_mode,
1190 .reduce_op,1221 .reduce_op,
1191 .call_options,1222 .call_options,
...@@ -1220,6 +1251,7 @@ pub const Type = extern union {...@@ -1220,6 +1251,7 @@ pub const Type = extern union {
1220 .var_args_param => unreachable,1251 .var_args_param => unreachable,
1221 .inferred_alloc_mut => unreachable,1252 .inferred_alloc_mut => unreachable,
1222 .inferred_alloc_const => unreachable,1253 .inferred_alloc_const => unreachable,
1254 .bound_fn => unreachable,
12231255
1224 .array_u8,1256 .array_u8,
1225 .array_u8_sentinel_0,1257 .array_u8_sentinel_0,
...@@ -1246,6 +1278,7 @@ pub const Type = extern union {...@@ -1246,6 +1278,7 @@ pub const Type = extern union {
1246 .@"union",1278 .@"union",
1247 .union_tagged,1279 .union_tagged,
1248 .enum_simple,1280 .enum_simple,
1281 .enum_numbered,
1249 .enum_full,1282 .enum_full,
1250 .enum_nonexhaustive,1283 .enum_nonexhaustive,
1251 => false, // TODO some of these should be `true` depending on their child types1284 => false, // TODO some of these should be `true` depending on their child types
...@@ -1301,6 +1334,7 @@ pub const Type = extern union {...@@ -1301,6 +1334,7 @@ pub const Type = extern union {
1301 .atomic_order => return Value.initTag(.atomic_order_type),1334 .atomic_order => return Value.initTag(.atomic_order_type),
1302 .atomic_rmw_op => return Value.initTag(.atomic_rmw_op_type),1335 .atomic_rmw_op => return Value.initTag(.atomic_rmw_op_type),
1303 .calling_convention => return Value.initTag(.calling_convention_type),1336 .calling_convention => return Value.initTag(.calling_convention_type),
1337 .address_space => return Value.initTag(.address_space_type),
1304 .float_mode => return Value.initTag(.float_mode_type),1338 .float_mode => return Value.initTag(.float_mode_type),
1305 .reduce_op => return Value.initTag(.reduce_op_type),1339 .reduce_op => return Value.initTag(.reduce_op_type),
1306 .call_options => return Value.initTag(.call_options_type),1340 .call_options => return Value.initTag(.call_options_type),
...@@ -1313,6 +1347,8 @@ pub const Type = extern union {...@@ -1313,6 +1347,8 @@ pub const Type = extern union {
1313 }1347 }
1314 }1348 }
13151349
1350 /// For structs and unions, if the type does not have their fields resolved
1351 /// this will return `false`.
1316 pub fn hasCodeGenBits(self: Type) bool {1352 pub fn hasCodeGenBits(self: Type) bool {
1317 return switch (self.tag()) {1353 return switch (self.tag()) {
1318 .u1,1354 .u1,
...@@ -1343,10 +1379,6 @@ pub const Type = extern union {...@@ -1343,10 +1379,6 @@ pub const Type = extern union {
1343 .f128,1379 .f128,
1344 .bool,1380 .bool,
1345 .anyerror,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 .single_const_pointer_to_comptime_int,1382 .single_const_pointer_to_comptime_int,
1351 .const_slice_u8,1383 .const_slice_u8,
1352 .array_u8_sentinel_0,1384 .array_u8_sentinel_0,
...@@ -1362,6 +1394,7 @@ pub const Type = extern union {...@@ -1362,6 +1394,7 @@ pub const Type = extern union {
1362 .atomic_order,1394 .atomic_order,
1363 .atomic_rmw_op,1395 .atomic_rmw_op,
1364 .calling_convention,1396 .calling_convention,
1397 .address_space,
1365 .float_mode,1398 .float_mode,
1366 .reduce_op,1399 .reduce_op,
1367 .call_options,1400 .call_options,
...@@ -1373,15 +1406,17 @@ pub const Type = extern union {...@@ -1373,15 +1406,17 @@ pub const Type = extern union {
13731406
1374 .function => !self.castTag(.function).?.data.is_generic,1407 .function => !self.castTag(.function).?.data.is_generic,
13751408
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 .@"struct" => {1415 .@"struct" => {
1377 // TODO introduce lazy value mechanism
1378 const struct_obj = self.castTag(.@"struct").?.data;1416 const struct_obj = self.castTag(.@"struct").?.data;
1379 if (struct_obj.known_has_bits) {1417 if (struct_obj.known_has_bits) {
1380 return true;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 for (struct_obj.fields.values()) |value| {1420 for (struct_obj.fields.values()) |value| {
1386 if (value.ty.hasCodeGenBits())1421 if (value.ty.hasCodeGenBits())
1387 return true;1422 return true;
...@@ -1397,7 +1432,7 @@ pub const Type = extern union {...@@ -1397,7 +1432,7 @@ pub const Type = extern union {
1397 const enum_simple = self.castTag(.enum_simple).?.data;1432 const enum_simple = self.castTag(.enum_simple).?.data;
1398 return enum_simple.fields.count() >= 2;1433 return enum_simple.fields.count() >= 2;
1399 },1434 },
1400 .enum_nonexhaustive => {1435 .enum_numbered, .enum_nonexhaustive => {
1401 var buffer: Payload.Bits = undefined;1436 var buffer: Payload.Bits = undefined;
1402 const int_tag_ty = self.intTagType(&buffer);1437 const int_tag_ty = self.intTagType(&buffer);
1403 return int_tag_ty.hasCodeGenBits();1438 return int_tag_ty.hasCodeGenBits();
...@@ -1448,6 +1483,7 @@ pub const Type = extern union {...@@ -1448,6 +1483,7 @@ pub const Type = extern union {
1448 .empty_struct_literal,1483 .empty_struct_literal,
1449 .@"opaque",1484 .@"opaque",
1450 .type_info,1485 .type_info,
1486 .bound_fn,
1451 => false,1487 => false,
14521488
1453 .inferred_alloc_const => unreachable,1489 .inferred_alloc_const => unreachable,
...@@ -1458,7 +1494,9 @@ pub const Type = extern union {...@@ -1458,7 +1494,9 @@ pub const Type = extern union {
1458 }1494 }
14591495
1460 pub fn isNoReturn(self: Type) bool {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 const fast_result = self.tag_if_small_enough == Tag.noreturn;1500 const fast_result = self.tag_if_small_enough == Tag.noreturn;
1463 assert(fast_result == definitely_correct_result);1501 assert(fast_result == definitely_correct_result);
1464 return fast_result;1502 return fast_result;
...@@ -1496,6 +1534,30 @@ pub const Type = extern union {...@@ -1496,6 +1534,30 @@ pub const Type = extern union {
1496 }1534 }
1497 }1535 }
14981536
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 /// Asserts that hasCodeGenBits() is true.1561 /// Asserts that hasCodeGenBits() is true.
1500 pub fn abiAlignment(self: Type, target: Target) u32 {1562 pub fn abiAlignment(self: Type, target: Target) u32 {
1501 return switch (self.tag()) {1563 return switch (self.tag()) {
...@@ -1508,6 +1570,7 @@ pub const Type = extern union {...@@ -1508,6 +1570,7 @@ pub const Type = extern union {
1508 .atomic_order,1570 .atomic_order,
1509 .atomic_rmw_op,1571 .atomic_rmw_op,
1510 .calling_convention,1572 .calling_convention,
1573 .address_space,
1511 .float_mode,1574 .float_mode,
1512 .reduce_op,1575 .reduce_op,
1513 .call_options,1576 .call_options,
...@@ -1583,7 +1646,11 @@ pub const Type = extern union {...@@ -1583,7 +1646,11 @@ pub const Type = extern union {
15831646
1584 .int_signed, .int_unsigned => {1647 .int_signed, .int_unsigned => {
1585 const bits: u16 = self.cast(Payload.Bits).?.data;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 },
15881655
1589 .optional => {1656 .optional => {
...@@ -1629,7 +1696,7 @@ pub const Type = extern union {...@@ -1629,7 +1696,7 @@ pub const Type = extern union {
1629 assert(biggest != 0);1696 assert(biggest != 0);
1630 return biggest;1697 return biggest;
1631 },1698 },
1632 .enum_full, .enum_nonexhaustive, .enum_simple => {1699 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
1633 var buffer: Payload.Bits = undefined;1700 var buffer: Payload.Bits = undefined;
1634 const int_tag_ty = self.intTagType(&buffer);1701 const int_tag_ty = self.intTagType(&buffer);
1635 return int_tag_ty.abiAlignment(target);1702 return int_tag_ty.abiAlignment(target);
...@@ -1676,6 +1743,7 @@ pub const Type = extern union {...@@ -1676,6 +1743,7 @@ pub const Type = extern union {
1676 .@"opaque",1743 .@"opaque",
1677 .var_args_param,1744 .var_args_param,
1678 .type_info,1745 .type_info,
1746 .bound_fn,
1679 => unreachable,1747 => unreachable,
16801748
1681 .generic_poison => unreachable,1749 .generic_poison => unreachable,
...@@ -1708,13 +1776,30 @@ pub const Type = extern union {...@@ -1708,13 +1776,30 @@ pub const Type = extern union {
1708 .var_args_param => unreachable,1776 .var_args_param => unreachable,
1709 .generic_poison => unreachable,1777 .generic_poison => unreachable,
1710 .type_info => unreachable,1778 .type_info => unreachable,
1779 .bound_fn => unreachable,
17111780
1712 .@"struct" => {1781 .@"struct" => {
1713 const s = self.castTag(.@"struct").?.data;1782 const s = self.castTag(.@"struct").?.data;
1714 assert(s.status == .have_layout);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 var buffer: Payload.Bits = undefined;1803 var buffer: Payload.Bits = undefined;
1719 const int_tag_ty = self.intTagType(&buffer);1804 const int_tag_ty = self.intTagType(&buffer);
1720 return int_tag_ty.abiSize(target);1805 return int_tag_ty.abiSize(target);
...@@ -1730,6 +1815,7 @@ pub const Type = extern union {...@@ -1730,6 +1815,7 @@ pub const Type = extern union {
1730 .atomic_order,1815 .atomic_order,
1731 .atomic_rmw_op,1816 .atomic_rmw_op,
1732 .calling_convention,1817 .calling_convention,
1818 .address_space,
1733 .float_mode,1819 .float_mode,
1734 .reduce_op,1820 .reduce_op,
1735 .call_options,1821 .call_options,
...@@ -1818,6 +1904,7 @@ pub const Type = extern union {...@@ -1818,6 +1904,7 @@ pub const Type = extern union {
18181904
1819 .int_signed, .int_unsigned => {1905 .int_signed, .int_unsigned => {
1820 const bits: u16 = self.cast(Payload.Bits).?.data;1906 const bits: u16 = self.cast(Payload.Bits).?.data;
1907 if (bits == 0) return 0;
1821 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);1908 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
1822 },1909 },
18231910
...@@ -1875,11 +1962,12 @@ pub const Type = extern union {...@@ -1875,11 +1962,12 @@ pub const Type = extern union {
1875 .@"opaque" => unreachable,1962 .@"opaque" => unreachable,
1876 .var_args_param => unreachable,1963 .var_args_param => unreachable,
1877 .generic_poison => unreachable,1964 .generic_poison => unreachable,
1965 .bound_fn => unreachable,
18781966
1879 .@"struct" => {1967 .@"struct" => {
1880 @panic("TODO bitSize struct");1968 @panic("TODO bitSize struct");
1881 },1969 },
1882 .enum_simple, .enum_full, .enum_nonexhaustive => {1970 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
1883 var buffer: Payload.Bits = undefined;1971 var buffer: Payload.Bits = undefined;
1884 const int_tag_ty = self.intTagType(&buffer);1972 const int_tag_ty = self.intTagType(&buffer);
1885 return int_tag_ty.bitSize(target);1973 return int_tag_ty.bitSize(target);
...@@ -2014,6 +2102,7 @@ pub const Type = extern union {...@@ -2014,6 +2102,7 @@ pub const Type = extern union {
2014 .atomic_order,2102 .atomic_order,
2015 .atomic_rmw_op,2103 .atomic_rmw_op,
2016 .calling_convention,2104 .calling_convention,
2105 .address_space,
2017 .float_mode,2106 .float_mode,
2018 .reduce_op,2107 .reduce_op,
2019 .call_options,2108 .call_options,
...@@ -2024,23 +2113,6 @@ pub const Type = extern union {...@@ -2024,23 +2113,6 @@ pub const Type = extern union {
2024 };2113 };
2025 }2114 }
20262115
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 pub fn isSinglePointer(self: Type) bool {2116 pub fn isSinglePointer(self: Type) bool {
2045 return switch (self.tag()) {2117 return switch (self.tag()) {
2046 .single_const_pointer,2118 .single_const_pointer,
...@@ -2100,42 +2172,82 @@ pub const Type = extern union {...@@ -2100,42 +2172,82 @@ pub const Type = extern union {
2100 };2172 };
2101 }2173 }
21022174
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 switch (self.tag()) {2181 switch (self.tag()) {
2105 .const_slice_u8 => return Type.initTag(.manyptr_const_u8),2182 .const_slice_u8 => return Type.initTag(.manyptr_const_u8),
21062183
2107 .const_slice => {2184 .const_slice => {
2108 const elem_type = self.castTag(.const_slice).?.data;2185 const elem_type = self.castTag(.const_slice).?.data;
2109 buffer.* = .{2186 buffer.* = .{
2110 .base = .{ .tag = .many_const_pointer },2187 .elem_type = .{
2111 .data = 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 .mut_slice => {2194 .mut_slice => {
2116 const elem_type = self.castTag(.mut_slice).?.data;2195 const elem_type = self.castTag(.mut_slice).?.data;
2117 buffer.* = .{2196 buffer.* = .{
2118 .base = .{ .tag = .many_mut_pointer },2197 .elem_type = .{
2119 .data = 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 },
21232204
2124 .pointer => {2205 .pointer => {
2125 const payload = self.castTag(.pointer).?.data;2206 const payload = self.castTag(.pointer).?.data;
2126 assert(payload.size == .Slice);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 buffer.* = .{2217 buffer.* = .{
2129 .base = .{ .tag = .many_mut_pointer },2218 .pointer = .{
2130 .data = payload.pointee_type,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 } else {2242 } else {
2133 buffer.* = .{2243 buffer.* = .{
2134 .base = .{ .tag = .many_const_pointer },2244 .elem_type = .{
2135 .data = payload.pointee_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 },
21402252
2141 else => unreachable,2253 else => unreachable,
...@@ -2191,6 +2303,44 @@ pub const Type = extern union {...@@ -2191,6 +2303,44 @@ pub const Type = extern union {
2191 };2303 };
2192 }2304 }
21932305
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 /// Asserts that the type is an optional2344 /// Asserts that the type is an optional
2195 pub fn isPtrLikeOptional(self: Type) bool {2345 pub fn isPtrLikeOptional(self: Type) bool {
2196 switch (self.tag()) {2346 switch (self.tag()) {
...@@ -2203,8 +2353,13 @@ pub const Type = extern union {...@@ -2203,8 +2353,13 @@ pub const Type = extern union {
2203 const child_type = self.optionalChild(&buf);2353 const child_type = self.optionalChild(&buf);
2204 // optionals of zero sized pointers behave like bools2354 // optionals of zero sized pointers behave like bools
2205 if (!child_type.hasCodeGenBits()) return false;2355 if (!child_type.hasCodeGenBits()) return false;
2356 if (child_type.zigTypeTag() != .Pointer) return false;
22062357
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 else => unreachable,2364 else => unreachable,
2210 }2365 }
...@@ -2252,12 +2407,14 @@ pub const Type = extern union {...@@ -2252,12 +2407,14 @@ pub const Type = extern union {
2252 };2407 };
2253 }2408 }
22542409
2255 /// Asserts the type is a pointer or array type.2410 /// For *[N]T, returns [N]T.
2256 pub fn elemType(self: Type) Type {2411 /// For *T, returns T.
2257 return switch (self.tag()) {2412 /// For [*]T, returns T.
2258 .vector => self.castTag(.vector).?.data.elem_type,2413 pub fn childType(ty: Type) Type {
2259 .array => self.castTag(.array).?.data.elem_type,2414 return switch (ty.tag()) {
2260 .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,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 .single_const_pointer,2418 .single_const_pointer,
2262 .single_mut_pointer,2419 .single_mut_pointer,
2263 .many_const_pointer,2420 .many_const_pointer,
...@@ -2266,7 +2423,48 @@ pub const Type = extern union {...@@ -2266,7 +2423,48 @@ pub const Type = extern union {
2266 .c_mut_pointer,2423 .c_mut_pointer,
2267 .const_slice,2424 .const_slice,
2268 .mut_slice,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(),
22702468
2271 .array_u8,2469 .array_u8,
2272 .array_u8_sentinel_0,2470 .array_u8_sentinel_0,
...@@ -2276,12 +2474,29 @@ pub const Type = extern union {...@@ -2276,12 +2474,29 @@ pub const Type = extern union {
2276 => Type.initTag(.u8),2474 => Type.initTag(.u8),
22772475
2278 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),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
22802488
2281 else => unreachable,2489 else => unreachable,
2282 };2490 };
2283 }2491 }
22842492
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 /// Asserts that the type is an optional.2500 /// Asserts that the type is an optional.
2286 /// Resulting `Type` will have inner memory referencing `buf`.2501 /// Resulting `Type` will have inner memory referencing `buf`.
2287 pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {2502 pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {
...@@ -2320,6 +2535,21 @@ pub const Type = extern union {...@@ -2320,6 +2535,21 @@ pub const Type = extern union {
2320 }2535 }
2321 }2536 }
23222537
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 /// Asserts that the type is an error union.2553 /// Asserts that the type is an error union.
2324 pub fn errorUnionPayload(self: Type) Type {2554 pub fn errorUnionPayload(self: Type) Type {
2325 return switch (self.tag()) {2555 return switch (self.tag()) {
...@@ -2476,7 +2706,8 @@ pub const Type = extern union {...@@ -2476,7 +2706,8 @@ pub const Type = extern union {
2476 };2706 };
2477 }2707 }
24782708
2479 pub fn isFloat(self: Type) bool {2709 /// Returns `false` for `comptime_float`.
2710 pub fn isRuntimeFloat(self: Type) bool {
2480 return switch (self.tag()) {2711 return switch (self.tag()) {
2481 .f16,2712 .f16,
2482 .f32,2713 .f32,
...@@ -2489,13 +2720,29 @@ pub const Type = extern union {...@@ -2489,13 +2720,29 @@ pub const Type = extern union {
2489 };2720 };
2490 }2721 }
24912722
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 pub fn floatBits(self: Type, target: Target) u16 {2740 pub fn floatBits(self: Type, target: Target) u16 {
2494 return switch (self.tag()) {2741 return switch (self.tag()) {
2495 .f16 => 16,2742 .f16 => 16,
2496 .f32 => 32,2743 .f32 => 32,
2497 .f64 => 64,2744 .f64 => 64,
2498 .f128 => 128,2745 .f128, .comptime_float => 128,
2499 .c_longdouble => CType.longdouble.sizeInBits(target),2746 .c_longdouble => CType.longdouble.sizeInBits(target),
25002747
2501 else => unreachable,2748 else => unreachable,
...@@ -2728,6 +2975,7 @@ pub const Type = extern union {...@@ -2728,6 +2975,7 @@ pub const Type = extern union {
2728 .atomic_order,2975 .atomic_order,
2729 .atomic_rmw_op,2976 .atomic_rmw_op,
2730 .calling_convention,2977 .calling_convention,
2978 .address_space,
2731 .float_mode,2979 .float_mode,
2732 .reduce_op,2980 .reduce_op,
2733 .call_options,2981 .call_options,
...@@ -2743,6 +2991,7 @@ pub const Type = extern union {...@@ -2743,6 +2991,7 @@ pub const Type = extern union {
2743 .single_const_pointer,2991 .single_const_pointer,
2744 .single_mut_pointer,2992 .single_mut_pointer,
2745 .pointer,2993 .pointer,
2994 .bound_fn,
2746 => return null,2995 => return null,
27472996
2748 .@"struct" => {2997 .@"struct" => {
...@@ -2772,6 +3021,7 @@ pub const Type = extern union {...@@ -2772,6 +3021,7 @@ pub const Type = extern union {
2772 }3021 }
2773 },3022 },
2774 .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,3023 .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
3024 .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty,
2775 .@"union" => {3025 .@"union" => {
2776 return null; // TODO3026 return null; // TODO
2777 },3027 },
...@@ -2832,7 +3082,7 @@ pub const Type = extern union {...@@ -2832,7 +3082,7 @@ pub const Type = extern union {
2832 }3082 }
28333083
2834 /// Asserts that self.zigTypeTag() == .Int.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 assert(self.zigTypeTag() == .Int);3086 assert(self.zigTypeTag() == .Int);
2837 const info = self.intInfo(target);3087 const info = self.intInfo(target);
28383088
...@@ -2842,35 +3092,35 @@ pub const Type = extern union {...@@ -2842,35 +3092,35 @@ pub const Type = extern union {
28423092
2843 if ((info.bits - 1) <= std.math.maxInt(u6)) {3093 if ((info.bits - 1) <= std.math.maxInt(u6)) {
2844 const n: i64 = -(@as(i64, 1) << @truncate(u6, info.bits - 1));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 }
28473097
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 try res.shiftLeft(res, info.bits - 1);3099 try res.shiftLeft(res, info.bits - 1);
2850 res.negate();3100 res.negate();
28513101
2852 const res_const = res.toConst();3102 const res_const = res.toConst();
2853 if (res_const.positive) {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 } else {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 }
28593109
2860 /// Asserts that self.zigTypeTag() == .Int.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 assert(self.zigTypeTag() == .Int);3112 assert(self.zigTypeTag() == .Int);
2863 const info = self.intInfo(target);3113 const info = self.intInfo(target);
28643114
2865 if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) {3115 if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) {
2866 const n: i64 = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1;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 } else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) {3118 } else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) {
2869 const n: u64 = (@as(u64, 1) << @truncate(u6, info.bits)) - 1;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 }
28723122
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 try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed));3124 try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed));
2875 const one = std.math.big.int.Const{3125 const one = std.math.big.int.Const{
2876 .limbs = &[_]std.math.big.Limb{1},3126 .limbs = &[_]std.math.big.Limb{1},
...@@ -2880,37 +3130,27 @@ pub const Type = extern union {...@@ -2880,37 +3130,27 @@ pub const Type = extern union {
28803130
2881 const res_const = res.toConst();3131 const res_const = res.toConst();
2882 if (res_const.positive) {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 } else {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 }
28883138
2889 /// Returns the integer tag type of the enum.3139 /// Asserts the type is an enum or a union.
2890 pub fn enumTagType(ty: Type, buffer: *Payload.Bits) Type {3140 /// TODO support unions
2891 switch (ty.tag()) {3141 pub fn intTagType(self: Type, buffer: *Payload.Bits) Type {
2892 .enum_full, .enum_nonexhaustive => {3142 switch (self.tag()) {
2893 const enum_full = ty.cast(Payload.EnumFull).?.data;3143 .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty,
2894 return enum_full.tag_ty;3144 .enum_numbered => return self.castTag(.enum_numbered).?.data.tag_ty,
2895 },
2896 .enum_simple => {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 buffer.* = .{3148 buffer.* = .{
2899 .base = .{ .tag = .int_unsigned },3149 .base = .{ .tag = .int_unsigned },
2900 .data = std.math.log2_int_ceil(usize, enum_simple.fields.count()),3150 .data = bits,
2901 };3151 };
2902 return Type.initPayload(&buffer.base);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 else => unreachable,3154 else => unreachable,
2915 }3155 }
2916 }3156 }
...@@ -2928,13 +3168,12 @@ pub const Type = extern union {...@@ -2928,13 +3168,12 @@ pub const Type = extern union {
2928 const enum_full = ty.cast(Payload.EnumFull).?.data;3168 const enum_full = ty.cast(Payload.EnumFull).?.data;
2929 return enum_full.fields.count();3169 return enum_full.fields.count();
2930 },3170 },
2931 .enum_simple => {3171 .enum_simple => return ty.castTag(.enum_simple).?.data.fields.count(),
2932 const enum_simple = ty.castTag(.enum_simple).?.data;3172 .enum_numbered => return ty.castTag(.enum_numbered).?.data.fields.count(),
2933 return enum_simple.fields.count();
2934 },
2935 .atomic_order,3173 .atomic_order,
2936 .atomic_rmw_op,3174 .atomic_rmw_op,
2937 .calling_convention,3175 .calling_convention,
3176 .address_space,
2938 .float_mode,3177 .float_mode,
2939 .reduce_op,3178 .reduce_op,
2940 .call_options,3179 .call_options,
...@@ -2956,9 +3195,14 @@ pub const Type = extern union {...@@ -2956,9 +3195,14 @@ pub const Type = extern union {
2956 const enum_simple = ty.castTag(.enum_simple).?.data;3195 const enum_simple = ty.castTag(.enum_simple).?.data;
2957 return enum_simple.fields.keys()[field_index];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 .atomic_order,3202 .atomic_order,
2960 .atomic_rmw_op,3203 .atomic_rmw_op,
2961 .calling_convention,3204 .calling_convention,
3205 .address_space,
2962 .float_mode,3206 .float_mode,
2963 .reduce_op,3207 .reduce_op,
2964 .call_options,3208 .call_options,
...@@ -2979,9 +3223,14 @@ pub const Type = extern union {...@@ -2979,9 +3223,14 @@ pub const Type = extern union {
2979 const enum_simple = ty.castTag(.enum_simple).?.data;3223 const enum_simple = ty.castTag(.enum_simple).?.data;
2980 return enum_simple.fields.getIndex(field_name);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 .atomic_order,3230 .atomic_order,
2983 .atomic_rmw_op,3231 .atomic_rmw_op,
2984 .calling_convention,3232 .calling_convention,
3233 .address_space,
2985 .float_mode,3234 .float_mode,
2986 .reduce_op,3235 .reduce_op,
2987 .call_options,3236 .call_options,
...@@ -3021,6 +3270,15 @@ pub const Type = extern union {...@@ -3021,6 +3270,15 @@ pub const Type = extern union {
3021 return enum_full.values.getIndexContext(enum_tag, .{ .ty = tag_ty });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 .enum_simple => {3282 .enum_simple => {
3025 const enum_simple = ty.castTag(.enum_simple).?.data;3283 const enum_simple = ty.castTag(.enum_simple).?.data;
3026 const fields_len = enum_simple.fields.count();3284 const fields_len = enum_simple.fields.count();
...@@ -3035,6 +3293,7 @@ pub const Type = extern union {...@@ -3035,6 +3293,7 @@ pub const Type = extern union {
3035 .atomic_order,3293 .atomic_order,
3036 .atomic_rmw_op,3294 .atomic_rmw_op,
3037 .calling_convention,3295 .calling_convention,
3296 .address_space,
3038 .float_mode,3297 .float_mode,
3039 .reduce_op,3298 .reduce_op,
3040 .call_options,3299 .call_options,
...@@ -3071,6 +3330,7 @@ pub const Type = extern union {...@@ -3071,6 +3330,7 @@ pub const Type = extern union {
3071 const enum_full = ty.cast(Payload.EnumFull).?.data;3330 const enum_full = ty.cast(Payload.EnumFull).?.data;
3072 return enum_full.srcLoc();3331 return enum_full.srcLoc();
3073 },3332 },
3333 .enum_numbered => return ty.castTag(.enum_numbered).?.data.srcLoc(),
3074 .enum_simple => {3334 .enum_simple => {
3075 const enum_simple = ty.castTag(.enum_simple).?.data;3335 const enum_simple = ty.castTag(.enum_simple).?.data;
3076 return enum_simple.srcLoc();3336 return enum_simple.srcLoc();
...@@ -3090,6 +3350,7 @@ pub const Type = extern union {...@@ -3090,6 +3350,7 @@ pub const Type = extern union {
3090 .atomic_order,3350 .atomic_order,
3091 .atomic_rmw_op,3351 .atomic_rmw_op,
3092 .calling_convention,3352 .calling_convention,
3353 .address_space,
3093 .float_mode,3354 .float_mode,
3094 .reduce_op,3355 .reduce_op,
3095 .call_options,3356 .call_options,
...@@ -3107,6 +3368,7 @@ pub const Type = extern union {...@@ -3107,6 +3368,7 @@ pub const Type = extern union {
3107 const enum_full = ty.cast(Payload.EnumFull).?.data;3368 const enum_full = ty.cast(Payload.EnumFull).?.data;
3108 return enum_full.owner_decl;3369 return enum_full.owner_decl;
3109 },3370 },
3371 .enum_numbered => return ty.castTag(.enum_numbered).?.data.owner_decl,
3110 .enum_simple => {3372 .enum_simple => {
3111 const enum_simple = ty.castTag(.enum_simple).?.data;3373 const enum_simple = ty.castTag(.enum_simple).?.data;
3112 return enum_simple.owner_decl;3374 return enum_simple.owner_decl;
...@@ -3127,6 +3389,7 @@ pub const Type = extern union {...@@ -3127,6 +3389,7 @@ pub const Type = extern union {
3127 .atomic_order,3389 .atomic_order,
3128 .atomic_rmw_op,3390 .atomic_rmw_op,
3129 .calling_convention,3391 .calling_convention,
3392 .address_space,
3130 .float_mode,3393 .float_mode,
3131 .reduce_op,3394 .reduce_op,
3132 .call_options,3395 .call_options,
...@@ -3163,6 +3426,15 @@ pub const Type = extern union {...@@ -3163,6 +3426,15 @@ pub const Type = extern union {
3163 return enum_full.values.containsContext(int, .{ .ty = tag_ty });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 .enum_simple => {3438 .enum_simple => {
3167 const enum_simple = ty.castTag(.enum_simple).?.data;3439 const enum_simple = ty.castTag(.enum_simple).?.data;
3168 const fields_len = enum_simple.fields.count();3440 const fields_len = enum_simple.fields.count();
...@@ -3177,6 +3449,7 @@ pub const Type = extern union {...@@ -3177,6 +3449,7 @@ pub const Type = extern union {
3177 .atomic_order,3449 .atomic_order,
3178 .atomic_rmw_op,3450 .atomic_rmw_op,
3179 .calling_convention,3451 .calling_convention,
3452 .address_space,
3180 .float_mode,3453 .float_mode,
3181 .reduce_op,3454 .reduce_op,
3182 .call_options,3455 .call_options,
...@@ -3237,6 +3510,7 @@ pub const Type = extern union {...@@ -3237,6 +3510,7 @@ pub const Type = extern union {
3237 atomic_order,3510 atomic_order,
3238 atomic_rmw_op,3511 atomic_rmw_op,
3239 calling_convention,3512 calling_convention,
3513 address_space,
3240 float_mode,3514 float_mode,
3241 reduce_op,3515 reduce_op,
3242 call_options,3516 call_options,
...@@ -3254,7 +3528,7 @@ pub const Type = extern union {...@@ -3254,7 +3528,7 @@ pub const Type = extern union {
3254 anyerror_void_error_union,3528 anyerror_void_error_union,
3255 generic_poison,3529 generic_poison,
3256 /// This is a special type for variadic parameters of a function call.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 var_args_param,3532 var_args_param,
3259 /// Same as `empty_struct` except it has an empty namespace.3533 /// Same as `empty_struct` except it has an empty namespace.
3260 empty_struct_literal,3534 empty_struct_literal,
...@@ -3264,6 +3538,7 @@ pub const Type = extern union {...@@ -3264,6 +3538,7 @@ pub const Type = extern union {
3264 inferred_alloc_mut,3538 inferred_alloc_mut,
3265 /// Same as `inferred_alloc_mut` but the local is `var` not `const`.3539 /// Same as `inferred_alloc_mut` but the local is `var` not `const`.
3266 inferred_alloc_const, // See last_no_payload_tag below.3540 inferred_alloc_const, // See last_no_payload_tag below.
3541 bound_fn,
3267 // After this, the tag requires a payload.3542 // After this, the tag requires a payload.
32683543
3269 array_u8,3544 array_u8,
...@@ -3298,10 +3573,11 @@ pub const Type = extern union {...@@ -3298,10 +3573,11 @@ pub const Type = extern union {
3298 @"union",3573 @"union",
3299 union_tagged,3574 union_tagged,
3300 enum_simple,3575 enum_simple,
3576 enum_numbered,
3301 enum_full,3577 enum_full,
3302 enum_nonexhaustive,3578 enum_nonexhaustive,
33033579
3304 pub const last_no_payload_tag = Tag.inferred_alloc_const;3580 pub const last_no_payload_tag = Tag.bound_fn;
3305 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;3581 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
33063582
3307 pub fn Type(comptime t: Tag) type {3583 pub fn Type(comptime t: Tag) type {
...@@ -3360,6 +3636,7 @@ pub const Type = extern union {...@@ -3360,6 +3636,7 @@ pub const Type = extern union {
3360 .atomic_order,3636 .atomic_order,
3361 .atomic_rmw_op,3637 .atomic_rmw_op,
3362 .calling_convention,3638 .calling_convention,
3639 .address_space,
3363 .float_mode,3640 .float_mode,
3364 .reduce_op,3641 .reduce_op,
3365 .call_options,3642 .call_options,
...@@ -3367,6 +3644,7 @@ pub const Type = extern union {...@@ -3367,6 +3644,7 @@ pub const Type = extern union {
3367 .extern_options,3644 .extern_options,
3368 .type_info,3645 .type_info,
3369 .@"anyframe",3646 .@"anyframe",
3647 .bound_fn,
3370 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),3648 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
33713649
3372 .array_u8,3650 .array_u8,
...@@ -3405,6 +3683,7 @@ pub const Type = extern union {...@@ -3405,6 +3683,7 @@ pub const Type = extern union {
3405 .@"union", .union_tagged => Payload.Union,3683 .@"union", .union_tagged => Payload.Union,
3406 .enum_full, .enum_nonexhaustive => Payload.EnumFull,3684 .enum_full, .enum_nonexhaustive => Payload.EnumFull,
3407 .enum_simple => Payload.EnumSimple,3685 .enum_simple => Payload.EnumSimple,
3686 .enum_numbered => Payload.EnumNumbered,
3408 .empty_struct => Payload.ContainerScope,3687 .empty_struct => Payload.ContainerScope,
3409 };3688 };
3410 }3689 }
...@@ -3415,12 +3694,12 @@ pub const Type = extern union {...@@ -3415,12 +3694,12 @@ pub const Type = extern union {
3415 }3694 }
34163695
3417 pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!file_struct.Type {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());3697 const p = try ally.create(t.Type());
3419 ptr.* = .{3698 p.* = .{
3420 .base = .{ .tag = t },3699 .base = .{ .tag = t },
3421 .data = data,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 }
34253704
3426 pub fn Data(comptime t: Tag) type {3705 pub fn Data(comptime t: Tag) type {
...@@ -3510,18 +3789,23 @@ pub const Type = extern union {...@@ -3510,18 +3789,23 @@ pub const Type = extern union {
3510 pub const base_tag = Tag.pointer;3789 pub const base_tag = Tag.pointer;
35113790
3512 base: Payload = Payload{ .tag = base_tag },3791 base: Payload = Payload{ .tag = base_tag },
3513 data: struct {3792 data: Data,
3793
3794 pub const Data = struct {
3514 pointee_type: Type,3795 pointee_type: Type,
3515 sentinel: ?Value,3796 sentinel: ?Value = null,
3516 /// If zero use pointee_type.AbiAlign()3797 /// If zero use pointee_type.AbiAlign()
3517 @"align": u32,3798 @"align": u32 = 0,
3518 bit_offset: u16,3799 /// See src/target.zig defaultAddressSpace function for how to obtain
3519 host_size: u16,3800 /// an appropriate value for this field.
3520 @"allowzero": bool,3801 @"addrspace": std.builtin.AddressSpace,
3521 mutable: bool,3802 bit_offset: u16 = 0,
3522 @"volatile": bool,3803 host_size: u16 = 0,
3523 size: std.builtin.TypeInfo.Pointer.Size,3804 @"allowzero": bool = false,
3524 },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 };
35263810
3527 pub const ErrorUnion = struct {3811 pub const ErrorUnion = struct {
...@@ -3576,7 +3860,64 @@ pub const Type = extern union {...@@ -3576,7 +3860,64 @@ pub const Type = extern union {
3576 base: Payload = .{ .tag = .enum_simple },3860 base: Payload = .{ .tag = .enum_simple },
3577 data: *Module.EnumSimple,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};
35813922
3582pub const CType = enum {3923pub const CType = enum {
...@@ -3633,6 +3974,7 @@ pub const CType = enum {...@@ -3633,6 +3974,7 @@ pub const CType = enum {
3633 .wasi,3974 .wasi,
3634 .emscripten,3975 .emscripten,
3635 .plan9,3976 .plan9,
3977 .solaris,
3636 => switch (self) {3978 => switch (self) {
3637 .short,3979 .short,
3638 .ushort,3980 .ushort,
...@@ -3684,7 +4026,6 @@ pub const CType = enum {...@@ -3684,7 +4026,6 @@ pub const CType = enum {
3684 .fuchsia,4026 .fuchsia,
3685 .kfreebsd,4027 .kfreebsd,
3686 .lv2,4028 .lv2,
3687 .solaris,
3688 .zos,4029 .zos,
3689 .haiku,4030 .haiku,
3690 .minix,4031 .minix,
src/value.zig+543-41
...@@ -63,6 +63,7 @@ pub const Value = extern union {...@@ -63,6 +63,7 @@ pub const Value = extern union {
63 atomic_order_type,63 atomic_order_type,
64 atomic_rmw_op_type,64 atomic_rmw_op_type,
65 calling_convention_type,65 calling_convention_type,
66 address_space_type,
66 float_mode_type,67 float_mode_type,
67 reduce_op_type,68 reduce_op_type,
68 call_options_type,69 call_options_type,
...@@ -158,6 +159,10 @@ pub const Value = extern union {...@@ -158,6 +159,10 @@ pub const Value = extern union {
158 /// Used to coordinate alloc_inferred, store_to_inferred_ptr, and resolve_inferred_alloc159 /// Used to coordinate alloc_inferred, store_to_inferred_ptr, and resolve_inferred_alloc
159 /// instructions for comptime code.160 /// instructions for comptime code.
160 inferred_alloc_comptime,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,
161166
162 pub const last_no_payload_tag = Tag.empty_array;167 pub const last_no_payload_tag = Tag.empty_array;
163 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;168 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
...@@ -226,6 +231,7 @@ pub const Value = extern union {...@@ -226,6 +231,7 @@ pub const Value = extern union {
226 .atomic_order_type,231 .atomic_order_type,
227 .atomic_rmw_op_type,232 .atomic_rmw_op_type,
228 .calling_convention_type,233 .calling_convention_type,
234 .address_space_type,
229 .float_mode_type,235 .float_mode_type,
230 .reduce_op_type,236 .reduce_op_type,
231 .call_options_type,237 .call_options_type,
...@@ -277,6 +283,7 @@ pub const Value = extern union {...@@ -277,6 +283,7 @@ pub const Value = extern union {
277 .inferred_alloc => Payload.InferredAlloc,283 .inferred_alloc => Payload.InferredAlloc,
278 .@"struct" => Payload.Struct,284 .@"struct" => Payload.Struct,
279 .@"union" => Payload.Union,285 .@"union" => Payload.Union,
286 .bound_fn => Payload.BoundFn,
280 };287 };
281 }288 }
282289
...@@ -412,6 +419,7 @@ pub const Value = extern union {...@@ -412,6 +419,7 @@ pub const Value = extern union {
412 .atomic_order_type,419 .atomic_order_type,
413 .atomic_rmw_op_type,420 .atomic_rmw_op_type,
414 .calling_convention_type,421 .calling_convention_type,
422 .address_space_type,
415 .float_mode_type,423 .float_mode_type,
416 .reduce_op_type,424 .reduce_op_type,
417 .call_options_type,425 .call_options_type,
...@@ -419,6 +427,7 @@ pub const Value = extern union {...@@ -419,6 +427,7 @@ pub const Value = extern union {
419 .extern_options_type,427 .extern_options_type,
420 .type_info_type,428 .type_info_type,
421 .generic_poison,429 .generic_poison,
430 .bound_fn,
422 => unreachable,431 => unreachable,
423432
424 .ty => {433 .ty => {
...@@ -625,6 +634,7 @@ pub const Value = extern union {...@@ -625,6 +634,7 @@ pub const Value = extern union {
625 .atomic_order_type => return out_stream.writeAll("std.builtin.AtomicOrder"),634 .atomic_order_type => return out_stream.writeAll("std.builtin.AtomicOrder"),
626 .atomic_rmw_op_type => return out_stream.writeAll("std.builtin.AtomicRmwOp"),635 .atomic_rmw_op_type => return out_stream.writeAll("std.builtin.AtomicRmwOp"),
627 .calling_convention_type => return out_stream.writeAll("std.builtin.CallingConvention"),636 .calling_convention_type => return out_stream.writeAll("std.builtin.CallingConvention"),
637 .address_space_type => return out_stream.writeAll("std.builtin.AddressSpace"),
628 .float_mode_type => return out_stream.writeAll("std.builtin.FloatMode"),638 .float_mode_type => return out_stream.writeAll("std.builtin.FloatMode"),
629 .reduce_op_type => return out_stream.writeAll("std.builtin.ReduceOp"),639 .reduce_op_type => return out_stream.writeAll("std.builtin.ReduceOp"),
630 .call_options_type => return out_stream.writeAll("std.builtin.CallOptions"),640 .call_options_type => return out_stream.writeAll("std.builtin.CallOptions"),
...@@ -712,6 +722,10 @@ pub const Value = extern union {...@@ -712,6 +722,10 @@ pub const Value = extern union {
712 try out_stream.writeAll("(opt_payload_ptr)");722 try out_stream.writeAll("(opt_payload_ptr)");
713 val = val.castTag(.opt_payload_ptr).?.data;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 }
717731
...@@ -792,6 +806,7 @@ pub const Value = extern union {...@@ -792,6 +806,7 @@ pub const Value = extern union {
792 .atomic_order_type => Type.initTag(.atomic_order),806 .atomic_order_type => Type.initTag(.atomic_order),
793 .atomic_rmw_op_type => Type.initTag(.atomic_rmw_op),807 .atomic_rmw_op_type => Type.initTag(.atomic_rmw_op),
794 .calling_convention_type => Type.initTag(.calling_convention),808 .calling_convention_type => Type.initTag(.calling_convention),
809 .address_space_type => Type.initTag(.address_space),
795 .float_mode_type => Type.initTag(.float_mode),810 .float_mode_type => Type.initTag(.float_mode),
796 .reduce_op_type => Type.initTag(.reduce_op),811 .reduce_op_type => Type.initTag(.reduce_op),
797 .call_options_type => Type.initTag(.call_options),812 .call_options_type => Type.initTag(.call_options),
...@@ -932,7 +947,7 @@ pub const Value = extern union {...@@ -932,7 +947,7 @@ pub const Value = extern union {
932 /// Asserts that the value is a float or an integer.947 /// Asserts that the value is a float or an integer.
933 pub fn toFloat(self: Value, comptime T: type) T {948 pub fn toFloat(self: Value, comptime T: type) T {
934 return switch (self.tag()) {949 return switch (self.tag()) {
935 .float_16 => @panic("TODO soft float"),950 .float_16 => @floatCast(T, self.castTag(.float_16).?.data),
936 .float_32 => @floatCast(T, self.castTag(.float_32).?.data),951 .float_32 => @floatCast(T, self.castTag(.float_32).?.data),
937 .float_64 => @floatCast(T, self.castTag(.float_64).?.data),952 .float_64 => @floatCast(T, self.castTag(.float_64).?.data),
938 .float_128 => @floatCast(T, self.castTag(.float_128).?.data),953 .float_128 => @floatCast(T, self.castTag(.float_128).?.data),
...@@ -947,6 +962,45 @@ pub const Value = extern union {...@@ -947,6 +962,45 @@ pub const Value = extern union {
947 };962 };
948 }963 }
949964
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 /// Asserts the value is an integer and not undefined.1004 /// Asserts the value is an integer and not undefined.
951 /// Returns the number of bits the value requires to represent stored in twos complement form.1005 /// Returns the number of bits the value requires to represent stored in twos complement form.
952 pub fn intBitCountTwosComp(self: Value) usize {1006 pub fn intBitCountTwosComp(self: Value) usize {
...@@ -1036,31 +1090,15 @@ pub const Value = extern union {...@@ -1036,31 +1090,15 @@ pub const Value = extern union {
1036 }1090 }
1037 }1091 }
10381092
1039 /// Converts an integer or a float to a float.1093 /// Converts an integer or a float to a float. May result in a loss of information.
1040 /// Returns `error.Overflow` if the value does not fit in the new type.1094 /// Caller can find out by equality checking the result against the operand.
1041 pub fn floatCast(self: Value, allocator: *Allocator, dest_ty: Type) !Value {1095 pub fn floatCast(self: Value, arena: *Allocator, dest_ty: Type) !Value {
1042 switch (dest_ty.tag()) {1096 switch (dest_ty.tag()) {
1043 .f16 => {1097 .f16 => return Value.Tag.float_16.create(arena, self.toFloat(f16)),
1044 @panic("TODO add __trunctfhf2 to compiler-rt");1098 .f32 => return Value.Tag.float_32.create(arena, self.toFloat(f32)),
1045 //const res = try Value.Tag.float_16.create(allocator, self.toFloat(f16));1099 .f64 => return Value.Tag.float_64.create(arena, self.toFloat(f64)),
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 },
1062 .f128, .comptime_float, .c_longdouble => {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 else => unreachable,1103 else => unreachable,
1066 }1104 }
...@@ -1286,7 +1324,12 @@ pub const Value = extern union {...@@ -1286,7 +1324,12 @@ pub const Value = extern union {
1286 }1324 }
1287 },1325 },
1288 .Union => {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 .Fn => {1334 .Fn => {
1292 @panic("TODO implement hashing function values");1335 @panic("TODO implement hashing function values");
...@@ -1442,6 +1485,14 @@ pub const Value = extern union {...@@ -1442,6 +1485,14 @@ pub const Value = extern union {
1442 }1485 }
1443 }1486 }
14441487
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 /// Returns a pointer to the element value at the index.1496 /// Returns a pointer to the element value at the index.
1446 pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value {1497 pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value {
1447 if (self.castTag(.elem_ptr)) |elem_ptr| {1498 if (self.castTag(.elem_ptr)) |elem_ptr| {
...@@ -1524,6 +1575,341 @@ pub const Value = extern union {...@@ -1524,6 +1575,341 @@ pub const Value = extern union {
1524 };1575 };
1525 }1576 }
15261577
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 pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {1913 pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
1528 // TODO is this a performance issue? maybe we should try the operation without1914 // TODO is this a performance issue? maybe we should try the operation without
1529 // resorting to BigInt first.1915 // resorting to BigInt first.
...@@ -1599,6 +1985,82 @@ pub const Value = extern union {...@@ -1599,6 +1985,82 @@ pub const Value = extern union {
1599 }1985 }
1600 }1986 }
16011987
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 pub fn intMul(lhs: Value, rhs: Value, allocator: *Allocator) !Value {2064 pub fn intMul(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
1603 // TODO is this a performance issue? maybe we should try the operation without2065 // TODO is this a performance issue? maybe we should try the operation without
1604 // resorting to BigInt first.2066 // resorting to BigInt first.
...@@ -1634,6 +2096,31 @@ pub const Value = extern union {...@@ -1634,6 +2096,31 @@ pub const Value = extern union {
1634 return Tag.int_u64.create(arena, truncated);2096 return Tag.int_u64.create(arena, truncated);
1635 }2097 }
16362098
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 pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value {2124 pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
1638 // TODO is this a performance issue? maybe we should try the operation without2125 // TODO is this a performance issue? maybe we should try the operation without
1639 // resorting to BigInt first.2126 // resorting to BigInt first.
...@@ -1667,10 +2154,9 @@ pub const Value = extern union {...@@ -1667,10 +2154,9 @@ pub const Value = extern union {
1667 ) !Value {2154 ) !Value {
1668 switch (float_type.tag()) {2155 switch (float_type.tag()) {
1669 .f16 => {2156 .f16 => {
1670 @panic("TODO add __trunctfhf2 to compiler-rt");2157 const lhs_val = lhs.toFloat(f16);
1671 //const lhs_val = lhs.toFloat(f16);2158 const rhs_val = rhs.toFloat(f16);
1672 //const rhs_val = rhs.toFloat(f16);2159 return Value.Tag.float_16.create(arena, lhs_val + rhs_val);
1673 //return Value.Tag.float_16.create(arena, lhs_val + rhs_val);
1674 },2160 },
1675 .f32 => {2161 .f32 => {
1676 const lhs_val = lhs.toFloat(f32);2162 const lhs_val = lhs.toFloat(f32);
...@@ -1699,10 +2185,9 @@ pub const Value = extern union {...@@ -1699,10 +2185,9 @@ pub const Value = extern union {
1699 ) !Value {2185 ) !Value {
1700 switch (float_type.tag()) {2186 switch (float_type.tag()) {
1701 .f16 => {2187 .f16 => {
1702 @panic("TODO add __trunctfhf2 to compiler-rt");2188 const lhs_val = lhs.toFloat(f16);
1703 //const lhs_val = lhs.toFloat(f16);2189 const rhs_val = rhs.toFloat(f16);
1704 //const rhs_val = rhs.toFloat(f16);2190 return Value.Tag.float_16.create(arena, lhs_val - rhs_val);
1705 //return Value.Tag.float_16.create(arena, lhs_val - rhs_val);
1706 },2191 },
1707 .f32 => {2192 .f32 => {
1708 const lhs_val = lhs.toFloat(f32);2193 const lhs_val = lhs.toFloat(f32);
...@@ -1731,10 +2216,9 @@ pub const Value = extern union {...@@ -1731,10 +2216,9 @@ pub const Value = extern union {
1731 ) !Value {2216 ) !Value {
1732 switch (float_type.tag()) {2217 switch (float_type.tag()) {
1733 .f16 => {2218 .f16 => {
1734 @panic("TODO add __trunctfhf2 to compiler-rt");2219 const lhs_val = lhs.toFloat(f16);
1735 //const lhs_val = lhs.toFloat(f16);2220 const rhs_val = rhs.toFloat(f16);
1736 //const rhs_val = rhs.toFloat(f16);2221 return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
1737 //return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
1738 },2222 },
1739 .f32 => {2223 .f32 => {
1740 const lhs_val = lhs.toFloat(f32);2224 const lhs_val = lhs.toFloat(f32);
...@@ -1763,10 +2247,9 @@ pub const Value = extern union {...@@ -1763,10 +2247,9 @@ pub const Value = extern union {
1763 ) !Value {2247 ) !Value {
1764 switch (float_type.tag()) {2248 switch (float_type.tag()) {
1765 .f16 => {2249 .f16 => {
1766 @panic("TODO add __trunctfhf2 to compiler-rt");2250 const lhs_val = lhs.toFloat(f16);
1767 //const lhs_val = lhs.toFloat(f16);2251 const rhs_val = rhs.toFloat(f16);
1768 //const rhs_val = rhs.toFloat(f16);2252 return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
1769 //return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
1770 },2253 },
1771 .f32 => {2254 .f32 => {
1772 const lhs_val = lhs.toFloat(f32);2255 const lhs_val = lhs.toFloat(f32);
...@@ -1972,6 +2455,16 @@ pub const Value = extern union {...@@ -1972,6 +2455,16 @@ pub const Value = extern union {
1972 val: Value,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 };
19762469
1977 /// Big enough to fit any non-BigInt value2470 /// Big enough to fit any non-BigInt value
...@@ -1980,4 +2473,13 @@ pub const Value = extern union {...@@ -1980,4 +2473,13 @@ pub const Value = extern union {
1980 /// are possible without using an allocator.2473 /// are possible without using an allocator.
1981 limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,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
2482var 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,7 +352,7 @@ extern "C" {
352 /// <summary> 352 /// <summary>
353 /// Gets product-specific properties. 353 /// Gets product-specific properties.
354 /// </summary> 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 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns> 356 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
357 STDMETHOD(GetProperties)( 357 STDMETHOD(GetProperties)(
358 _Outptr_result_maybenull_ ISetupPropertyStore** ppProperties 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,10 +1691,10 @@ void ZigClang_detect_enum_ConstantExprKind(clang::Expr::ConstantExprKind x) {
1691 break;1691 break;
1692 }1692 }
1693}1693}
1694static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_Normal == clang::Expr::ConstantExprKind::Normal, "");1694static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_Normal == clang::Expr::ConstantExprKind::Normal, "");
1695static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_NonClassTemplateArgument == clang::Expr::ConstantExprKind::NonClassTemplateArgument, "");1695static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_NonClassTemplateArgument == clang::Expr::ConstantExprKind::NonClassTemplateArgument, "");
1696static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_ClassTemplateArgument == clang::Expr::ConstantExprKind::ClassTemplateArgument, "");1696static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_ClassTemplateArgument == clang::Expr::ConstantExprKind::ClassTemplateArgument, "");
1697static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_ImmediateInvocation == clang::Expr::ConstantExprKind::ImmediateInvocation, "");1697static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_ImmediateInvocation == clang::Expr::ConstantExprKind::ImmediateInvocation, "");
16981698
16991699
1700static_assert(sizeof(ZigClangAPValue) == sizeof(clang::APValue), "");1700static_assert(sizeof(ZigClangAPValue) == sizeof(clang::APValue), "");
src/zig_clang.h+4-4
...@@ -1011,10 +1011,10 @@ enum ZigClangPreprocessedEntity_EntityKind {...@@ -1011,10 +1011,10 @@ enum ZigClangPreprocessedEntity_EntityKind {
1011};1011};
10121012
1013enum ZigClangExpr_ConstantExprKind {1013enum ZigClangExpr_ConstantExprKind {
1014 ZigClangExpr_ContantExprKind_Normal,1014 ZigClangExpr_ConstantExprKind_Normal,
1015 ZigClangExpr_ContantExprKind_NonClassTemplateArgument,1015 ZigClangExpr_ConstantExprKind_NonClassTemplateArgument,
1016 ZigClangExpr_ContantExprKind_ClassTemplateArgument,1016 ZigClangExpr_ConstantExprKind_ClassTemplateArgument,
1017 ZigClangExpr_ContantExprKind_ImmediateInvocation,1017 ZigClangExpr_ConstantExprKind_ImmediateInvocation,
1018};1018};
10191019
1020enum ZigClangUnaryExprOrTypeTrait_Kind {1020enum ZigClangUnaryExprOrTypeTrait_Kind {
src/zig_llvm-ar.cpp+1-1
...@@ -232,7 +232,7 @@ static std::string ArchiveName;...@@ -232,7 +232,7 @@ static std::string ArchiveName;
232static std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;232static std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
233static std::vector<std::unique_ptr<object::Archive>> Archives;233static std::vector<std::unique_ptr<object::Archive>> Archives;
234234
235// This variable holds the list of member files to proecess, as given235// This variable holds the list of member files to process, as given
236// on the command line.236// on the command line.
237static std::vector<StringRef> Members;237static std::vector<StringRef> Members;
238238
src/zig_llvm.cpp+5
...@@ -412,6 +412,11 @@ ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref) {...@@ -412,6 +412,11 @@ ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref) {
412 return wrap(Type::getTokenTy(*unwrap(context_ref)));412 return wrap(Type::getTokenTy(*unwrap(context_ref)));
413}413}
414414
415LLVMValueRef 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
415LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,420LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
416 unsigned NumArgs, ZigLLVM_CallingConv CC, ZigLLVM_CallAttr attr, const char *Name)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,6 +65,9 @@ ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, co
6565
66ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);66ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);
6767
68ZIG_EXTERN_C LLVMValueRef ZigLLVMAddFunctionInAddressSpace(LLVMModuleRef M, const char *Name,
69 LLVMTypeRef FunctionTy, unsigned AddressSpace);
70
68enum ZigLLVM_CallingConv {71enum ZigLLVM_CallingConv {
69 ZigLLVM_C = 0,72 ZigLLVM_C = 0,
70 ZigLLVM_Fast = 8,73 ZigLLVM_Fast = 8,
test/behavior.zig+43-38
...@@ -2,19 +2,38 @@ const builtin = @import("builtin");...@@ -2,19 +2,38 @@ const builtin = @import("builtin");
22
3test {3test {
4 // Tests that pass for both.4 // Tests that pass for both.
5 _ = @import("behavior/bool.zig");5 _ = @import("behavior/array.zig");
6 _ = @import("behavior/atomics.zig");
6 _ = @import("behavior/basic.zig");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 _ = @import("behavior/eval.zig");19 _ = @import("behavior/eval.zig");
9 _ = @import("behavior/pointers.zig");20 _ = @import("behavior/generics.zig");
10 _ = @import("behavior/if.zig");21 _ = @import("behavior/if.zig");
11 _ = @import("behavior/cast.zig");22 _ = @import("behavior/math.zig");
12 _ = @import("behavior/array.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 _ = @import("behavior/usingnamespace.zig");30 _ = @import("behavior/usingnamespace.zig");
14 _ = @import("behavior/atomics.zig");31 _ = @import("behavior/widening.zig");
1532
16 if (!builtin.zig_is_stage2) {33 if (builtin.zig_is_stage2) {
17 // Tests that only pass for stage1.34 // When all comptime_memory.zig tests pass, #9646 can be closed.
35 // _ = @import("behavior/comptime_memory.zig");
36 } else {
18 _ = @import("behavior/align.zig");37 _ = @import("behavior/align.zig");
19 _ = @import("behavior/alignof.zig");38 _ = @import("behavior/alignof.zig");
20 _ = @import("behavior/array_stage1.zig");39 _ = @import("behavior/array_stage1.zig");
...@@ -22,16 +41,25 @@ test {...@@ -22,16 +41,25 @@ test {
22 _ = @import("behavior/asm.zig");41 _ = @import("behavior/asm.zig");
23 _ = @import("behavior/async_fn.zig");42 _ = @import("behavior/async_fn.zig");
24 }43 }
25 _ = @import("behavior/atomics_stage1.zig");
26 _ = @import("behavior/await_struct.zig");44 _ = @import("behavior/await_struct.zig");
27 _ = @import("behavior/bit_shifting.zig");45 _ = @import("behavior/bit_shifting.zig");
28 _ = @import("behavior/bitcast.zig");46 _ = @import("behavior/bitcast.zig");
29 _ = @import("behavior/bitreverse.zig");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 _ = @import("behavior/bugs/1025.zig");59 _ = @import("behavior/bugs/1025.zig");
31 _ = @import("behavior/bugs/1076.zig");60 _ = @import("behavior/bugs/1076.zig");
32 _ = @import("behavior/bugs/1111.zig");61 _ = @import("behavior/bugs/1111.zig");
33 _ = @import("behavior/bugs/1120.zig");62 _ = @import("behavior/bugs/1120.zig");
34 _ = @import("behavior/bugs/1277.zig");
35 _ = @import("behavior/bugs/1310.zig");63 _ = @import("behavior/bugs/1310.zig");
36 _ = @import("behavior/bugs/1322.zig");64 _ = @import("behavior/bugs/1322.zig");
37 _ = @import("behavior/bugs/1381.zig");65 _ = @import("behavior/bugs/1381.zig");
...@@ -41,14 +69,11 @@ test {...@@ -41,14 +69,11 @@ test {
41 _ = @import("behavior/bugs/1500.zig");69 _ = @import("behavior/bugs/1500.zig");
42 _ = @import("behavior/bugs/1607.zig");70 _ = @import("behavior/bugs/1607.zig");
43 _ = @import("behavior/bugs/1735.zig");71 _ = @import("behavior/bugs/1735.zig");
44 _ = @import("behavior/bugs/1741.zig");
45 _ = @import("behavior/bugs/1851.zig");72 _ = @import("behavior/bugs/1851.zig");
46 _ = @import("behavior/bugs/1914.zig");73 _ = @import("behavior/bugs/1914.zig");
47 _ = @import("behavior/bugs/2006.zig");74 _ = @import("behavior/bugs/2006.zig");
48 _ = @import("behavior/bugs/2114.zig");75 _ = @import("behavior/bugs/2114.zig");
49 _ = @import("behavior/bugs/2346.zig");
50 _ = @import("behavior/bugs/2578.zig");76 _ = @import("behavior/bugs/2578.zig");
51 _ = @import("behavior/bugs/2692.zig");
52 _ = @import("behavior/bugs/2889.zig");77 _ = @import("behavior/bugs/2889.zig");
53 _ = @import("behavior/bugs/3007.zig");78 _ = @import("behavior/bugs/3007.zig");
54 _ = @import("behavior/bugs/3046.zig");79 _ = @import("behavior/bugs/3046.zig");
...@@ -60,8 +85,6 @@ test {...@@ -60,8 +85,6 @@ test {
60 _ = @import("behavior/bugs/3779.zig");85 _ = @import("behavior/bugs/3779.zig");
61 _ = @import("behavior/bugs/4328.zig");86 _ = @import("behavior/bugs/4328.zig");
62 _ = @import("behavior/bugs/4560.zig");87 _ = @import("behavior/bugs/4560.zig");
63 _ = @import("behavior/bugs/4769_a.zig");
64 _ = @import("behavior/bugs/4769_b.zig");
65 _ = @import("behavior/bugs/4954.zig");88 _ = @import("behavior/bugs/4954.zig");
66 _ = @import("behavior/bugs/5398.zig");89 _ = @import("behavior/bugs/5398.zig");
67 _ = @import("behavior/bugs/5413.zig");90 _ = @import("behavior/bugs/5413.zig");
...@@ -69,30 +92,14 @@ test {...@@ -69,30 +92,14 @@ test {
69 _ = @import("behavior/bugs/5487.zig");92 _ = @import("behavior/bugs/5487.zig");
70 _ = @import("behavior/bugs/6456.zig");93 _ = @import("behavior/bugs/6456.zig");
71 _ = @import("behavior/bugs/6781.zig");94 _ = @import("behavior/bugs/6781.zig");
72 _ = @import("behavior/bugs/6850.zig");
73 _ = @import("behavior/bugs/7027.zig");95 _ = @import("behavior/bugs/7027.zig");
74 _ = @import("behavior/bugs/7047.zig");96 _ = @import("behavior/bugs/7047.zig");
75 _ = @import("behavior/bugs/7003.zig");97 _ = @import("behavior/bugs/7003.zig");
76 _ = @import("behavior/bugs/7250.zig");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 _ = @import("behavior/byteswap.zig");99 _ = @import("behavior/byteswap.zig");
91 _ = @import("behavior/byval_arg_var.zig");100 _ = @import("behavior/byval_arg_var.zig");
92 _ = @import("behavior/call.zig");101 _ = @import("behavior/call.zig");
93 _ = @import("behavior/cast_stage1.zig");102 _ = @import("behavior/cast_stage1.zig");
94 // When these tests pass, #9646 can be closed.
95 // _ = @import("behavior/comptime_memory.zig");
96 _ = @import("behavior/const_slice_child.zig");103 _ = @import("behavior/const_slice_child.zig");
97 _ = @import("behavior/defer.zig");104 _ = @import("behavior/defer.zig");
98 _ = @import("behavior/enum.zig");105 _ = @import("behavior/enum.zig");
...@@ -113,7 +120,7 @@ test {...@@ -113,7 +120,7 @@ test {
113 _ = @import("behavior/incomplete_struct_param_tld.zig");120 _ = @import("behavior/incomplete_struct_param_tld.zig");
114 _ = @import("behavior/inttoptr.zig");121 _ = @import("behavior/inttoptr.zig");
115 _ = @import("behavior/ir_block_deps.zig");122 _ = @import("behavior/ir_block_deps.zig");
116 _ = @import("behavior/math.zig");123 _ = @import("behavior/math_stage1.zig");
117 _ = @import("behavior/maximum_minimum.zig");124 _ = @import("behavior/maximum_minimum.zig");
118 _ = @import("behavior/merge_error_sets.zig");125 _ = @import("behavior/merge_error_sets.zig");
119 _ = @import("behavior/misc.zig");126 _ = @import("behavior/misc.zig");
...@@ -130,16 +137,15 @@ test {...@@ -130,16 +137,15 @@ test {
130 _ = @import("behavior/saturating_arithmetic.zig");137 _ = @import("behavior/saturating_arithmetic.zig");
131 _ = @import("behavior/shuffle.zig");138 _ = @import("behavior/shuffle.zig");
132 _ = @import("behavior/select.zig");139 _ = @import("behavior/select.zig");
133 _ = @import("behavior/sizeof_and_typeof.zig");140 _ = @import("behavior/sizeof_and_typeof_stage1.zig");
134 _ = @import("behavior/slice.zig");141 _ = @import("behavior/slice.zig");
135 _ = @import("behavior/slice_sentinel_comptime.zig");142 _ = @import("behavior/slice_sentinel_comptime.zig");
136 _ = @import("behavior/struct.zig");143 _ = @import("behavior/struct_stage1.zig");
137 _ = @import("behavior/struct_contains_null_ptr_itself.zig");144 _ = @import("behavior/struct_contains_null_ptr_itself.zig");
138 _ = @import("behavior/struct_contains_slice_of_itself.zig");145 _ = @import("behavior/struct_contains_slice_of_itself.zig");
139 _ = @import("behavior/switch.zig");146 _ = @import("behavior/switch.zig");
140 _ = @import("behavior/switch_prong_err_enum.zig");147 _ = @import("behavior/switch_prong_err_enum.zig");
141 _ = @import("behavior/switch_prong_implicit_cast.zig");148 _ = @import("behavior/switch_prong_implicit_cast.zig");
142 _ = @import("behavior/this.zig");
143 _ = @import("behavior/truncate.zig");149 _ = @import("behavior/truncate.zig");
144 _ = @import("behavior/try.zig");150 _ = @import("behavior/try.zig");
145 _ = @import("behavior/tuple.zig");151 _ = @import("behavior/tuple.zig");
...@@ -148,7 +154,7 @@ test {...@@ -148,7 +154,7 @@ test {
148 _ = @import("behavior/typename.zig");154 _ = @import("behavior/typename.zig");
149 _ = @import("behavior/undefined.zig");155 _ = @import("behavior/undefined.zig");
150 _ = @import("behavior/underscore.zig");156 _ = @import("behavior/underscore.zig");
151 _ = @import("behavior/union.zig");157 _ = @import("behavior/union_stage1.zig");
152 _ = @import("behavior/usingnamespace_stage1.zig");158 _ = @import("behavior/usingnamespace_stage1.zig");
153 _ = @import("behavior/var_args.zig");159 _ = @import("behavior/var_args.zig");
154 _ = @import("behavior/vector.zig");160 _ = @import("behavior/vector.zig");
...@@ -157,8 +163,7 @@ test {...@@ -157,8 +163,7 @@ test {
157 _ = @import("behavior/wasm.zig");163 _ = @import("behavior/wasm.zig");
158 }164 }
159 _ = @import("behavior/while.zig");165 _ = @import("behavior/while.zig");
160 _ = @import("behavior/widening.zig");
161 _ = @import("behavior/src.zig");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,3 +27,9 @@ test "arrays" {
27fn getArrayLen(a: []const u32) usize {27fn getArrayLen(a: []const u32) usize {
28 return a.len;28 return a.len;
29}29}
30
31test "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,7 +140,7 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {
140 return b[0];140 return b[0];
141}141}
142142
143test "comptime evalutating function that takes array by value" {143test "comptime evaluating function that takes array by value" {
144 const arr = [_]u8{ 0, 1 };144 const arr = [_]u8{ 0, 1 };
145 _ = comptime testArrayByValAtComptime(arr);145 _ = comptime testArrayByValAtComptime(arr);
146 _ = comptime testArrayByValAtComptime(arr);146 _ = comptime testArrayByValAtComptime(arr);
test/behavior/atomics.zig+195
...@@ -24,3 +24,198 @@ fn testCmpxchg() !void {...@@ -24,3 +24,198 @@ fn testCmpxchg() !void {
24 try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null);24 try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null);
25 try expect(x == 42);25 try expect(x == 42);
26}26}
27
28test "fence" {
29 var x: i32 = 1234;
30 @fence(.SeqCst);
31 x = 5678;
32}
33
34test "atomicrmw and atomicload" {
35 var data: u8 = 200;
36 try testAtomicRmw(&data);
37 try expect(data == 42);
38 try testAtomicLoad(&data);
39}
40
41fn 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
52fn testAtomicLoad(ptr: *u8) !void {
53 const x = @atomicLoad(u8, ptr, .SeqCst);
54 try expect(x == 42);
55}
56
57test "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
77test "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
85test "128-bit cmpxchg" {
86 try test_u128_cmpxchg();
87 comptime try test_u128_cmpxchg();
88}
89
90fn 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
115var a_global_variable = @as(u32, 1234);
116
117test "cmpxchg on a global variable" {
118 _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic);
119 try expect(a_global_variable == 42);
120}
121
122test "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
134test "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
142test "atomic store comptime" {
143 comptime try testAtomicStore();
144 try testAtomicStore();
145}
146
147fn 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
155test "atomicrmw with floats" {
156 try testAtomicRmwFloat();
157 comptime try testAtomicRmwFloat();
158}
159
160fn 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
171test "atomicrmw with ints" {
172 try testAtomicRmwInt();
173 comptime try testAtomicRmwInt();
174}
175
176fn 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
199test "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
212fn 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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4const builtin = @import("builtin");
5
6test "fence" {
7 var x: i32 = 1234;
8 @fence(.SeqCst);
9 x = 5678;
10}
11
12test "atomicrmw and atomicload" {
13 var data: u8 = 200;
14 try testAtomicRmw(&data);
15 try expect(data == 42);
16 try testAtomicLoad(&data);
17}
18
19fn 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
30fn testAtomicLoad(ptr: *u8) !void {
31 const x = @atomicLoad(u8, ptr, .SeqCst);
32 try expect(x == 42);
33}
34
35test "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
55test "128-bit cmpxchg" {
56 try test_u128_cmpxchg();
57 comptime try test_u128_cmpxchg();
58}
59
60fn 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
80test "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
88var a_global_variable = @as(u32, 1234);
89
90test "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
95test "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
111test "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
119test "atomic store comptime" {
120 comptime try testAtomicStore();
121 try testAtomicStore();
122}
123
124fn 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
132test "atomicrmw with floats" {
133 try testAtomicRmwFloat();
134 comptime try testAtomicRmwFloat();
135}
136
137fn 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
148test "atomicrmw with ints" {
149 try testAtomicRmwInt();
150 comptime try testAtomicRmwInt();
151}
152
153fn 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
176test "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
185fn 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,3 +170,21 @@ test "string concatenation" {
170test "array mult operator" {170test "array mult operator" {
171 try expect(mem.eql(u8, "ab" ** 5, "ababababab"));171 try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
172}172}
173
174test "memcpy and memset intrinsics" {
175 try testMemcpyMemset();
176 // TODO add comptime test coverage
177 //comptime try testMemcpyMemset();
178}
179
180fn 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,7 +40,7 @@ const a = struct {
40 }40 }
41};41};
4242
43test "intialization" {43test "initialization" {
44 var t = a.init();44 var t = a.init();
45 try std.testing.expect(t.foo.len == 0);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,3 +155,31 @@ fn MakeType(comptime T: type) type {
155 field: T,155 field: T,
156 };156 };
157}157}
158
159test "try to trick eval with runtime if" {
160 try expect(testTryToTrickEvalWithRuntimeIf(true) == 10);
161}
162
163fn 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
174test "@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,7 +88,7 @@ var st_init_str_foo = StInitStrFoo{
88 .y = true,88 .y = true,
89};89};
9090
91test "statically initalized array literal" {91test "statically initialized array literal" {
92 const y: [4]u8 = st_init_arr_lit_x;92 const y: [4]u8 = st_init_arr_lit_x;
93 try expect(y[3] == 4);93 try expect(y[3] == 4);
94}94}
...@@ -109,21 +109,6 @@ test "const slice" {...@@ -109,21 +109,6 @@ test "const slice" {
109 }109 }
110}110}
111111
112test "try to trick eval with runtime if" {
113 try expect(testTryToTrickEvalWithRuntimeIf(true) == 10);
114}
115
116fn 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
127test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" {112test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" {
128 var runtime = [1]i32{3};113 var runtime = [1]i32{3};
129 comptime var i: usize = 0;114 comptime var i: usize = 0;
...@@ -276,19 +261,6 @@ fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void {...@@ -276,19 +261,6 @@ fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void {
276 try expect(ptr1 == ptr2);261 try expect(ptr1 == ptr2);
277}262}
278263
279test "@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
292test "float literal at compile time not lossy" {264test "float literal at compile time not lossy" {
293 try expect(16777216.0 + 1.0 == 16777217.0);265 try expect(16777216.0 + 1.0 == 16777217.0);
294 try expect(9007199254740992.0 + 1.0 == 9007199254740993.0);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,3 +118,19 @@ pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type {
118 prealloc_items: [STATIC_SIZE]T,118 prealloc_items: [STATIC_SIZE]T,
119 };119 };
120}120}
121
122test "const decls in struct" {
123 try expect(GenericDataThing(3).count_plus_one == 4);
124}
125fn GenericDataThing(comptime count: isize) type {
126 return struct {
127 const count_plus_one = count + 1;
128 };
129}
130
131test "use generic param in generic param" {
132 try expect(aGenericFn(i32, 3, 4) == 7);
133}
134fn 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,22 +26,6 @@ fn GenNode(comptime T: type) type {
26 };26 };
27}27}
2828
29test "const decls in struct" {
30 try expect(GenericDataThing(3).count_plus_one == 4);
31}
32fn GenericDataThing(comptime count: isize) type {
33 return struct {
34 const count_plus_one = count + 1;
35 };
36}
37
38test "use generic param in generic param" {
39 try expect(aGenericFn(i32, 3, 4) == 7);
40}
41fn aGenericFn(comptime T: type, comptime a: T, b: T) T {
42 return a + b;
43}
44
45test "generic fn with implicit cast" {29test "generic fn with implicit cast" {
46 try expect(getFirstByte(u8, &[_]u8{13}) == 13);30 try expect(getFirstByte(u8, &[_]u8{13}) == 13);
47 try expect(getFirstByte(u16, &[_]u16{31 try expect(getFirstByte(u16, &[_]u16{
test/behavior/math.zig+33-699
...@@ -6,171 +6,6 @@ const maxInt = std.math.maxInt;...@@ -6,171 +6,6 @@ const maxInt = std.math.maxInt;
6const minInt = std.math.minInt;6const minInt = std.math.minInt;
7const mem = std.mem;7const mem = std.mem;
88
9test "division" {
10 try testDivision();
11 comptime try testDivision();
12}
13fn 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}
79fn div(comptime T: type, a: T, b: T) T {
80 return a / b;
81}
82fn divExact(comptime T: type, a: T, b: T) T {
83 return @divExact(a, b);
84}
85fn divFloor(comptime T: type, a: T, b: T) T {
86 return @divFloor(a, b);
87}
88fn divTrunc(comptime T: type, a: T, b: T) T {
89 return @divTrunc(a, b);
90}
91fn mod(comptime T: type, a: T, b: T) T {
92 return @mod(a, b);
93}
94
95test "@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
105test "@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
112test "@*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
120test "@clz" {
121 try testClz();
122 comptime try testClz();
123}
124
125fn 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
134test "@clz vectors" {
135 try testClzVectors();
136 comptime try testClzVectors();
137}
138
139fn 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
149test "@ctz" {
150 try testCtz();
151 comptime try testCtz();
152}
153
154fn 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
161test "@ctz vectors" {
162 try testClzVectors();
163 comptime try testClzVectors();
164}
165
166fn 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
174test "assignment operators" {9test "assignment operators" {
175 var i: u32 = 0;10 var i: u32 = 0;
176 i += 5;11 i += 5;
...@@ -219,108 +54,31 @@ fn assertFalse(b: bool) !void {...@@ -219,108 +54,31 @@ fn assertFalse(b: bool) !void {
219 try expect(!b);54 try expect(!b);
220}55}
22156
222test "const number literal" {57test "@clz" {
223 const one = 1;58 try testClz();
224 const eleven = ten + one;59 comptime try testClz();
225
226 try expect(eleven == 11);
227}
228const ten = 10;
229
230test "unsigned wrapping" {
231 try testUnsignedWrappingEval(maxInt(u32));
232 comptime try testUnsignedWrappingEval(maxInt(u32));
233}
234fn 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
241test "signed wrapping" {
242 try testSignedWrappingEval(maxInt(i32));
243 comptime try testSignedWrappingEval(maxInt(i32));
244}
245fn 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
252test "signed negation wrapping" {
253 try testSignedNegationWrappingEval(minInt(i16));
254 comptime try testSignedNegationWrappingEval(minInt(i16));
255}
256fn testSignedNegationWrappingEval(x: i16) !void {
257 try expect(x == -32768);
258 const neg = -%x;
259 try expect(neg == -32768);
260}
261
262test "unsigned negation wrapping" {
263 try testUnsignedNegationWrappingEval(1);
264 comptime try testUnsignedNegationWrappingEval(1);
265}
266fn testUnsignedNegationWrappingEval(x: u16) !void {
267 try expect(x == 1);
268 const neg = -%x;
269 try expect(neg == maxInt(u16));
270}60}
27161
272test "unsigned 64-bit division" {62fn testClz() !void {
273 try test_u64_div();63 try expect(testOneClz(u8, 0b10001010) == 0);
274 comptime try test_u64_div();64 try expect(testOneClz(u8, 0b00001010) == 4);
275}65 try expect(testOneClz(u8, 0b00011010) == 3);
276fn test_u64_div() !void {66 try expect(testOneClz(u8, 0b00000000) == 8);
277 const result = divWithResult(1152921504606846976, 34359738365);67 try expect(testOneClz(u128, 0xffffffffffffffff) == 64);
278 try expect(result.quotient == 33554432);68 try expect(testOneClz(u128, 0x10000000000000000) == 63);
279 try expect(result.remainder == 100663296);
280}
281fn divWithResult(a: u64, b: u64) DivResult {
282 return DivResult{
283 .quotient = a / b,
284 .remainder = a % b,
285 };
286}
287const DivResult = struct {
288 quotient: u64,
289 remainder: u64,
290};
291
292test "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);
300}69}
30170
302fn testBinaryNot(x: u16) !void {71fn testOneClz(comptime T: type, x: T) u32 {
303 try expect(~x == 0b0101010101010101);72 return @clz(T, x);
304}73}
30574
306test "small int addition" {75test "const number literal" {
307 var x: u2 = 0;76 const one = 1;
308 try expect(x == 0);77 const eleven = ten + one;
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));
32178
322 try expect(result == 0);79 try expect(eleven == 11);
323}80}
81const ten = 10;
32482
325test "float equality" {83test "float equality" {
326 const x: f64 = 0.012;84 const x: f64 = 0.012;
...@@ -335,15 +93,6 @@ fn testFloatEqualityImpl(x: f64, y: f64) !void {...@@ -335,15 +93,6 @@ fn testFloatEqualityImpl(x: f64, y: f64) !void {
335 try expect(y == y2);93 try expect(y == y2);
336}94}
33795
338test "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
347test "hex float literal parsing" {96test "hex float literal parsing" {
348 comptime try expect(0x1.0 == 1.0);97 comptime try expect(0x1.0 == 1.0);
349}98}
...@@ -353,102 +102,10 @@ test "quad hex float literal parsing in range" {...@@ -353,102 +102,10 @@ test "quad hex float literal parsing in range" {
353 const b = 0x1.dedafcff354b6ae9758763545432p-9;102 const b = 0x1.dedafcff354b6ae9758763545432p-9;
354 const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534;103 const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534;
355 const d = 0x1.edcbff8ad76ab5bf46463233214fp-435;104 const d = 0x1.edcbff8ad76ab5bf46463233214fp-435;
356 if (false) {105 _ = a;
357 a;106 _ = b;
358 b;107 _ = c;
359 c;108 _ = d;
360 d;
361 }
362}
363
364test "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();
452}109}
453110
454test "underscore separator parsing" {111test "underscore separator parsing" {
...@@ -483,66 +140,9 @@ test "hex float literal within range" {...@@ -483,66 +140,9 @@ test "hex float literal within range" {
483 const a = 0x1.0p16383;140 const a = 0x1.0p16383;
484 const b = 0x0.1p16387;141 const b = 0x0.1p16387;
485 const c = 0x1.0p-16382;142 const c = 0x1.0p-16382;
486 if (false) {143 _ = a;
487 a;144 _ = b;
488 b;145 _ = c;
489 c;
490 }
491}
492
493test "truncating shift left" {
494 try testShlTrunc(maxInt(u16));
495 comptime try testShlTrunc(maxInt(u16));
496}
497fn testShlTrunc(x: u16) !void {
498 const shifted = x << 1;
499 try expect(shifted == 65534);
500}
501
502test "truncating shift right" {
503 try testShrTrunc(maxInt(u16));
504 comptime try testShrTrunc(maxInt(u16));
505}
506fn testShrTrunc(x: u16) !void {
507 const shifted = x >> 1;
508 try expect(shifted == 32767);
509}
510
511test "exact shift left" {
512 try testShlExact(0b00110101);
513 comptime try testShlExact(0b00110101);
514}
515fn testShlExact(x: u8) !void {
516 const shifted = @shlExact(x, 2);
517 try expect(shifted == 0b11010100);
518}
519
520test "exact shift right" {
521 try testShrExact(0b10110100);
522 comptime try testShrExact(0b10110100);
523}
524fn testShrExact(x: u8) !void {
525 const shifted = @shrExact(x, 2);
526 try expect(shifted == 0b00101101);
527}
528
529test "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();
546}146}
547147
548test "comptime_int addition" {148test "comptime_int addition" {
...@@ -600,11 +200,15 @@ test "xor" {...@@ -600,11 +200,15 @@ test "xor" {
600}200}
601201
602fn test_xor() !void {202fn test_xor() !void {
603 try expect(0xFF ^ 0x00 == 0xFF);203 try testOneXor(0xFF, 0x00, 0xFF);
604 try expect(0xF0 ^ 0x0F == 0xFF);204 try testOneXor(0xF0, 0x0F, 0xFF);
605 try expect(0xFF ^ 0xF0 == 0x0F);205 try testOneXor(0xFF, 0xF0, 0x0F);
606 try expect(0xFF ^ 0x0F == 0xF0);206 try testOneXor(0xFF, 0x0F, 0xF0);
607 try expect(0xFF ^ 0xFF == 0x00);207 try testOneXor(0xFF, 0xFF, 0x00);
208}
209
210fn testOneXor(a: u8, b: u8, c: u8) !void {
211 try expect(a ^ b == c);
608}212}
609213
610test "comptime_int xor" {214test "comptime_int xor" {
...@@ -620,191 +224,6 @@ test "comptime_int xor" {...@@ -620,191 +224,6 @@ test "comptime_int xor" {
620 }224 }
621}225}
622226
623test "f128" {
624 try test_f128();
625 comptime try test_f128();
626}
627
628fn make_f128(x: f128) f128 {
629 return x;
630}
631
632fn 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
642fn should_not_be_zero(x: f128) !void {
643 try expect(x != 0.0);
644}
645
646test "comptime float rem int" {
647 comptime {
648 var x = @as(f32, 1) % 2;
649 try expect(x == 1.0);
650 }
651}
652
653test "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
663fn 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
668test "@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
682fn testSqrt(comptime T: type, x: T) !void {
683 try expect(@sqrt(x * x) == x);
684}
685
686test "@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
702fn testFabs(comptime T: type, x: T) !void {
703 const y = -x;
704 const z = @fabs(y);
705 try expectEqual(x, z);
706}
707
708test "@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
725fn 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
731test "@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
748fn 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
754test "@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
771fn 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
785test "@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
802fn 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
808test "comptime_int param and return" {227test "comptime_int param and return" {
809 const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);228 const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);
810 try expect(a == 137114567242441932203689521744947848950);229 try expect(a == 137114567242441932203689521744947848950);
...@@ -816,88 +235,3 @@ test "comptime_int param and return" {...@@ -816,88 +235,3 @@ test "comptime_int param and return" {
816fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {235fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
817 return a + b;236 return a + b;
818}237}
819
820test "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
835test "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
846fn 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
857test "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
864test "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
881test "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
888test "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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4const expectEqualSlices = std.testing.expectEqualSlices;
5const maxInt = std.math.maxInt;
6const minInt = std.math.minInt;
7const mem = std.mem;
8
9test "division" {
10 try testDivision();
11 comptime try testDivision();
12}
13fn 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}
79fn div(comptime T: type, a: T, b: T) T {
80 return a / b;
81}
82fn divExact(comptime T: type, a: T, b: T) T {
83 return @divExact(a, b);
84}
85fn divFloor(comptime T: type, a: T, b: T) T {
86 return @divFloor(a, b);
87}
88fn divTrunc(comptime T: type, a: T, b: T) T {
89 return @divTrunc(a, b);
90}
91fn mod(comptime T: type, a: T, b: T) T {
92 return @mod(a, b);
93}
94
95test "@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
105test "@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
112test "@*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
120test "@clz vectors" {
121 try testClzVectors();
122 comptime try testClzVectors();
123}
124
125fn 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
135test "@ctz" {
136 try testCtz();
137 comptime try testCtz();
138}
139
140fn 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
147test "@ctz vectors" {
148 try testClzVectors();
149 comptime try testClzVectors();
150}
151
152fn 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
160test "unsigned wrapping" {
161 try testUnsignedWrappingEval(maxInt(u32));
162 comptime try testUnsignedWrappingEval(maxInt(u32));
163}
164fn 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
171test "signed wrapping" {
172 try testSignedWrappingEval(maxInt(i32));
173 comptime try testSignedWrappingEval(maxInt(i32));
174}
175fn 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
182test "signed negation wrapping" {
183 try testSignedNegationWrappingEval(minInt(i16));
184 comptime try testSignedNegationWrappingEval(minInt(i16));
185}
186fn testSignedNegationWrappingEval(x: i16) !void {
187 try expect(x == -32768);
188 const neg = -%x;
189 try expect(neg == -32768);
190}
191
192test "unsigned negation wrapping" {
193 try testUnsignedNegationWrappingEval(1);
194 comptime try testUnsignedNegationWrappingEval(1);
195}
196fn testUnsignedNegationWrappingEval(x: u16) !void {
197 try expect(x == 1);
198 const neg = -%x;
199 try expect(neg == maxInt(u16));
200}
201
202test "unsigned 64-bit division" {
203 try test_u64_div();
204 comptime try test_u64_div();
205}
206fn test_u64_div() !void {
207 const result = divWithResult(1152921504606846976, 34359738365);
208 try expect(result.quotient == 33554432);
209 try expect(result.remainder == 100663296);
210}
211fn divWithResult(a: u64, b: u64) DivResult {
212 return DivResult{
213 .quotient = a / b,
214 .remainder = a % b,
215 };
216}
217const DivResult = struct {
218 quotient: u64,
219 remainder: u64,
220};
221
222test "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
232fn testBinaryNot(x: u16) !void {
233 try expect(~x == 0b0101010101010101);
234}
235
236test "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
255test "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
264test "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
354test "truncating shift left" {
355 try testShlTrunc(maxInt(u16));
356 comptime try testShlTrunc(maxInt(u16));
357}
358fn testShlTrunc(x: u16) !void {
359 const shifted = x << 1;
360 try expect(shifted == 65534);
361}
362
363test "truncating shift right" {
364 try testShrTrunc(maxInt(u16));
365 comptime try testShrTrunc(maxInt(u16));
366}
367fn testShrTrunc(x: u16) !void {
368 const shifted = x >> 1;
369 try expect(shifted == 32767);
370}
371
372test "exact shift left" {
373 try testShlExact(0b00110101);
374 comptime try testShlExact(0b00110101);
375}
376fn testShlExact(x: u8) !void {
377 const shifted = @shlExact(x, 2);
378 try expect(shifted == 0b11010100);
379}
380
381test "exact shift right" {
382 try testShrExact(0b10110100);
383 comptime try testShrExact(0b10110100);
384}
385fn testShrExact(x: u8) !void {
386 const shifted = @shrExact(x, 2);
387 try expect(shifted == 0b00101101);
388}
389
390test "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
409test "f128" {
410 try test_f128();
411 comptime try test_f128();
412}
413
414fn make_f128(x: f128) f128 {
415 return x;
416}
417
418fn 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
428fn should_not_be_zero(x: f128) !void {
429 try expect(x != 0.0);
430}
431
432test "comptime float rem int" {
433 comptime {
434 var x = @as(f32, 1) % 2;
435 try expect(x == 1.0);
436 }
437}
438
439test "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
449fn 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
454test "@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
468fn testSqrt(comptime T: type, x: T) !void {
469 try expect(@sqrt(x * x) == x);
470}
471
472test "@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
488fn testFabs(comptime T: type, x: T) !void {
489 const y = -x;
490 const z = @fabs(y);
491 try expectEqual(x, z);
492}
493
494test "@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
511fn 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
517test "@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
534fn 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
540test "@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
557fn 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
571test "@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
588fn 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
594test "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
609test "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
620fn 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
631test "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
638test "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
655test "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
662test "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 @@
1const expect = @import("std").testing.expect;
2
3const 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
27test "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
66test "@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,16 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings;
5const mem = std.mem;5const mem = std.mem;
6const builtin = @import("builtin");6const builtin = @import("builtin");
77
8test "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
18test "slicing" {8test "slicing" {
19 var array: [20]i32 = undefined;9 var array: [20]i32 = undefined;
2010
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,16 +11,28 @@ fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void {
11 const a = test_data[0];11 const a = test_data[0];
12 const b = test_data[1];12 const b = test_data[1];
13 const expected = test_data[2];13 const expected = test_data[2];
14 const actual = switch (op) {14 {
15 .add => @addWithSaturation(a, b),15 const actual = switch (op) {
16 .sub => @subWithSaturation(a, b),16 .add => a +| b,
17 .mul => @mulWithSaturation(a, b),17 .sub => a -| b,
18 .shl => @shlWithSaturation(a, b),18 .mul => a *| b,
19 };19 .shl => a <<| b,
20 try expectEqual(expected, actual);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}
2234
23test "@addWithSaturation" {35test "saturating add" {
24 const S = struct {36 const S = struct {
25 fn doTheTest() !void {37 fn doTheTest() !void {
26 // .{a, b, expected a+b}38 // .{a, b, expected a+b}
...@@ -38,22 +50,16 @@ test "@addWithSaturation" {...@@ -38,22 +50,16 @@ test "@addWithSaturation" {
38 try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) });50 try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) });
3951
40 const u8x3 = std.meta.Vector(3, u8);52 const u8x3 = std.meta.Vector(3, u8);
41 try expectEqual(u8x3{ 255, 255, 255 }, @addWithSaturation(53 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 }));
42 u8x3{ 255, 254, 1 },
43 u8x3{ 1, 2, 255 },
44 ));
45 const i8x3 = std.meta.Vector(3, i8);54 const i8x3 = std.meta.Vector(3, i8);
46 try expectEqual(i8x3{ 127, 127, 127 }, @addWithSaturation(55 try expectEqual(i8x3{ 127, 127, 127 }, (i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 }));
47 i8x3{ 127, 126, 1 },
48 i8x3{ 1, 2, 127 },
49 ));
50 }56 }
51 };57 };
52 try S.doTheTest();58 try S.doTheTest();
53 comptime try S.doTheTest();59 comptime try S.doTheTest();
54}60}
5561
56test "@subWithSaturation" {62test "saturating subtraction" {
57 const S = struct {63 const S = struct {
58 fn doTheTest() !void {64 fn doTheTest() !void {
59 // .{a, b, expected a-b}65 // .{a, b, expected a-b}
...@@ -69,17 +75,14 @@ test "@subWithSaturation" {...@@ -69,17 +75,14 @@ test "@subWithSaturation" {
69 try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 });75 try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 });
7076
71 const u8x3 = std.meta.Vector(3, u8);77 const u8x3 = std.meta.Vector(3, u8);
72 try expectEqual(u8x3{ 0, 0, 0 }, @subWithSaturation(78 try expectEqual(u8x3{ 0, 0, 0 }, (u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 }));
73 u8x3{ 0, 0, 0 },
74 u8x3{ 255, 255, 255 },
75 ));
76 }79 }
77 };80 };
78 try S.doTheTest();81 try S.doTheTest();
79 comptime try S.doTheTest();82 comptime try S.doTheTest();
80}83}
8184
82test "@mulWithSaturation" {85test "saturating multiplication" {
83 // TODO: once #9660 has been solved, remove this line86 // TODO: once #9660 has been solved, remove this line
84 if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;87 if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;
8588
...@@ -100,10 +103,7 @@ test "@mulWithSaturation" {...@@ -100,10 +103,7 @@ test "@mulWithSaturation" {
100 try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) });103 try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) });
101104
102 const u8x3 = std.meta.Vector(3, u8);105 const u8x3 = std.meta.Vector(3, u8);
103 try expectEqual(u8x3{ 255, 255, 255 }, @mulWithSaturation(106 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 }));
104 u8x3{ 2, 2, 2 },
105 u8x3{ 255, 255, 255 },
106 ));
107 }107 }
108 };108 };
109109
...@@ -111,7 +111,7 @@ test "@mulWithSaturation" {...@@ -111,7 +111,7 @@ test "@mulWithSaturation" {
111 comptime try S.doTheTest();111 comptime try S.doTheTest();
112}112}
113113
114test "@shlWithSaturation" {114test "saturating shift-left" {
115 const S = struct {115 const S = struct {
116 fn doTheTest() !void {116 fn doTheTest() !void {
117 // .{a, b, expected a<<b}117 // .{a, b, expected a<<b}
...@@ -128,10 +128,7 @@ test "@shlWithSaturation" {...@@ -128,10 +128,7 @@ test "@shlWithSaturation" {
128 try testSaturatingOp(.shl, u8, .{ 255, 1, 255 });128 try testSaturatingOp(.shl, u8, .{ 255, 1, 255 });
129129
130 const u8x3 = std.meta.Vector(3, u8);130 const u8x3 = std.meta.Vector(3, u8);
131 try expectEqual(u8x3{ 255, 255, 255 }, @shlWithSaturation(131 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 }));
132 u8x3{ 255, 255, 255 },
133 u8x3{ 1, 1, 1 },
134 ));
135 }132 }
136 };133 };
137 try S.doTheTest();134 try S.doTheTest();
test/behavior/sizeof_and_typeof.zig-214
...@@ -10,118 +10,6 @@ test "@sizeOf and @TypeOf" {...@@ -10,118 +10,6 @@ test "@sizeOf and @TypeOf" {
10const x: u16 = 13;10const x: u16 = 13;
11const z: @TypeOf(x) = 19;11const z: @TypeOf(x) = 19;
1212
13const 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
25const 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
37test "@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
62test "@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
106test "@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
125test "@sizeOf on compile-time types" {13test "@sizeOf on compile-time types" {
126 try expect(@sizeOf(comptime_int) == 0);14 try expect(@sizeOf(comptime_int) == 0);
127 try expect(@sizeOf(comptime_float) == 0);15 try expect(@sizeOf(comptime_float) == 0);
...@@ -129,34 +17,6 @@ test "@sizeOf on compile-time types" {...@@ -129,34 +17,6 @@ test "@sizeOf on compile-time types" {
129 try expect(@sizeOf(@TypeOf(type)) == 0);17 try expect(@sizeOf(@TypeOf(type)) == 0);
130}18}
13119
132test "@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
147test "@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
160test "@TypeOf() with multiple arguments" {20test "@TypeOf() with multiple arguments" {
161 {21 {
162 var var_1: u32 = undefined;22 var var_1: u32 = undefined;
...@@ -180,19 +40,6 @@ test "@TypeOf() with multiple arguments" {...@@ -180,19 +40,6 @@ test "@TypeOf() with multiple arguments" {
180 }40 }
181}41}
18242
183test "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
196fn fn1(alpha: bool) void {43fn fn1(alpha: bool) void {
197 const n: usize = 7;44 const n: usize = 7;
198 _ = if (alpha) n else @sizeOf(usize);45 _ = if (alpha) n else @sizeOf(usize);
...@@ -201,64 +48,3 @@ fn fn1(alpha: bool) void {...@@ -201,64 +48,3 @@ fn fn1(alpha: bool) void {
201test "lazy @sizeOf result is checked for definedness" {48test "lazy @sizeOf result is checked for definedness" {
202 _ = fn1;49 _ = fn1;
203}50}
204
205test "@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
216test "@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 @@
1const std = @import("std");
2const builtin = std.builtin;
3const expect = std.testing.expect;
4const expectEqual = std.testing.expectEqual;
5
6const 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
18const 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
30test "@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
55test "@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
99test "@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
118test "@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
133test "@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
146test "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
159test "@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
170test "@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,56 +5,38 @@ const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;5const expectEqual = std.testing.expectEqual;
6const expectEqualSlices = std.testing.expectEqualSlices;6const expectEqualSlices = std.testing.expectEqualSlices;
7const maxInt = std.math.maxInt;7const maxInt = std.math.maxInt;
8
8const StructWithNoFields = struct {9const StructWithNoFields = struct {
9 fn add(a: i32, b: i32) i32 {10 fn add(a: i32, b: i32) i32 {
10 return a + b;11 return a + b;
11 }12 }
12};13};
13const empty_global_instance = StructWithNoFields{};
14
15top_level_field: i32,
16
17test "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}
2414
25test "call struct static method" {15test "call struct static method" {
26 const result = StructWithNoFields.add(3, 4);16 const result = StructWithNoFields.add(3, 4);
27 try expect(result == 7);17 try expect(result == 7);
28}18}
2919
30test "return empty struct instance" {
31 _ = returnEmptyStructInstance();
32}
33fn returnEmptyStructInstance() StructWithNoFields {
34 return empty_global_instance;
35}
36
37const should_be_11 = StructWithNoFields.add(5, 6);20const should_be_11 = StructWithNoFields.add(5, 6);
3821
39test "invoke static method in global scope" {22test "invoke static method in global scope" {
40 try expect(should_be_11 == 11);23 try expect(should_be_11 == 11);
41}24}
4225
43test "void struct fields" {26const empty_global_instance = StructWithNoFields{};
44 const foo = VoidStructFieldsFoo{27
45 .a = void{},28test "return empty struct instance" {
46 .b = 1,29 _ = returnEmptyStructInstance();
47 .c = void{},30}
48 };31fn returnEmptyStructInstance() StructWithNoFields {
49 try expect(foo.b == 1);32 return empty_global_instance;
50 try expect(@sizeOf(VoidStructFieldsFoo) == 4);
51}33}
52const VoidStructFieldsFoo = struct {
53 a: void,
54 b: i32,
55 c: void,
56};
5734
35const StructFoo = struct {
36 a: i32,
37 b: bool,
38 c: f32,
39};
58test "structs" {40test "structs" {
59 var foo: StructFoo = undefined;41 var foo: StructFoo = undefined;
60 @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo));42 @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo));
...@@ -64,11 +46,6 @@ test "structs" {...@@ -64,11 +46,6 @@ test "structs" {
64 testMutation(&foo);46 testMutation(&foo);
65 try expect(foo.c == 100);47 try expect(foo.c == 100);
66}48}
67const StructFoo = struct {
68 a: i32,
69 b: bool,
70 c: f32,
71};
72fn testFoo(foo: StructFoo) !void {49fn testFoo(foo: StructFoo) !void {
73 try expect(foo.b);50 try expect(foo.b);
74}51}
...@@ -76,28 +53,6 @@ fn testMutation(foo: *StructFoo) void {...@@ -76,28 +53,6 @@ fn testMutation(foo: *StructFoo) void {
76 foo.c = 100;53 foo.c = 100;
77}54}
7855
79const Node = struct {
80 val: Val,
81 next: *Node,
82};
83
84const Val = struct {
85 x: i32,
86};
87
88test "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
101test "struct byval assign" {56test "struct byval assign" {
102 var foo1: StructFoo = undefined;57 var foo1: StructFoo = undefined;
103 var foo2: StructFoo = undefined;58 var foo2: StructFoo = undefined;
...@@ -109,34 +64,20 @@ test "struct byval assign" {...@@ -109,34 +64,20 @@ test "struct byval assign" {
109 try expect(foo2.a == 1234);64 try expect(foo2.a == 1234);
110}65}
11166
112fn structInitializer() void {67const Node = struct {
113 const val = Val{ .x = 42 };68 val: Val,
114 try expect(val.x == 42);69 next: *Node,
115}70};
116
117test "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 }
12571
126 fn callStructField(foo: Foo) i32 {72const Val = struct {
127 return foo.ptr();73 x: i32,
128 }74};
129 };
13075
131 try expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13);76test "struct initializer" {
77 const val = Val{ .x = 42 };
78 try expect(val.x == 42);
132}79}
13380
134test "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}
140const MemberFnTestFoo = struct {81const MemberFnTestFoo = struct {
141 x: i32,82 x: i32,
142 fn member(foo: MemberFnTestFoo) i32 {83 fn member(foo: MemberFnTestFoo) i32 {
...@@ -150,804 +91,41 @@ test "call member function directly" {...@@ -150,804 +91,41 @@ test "call member function directly" {
150 try expect(result == 1234);91 try expect(result == 1234);
151}92}
15293
153test "member functions" {94test "struct point to self" {
154 const r = MemberFnRand{ .seed = 1234 };95 var root: Node = undefined;
155 try expect(r.getSeed() == 1234);96 root.val.x = 1;
156}
157const MemberFnRand = struct {
158 seed: u32,
159 pub fn getSeed(r: *const MemberFnRand) u32 {
160 return r.seed;
161 }
162};
163
164test "return struct byval from function" {
165 const bar = makeBar2(1234, 5678);
166 try expect(bar.y == 5678);
167}
168const Bar = struct {
169 x: i32,
170 y: i32,
171};
172fn makeBar2(x: i32, y: i32) Bar {
173 return Bar{
174 .x = x,
175 .y = y,
176 };
177}
178
179test "empty struct method call" {
180 const es = EmptyStruct{};
181 try expect(es.method() == 1234);
182}
183const EmptyStruct = struct {
184 fn method(es: *const EmptyStruct) i32 {
185 _ = es;
186 return 1234;
187 }
188};
189
190test "return empty struct from fn" {
191 _ = testReturnEmptyStructFromFn();
192}
193const EmptyStruct2 = struct {};
194fn testReturnEmptyStructFromFn() EmptyStruct2 {
195 return EmptyStruct2{};
196}
197
198test "pass slice of empty struct to fn" {
199 try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1);
200}
201fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize {
202 return slice.len;
203}
204
205const APackedStruct = packed struct {
206 x: u8,
207 y: u8,
208};
209
210test "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
220const BitField1 = packed struct {
221 a: u3,
222 b: u3,
223 c: u2,
224};
225
226const bit_field_1 = BitField1{
227 .a = 1,
228 .b = 2,
229 .c = 3,
230};
231
232test "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
247fn getA(data: *const BitField1) u3 {
248 return data.a;
249}
250
251fn getB(data: *const BitField1) u3 {
252 return data.b;
253}
254
255fn getC(data: *const BitField1) u2 {
256 return data.c;
257}
258
259const Foo24Bits = packed struct {
260 field: u24,
261};
262const Foo96Bits = packed struct {
263 a: u24,
264 b: u24,
265 c: u24,
266 d: u24,
267};
268
269test "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
310const Foo32Bits = packed struct {
311 field: u24,
312 pad: u8,
313};
314
315const 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
322test "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
363const FooStructAligned = packed struct {
364 a: u8,
365 b: u8,
366};
367
368const FooArrayOfAligned = packed struct {
369 a: [2]FooStructAligned,
370};
37197
372test "aligned array of packed struct" {98 var node: Node = undefined;
373 comptime {99 node.next = &root;
374 try expect(@sizeOf(FooStructAligned) == 2);100 node.val.x = 2;
375 try expect(@sizeOf(FooArrayOfAligned) == 2 * 2);
376 }
377101
378 var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned);102 root.next = &node;
379 const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0];
380103
381 try expect(ptr.a[0].a == 0xbb);104 try expect(node.next.next.next.val.x == 1);
382 try expect(ptr.a[0].b == 0xbb);
383 try expect(ptr.a[1].a == 0xbb);
384 try expect(ptr.a[1].b == 0xbb);
385}105}
386106
387test "runtime struct initialization of bitfield" {107test "void struct fields" {
388 const s1 = Nibbles{108 const foo = VoidStructFieldsFoo{
389 .x = x1,109 .a = void{},
390 .y = x1,110 .b = 1,
391 };111 .c = void{},
392 const s2 = Nibbles{
393 .x = @intCast(u4, x2),
394 .y = @intCast(u4, x2),
395 };112 };
396113 try expect(foo.b == 1);
397 try expect(s1.x == x1);114 try expect(@sizeOf(VoidStructFieldsFoo) == 4);
398 try expect(s1.y == x1);
399 try expect(s2.x == @intCast(u4, x2));
400 try expect(s2.y == @intCast(u4, x2));
401}
402
403var x1 = @as(u4, 1);
404var x2 = @as(u8, 2);
405
406const Nibbles = packed struct {
407 x: u4,
408 y: u4,
409};
410
411const 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
421test "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
439test "align 1 field before self referential align 8 field as slice return type" {
440 const result = alloc(Expr);
441 try expect(result.len == 0);
442}115}
443116const VoidStructFieldsFoo = struct {
444const Expr = union(enum) {117 a: void,
445 Literal: u8,118 b: i32,
446 Question: *Expr,119 c: void,
447};120};
448121
449fn alloc(comptime T: type) []T {122test "member functions" {
450 return &[_]T{};123 const r = MemberFnRand{ .seed = 1234 };
451}124 try expect(r.getSeed() == 1234);
452
453test "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
472test "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
488test "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
501test "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
523test "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);
529}125}
530126const MemberFnRand = struct {
531const S0 = struct {127 seed: u32,
532 bar: S1,128 pub fn getSeed(r: *const MemberFnRand) u32 {
533129 return r.seed;
534 pub const S1 = struct {
535 value: u8,
536 };
537
538 fn init() @This() {
539 return S0{ .bar = S1{ .value = 123 } };
540 }130 }
541};131};
542
543var g_foo: S0 = S0.init();
544
545test "access to global struct fields" {
546 g_foo.bar.value = 42;
547 try expect(g_foo.bar.value == 42);
548}
549
550test "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
571test "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
582test "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
601test "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
622test "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
657test "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
666test "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
684test "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
696test "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
715test "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
735test "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
755test "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
777test "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
794test "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
801test "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
814test "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
824test "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
838test "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
847test "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
863test "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
896test "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
929test "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 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const native_endian = builtin.target.cpu.arch.endian();
4const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;
6const expectEqualSlices = std.testing.expectEqualSlices;
7const maxInt = std.math.maxInt;
8
9top_level_field: i32,
10
11test "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
19const StructFoo = struct {
20 a: i32,
21 b: bool,
22 c: f32,
23};
24
25const Node = struct {
26 val: Val,
27 next: *Node,
28};
29
30const Val = struct {
31 x: i32,
32};
33
34test "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
51const MemberFnTestFoo = struct {
52 x: i32,
53 fn member(foo: MemberFnTestFoo) i32 {
54 return foo.x;
55 }
56};
57test "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
64test "return struct byval from function" {
65 const bar = makeBar2(1234, 5678);
66 try expect(bar.y == 5678);
67}
68const Bar = struct {
69 x: i32,
70 y: i32,
71};
72fn makeBar2(x: i32, y: i32) Bar {
73 return Bar{
74 .x = x,
75 .y = y,
76 };
77}
78
79test "empty struct method call" {
80 const es = EmptyStruct{};
81 try expect(es.method() == 1234);
82}
83const EmptyStruct = struct {
84 fn method(es: *const EmptyStruct) i32 {
85 _ = es;
86 return 1234;
87 }
88};
89
90test "return empty struct from fn" {
91 _ = testReturnEmptyStructFromFn();
92}
93const EmptyStruct2 = struct {};
94fn testReturnEmptyStructFromFn() EmptyStruct2 {
95 return EmptyStruct2{};
96}
97
98test "pass slice of empty struct to fn" {
99 try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1);
100}
101fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize {
102 return slice.len;
103}
104
105const APackedStruct = packed struct {
106 x: u8,
107 y: u8,
108};
109
110test "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
120const BitField1 = packed struct {
121 a: u3,
122 b: u3,
123 c: u2,
124};
125
126const bit_field_1 = BitField1{
127 .a = 1,
128 .b = 2,
129 .c = 3,
130};
131
132test "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
147fn getA(data: *const BitField1) u3 {
148 return data.a;
149}
150
151fn getB(data: *const BitField1) u3 {
152 return data.b;
153}
154
155fn getC(data: *const BitField1) u2 {
156 return data.c;
157}
158
159const Foo24Bits = packed struct {
160 field: u24,
161};
162const Foo96Bits = packed struct {
163 a: u24,
164 b: u24,
165 c: u24,
166 d: u24,
167};
168
169test "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
210const Foo32Bits = packed struct {
211 field: u24,
212 pad: u8,
213};
214
215const 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
222test "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
263const FooStructAligned = packed struct {
264 a: u8,
265 b: u8,
266};
267
268const FooArrayOfAligned = packed struct {
269 a: [2]FooStructAligned,
270};
271
272test "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
287test "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
303var x1 = @as(u4, 1);
304var x2 = @as(u8, 2);
305
306const Nibbles = packed struct {
307 x: u4,
308 y: u4,
309};
310
311const 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
321test "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
339test "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
344const Expr = union(enum) {
345 Literal: u8,
346 Question: *Expr,
347};
348
349fn alloc(comptime T: type) []T {
350 return &[_]T{};
351}
352
353test "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
372test "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
388test "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
401test "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
423test "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
431const 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
443var g_foo: S0 = S0.init();
444
445test "access to global struct fields" {
446 g_foo.bar.value = 42;
447 try expect(g_foo.bar.value == 42);
448}
449
450test "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
471test "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
482test "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
501test "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
522test "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
557test "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
566test "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
584test "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
596test "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
615test "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
635test "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
655test "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
677test "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
694test "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
701test "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
714test "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
724test "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
738test "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
747test "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
763test "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
796test "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
829test "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,18 +65,18 @@ fn nonConstSwitchOnEnum(fruit: Fruit) void {
65}65}
6666
67test "switch statement" {67test "switch statement" {
68 try nonConstSwitch(SwitchStatmentFoo.C);68 try nonConstSwitch(SwitchStatementFoo.C);
69}69}
70fn nonConstSwitch(foo: SwitchStatmentFoo) !void {70fn nonConstSwitch(foo: SwitchStatementFoo) !void {
71 const val = switch (foo) {71 const val = switch (foo) {
72 SwitchStatmentFoo.A => @as(i32, 1),72 SwitchStatementFoo.A => @as(i32, 1),
73 SwitchStatmentFoo.B => 2,73 SwitchStatementFoo.B => 2,
74 SwitchStatmentFoo.C => 3,74 SwitchStatementFoo.C => 3,
75 SwitchStatmentFoo.D => 4,75 SwitchStatementFoo.D => 4,
76 };76 };
77 try expect(val == 3);77 try expect(val == 3);
78}78}
79const SwitchStatmentFoo = enum {79const SwitchStatementFoo = enum {
80 A,80 A,
81 B,81 B,
82 C,82 C,
test/behavior/this.zig+4-5
...@@ -24,11 +24,10 @@ test "this refer to module call private fn" {...@@ -24,11 +24,10 @@ test "this refer to module call private fn" {
24}24}
2525
26test "this refer to container" {26test "this refer to container" {
27 var pt = Point(i32){27 var pt: Point(i32) = undefined;
28 .x = 12,28 pt.x = 12;
29 .y = 34,29 pt.y = 34;
30 };30 Point(i32).addOne(&pt);
31 pt.addOne();
32 try expect(pt.x == 13);31 try expect(pt.x == 13);
33 try expect(pt.y == 35);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,28 +3,6 @@ const expectEqual = @import("std").testing.expectEqual;
33
4const h = @cImport(@cInclude("behavior/translate_c_macros.h"));4const h = @cImport(@cInclude("behavior/translate_c_macros.h"));
55
6test "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
15test "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
20test "reference to a struct type" {
21 try expectEqual(@sizeOf(h.struct_Foo), h.SIZE_OF_FOO);
22}
23
24test "cast negative integer to pointer" {
25 try expectEqual(@intToPtr(?*c_void, @bitCast(usize, @as(isize, -1))), h.MAP_FAILED);
26}
27
28test "casting to void with a macro" {6test "casting to void with a macro" {
29 h.IGNORE_ME_1(42);7 h.IGNORE_ME_1(42);
30 h.IGNORE_ME_2(42);8 h.IGNORE_ME_2(42);
test/behavior/translate_c_macros_stage1.zig created+26
...@@ -0,0 +1,26 @@
1const expect = @import("std").testing.expect;
2const expectEqual = @import("std").testing.expectEqual;
3
4const h = @cImport(@cInclude("behavior/translate_c_macros.h"));
5
6test "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
15test "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
20test "reference to a struct type" {
21 try expectEqual(@sizeOf(h.struct_Foo), h.SIZE_OF_FOO);
22}
23
24test "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,6 +137,7 @@ test "@Type create slice with null sentinel" {
137 .is_volatile = false,137 .is_volatile = false,
138 .is_allowzero = false,138 .is_allowzero = false,
139 .alignment = 8,139 .alignment = 8,
140 .address_space = .generic,
140 .child = *i32,141 .child = *i32,
141 .sentinel = null,142 .sentinel = null,
142 },143 },
test/behavior/union.zig-783
...@@ -3,42 +3,6 @@ const expect = std.testing.expect;...@@ -3,42 +3,6 @@ const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
4const Tag = std.meta.Tag;4const Tag = std.meta.Tag;
55
6const Value = union(enum) {
7 Int: u64,
8 Array: [9]u8,
9};
10
11const Agg = struct {
12 val1: Value,
13 val2: Value,
14};
15
16const v1 = Value{ .Int = 1234 };
17const v2 = Value{ .Array = [_]u8{3} ** 9 };
18
19const err = @as(anyerror!Agg, Agg{
20 .val1 = v1,
21 .val2 = v2,
22});
23
24const array = [_]Value{
25 v1,
26 v2,
27 v1,
28 v2,
29};
30
31test "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
42const Foo = union {6const Foo = union {
43 float: f64,7 float: f64,
44 int: i32,8 int: i32,
...@@ -51,16 +15,6 @@ test "basic unions" {...@@ -51,16 +15,6 @@ test "basic unions" {
51 try expect(foo.float == 12.34);15 try expect(foo.float == 12.34);
52}16}
5317
54test "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
64test "init union with runtime value" {18test "init union with runtime value" {
65 var foo: Foo = undefined;19 var foo: Foo = undefined;
6620
...@@ -78,740 +32,3 @@ fn setFloat(foo: *Foo, x: f64) void {...@@ -78,740 +32,3 @@ fn setFloat(foo: *Foo, x: f64) void {
78fn setInt(foo: *Foo, x: i32) void {32fn setInt(foo: *Foo, x: i32) void {
79 foo.* = Foo{ .int = x };33 foo.* = Foo{ .int = x };
80}34}
81
82const FooExtern = extern union {
83 float: f64,
84 int: i32,
85};
86
87test "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
94const Letter = enum {
95 A,
96 B,
97 C,
98};
99const Payload = union(Letter) {
100 A: i32,
101 B: f64,
102 C: bool,
103};
104
105test "union with specified enum tag" {
106 try doTest();
107 comptime try doTest();
108}
109
110fn doTest() error{TestUnexpectedResult}!void {
111 try expect((try bar(Payload{ .A = 1234 })) == -10);
112}
113
114fn 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
123const MultipleChoice = union(enum(u32)) {
124 A = 20,
125 B = 40,
126 C = 60,
127 D = 1000,
128};
129test "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
135const 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
147test "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
153fn 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
168const ExternPtrOrInt = extern union {
169 ptr: *u8,
170 int: u64,
171};
172test "extern union size" {
173 comptime try expect(@sizeOf(ExternPtrOrInt) == 8);
174}
175
176const PackedPtrOrInt = packed union {
177 ptr: *u8,
178 int: u64,
179};
180test "extern union size" {
181 comptime try expect(@sizeOf(PackedPtrOrInt) == 8);
182}
183
184const ZeroBits = union {
185 OnlyField: void,
186};
187test "union with only 1 field which is void should be zero bits" {
188 comptime try expect(@sizeOf(ZeroBits) == 0);
189}
190
191const TheTag = enum {
192 A,
193 B,
194 C,
195};
196const TheUnion = union(TheTag) {
197 A: i32,
198 B: i32,
199 C: i32,
200};
201test "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
207test "cast union to tag type of union" {
208 try testCastUnionToTag(TheUnion{ .B = 1234 });
209 comptime try testCastUnionToTag(TheUnion{ .B = 1234 });
210}
211
212fn testCastUnionToTag(x: TheUnion) !void {
213 try expect(@as(TheTag, x) == TheTag.B);
214}
215
216test "cast tag type of union to union" {
217 var x: Value2 = Letter2.B;
218 try expect(@as(Letter2, x) == Letter2.B);
219}
220const Letter2 = enum {
221 A,
222 B,
223 C,
224};
225const Value2 = union(Letter2) {
226 A: i32,
227 B,
228 C,
229};
230
231test "implicit cast union to its tag type" {
232 var x: Value2 = Letter2.B;
233 try expect(x == Letter2.B);
234 try giveMeLetterB(x);
235}
236fn giveMeLetterB(x: Letter2) !void {
237 try expect(x == Value2.B);
238}
239
240pub const PackThis = union(enum) {
241 Invalid: bool,
242 StringLiteral: u2,
243};
244
245test "constant packed union" {
246 try testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }});
247}
248
249fn testConstPackedUnion(expected_tokens: []const PackThis) !void {
250 try expect(expected_tokens[0].StringLiteral == 1);
251}
252
253test "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
271const PartialInst = union(enum) {
272 Compiled,
273};
274
275const PartialInstWithPayload = union(enum) {
276 Compiled: i32,
277};
278
279test "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
291test "tagged union initialization with runtime void" {
292 try expect(testTaggedUnionInit({}));
293}
294
295const TaggedUnionWithAVoid = union(enum) {
296 A,
297 B: i32,
298};
299
300fn testTaggedUnionInit(x: anytype) bool {
301 const y = TaggedUnionWithAVoid{ .A = x };
302 return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;
303}
304
305pub const UnionEnumNoPayloads = union(enum) {
306 A,
307 B,
308};
309
310test "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
318test "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
335test "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
357test "@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
372const Attribute = union(enum) {
373 A: bool,
374 B: u8,
375};
376
377fn setAttribute(attr: Attribute) void {
378 _ = attr;
379}
380
381fn Setter(attr: Attribute) type {
382 return struct {
383 fn set() void {
384 setAttribute(attr);
385 }
386 };
387}
388
389test "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
411test "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
431test "@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
450test "@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
466test "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
478fn 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
488test "comparison between union and enum literal" {
489 try testComparison();
490 comptime try testComparison();
491}
492
493test "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
505test "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
512test "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
521test "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
531const Foo1 = union(enum) {
532 f: struct {
533 x: usize,
534 },
535};
536var glbl: Foo1 = undefined;
537
538test "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
545pub const FooUnion = union(enum) {
546 U0: usize,
547 U1: u8,
548};
549
550var glbl_array: [2]FooUnion = undefined;
551
552test "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
559test "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
581test "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
592test "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
621test "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
630test "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
639test "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
648test "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
675test "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
702test "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
724test "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
747test "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
771test "@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
800test "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}
814test "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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4const Tag = std.meta.Tag;
5
6const Value = union(enum) {
7 Int: u64,
8 Array: [9]u8,
9};
10
11const Agg = struct {
12 val1: Value,
13 val2: Value,
14};
15
16const v1 = Value{ .Int = 1234 };
17const v2 = Value{ .Array = [_]u8{3} ** 9 };
18
19const err = @as(anyerror!Agg, Agg{
20 .val1 = v1,
21 .val2 = v2,
22});
23
24const array = [_]Value{ v1, v2, v1, v2 };
25
26test "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
37const Foo = union {
38 float: f64,
39 int: i32,
40};
41
42test "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
52const FooExtern = extern union {
53 float: f64,
54 int: i32,
55};
56
57test "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
64const Letter = enum { A, B, C };
65const Payload = union(Letter) {
66 A: i32,
67 B: f64,
68 C: bool,
69};
70
71test "union with specified enum tag" {
72 try doTest();
73 comptime try doTest();
74}
75
76fn doTest() error{TestUnexpectedResult}!void {
77 try expect((try bar(Payload{ .A = 1234 })) == -10);
78}
79
80fn 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
89const MultipleChoice = union(enum(u32)) {
90 A = 20,
91 B = 40,
92 C = 60,
93 D = 1000,
94};
95test "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
101const 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
113test "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
119fn 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
134const ExternPtrOrInt = extern union {
135 ptr: *u8,
136 int: u64,
137};
138test "extern union size" {
139 comptime try expect(@sizeOf(ExternPtrOrInt) == 8);
140}
141
142const PackedPtrOrInt = packed union {
143 ptr: *u8,
144 int: u64,
145};
146test "extern union size" {
147 comptime try expect(@sizeOf(PackedPtrOrInt) == 8);
148}
149
150const ZeroBits = union {
151 OnlyField: void,
152};
153test "union with only 1 field which is void should be zero bits" {
154 comptime try expect(@sizeOf(ZeroBits) == 0);
155}
156
157const TheTag = enum { A, B, C };
158const TheUnion = union(TheTag) {
159 A: i32,
160 B: i32,
161 C: i32,
162};
163test "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
169test "cast union to tag type of union" {
170 try testCastUnionToTag();
171 comptime try testCastUnionToTag();
172}
173
174fn testCastUnionToTag() !void {
175 var u = TheUnion{ .B = 1234 };
176 try expect(@as(TheTag, u) == TheTag.B);
177}
178
179test "cast tag type of union to union" {
180 var x: Value2 = Letter2.B;
181 try expect(@as(Letter2, x) == Letter2.B);
182}
183const Letter2 = enum { A, B, C };
184const Value2 = union(Letter2) {
185 A: i32,
186 B,
187 C,
188};
189
190test "implicit cast union to its tag type" {
191 var x: Value2 = Letter2.B;
192 try expect(x == Letter2.B);
193 try giveMeLetterB(x);
194}
195fn 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.
201pub const PackThis = union(enum) {
202 Invalid: bool,
203 StringLiteral: u2,
204};
205
206test "constant packed union" {
207 try testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }});
208}
209
210fn testConstPackedUnion(expected_tokens: []const PackThis) !void {
211 try expect(expected_tokens[0].StringLiteral == 1);
212}
213
214test "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
232const PartialInst = union(enum) {
233 Compiled,
234};
235
236const PartialInstWithPayload = union(enum) {
237 Compiled: i32,
238};
239
240test "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
252test "tagged union initialization with runtime void" {
253 try expect(testTaggedUnionInit({}));
254}
255
256const TaggedUnionWithAVoid = union(enum) {
257 A,
258 B: i32,
259};
260
261fn testTaggedUnionInit(x: anytype) bool {
262 const y = TaggedUnionWithAVoid{ .A = x };
263 return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;
264}
265
266pub const UnionEnumNoPayloads = union(enum) { A, B };
267
268test "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
276test "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
293test "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
315test "@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
330const Attribute = union(enum) {
331 A: bool,
332 B: u8,
333};
334
335fn setAttribute(attr: Attribute) void {
336 _ = attr;
337}
338
339fn Setter(attr: Attribute) type {
340 return struct {
341 fn set() void {
342 setAttribute(attr);
343 }
344 };
345}
346
347test "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
369test "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
389test "@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
408test "@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
424test "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
436fn 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
446test "comparison between union and enum literal" {
447 try testComparison();
448 comptime try testComparison();
449}
450
451test "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
463test "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
470test "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
479test "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
489const Foo1 = union(enum) {
490 f: struct {
491 x: usize,
492 },
493};
494var glbl: Foo1 = undefined;
495
496test "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
503pub const FooUnion = union(enum) {
504 U0: usize,
505 U1: u8,
506};
507
508var glbl_array: [2]FooUnion = undefined;
509
510test "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
517test "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
539test "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
550test "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
579test "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
588test "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
597test "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
606test "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
633test "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
660test "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
682test "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
705test "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
729test "@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
758test "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}
772test "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,7 +116,7 @@ test "array to vector" {
116 _ = vec;116 _ = vec;
117}117}
118118
119test "vector casts of sizes not divisable by 8" {119test "vector casts of sizes not divisible by 8" {
120 const S = struct {120 const S = struct {
121 fn doTheTest() !void {121 fn doTheTest() !void {
122 {122 {
test/behavior/widening.zig+14-2
...@@ -19,6 +19,12 @@ test "implicit unsigned integer to signed integer" {...@@ -19,6 +19,12 @@ test "implicit unsigned integer to signed integer" {
19}19}
2020
21test "float widening" {21test "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 var a: f16 = 12.34;28 var a: f16 = 12.34;
23 var b: f32 = a;29 var b: f32 = a;
24 var c: f64 = b;30 var c: f64 = b;
...@@ -29,9 +35,15 @@ test "float widening" {...@@ -29,9 +35,15 @@ test "float widening" {
29}35}
3036
31test "float widening f16 to f128" {37test "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 // TODO https://github.com/ziglang/zig/issues/328244 // TODO https://github.com/ziglang/zig/issues/3282
33 if (@import("builtin").target.cpu.arch == .aarch64) return error.SkipZigTest;45 if (@import("builtin").stage2_arch == .aarch64) return error.SkipZigTest;
34 if (@import("builtin").target.cpu.arch == .powerpc64le) return error.SkipZigTest;46 if (@import("builtin").stage2_arch == .powerpc64le) return error.SkipZigTest;
3547
36 var x: f16 = 12.34;48 var x: f16 = 12.34;
37 var y: f128 = x;49 var y: f128 = x;
test/cases.zig+13-1
...@@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void {
26 var case = ctx.exe("hello world with updates", linux_x64);26 var case = ctx.exe("hello world with updates", linux_x64);
2727
28 case.addError("", &[_][]const u8{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 });
3131
32 // Incorrect return type32 // Incorrect return type
...@@ -1807,4 +1807,16 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -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,7 +683,7 @@ pub fn addCases(ctx: *TestContext) !void {
683 \\ _ = u;683 \\ _ = u;
684 \\}684 \\}
685 , &[_][]const u8{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 "tmp.zig:17:16: error: no tag by value 15",687 "tmp.zig:17:16: error: no tag by value 15",
688 });688 });
689689
...@@ -711,6 +711,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -711,6 +711,7 @@ pub fn addCases(ctx: *TestContext) !void {
711 \\ .is_const = false,711 \\ .is_const = false,
712 \\ .is_volatile = false,712 \\ .is_volatile = false,
713 \\ .alignment = 1,713 \\ .alignment = 1,
714 \\ .address_space = .generic,
714 \\ .child = u8,715 \\ .child = u8,
715 \\ .is_allowzero = false,716 \\ .is_allowzero = false,
716 \\ .sentinel = 0,717 \\ .sentinel = 0,
...@@ -720,6 +721,23 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -720,6 +721,23 @@ pub fn addCases(ctx: *TestContext) !void {
720 "tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers",721 "tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers",
721 });722 });
722723
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 ctx.testErrStage1("helpful return type error message",741 ctx.testErrStage1("helpful return type error message",
724 \\export fn foo() u32 {742 \\export fn foo() u32 {
725 \\ return error.Ohno;743 \\ return error.Ohno;
...@@ -6127,9 +6145,9 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -6127,9 +6145,9 @@ pub fn addCases(ctx: *TestContext) !void {
6127 });6145 });
61286146
6129 ctx.objErrStage1("endless loop in function evaluation",6147 ctx.objErrStage1("endless loop in function evaluation",
6130 \\const seventh_fib_number = fibbonaci(7);6148 \\const seventh_fib_number = fibonacci(7);
6131 \\fn fibbonaci(x: i32) i32 {6149 \\fn fibonacci(x: i32) i32 {
6132 \\ return fibbonaci(x - 1) + fibbonaci(x - 2);6150 \\ return fibonacci(x - 1) + fibonacci(x - 2);
6133 \\}6151 \\}
6134 \\6152 \\
6135 \\export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); }6153 \\export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); }
...@@ -6757,7 +6775,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -6757,7 +6775,7 @@ pub fn addCases(ctx: *TestContext) !void {
6757 "tmp.zig:2:5: error: expression value is ignored",6775 "tmp.zig:2:5: error: expression value is ignored",
6758 });6776 });
67596777
6760 ctx.objErrStage1("ignored defered statement value",6778 ctx.objErrStage1("ignored deferred statement value",
6761 \\export fn foo() void {6779 \\export fn foo() void {
6762 \\ defer {1;}6780 \\ defer {1;}
6763 \\}6781 \\}
...@@ -6765,7 +6783,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -6765,7 +6783,7 @@ pub fn addCases(ctx: *TestContext) !void {
6765 "tmp.zig:2:12: error: expression value is ignored",6783 "tmp.zig:2:12: error: expression value is ignored",
6766 });6784 });
67676785
6768 ctx.objErrStage1("ignored defered function call",6786 ctx.objErrStage1("ignored deferred function call",
6769 \\export fn foo() void {6787 \\export fn foo() void {
6770 \\ defer bar();6788 \\ defer bar();
6771 \\}6789 \\}
...@@ -8841,9 +8859,9 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -8841,9 +8859,9 @@ pub fn addCases(ctx: *TestContext) !void {
8841 "tmp.zig:3:12: note: crosses namespace boundary here",8859 "tmp.zig:3:12: note: crosses namespace boundary here",
8842 });8860 });
88438861
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 \\pub fn main() !void {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 , &[_][]const u8{8866 , &[_][]const u8{
8849 "error: invalid operands to binary expression: 'f32' and 'f32'",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,7 +24,7 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
24 \\}24 \\}
25 , "DEG2RAD is: 0.017453" ++ nl);25 , "DEG2RAD is: 0.017453" ++ nl);
2626
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 \\void bar(void);28 \\void bar(void);
29 \\void baz(void);29 \\void baz(void);
30 \\struct foo { int x; };30 \\struct foo { int x; };
...@@ -394,7 +394,7 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -394,7 +394,7 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
394 \\}394 \\}
395 , "");395 , "");
396396
397 cases.add("ensure array casts outisde +=",397 cases.add("ensure array casts outside +=",
398 \\#include <stdlib.h>398 \\#include <stdlib.h>
399 \\static int hash_binary(int k)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,9 +12,9 @@ pub fn addCases(ctx: *TestContext) !void {
12 .os_tag = .macos,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 case.addError("", &[_][]const u8{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 });
1919
20 // Incorrect return type20 // Incorrect return type
...@@ -118,7 +118,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -118,7 +118,7 @@ pub fn addCases(ctx: *TestContext) !void {
118 {118 {
119 var case = ctx.exe("corner case - update existing, singular TextBlock", target);119 var case = ctx.exe("corner case - update existing, singular TextBlock", target);
120120
121 // This test case also covers an infrequent scenarion where the string table *may* be relocated121 // This test case also covers an infrequent scenario where the string table *may* be relocated
122 // into the position preceeding the symbol table which results in a dyld error.122 // into the position preceeding the symbol table which results in a dyld error.
123 case.addCompareOutput(123 case.addCompareOutput(
124 \\extern fn exit(usize) noreturn;124 \\extern fn exit(usize) noreturn;
test/stage2/llvm.zig+180
...@@ -242,4 +242,184 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -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,6 +28,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
28 cases.addBuildFile("test/standalone/empty_env/build.zig", .{});28 cases.addBuildFile("test/standalone/empty_env/build.zig", .{});
29 cases.addBuildFile("test/standalone/issue_7030/build.zig", .{});29 cases.addBuildFile("test/standalone/issue_7030/build.zig", .{});
30 cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{});30 cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{});
31 cases.addBuildFile("test/standalone/issue_9812/build.zig", .{});
31 if (std.Target.current.os.tag != .wasi) {32 if (std.Target.current.os.tag != .wasi) {
32 cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{});33 cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{});
33 }34 }
...@@ -36,6 +37,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -36,6 +37,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
36 }37 }
37 cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ .build_modes = true, .cross_targets = true });38 cases.addBuildFile("test/standalone/c_compiler/build.zig", .{ .build_modes = true, .cross_targets = true });
3839
40 if (std.Target.current.os.tag == .windows) {
41 cases.addC("test/standalone/issue_9402/main.zig");
42 }
39 // Try to build and run a PIE executable.43 // Try to build and run a PIE executable.
40 if (std.Target.current.os.tag == .linux) {44 if (std.Target.current.os.tag == .linux) {
41 cases.addBuildFile("test/standalone/pie/build.zig", .{});45 cases.addBuildFile("test/standalone/pie/build.zig", .{});
test/standalone/issue_9402/main.zig created+14
...@@ -0,0 +1,14 @@
1const FILE = extern struct {
2 dummy_field: u8,
3};
4
5extern fn _ftelli64([*c]FILE) i64;
6extern fn _fseeki64([*c]FILE, i64, c_int) c_int;
7
8pub 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 @@
1const std = @import("std");
2
3pub 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 @@
1const std = @import("std");
2const testing = std.testing;
3
4const c = @cImport({
5 @cInclude("zip.h");
6});
7
8const Error = error{
9 FailedToWriteEntry,
10 FileNotFound,
11 FailedToCreateEntry,
12 Overflow,
13 OutOfMemory,
14 InvalidCmdLine,
15};
16
17test "" {
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
326extern "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!
333typedef 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.
338void 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.
343mz_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.
348mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
349
350// Compression strategies.
351enum {
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. */
361typedef 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.
405typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
406typedef void (*mz_free_func)(void *opaque, void *address);
407typedef 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).
419enum {
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.
429enum {
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.
445enum {
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
457struct mz_internal_state;
458
459// Compression/decompression stream struct.
460typedef 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
482typedef mz_stream *mz_streamp;
483
484// Returns the version string of miniz.c.
485const 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.
500int 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)
509int 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().
514int 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.)
531int 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.
537int 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.
542mz_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.
547int mz_compress(unsigned char *pDest, mz_ulong *pDest_len,
548 const unsigned char *pSource, mz_ulong source_len);
549int 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().
554mz_ulong mz_compressBound(mz_ulong source_len);
555
556// Initializes a decompressor.
557int 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).
563int 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).
588int mz_inflate(mz_streamp pStream, int flush);
589
590// Deinitializes a decompressor.
591int 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.
596int 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.
601const 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
608typedef unsigned char Byte;
609typedef unsigned int uInt;
610typedef mz_ulong uLong;
611typedef Byte Bytef;
612typedef uInt uIntf;
613typedef char charf;
614typedef int intf;
615typedef void *voidpf;
616typedef uLong uLongf;
617typedef void *voidp;
618typedef 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
684typedef unsigned char mz_uint8;
685typedef signed short mz_int16;
686typedef unsigned short mz_uint16;
687typedef unsigned int mz_uint32;
688typedef unsigned int mz_uint;
689typedef long long mz_int64;
690typedef unsigned long long mz_uint64;
691typedef 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
708enum {
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
714typedef 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
735typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs,
736 void *pBuf, size_t n);
737typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs,
738 const void *pBuf, size_t n);
739typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque);
740
741struct mz_zip_internal_state_tag;
742typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
743
744typedef 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
751typedef 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
761typedef 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
787typedef 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.
798mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size,
799 mz_uint32 flags);
800mz_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
804mz_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.
809mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);
810
811// Returns detailed information about an archive file entry.
812mz_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.
816mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip,
817 mz_uint file_index);
818mz_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.
825mz_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.
831int 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.
835mz_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);
840mz_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.
845mz_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);
848mz_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.
853void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index,
854 size_t *pSize, mz_uint flags);
855void *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.
860mz_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);
864mz_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.
873mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index,
874 const char *pDst_filename, mz_uint flags);
875mz_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.
883mz_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.
890mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
891mz_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
896mz_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.
912mz_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.
921mz_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);
924mz_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.
936mz_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.
945mz_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.
953mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
954mz_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.
960mz_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.
968mz_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.
975void *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.
996enum {
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.
1013void *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))
1020size_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.
1027typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser);
1028int 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
1032struct tinfl_decompressor_tag;
1033typedef struct tinfl_decompressor_tag tinfl_decompressor;
1034
1035// Max size of LZ dictionary.
1036#define TINFL_LZ_DICT_SIZE 32768
1037
1038// Return status.
1039typedef 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.
1062tinfl_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.
1069enum {
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
1078typedef 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
1089typedef mz_uint64 tinfl_bit_buf_t;
1090#define TINFL_BITBUF_SIZE (64)
1091#else
1092typedef mz_uint32 tinfl_bit_buf_t;
1093#define TINFL_BITBUF_SIZE (32)
1094#endif
1095
1096struct 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).
1118enum {
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).
1138enum {
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.
1160void *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.
1165size_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.
1182void *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);
1186void *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.
1191typedef 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.
1196mz_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
1200enum {
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
1214enum {
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
1224enum {
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.
1238typedef 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
1246typedef 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.
1254typedef 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.)
1290tdefl_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.
1297tdefl_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.
1304tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf,
1305 size_t in_buf_size, tdefl_flush flush);
1306
1307tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
1308mz_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
1318mz_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
1336typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1];
1337typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1];
1338typedef 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
1387extern "C" {
1388#endif
1389
1390// ------------------- zlib-style API's
1391
1392mz_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/
1420mz_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
1437void mz_free(void *p) { MZ_FREE(p); }
1438
1439#ifndef MINIZ_NO_ZLIB_APIS
1440
1441static 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}
1445static void def_free_func(void *opaque, void *address) {
1446 (void)opaque, (void)address;
1447 MZ_FREE(address);
1448}
1449static 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
1455const char *mz_version(void) { return MZ_VERSION; }
1456
1457int 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
1462int 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
1502int 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
1512int 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
1567int 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
1577mz_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
1585int 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
1614int 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
1620mz_ulong mz_compressBound(mz_ulong source_len) {
1621 return mz_deflateBound(NULL, source_len);
1622}
1623
1624typedef 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
1632int 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
1669int mz_inflateInit(mz_streamp pStream) {
1670 return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
1671}
1672
1673int 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
1796int 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
1806int 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
1836const 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
2000tinfl_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
2400common_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.
2444void *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
2484size_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
2499int 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.
2536static 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
2556static 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
2569static 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
2598static 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
2621static 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
2630static 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.
2641typedef struct {
2642 mz_uint16 m_key, m_sym_index;
2643} tdefl_sym_freq;
2644static 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.
2678static 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.
2724enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 };
2725static 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
2748static 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
2856static 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
2859static 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
2945static 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
2966static 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
2972static 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
3067static 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
3111static 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
3119static 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)
3251static MZ_FORCEINLINE void
3252tdefl_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
3307static MZ_FORCEINLINE void
3308tdefl_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
3356static 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
3535static 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
3547static MZ_FORCEINLINE void
3548tdefl_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
3577static 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
3720static 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
3741tdefl_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
3813tdefl_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
3819tdefl_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
3857tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) {
3858 return d->m_prev_return_status;
3859}
3860
3861mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; }
3862
3863mz_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
3882typedef struct {
3883 size_t m_size, m_capacity;
3884 mz_uint8 *m_pBuf;
3885 mz_bool m_expandable;
3886} tdefl_output_buffer;
3887
3888static 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
3911void *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
3927size_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
3943static 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).
3949mz_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.
3984void *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}
4093void *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
4120static 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
4127static 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
4138static 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.
4247enum {
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
4332typedef struct {
4333 void *m_p;
4334 size_t m_size, m_capacity;
4335 mz_uint m_element_size;
4336} mz_zip_array;
4337
4338struct 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
4370static 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
4376static 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
4398static 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
4409static 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
4421static 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
4427static 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
4445static 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
4459static 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
4483static 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
4498static 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
4511static 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
4518static 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
4549static MZ_FORCEINLINE mz_bool
4550mz_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.)
4587static void
4588mz_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
4635static 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
4683static 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
4986mz_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
5000static 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
5010mz_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
5031static 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
5042mz_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
5069mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) {
5070 return pZip ? pZip->m_total_files : 0;
5071}
5072
5073static MZ_FORCEINLINE const mz_uint8 *
5074mz_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
5084mz_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
5094mz_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
5120mz_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
5166mz_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
5184static 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
5197static MZ_FORCEINLINE int
5198mz_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
5219static 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
5244int 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
5298mz_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
5441mz_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
5452mz_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
5459mz_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
5466void *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
5498void *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
5510mz_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
5688mz_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
5700static 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
5706mz_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
5732mz_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
5758mz_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
5774static void mz_write_le16(mz_uint8 *p, mz_uint16 v) {
5775 p[0] = (mz_uint8)v;
5776 p[1] = (mz_uint8)(v >> 8);
5777}
5778static 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
5787mz_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
5823static 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
5849mz_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
5869static 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
5880mz_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
5910mz_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
5965mz_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
5972typedef 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
5978static 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
5990static 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
6011static 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
6040static 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
6084static 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
6098static mz_uint
6099mz_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
6108static 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
6122mz_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
6302mz_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
6510mz_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
6660mz_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
6714mz_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
6730mz_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
6762mz_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
6811void *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
72struct 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
88struct zip_t {
89 mz_zip_archive archive;
90 mz_uint level;
91 struct zip_entry_t entry;
92};
93
94enum zip_modify_t {
95 MZ_KEEP = 0,
96 MZ_DELETE = 1,
97 MZ_MOVE = 2,
98};
99
100struct 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
107static 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
140const 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
149static 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
168static 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
203static 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
222static 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
255static 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
267static 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
282static 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
397out:
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
406static inline void zip_archive_finalize(mz_zip_archive *pzip) {
407 mz_zip_writer_finalize_archive(pzip);
408 zip_archive_truncate(pzip);
409}
410
411static 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
463static 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
474static 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
487static 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
498static 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
536static 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
549static 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
576static 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
606cleanup:
607 CLEANUP(move_buf);
608 return moved_length;
609}
610
611static 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
657static 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
706static 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
779struct 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
831cleanup:
832 CLEANUP(zip);
833 return NULL;
834}
835
836void 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
849int 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
858int 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
1009cleanup:
1010 CLEANUP(zip->entry.name);
1011 return err;
1012}
1013
1014int 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
1087int 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
1160cleanup:
1161 if (zip) {
1162 zip->entry.m_time = 0;
1163 CLEANUP(zip->entry.name);
1164 }
1165 return err;
1166}
1167
1168const 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
1177int 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
1186int 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
1201unsigned long long zip_entry_size(struct zip_t *zip) {
1202 return zip ? zip->entry.uncomp_size : 0;
1203}
1204
1205unsigned int zip_entry_crc32(struct zip_t *zip) {
1206 return zip ? zip->entry.uncomp_crc32 : 0;
1207}
1208
1209int 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
1247int 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
1295ssize_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
1324ssize_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
1346int 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
1393int 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
1417int 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
1426int 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
1461int 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
1481struct 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
1512cleanup:
1513 CLEANUP(zip);
1514 return NULL;
1515}
1516
1517ssize_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
1533void 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
1541int 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
1601int 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
33extern "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
40typedef long long ssize_t; /* byte count or error */
41#else
42typedef 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 */
105extern 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 */
113struct 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 */
127extern 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 */
135extern 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 */
145extern 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 */
159extern 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 */
171extern 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 */
180extern 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 */
196extern 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 */
205extern 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 */
215extern 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 */
224extern 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 */
233extern 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 */
244extern 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 */
255extern 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 */
272extern 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 */
291extern 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 */
302extern 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 */
314extern ZIP_EXPORT int
315zip_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 */
328extern 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 */
338extern 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 */
358extern ZIP_EXPORT int
359zip_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 */
371extern 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 */
383extern 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 */
393extern 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 */
404extern 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 */
423extern 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,7 +455,7 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St
455 const cases = b.allocator.create(TranslateCContext) catch unreachable;455 const cases = b.allocator.create(TranslateCContext) catch unreachable;
456 cases.* = TranslateCContext{456 cases.* = TranslateCContext{
457 .b = b,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 .test_index = 0,459 .test_index = 0,
460 .test_filter = test_filter,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,7 +3188,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3188 \\}3188 \\}
3189 });3189 });
31903190
3191 cases.add("macro comparisions",3191 cases.add("macro comparisons",
3192 \\#define MIN(a, b) ((b) < (a) ? (b) : (a))3192 \\#define MIN(a, b) ((b) < (a) ? (b) : (a))
3193 \\#define MAX(a, b) ((b) > (a) ? (b) : (a))3193 \\#define MAX(a, b) ((b) > (a) ? (b) : (a))
3194 , &[_][]const u8{3194 , &[_][]const u8{
...@@ -3443,7 +3443,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3443,7 +3443,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3443 });3443 });
3444 }3444 }
34453445
3446 cases.add("unnamed fields have predictabile names",3446 cases.add("unnamed fields have predictable names",
3447 \\struct a {3447 \\struct a {
3448 \\ struct {};3448 \\ struct {};
3449 \\};3449 \\};
tools/update_clang_options.zig+4
...@@ -376,6 +376,10 @@ const known_options = [_]KnownOpt{...@@ -376,6 +376,10 @@ const known_options = [_]KnownOpt{
376 .name = "mexec-model",376 .name = "mexec-model",
377 .ident = "exec_model",377 .ident = "exec_model",
378 },378 },
379 .{
380 .name = "emit-llvm",
381 .ident = "emit_llvm",
382 },
379};383};
380384
381const blacklisted_options = [_][]const u8{};385const blacklisted_options = [_][]const u8{};