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 @@
11image: freebsd/latest
22secrets:
33 - 51bfddf5-86a6-4e01-8576-358c72a4a0a4
4 - 5cfede76-914e-4071-893e-e5e2e6ae3cea
4 - 512ed797-0927-475a-83fd-bc997792860c
55sources:
66 - https://github.com/ziglang/zig
77tasks:
.builds/netbsd.yml+1-1
......@@ -1,7 +1,7 @@
11image: netbsd/latest
22secrets:
33 - 51bfddf5-86a6-4e01-8576-358c72a4a0a4
4 - 5cfede76-914e-4071-893e-e5e2e6ae3cea
4 - 512ed797-0927-475a-83fd-bc997792860c
55sources:
66 - https://github.com/ziglang/zig
77tasks:
CMakeLists.txt+16-14
......@@ -537,41 +537,43 @@ set(ZIG_STAGE2_SOURCES
537537 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
538538 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig"
539539 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
540 "${CMAKE_SOURCE_DIR}/src/Air.zig"
541 "${CMAKE_SOURCE_DIR}/src/AstGen.zig"
540542 "${CMAKE_SOURCE_DIR}/src/Cache.zig"
541543 "${CMAKE_SOURCE_DIR}/src/Compilation.zig"
542544 "${CMAKE_SOURCE_DIR}/src/DepTokenizer.zig"
545 "${CMAKE_SOURCE_DIR}/src/Liveness.zig"
543546 "${CMAKE_SOURCE_DIR}/src/Module.zig"
544547 "${CMAKE_SOURCE_DIR}/src/Package.zig"
545548 "${CMAKE_SOURCE_DIR}/src/RangeSet.zig"
549 "${CMAKE_SOURCE_DIR}/src/Sema.zig"
546550 "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig"
547551 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"
548552 "${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"
550558 "${CMAKE_SOURCE_DIR}/src/clang.zig"
551559 "${CMAKE_SOURCE_DIR}/src/clang_options.zig"
552560 "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"
553561 "${CMAKE_SOURCE_DIR}/src/codegen.zig"
554 "${CMAKE_SOURCE_DIR}/src/codegen/aarch64.zig"
555 "${CMAKE_SOURCE_DIR}/src/codegen/arm.zig"
556562 "${CMAKE_SOURCE_DIR}/src/codegen/c.zig"
557563 "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig"
558564 "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig"
559 "${CMAKE_SOURCE_DIR}/src/codegen/riscv64.zig"
560565 "${CMAKE_SOURCE_DIR}/src/codegen/wasm.zig"
561 "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig"
562566 "${CMAKE_SOURCE_DIR}/src/glibc.zig"
563567 "${CMAKE_SOURCE_DIR}/src/introspect.zig"
564 "${CMAKE_SOURCE_DIR}/src/Air.zig"
565568 "${CMAKE_SOURCE_DIR}/src/libc_installation.zig"
566569 "${CMAKE_SOURCE_DIR}/src/libcxx.zig"
567570 "${CMAKE_SOURCE_DIR}/src/libtsan.zig"
568571 "${CMAKE_SOURCE_DIR}/src/libunwind.zig"
569572 "${CMAKE_SOURCE_DIR}/src/link.zig"
570573 "${CMAKE_SOURCE_DIR}/src/link/C.zig"
574 "${CMAKE_SOURCE_DIR}/src/link/C/zig.h"
571575 "${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
572576 "${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
573 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
574 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
575577 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
576578 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
577579 "${CMAKE_SOURCE_DIR}/src/link/MachO/Atom.zig"
......@@ -582,20 +584,22 @@ set(ZIG_STAGE2_SOURCES
582584 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
583585 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
584586 "${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"
585589 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
590 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
586591 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"
592 "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig"
587593 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"
588594 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse/test.zig"
589 "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig"
590595 "${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"
594596 "${CMAKE_SOURCE_DIR}/src/main.zig"
595597 "${CMAKE_SOURCE_DIR}/src/mingw.zig"
596598 "${CMAKE_SOURCE_DIR}/src/musl.zig"
599 "${CMAKE_SOURCE_DIR}/src/print_air.zig"
597600 "${CMAKE_SOURCE_DIR}/src/print_env.zig"
598601 "${CMAKE_SOURCE_DIR}/src/print_targets.zig"
602 "${CMAKE_SOURCE_DIR}/src/print_zir.zig"
599603 "${CMAKE_SOURCE_DIR}/src/stage1.zig"
600604 "${CMAKE_SOURCE_DIR}/src/target.zig"
601605 "${CMAKE_SOURCE_DIR}/src/tracy.zig"
......@@ -605,8 +609,6 @@ set(ZIG_STAGE2_SOURCES
605609 "${CMAKE_SOURCE_DIR}/src/value.zig"
606610 "${CMAKE_SOURCE_DIR}/src/wasi_libc.zig"
607611 "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"
608 "${CMAKE_SOURCE_DIR}/src/Zir.zig"
609 "${CMAKE_SOURCE_DIR}/src/Sema.zig"
610612)
611613
612614if(MSVC)
build.zig+53-26
......@@ -18,6 +18,7 @@ pub fn build(b: *Builder) !void {
1818 const mode = b.standardReleaseOptions();
1919 const target = b.standardTargetOptions(.{});
2020 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
2223 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
2324 docgen_exe.single_threaded = single_threaded;
......@@ -125,10 +126,18 @@ pub fn build(b: *Builder) !void {
125126 exe.install();
126127 exe.setBuildMode(mode);
127128 exe.setTarget(target);
128 toolchain_step.dependOn(&exe.step);
129 if (!skip_stage2_tests) {
130 toolchain_step.dependOn(&exe.step);
131 }
129132 b.default_step.dependOn(&exe.step);
130133 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
132141 const exe_options = b.addOptions();
133142 exe.addOptions("build_options", exe_options);
134143
......@@ -182,8 +191,8 @@ pub fn build(b: *Builder) !void {
182191 b.addSearchPrefix(cfg.cmake_prefix_path);
183192 }
184193
185 try addCmakeCfgOptionsToExe(b, cfg, exe);
186 try addCmakeCfgOptionsToExe(b, cfg, test_stage2);
194 try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx);
195 try addCmakeCfgOptionsToExe(b, cfg, test_stage2, use_zig_libcxx);
187196 } else {
188197 // Here we are -Denable-llvm but no cmake integration.
189198 try addStaticLlvmOptionsToExe(exe);
......@@ -260,12 +269,24 @@ pub fn build(b: *Builder) !void {
260269 b.allocator,
261270 &[_][]const u8{ tracy_path, "TracyClient.cpp" },
262271 ) 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
263279 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);
265281 if (!enable_llvm) {
266282 exe.linkSystemLibraryName("c++");
267283 }
268284 exe.linkLibC();
285
286 if (target.isWindows()) {
287 exe.linkSystemLibrary("dbghelp");
288 exe.linkSystemLibrary("ws2_32");
289 }
269290 }
270291
271292 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
......@@ -434,6 +455,7 @@ fn addCmakeCfgOptionsToExe(
434455 b: *Builder,
435456 cfg: CMakeConfig,
436457 exe: *std.build.LibExeObjStep,
458 use_zig_libcxx: bool,
437459) !void {
438460 exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{
439461 cfg.cmake_binary_dir,
......@@ -446,28 +468,32 @@ fn addCmakeCfgOptionsToExe(
446468 addCMakeLibraryList(exe, cfg.lld_libraries);
447469 addCMakeLibraryList(exe, cfg.llvm_libraries);
448470
449 const need_cpp_includes = true;
450
451 // System -lc++ must be used because in this code path we are attempting to link
452 // against system-provided LLVM, Clang, LLD.
453 if (exe.target.getOsTag() == .linux) {
454 // First we try to static link against gcc libstdc++. If that doesn't work,
455 // we fall back to -lc++ and cross our fingers.
456 addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
457 error.RequiredLibraryNotFound => {
458 exe.linkSystemLibrary("c++");
459 },
460 else => |e| return e,
461 };
462 exe.linkSystemLibrary("unwind");
463 } else if (exe.target.isFreeBSD()) {
464 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
465 exe.linkSystemLibrary("pthread");
466 } else if (exe.target.getOsTag() == .openbsd) {
467 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
468 try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);
469 } else if (exe.target.isDarwin()) {
470 exe.linkSystemLibrary("c++");
471 if (use_zig_libcxx) {
472 exe.linkLibCpp();
473 } else {
474 const need_cpp_includes = true;
475
476 // System -lc++ must be used because in this code path we are attempting to link
477 // against system-provided LLVM, Clang, LLD.
478 if (exe.target.getOsTag() == .linux) {
479 // First we try to static link against gcc libstdc++. If that doesn't work,
480 // we fall back to -lc++ and cross our fingers.
481 addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
482 error.RequiredLibraryNotFound => {
483 exe.linkSystemLibrary("c++");
484 },
485 else => |e| return e,
486 };
487 exe.linkSystemLibrary("unwind");
488 } else if (exe.target.isFreeBSD()) {
489 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
490 exe.linkSystemLibrary("pthread");
491 } else if (exe.target.getOsTag() == .openbsd) {
492 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
493 try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);
494 } else if (exe.target.isDarwin()) {
495 exe.linkSystemLibrary("c++");
496 }
471497 }
472498
473499 if (cfg.dia_guids_lib.len != 0) {
......@@ -504,6 +530,7 @@ fn addStaticLlvmOptionsToExe(
504530 if (exe.target.getOs().tag == .windows) {
505531 exe.linkSystemLibrary("version");
506532 exe.linkSystemLibrary("uuid");
533 exe.linkSystemLibrary("ole32");
507534 }
508535}
509536
ci/azure/linux_script+17-4
......@@ -20,7 +20,7 @@ cd $HOME
2020wget -nv "https://ziglang.org/deps/$CACHE_BASENAME.tar.xz"
2121tar xf "$CACHE_BASENAME.tar.xz"
2222
23QEMUBASE="qemu-linux-x86_64-5.2.0.1"
23QEMUBASE="qemu-linux-x86_64-6.1.0.1"
2424wget -nv "https://ziglang.org/deps/$QEMUBASE.tar.xz"
2525tar xf "$QEMUBASE.tar.xz"
2626export PATH="$(pwd)/$QEMUBASE/bin:$PATH"
......@@ -71,9 +71,22 @@ make $JOBS install
7171
7272release/bin/zig test ../test/behavior.zig -fno-stage1 -fLLVM -I ../test
7373
74release/bin/zig build test-toolchain -Denable-qemu -Denable-wasmtime
75release/bin/zig build test-std -Denable-qemu -Denable-wasmtime
76release/bin/zig build docs -Denable-qemu -Denable-wasmtime
74release/bin/zig build test-behavior -Denable-qemu -Denable-wasmtime
75release/bin/zig build test-compiler-rt -Denable-qemu -Denable-wasmtime
76release/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
7891# Look for HTML errors.
7992tidy -qe ../zig-cache/langref.html
ci/azure/pipelines.yml+1-2
......@@ -38,9 +38,8 @@ jobs:
3838 timeoutInMinutes: 360
3939 steps:
4040 - 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")
4242 .\sfx.exe -y -o\
43 del sfx.exe
4443 displayName: Download/Extract/Install MSYS2
4544 - script: |
4645 @REM install updated filesystem package first without dependency checking
ci/azure/windows_msvc_script.bat+1-1
......@@ -1,7 +1,7 @@
11@echo on
22SET "SRCROOT=%cd%"
33SET "PREVPATH=%PATH%"
4SET "PREVMSYSEM=%MSYSTEM%"
4SET "PREVMSYSTEM=%MSYSTEM%"
55
66set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem"
77SET "MSYSTEM=MINGW64"
doc/docgen.zig+10-3
......@@ -901,6 +901,7 @@ fn tokenizeAndPrintRaw(
901901 switch (token.tag) {
902902 .eof => break,
903903
904 .keyword_addrspace,
904905 .keyword_align,
905906 .keyword_and,
906907 .keyword_asm,
......@@ -1057,15 +1058,21 @@ fn tokenizeAndPrintRaw(
10571058 .plus_equal,
10581059 .plus_percent,
10591060 .plus_percent_equal,
1061 .plus_pipe,
1062 .plus_pipe_equal,
10601063 .minus,
10611064 .minus_equal,
10621065 .minus_percent,
10631066 .minus_percent_equal,
1067 .minus_pipe,
1068 .minus_pipe_equal,
10641069 .asterisk,
10651070 .asterisk_equal,
10661071 .asterisk_asterisk,
10671072 .asterisk_percent,
10681073 .asterisk_percent_equal,
1074 .asterisk_pipe,
1075 .asterisk_pipe_equal,
10691076 .arrow,
10701077 .colon,
10711078 .slash,
......@@ -1078,6 +1085,8 @@ fn tokenizeAndPrintRaw(
10781085 .angle_bracket_left_equal,
10791086 .angle_bracket_angle_bracket_left,
10801087 .angle_bracket_angle_bracket_left_equal,
1088 .angle_bracket_angle_bracket_left_pipe,
1089 .angle_bracket_angle_bracket_left_pipe_equal,
10811090 .angle_bracket_right,
10821091 .angle_bracket_right_equal,
10831092 .angle_bracket_angle_bracket_right,
......@@ -1222,9 +1231,7 @@ fn genHtml(
12221231
12231232 try printSourceBlock(allocator, tokenizer, out, syntax_block);
12241233
1225 // TODO: remove code.just_check_syntax after updating code samples
1226 // that have stopped working due to a change in the compiler.
1227 if (!do_code_tests or code.just_check_syntax) {
1234 if (!do_code_tests) {
12281235 continue;
12291236 }
12301237
doc/langref.html.in+268-184
......@@ -1244,8 +1244,9 @@ fn divide(a: i32, b: i32) i32 {
12441244 </p>
12451245 <p>
12461246 Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on
1247 integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and
1248 {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.
1247 integer overflow. Alternative operators are provided for wrapping and saturating arithmetic on all targets.
1248 {#syntax#}+%{#endsyntax#} and {#syntax#}-%{#endsyntax#} perform wrapping arithmetic
1249 while {#syntax#}+|{#endsyntax#} and {#syntax#}-|{#endsyntax#} perform saturating arithmetic.
12491250 </p>
12501251 <p>
12511252 Zig supports arbitrary bit-width integers, referenced by using
......@@ -1395,6 +1396,23 @@ a +%= b{#endsyntax#}</pre></th>
13951396 <pre>{#syntax#}@as(u32, std.math.maxInt(u32)) +% 1 == 0{#endsyntax#}</pre>
13961397 </td>
13971398 </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>
13981416 <tr>
13991417 <th scope="row"><pre>{#syntax#}a - b
14001418a -= b{#endsyntax#}</pre></th>
......@@ -1434,6 +1452,23 @@ a -%= b{#endsyntax#}</pre></th>
14341452 <pre>{#syntax#}@as(u32, 0) -% 1 == std.math.maxInt(u32){#endsyntax#}</pre>
14351453 </td>
14361454 </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>
14371472 <tr>
14381473 <th scope="row"><pre>{#syntax#}-a{#endsyntax#}</pre></th>
14391474 <td>
......@@ -1508,6 +1543,23 @@ a *%= b{#endsyntax#}</pre></th>
15081543 <pre>{#syntax#}@as(u8, 200) *% 2 == 144{#endsyntax#}</pre>
15091544 </td>
15101545 </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>
15111563 <tr>
15121564 <th scope="row"><pre>{#syntax#}a / b
15131565a /= b{#endsyntax#}</pre></th>
......@@ -1577,6 +1629,24 @@ a <<= b{#endsyntax#}</pre></th>
15771629 <pre>{#syntax#}1 << 8 == 256{#endsyntax#}</pre>
15781630 </td>
15791631 </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>
15801650 <tr>
15811651 <th scope="row"><pre>{#syntax#}a >> b
15821652a >>= b{#endsyntax#}</pre></th>
......@@ -1968,14 +2038,14 @@ const B = error{Two};
19682038a!b
19692039x{}
19702040!x -x -%x ~x &x ?x
1971* / % ** *% ||
1972+ - ++ +% -%
1973<< >>
2041* / % ** *% *| ||
2042+ - ++ +% -% +| -|
2043<< >> <<|
19742044& ^ | orelse catch
19752045== != < > <= >=
19762046and
19772047or
1978= *= /= %= += -= <<= >>= &= ^= |={#endsyntax#}</pre>
2048= *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |={#endsyntax#}</pre>
19792049 {#header_close#}
19802050 {#header_close#}
19812051 {#header_open|Arrays#}
......@@ -2125,7 +2195,7 @@ fn dump(args: anytype) !void {
21252195
21262196 {#header_open|Multidimensional Arrays#}
21272197 <p>
2128 Mutlidimensional arrays can be created by nesting arrays:
2198 Multidimensional arrays can be created by nesting arrays:
21292199 </p>
21302200 {#code_begin|test|multidimensional#}
21312201const std = @import("std");
......@@ -2898,7 +2968,7 @@ fn bar(x: *const u3) u3 {
28982968}
28992969 {#code_end#}
29002970 <p>
2901 In this case, the function {#syntax#}bar{#endsyntax#} cannot be called becuse the pointer
2971 In this case, the function {#syntax#}bar{#endsyntax#} cannot be called because the pointer
29022972 to the non-ABI-aligned field mentions the bit offset, but the function expects an ABI-aligned pointer.
29032973 </p>
29042974 <p>
......@@ -4771,6 +4841,8 @@ test "parse u64" {
47714841 {#header_open|catch#}
47724842 <p>If you want to provide a default value, you can use the {#syntax#}catch{#endsyntax#} binary operator:</p>
47734843 {#code_begin|syntax#}
4844const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
4845
47744846fn doAThing(str: []u8) void {
47754847 const number = parseU64(str, 10) catch 13;
47764848 _ = number; // ...
......@@ -4786,6 +4858,8 @@ fn doAThing(str: []u8) void {
47864858 <p>Let's say you wanted to return the error if you got one, otherwise continue with the
47874859 function logic:</p>
47884860 {#code_begin|syntax#}
4861const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
4862
47894863fn doAThing(str: []u8) !void {
47904864 const number = parseU64(str, 10) catch |err| return err;
47914865 _ = number; // ...
......@@ -4795,6 +4869,8 @@ fn doAThing(str: []u8) !void {
47954869 There is a shortcut for this. The {#syntax#}try{#endsyntax#} expression:
47964870 </p>
47974871 {#code_begin|syntax#}
4872const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
4873
47984874fn doAThing(str: []u8) !void {
47994875 const number = try parseU64(str, 10);
48004876 _ = number; // ...
......@@ -4810,7 +4886,7 @@ fn doAThing(str: []u8) !void {
48104886 Maybe you know with complete certainty that an expression will never be an error.
48114887 In this case you can do this:
48124888 </p>
4813 {#code_begin|syntax#}const number = parseU64("1234", 10) catch unreachable;{#code_end#}
4889 {#syntax#}const number = parseU64("1234", 10) catch unreachable;{#endsyntax#}
48144890 <p>
48154891 Here we know for sure that "1234" will parse successfully. So we put the
48164892 {#syntax#}unreachable{#endsyntax#} value on the right hand side. {#syntax#}unreachable{#endsyntax#} generates
......@@ -4822,7 +4898,7 @@ fn doAThing(str: []u8) !void {
48224898 Finally, you may want to take a different action for every situation. For that, we combine
48234899 the {#link|if#} and {#link|switch#} expression:
48244900 </p>
4825 {#code_begin|syntax#}
4901 {#syntax_block|zig|handle_all_error_scenarios.zig#}
48264902fn doAThing(str: []u8) void {
48274903 if (parseU64(str, 10)) |number| {
48284904 doSomethingWithNumber(number);
......@@ -4834,7 +4910,7 @@ fn doAThing(str: []u8) void {
48344910 error.InvalidChar => unreachable,
48354911 }
48364912}
4837 {#code_end#}
4913 {#end_syntax_block#}
48384914 {#header_open|errdefer#}
48394915 <p>
48404916 The other component to error handling is defer statements.
......@@ -4845,7 +4921,7 @@ fn doAThing(str: []u8) void {
48454921 <p>
48464922 Example:
48474923 </p>
4848 {#code_begin|syntax#}
4924 {#syntax_block|zig|errdefer_example.zig#}
48494925fn createFoo(param: i32) !Foo {
48504926 const foo = try tryToAllocateFoo();
48514927 // now we have allocated foo. we need to free it if the function fails.
......@@ -4863,7 +4939,7 @@ fn createFoo(param: i32) !Foo {
48634939 // but the defer will run!
48644940 return foo;
48654941}
4866 {#code_end#}
4942 {#end_syntax_block#}
48674943 <p>
48684944 The neat thing about this is that you get robust error handling without
48694945 the verbosity and cognitive overhead of trying to make sure every exit path
......@@ -4955,7 +5031,7 @@ test "merge error sets" {
49555031 {#header_open|Inferred Error Sets#}
49565032 <p>
49575033 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#}:
49595035 </p>
49605036{#code_begin|test|inferred_error_sets#}
49615037// With an inferred error set
......@@ -5132,12 +5208,12 @@ fn bang2() void {
51325208 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#}.
51335209 This is to initialize this struct in the stack memory:
51345210 </p>
5135 {#code_begin|syntax#}
5211 {#syntax_block|zig|stack_trace_struct.zig#}
51365212pub const StackTrace = struct {
51375213 index: usize,
51385214 instruction_addresses: [N]usize,
51395215};
5140 {#code_end#}
5216 {#end_syntax_block#}
51415217 <p>
51425218 Here, N is the maximum function call depth as determined by call graph analysis. Recursion is ignored and counts for 2.
51435219 </p>
......@@ -5150,13 +5226,13 @@ pub const StackTrace = struct {
51505226 <p>
51515227 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:
51525228 </p>
5153 {#code_begin|syntax#}
5229 {#syntax_block|zig|zig_return_error_fn.zig#}
51545230// marked as "no-inline" in LLVM IR
51555231fn __zig_return_error(stack_trace: *StackTrace) void {
51565232 stack_trace.instruction_addresses[stack_trace.index] = @returnAddress();
51575233 stack_trace.index = (stack_trace.index + 1) % N;
51585234}
5159 {#code_end#}
5235 {#end_syntax_block#}
51605236 <p>
51615237 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.
51625238 </p>
......@@ -5206,16 +5282,16 @@ const optional_int: ?i32 = 5678;
52065282 Task: call malloc, if the result is null, return null.
52075283 </p>
52085284 <p>C code</p>
5209 <pre><code class="cpp">// malloc prototype included for reference
5285 {#syntax_block|c|call_malloc_in_c.c#}// malloc prototype included for reference
52105286void *malloc(size_t size);
52115287
52125288struct Foo *do_a_thing(void) {
52135289 char *ptr = malloc(1234);
52145290 if (!ptr) return NULL;
52155291 // ...
5216}</code></pre>
5292}{#end_syntax_block#}
52175293 <p>Zig code</p>
5218 {#code_begin|syntax#}
5294 {#syntax_block|zig|call_malloc_from_zig.zig#}
52195295// malloc prototype included for reference
52205296extern fn malloc(size: size_t) ?*u8;
52215297
......@@ -5223,7 +5299,7 @@ fn doAThing() ?*Foo {
52235299 const ptr = malloc(1234) orelse return null;
52245300 _ = ptr; // ...
52255301}
5226 {#code_end#}
5302 {#end_syntax_block#}
52275303 <p>
52285304 Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr"
52295305 is {#syntax#}*u8{#endsyntax#} <em>not</em> {#syntax#}?*u8{#endsyntax#}. The {#syntax#}orelse{#endsyntax#} keyword
......@@ -5233,7 +5309,7 @@ fn doAThing() ?*Foo {
52335309 <p>
52345310 The other form of checking against NULL you might see looks like this:
52355311 </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) {
52375313 // do some stuff
52385314
52395315 if (foo) {
......@@ -5241,11 +5317,14 @@ fn doAThing() ?*Foo {
52415317 }
52425318
52435319 // do some stuff
5244}</code></pre>
5320}{#end_syntax_block#}
52455321 <p>
52465322 In Zig you can accomplish the same thing:
52475323 </p>
5248 {#code_begin|syntax#}
5324 {#code_begin|syntax|checking_null_in_zig#}
5325const Foo = struct{};
5326fn doSomethingWithFoo(foo: *Foo) void { _ = foo; }
5327
52495328fn doAThing(optional_foo: ?*Foo) void {
52505329 // do some stuff
52515330
......@@ -5540,7 +5619,7 @@ test "coerce to optionals" {
55405619}
55415620 {#code_end#}
55425621 <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#}
55445623const std = @import("std");
55455624const expect = std.testing.expect;
55465625
......@@ -6111,7 +6190,7 @@ test "perform fn" {
61116190 different code. In this example, the function {#syntax#}performFn{#endsyntax#} is generated three different times,
61126191 for the different values of {#syntax#}prefix_char{#endsyntax#} provided:
61136192 </p>
6114 {#code_begin|syntax#}
6193 {#syntax_block|zig|performFn_1#}
61156194// From the line:
61166195// expect(performFn('t', 1) == 6);
61176196fn performFn(start_value: i32) i32 {
......@@ -6120,8 +6199,8 @@ fn performFn(start_value: i32) i32 {
61206199 result = three(result);
61216200 return result;
61226201}
6123 {#code_end#}
6124 {#code_begin|syntax#}
6202 {#end_syntax_block#}
6203 {#syntax_block|zig|performFn_2#}
61256204// From the line:
61266205// expect(performFn('o', 0) == 1);
61276206fn performFn(start_value: i32) i32 {
......@@ -6129,15 +6208,15 @@ fn performFn(start_value: i32) i32 {
61296208 result = one(result);
61306209 return result;
61316210}
6132 {#code_end#}
6133 {#code_begin|syntax#}
6211 {#end_syntax_block#}
6212 {#syntax_block|zig|performFn_3#}
61346213// From the line:
61356214// expect(performFn('w', 99) == 99);
61366215fn performFn(start_value: i32) i32 {
61376216 var result: i32 = start_value;
61386217 return result;
61396218}
6140 {#code_end#}
6219 {#end_syntax_block#}
61416220 <p>
61426221 Note that this happens even in a debug build; in a release build these generated functions still
61436222 pass through rigorous LLVM optimizations. The important thing to note, however, is not that this
......@@ -6367,11 +6446,11 @@ const Node = struct {
63676446 it works fine.
63686447 </p>
63696448 {#header_close#}
6370 {#header_open|Case Study: printf in Zig#}
6449 {#header_open|Case Study: print in Zig#}
63716450 <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.
63736452 </p>
6374 {#code_begin|exe|printf#}
6453 {#code_begin|exe|print#}
63756454const print = @import("std").debug.print;
63766455
63776456const a_number: i32 = 1234;
......@@ -6386,67 +6465,84 @@ pub fn main() void {
63866465 Let's crack open the implementation of this and see how it works:
63876466 </p>
63886467
6389 {#code_begin|syntax#}
6390/// Calls print and then flushes the buffer.
6391pub fn printf(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void {
6392 const State = enum {
6393 start,
6394 open_brace,
6395 close_brace,
6396 };
6397
6398 comptime var start_index: usize = 0;
6399 comptime var state = State.start;
6400 comptime var next_arg: usize = 0;
6468 {#code_begin|syntax|poc_print_fn#}
6469const Writer = struct {
6470 /// Calls print and then flushes the buffer.
6471 pub fn print(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void {
6472 const State = enum {
6473 start,
6474 open_brace,
6475 close_brace,
6476 };
64016477
6402 inline for (format) |c, i| {
6403 switch (state) {
6404 State.start => switch (c) {
6405 '{' => {
6406 if (start_index < i) try self.write(format[start_index..i]);
6407 state = State.open_brace;
6478 comptime var start_index: usize = 0;
6479 comptime var state = State.start;
6480 comptime var next_arg: usize = 0;
6481
6482 inline for (format) |c, i| {
6483 switch (state) {
6484 State.start => switch (c) {
6485 '{' => {
6486 if (start_index < i) try self.write(format[start_index..i]);
6487 state = State.open_brace;
6488 },
6489 '}' => {
6490 if (start_index < i) try self.write(format[start_index..i]);
6491 state = State.close_brace;
6492 },
6493 else => {},
64086494 },
6409 '}' => {
6410 if (start_index < i) try self.write(format[start_index..i]);
6411 state = State.close_brace;
6495 State.open_brace => switch (c) {
6496 '{' => {
6497 state = State.start;
6498 start_index = i;
6499 },
6500 '}' => {
6501 try self.printValue(args[next_arg]);
6502 next_arg += 1;
6503 state = State.start;
6504 start_index = i + 1;
6505 },
6506 's' => {
6507 continue;
6508 },
6509 else => @compileError("Unknown format character: " ++ [1]u8{c}),
64126510 },
6413 else => {},
6414 },
6415 State.open_brace => switch (c) {
6416 '{' => {
6417 state = State.start;
6418 start_index = i;
6419 },
6420 '}' => {
6421 try self.printValue(args[next_arg]);
6422 next_arg += 1;
6423 state = State.start;
6424 start_index = i + 1;
6511 State.close_brace => switch (c) {
6512 '}' => {
6513 state = State.start;
6514 start_index = i;
6515 },
6516 else => @compileError("Single '}' encountered in format string"),
64256517 },
6426 else => @compileError("Unknown format character: " ++ c),
6427 },
6428 State.close_brace => switch (c) {
6429 '}' => {
6430 state = State.start;
6431 start_index = i;
6432 },
6433 else => @compileError("Single '}' encountered in format string"),
6434 },
6518 }
64356519 }
6436 }
6437 comptime {
6438 if (args.len != next_arg) {
6439 @compileError("Unused arguments");
6520 comptime {
6521 if (args.len != next_arg) {
6522 @compileError("Unused arguments");
6523 }
6524 if (state != State.start) {
6525 @compileError("Incomplete format string: " ++ format);
6526 }
64406527 }
6441 if (state != State.Start) {
6442 @compileError("Incomplete format string: " ++ format);
6528 if (start_index < format.len) {
6529 try self.write(format[start_index..format.len]);
64436530 }
6531 try self.flush();
64446532 }
6445 if (start_index < format.len) {
6446 try self.write(format[start_index..format.len]);
6533
6534 fn write(self: *Writer, value: []const u8) !void {
6535 _ = self;
6536 _ = value;
64476537 }
6448 try self.flush();
6449}
6538 pub fn printValue(self: *Writer, value: anytype) !void {
6539 _ = self;
6540 _ = value;
6541 }
6542 fn flush(self: *Writer) !void {
6543 _ = self;
6544 }
6545};
64506546 {#code_end#}
64516547 <p>
64526548 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
64596555 When this function is analyzed from our example code above, Zig partially evaluates the function
64606556 and emits a function that actually looks like this:
64616557 </p>
6462 {#code_begin|syntax#}
6463pub fn printf(self: *Writer, arg0: i32, arg1: []const u8) !void {
6558 {#syntax_block|zig|Emitted print Function#}
6559pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
64646560 try self.write("here is a string: '");
64656561 try self.printValue(arg0);
64666562 try self.write("' here is a number: ");
......@@ -6468,28 +6564,46 @@ pub fn printf(self: *Writer, arg0: i32, arg1: []const u8) !void {
64686564 try self.write("\n");
64696565 try self.flush();
64706566}
6471 {#code_end#}
6567 {#end_syntax_block#}
64726568 <p>
64736569 {#syntax#}printValue{#endsyntax#} is a function that takes a parameter of any type, and does different things depending
64746570 on the type:
64756571 </p>
6476 {#code_begin|syntax#}
6477pub fn printValue(self: *Writer, value: anytype) !void {
6478 switch (@typeInfo(@TypeOf(value))) {
6479 .Int => {
6480 return self.printInt(T, value);
6481 },
6482 .Float => {
6483 return self.printFloat(T, value);
6484 },
6485 else => {
6486 @compileError("Unable to print type '" ++ @typeName(T) ++ "'");
6487 },
6572 {#code_begin|syntax|poc_printValue_fn#}
6573 const Writer = struct {
6574 pub fn printValue(self: *Writer, value: anytype) !void {
6575 switch (@typeInfo(@TypeOf(value))) {
6576 .Int => {
6577 return self.writeInt(value);
6578 },
6579 .Float => {
6580 return self.writeFloat(value);
6581 },
6582 .Pointer => {
6583 return self.write(value);
6584 },
6585 else => {
6586 @compileError("Unable to print type '" ++ @typeName(@TypeOf(value)) ++ "'");
6587 },
6588 }
64886589 }
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};
64906604 {#code_end#}
64916605 <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#}?
64936607 </p>
64946608 {#code_begin|test_err|Unused argument in 'here is a string: '{s}' here is a number: {}#}
64956609const print = @import("std").debug.print;
......@@ -6497,7 +6611,7 @@ const print = @import("std").debug.print;
64976611const a_number: i32 = 1234;
64986612const a_string = "foobar";
64996613
6500test "printf too many arguments" {
6614test "print too many arguments" {
65016615 print("here is a string: '{s}' here is a number: {}\n", .{
65026616 a_string,
65036617 a_number,
......@@ -6512,7 +6626,7 @@ test "printf too many arguments" {
65126626 Zig doesn't care whether the format argument is a string literal,
65136627 only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:
65146628 </p>
6515 {#code_begin|exe|printf#}
6629 {#code_begin|exe|print#}
65166630const print = @import("std").debug.print;
65176631
65186632const a_number: i32 = 1234;
......@@ -7118,16 +7232,6 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
71187232 If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
71197233 </p>
71207234 {#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#}
71317235 {#header_open|@alignCast#}
71327236 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre>
71337237 <p>
......@@ -7216,7 +7320,9 @@ fn func(y: *i32) void {
72167320 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
72177321 an integer or an enum.
72187322 </p>
7323 {#see_also|@atomicStore|@atomicRmw|@fence|@cmpxchgWeak|@cmpxchgStrong#}
72197324 {#header_close#}
7325
72207326 {#header_open|@atomicRmw#}
72217327 <pre>{#syntax#}@atomicRmw(comptime T: type, ptr: *T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre>
72227328 <p>
......@@ -7242,7 +7348,9 @@ fn func(y: *i32) void {
72427348 <li>{#syntax#}.Max{#endsyntax#} - stores the operand if it is larger. Supports integers and floats.</li>
72437349 <li>{#syntax#}.Min{#endsyntax#} - stores the operand if it is smaller. Supports integers and floats.</li>
72447350 </ul>
7351 {#see_also|@atomicStore|@atomicLoad|@fence|@cmpxchgWeak|@cmpxchgStrong#}
72457352 {#header_close#}
7353
72467354 {#header_open|@atomicStore#}
72477355 <pre>{#syntax#}@atomicStore(comptime T: type, ptr: *T, value: T, comptime ordering: builtin.AtomicOrder) void{#endsyntax#}</pre>
72487356 <p>
......@@ -7252,6 +7360,7 @@ fn func(y: *i32) void {
72527360 {#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float,
72537361 an integer or an enum.
72547362 </p>
7363 {#see_also|@atomicLoad|@atomicRmw|@fence|@cmpxchgWeak|@cmpxchgStrong#}
72557364 {#header_close#}
72567365
72577366 {#header_open|@bitCast#}
......@@ -7396,9 +7505,11 @@ fn add(a: i32, b: i32) i32 {
73967505 {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The
73977506 {#syntax#}CallOptions{#endsyntax#} struct is reproduced here:
73987507 </p>
7399 {#code_begin|syntax#}
7508 {#syntax_block|zig|builtin.CallOptions struct#}
74007509pub const CallOptions = struct {
74017510 modifier: Modifier = .auto,
7511
7512 /// Only valid when `Modifier` is `Modifier.async_kw`.
74027513 stack: ?[]align(std.Target.stack_align) u8 = null,
74037514
74047515 pub const Modifier = enum {
......@@ -7435,7 +7546,7 @@ pub const CallOptions = struct {
74357546 compile_time,
74367547 };
74377548};
7438 {#code_end#}
7549 {#end_syntax_block#}
74397550 {#header_close#}
74407551
74417552 {#header_open|@cDefine#}
......@@ -7540,15 +7651,16 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v
75407651 an integer or an enum.
75417652 </p>
75427653 <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#}
75447655 {#header_close#}
7656
75457657 {#header_open|@cmpxchgWeak#}
75467658 <pre>{#syntax#}@cmpxchgWeak(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T{#endsyntax#}</pre>
75477659 <p>
75487660 This function performs a weak atomic compare exchange operation. It's the equivalent of this code,
75497661 except atomic:
75507662 </p>
7551 {#code_begin|syntax#}
7663 {#syntax_block|zig|cmpxchgWeakButNotAtomic#}
75527664fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T {
75537665 const old_value = ptr.*;
75547666 if (old_value == expected_value and usuallyTrueButSometimesFalse()) {
......@@ -7558,7 +7670,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
75587670 return old_value;
75597671 }
75607672}
7561 {#code_end#}
7673 {#end_syntax_block#}
75627674 <p>
75637675 If you are using cmpxchg in a loop, the sporadic failure will be no problem, and {#syntax#}cmpxchgWeak{#endsyntax#}
75647676 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
75697681 an integer or an enum.
75707682 </p>
75717683 <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#}
75737685 {#header_close#}
75747686
75757687 {#header_open|@compileError#}
......@@ -7617,7 +7729,7 @@ test "main" {
76177729}
76187730 {#code_end#}
76197731 <p>
7620 will ouput:
7732 will output:
76217733 </p>
76227734 <p>
76237735 If all {#syntax#}@compileLog{#endsyntax#} calls are removed or
......@@ -7734,7 +7846,7 @@ test "main" {
77347846 the tag value is used as the enumeration value.
77357847 </p>
77367848 <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#}
77387850 known at {#link|comptime#}.
77397851 </p>
77407852 {#see_also|@intToEnum#}
......@@ -7849,7 +7961,7 @@ export fn @"A function name that is a complete sentence."() void {}
78497961 <p>
78507962 {#syntax#}AtomicOrder{#endsyntax#} can be found with {#syntax#}@import("std").builtin.AtomicOrder{#endsyntax#}.
78517963 </p>
7852 {#see_also|Compile Variables#}
7964 {#see_also|@atomicStore|@atomicLoad|@atomicRmw|@cmpxchgWeak|@cmpxchgStrong#}
78537965 {#header_close#}
78547966
78557967 {#header_open|@field#}
......@@ -8241,22 +8353,6 @@ test "@wasmMemoryGrow" {
82418353 </p>
82428354 {#header_close#}
82438355
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
82608356 {#header_open|@panic#}
82618357 <pre>{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}</pre>
82628358 <p>
......@@ -8474,14 +8570,16 @@ test "@setRuntimeSafety" {
84748570 {#header_open|@shlExact#}
84758571 <pre>{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>
84768572 <p>
8477 Performs the left shift operation ({#syntax#}<<{#endsyntax#}). Caller guarantees
8478 that the shift will not shift any 1 bits out.
8573 Performs the left shift operation ({#syntax#}<<{#endsyntax#}).
8574 For unsigned integers, the result is {#link|undefined#} if any 1 bits
8575 are shifted out. For signed integers, the result is {#link|undefined#} if
8576 any bits that disagree with the resultant sign bit are shifted out.
84798577 </p>
84808578 <p>
84818579 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
84828580 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
84838581 </p>
8484 {#see_also|@shrExact|@shlWithOverflow|@shlWithSaturation#}
8582 {#see_also|@shrExact|@shlWithOverflow#}
84858583 {#header_close#}
84868584
84878585 {#header_open|@shlWithOverflow#}
......@@ -8495,24 +8593,9 @@ test "@setRuntimeSafety" {
84958593 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
84968594 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
84978595 </p>
8498 {#see_also|@shlExact|@shrExact|@shlWithSaturation#}
8596 {#see_also|@shlExact|@shrExact#}
84998597 {#header_close#}
85008598
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
85168599 {#header_open|@shrExact#}
85178600 <pre>{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>
85188601 <p>
......@@ -8523,7 +8606,7 @@ test "@setRuntimeSafety" {
85238606 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
85248607 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
85258608 </p>
8526 {#see_also|@shlExact|@shlWithOverflow|@shlWithSaturation#}
8609 {#see_also|@shlExact|@shlWithOverflow#}
85278610 {#header_close#}
85288611
85298612 {#header_open|@shuffle#}
......@@ -8684,7 +8767,7 @@ fn doTheTest() !void {
86848767 {#header_open|@sin#}
86858768 <pre>{#syntax#}@sin(value: anytype) @TypeOf(value){#endsyntax#}</pre>
86868769 <p>
8687 Sine trigometric function on a floating point number. Uses a dedicated hardware instruction
8770 Sine trigonometric function on a floating point number. Uses a dedicated hardware instruction
86888771 when available.
86898772 </p>
86908773 <p>
......@@ -8695,7 +8778,7 @@ fn doTheTest() !void {
86958778 {#header_open|@cos#}
86968779 <pre>{#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}</pre>
86978780 <p>
8698 Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction
8781 Cosine trigonometric function on a floating point number. Uses a dedicated hardware instruction
86998782 when available.
87008783 </p>
87018784 <p>
......@@ -8783,7 +8866,7 @@ fn doTheTest() !void {
87838866 {#header_open|@ceil#}
87848867 <pre>{#syntax#}@ceil(value: anytype) @TypeOf(value){#endsyntax#}</pre>
87858868 <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.
87878870 Uses a dedicated hardware instruction when available.
87888871 </p>
87898872 <p>
......@@ -8823,17 +8906,6 @@ fn doTheTest() !void {
88238906 </p>
88248907 {#header_close#}
88258908
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
88378909 {#header_open|@tagName#}
88388910 <pre>{#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}</pre>
88398911 <p>
......@@ -10153,7 +10225,7 @@ pub fn main() void {
1015310225 This expression is evaluated at compile-time and is used to control
1015410226 preprocessor directives and include multiple <code class="file">.h</code> files:
1015510227 </p>
10156 {#code_begin|syntax#}
10228 {#syntax_block|zig|@cImport Expression#}
1015710229const builtin = @import("builtin");
1015810230
1015910231const c = @cImport({
......@@ -10167,7 +10239,7 @@ const c = @cImport({
1016710239 }
1016810240 @cInclude("soundio.h");
1016910241});
10170 {#code_end#}
10242 {#end_syntax_block#}
1017110243 {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#}
1017210244 {#header_close#}
1017310245
......@@ -10273,7 +10345,7 @@ pub fn main() void {
1027310345 <p>
1027410346 Some C constructs cannot be translated to Zig - for example, <em>goto</em>,
1027510347 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.
1027710349 </p>
1027810350 <p>
1027910351 Demotion comes in three varieties - {#link|opaque#}, <em>extern</em>, and
......@@ -10283,13 +10355,13 @@ pub fn main() void {
1028310355 Functions that contain opaque types or code constructs that cannot be translated will be demoted
1028410356 to {#syntax#}extern{#endsyntax#} declarations.
1028510357
10286 Thus, non-translateable types can still be used as pointers, and non-translateable functions
10358 Thus, non-translatable types can still be used as pointers, and non-translatable functions
1028710359 can be called so long as the linker is aware of the compiled function.
1028810360 </p>
1028910361 <p>
1029010362 {#syntax#}@compileError{#endsyntax#} is used when top-level definitions (global variables,
1029110363 function prototypes, macros) cannot be translated or demoted. Since Zig uses lazy analysis for
10292 top-level declarations, untranslateable entities will not cause a compile error in your code unless
10364 top-level declarations, untranslatable entities will not cause a compile error in your code unless
1029310365 you actually use them.
1029410366 </p>
1029510367 {#see_also|opaque|extern|@compileError#}
......@@ -10301,7 +10373,7 @@ pub fn main() void {
1030110373 can be translated to Zig. Macros that cannot be translated will be be demoted to
1030210374 {#syntax#}@compileError{#endsyntax#}. Note that C code which <em>uses</em> macros will be
1030310375 translated without any additional issues (since Zig operates on the pre-processed source
10304 with macros expanded). It is merely the macros themselves which may not be translateable to
10376 with macros expanded). It is merely the macros themselves which may not be translatable to
1030510377 Zig.
1030610378 </p>
1030710379 <p>Consider the following example:</p>
......@@ -10321,7 +10393,7 @@ pub export fn foo() c_int {
1032110393}
1032210394pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected token .Equal"); // macro.c:1:9
1032310395 {#code_end#}
10324 <p>Note that {#syntax#}foo{#endsyntax#} was translated correctly despite using a non-translateable
10396 <p>Note that {#syntax#}foo{#endsyntax#} was translated correctly despite using a non-translatable
1032510397 macro. {#syntax#}MAKELOCAL{#endsyntax#} was demoted to {#syntax#}@compileError{#endsyntax#} since
1032610398 it cannot be expressed as a Zig function; this simply means that you cannot directly use
1032710399 {#syntax#}MAKELOCAL{#endsyntax#} from Zig.
......@@ -10491,8 +10563,8 @@ const typedArray = new Uint8Array(source);
1049110563
1049210564WebAssembly.instantiate(typedArray, {
1049310565 env: {
10494 print: (result) =&gt; { console.log(`The result is ${result}`); }
10495 }}).then(result =&gt; {
10566 print: (result) => { console.log(`The result is ${result}`); }
10567 }}).then(result => {
1049610568 const add = result.instance.exports.add;
1049710569 add(1, 2);
1049810570});{#end_syntax_block#}
......@@ -11787,6 +11859,7 @@ AssignOp
1178711859 / PLUSEQUAL
1178811860 / MINUSEQUAL
1178911861 / LARROW2EQUAL
11862 / LARROW2PIPEEQUAL
1179011863 / RARROW2EQUAL
1179111864 / AMPERSANDEQUAL
1179211865 / CARETEQUAL
......@@ -11821,6 +11894,8 @@ AdditionOp
1182111894 / PLUS2
1182211895 / PLUSPERCENT
1182311896 / MINUSPERCENT
11897 / PLUSPIPE
11898 / MINUSPIPE
1182411899
1182511900MultiplyOp
1182611901 &lt;- PIPE2
......@@ -11829,6 +11904,7 @@ MultiplyOp
1182911904 / PERCENT
1183011905 / ASTERISK2
1183111906 / ASTERISKPERCENT
11907 / ASTERISKPIPE
1183211908
1183311909PrefixOp
1183411910 &lt;- EXCLAMATIONMARK
......@@ -11992,6 +12068,8 @@ ASTERISK2 &lt;- '**' skip
1199212068ASTERISKEQUAL &lt;- '*=' skip
1199312069ASTERISKPERCENT &lt;- '*%' ![=] skip
1199412070ASTERISKPERCENTEQUAL &lt;- '*%=' skip
12071ASTERISKPIPE &lt;- '*|' ![=] skip
12072ASTERISKPIPEEQUAL &lt;- '*|=' skip
1199512073CARET &lt;- '^' ![=] skip
1199612074CARETEQUAL &lt;- '^=' skip
1199712075COLON &lt;- ':' skip
......@@ -12008,6 +12086,8 @@ EXCLAMATIONMARK &lt;- '!' ![=] skip
1200812086EXCLAMATIONMARKEQUAL &lt;- '!=' skip
1200912087LARROW &lt;- '&lt;' ![&lt;=] skip
1201012088LARROW2 &lt;- '&lt;&lt;' ![=] skip
12089LARROW2PIPE &lt;- '&lt;&lt;|' ![=] skip
12090LARROW2PIPEEQUAL &lt;- '&lt;&lt;|=' ![=] skip
1201112091LARROW2EQUAL &lt;- '&lt;&lt;=' skip
1201212092LARROWEQUAL &lt;- '&lt;=' skip
1201312093LBRACE &lt;- '{' skip
......@@ -12017,6 +12097,8 @@ MINUS &lt;- '-' ![%=&gt;] skip
1201712097MINUSEQUAL &lt;- '-=' skip
1201812098MINUSPERCENT &lt;- '-%' ![=] skip
1201912099MINUSPERCENTEQUAL &lt;- '-%=' skip
12100MINUSPIPE &lt;- '-|' ![=] skip
12101MINUSPIPEEQUAL &lt;- '-|=' skip
1202012102MINUSRARROW &lt;- '-&gt;' skip
1202112103PERCENT &lt;- '%' ![=] skip
1202212104PERCENTEQUAL &lt;- '%=' skip
......@@ -12028,6 +12110,8 @@ PLUS2 &lt;- '++' skip
1202812110PLUSEQUAL &lt;- '+=' skip
1202912111PLUSPERCENT &lt;- '+%' ![=] skip
1203012112PLUSPERCENTEQUAL &lt;- '+%=' skip
12113PLUSPIPE &lt;- '+|' ![=] skip
12114PLUSPIPEEQUAL &lt;- '+|=' skip
1203112115LETTERC &lt;- 'c' skip
1203212116QUESTIONMARK &lt;- '?' skip
1203312117RARROW &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) {
4141 .netbsd => 31,
4242 .freebsd => 15,
4343 .openbsd => 31,
44 .solaris => 31,
4445 else => 0,
4546};
4647
......@@ -112,7 +113,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
112113 else => |e| return os.unexpectedErrno(e),
113114 }
114115 },
115 .netbsd => if (use_pthreads) {
116 .netbsd, .solaris => if (use_pthreads) {
116117 const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr, null);
117118 switch (err) {
118119 .SUCCESS => return,
......@@ -202,7 +203,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
202203 else => |e| return os.unexpectedErrno(e),
203204 }
204205 },
205 .netbsd => if (use_pthreads) {
206 .netbsd, .solaris => if (use_pthreads) {
206207 const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);
207208 switch (err) {
208209 .SUCCESS => return std.mem.sliceTo(buffer, 0),
......@@ -565,6 +566,16 @@ const PosixThreadImpl = struct {
565566 };
566567 return @intCast(usize, count);
567568 },
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 },
568579 .haiku => {
569580 var count: u32 = undefined;
570581 var system_info: os.system_info = undefined;
lib/std/Thread/Condition.zig+1-1
......@@ -54,7 +54,7 @@ pub const WindowsCondition = struct {
5454 pub fn wait(cond: *WindowsCondition, mutex: *Mutex) void {
5555 const rc = windows.kernel32.SleepConditionVariableSRW(
5656 &cond.cond,
57 &mutex.srwlock,
57 &mutex.impl.srwlock,
5858 windows.INFINITE,
5959 @as(windows.ULONG, 0),
6060 );
lib/std/Thread/Mutex.zig+97-80
......@@ -33,17 +33,29 @@ const testing = std.testing;
3333const StaticResetEvent = std.thread.StaticResetEvent;
3434
3535/// Try to acquire the mutex without blocking. Returns `null` if the mutex is
36/// unavailable. Otherwise returns `Held`. Call `release` on `Held`.
37pub fn tryAcquire(m: *Mutex) ?Impl.Held {
36/// unavailable. Otherwise returns `Held`. Call `release` on `Held`, or use
37/// releaseDirect().
38pub fn tryAcquire(m: *Mutex) ?Held {
3839 return m.impl.tryAcquire();
3940}
4041
4142/// Acquire the mutex. Deadlocks if the mutex is already
4243/// held by the calling thread.
43pub fn acquire(m: *Mutex) Impl.Held {
44pub fn acquire(m: *Mutex) Held {
4445 return m.impl.acquire();
4546}
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
4759const Impl = if (builtin.single_threaded)
4860 Dummy
4961else if (builtin.os.tag == .windows)
......@@ -53,6 +65,32 @@ else if (std.Thread.use_pthreads)
5365else
5466 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
5694pub const AtomicMutex = struct {
5795 state: State = .unlocked,
5896
......@@ -62,39 +100,32 @@ pub const AtomicMutex = struct {
62100 waiting,
63101 };
64102
65 pub const Held = struct {
66 mutex: *AtomicMutex,
103 pub usingnamespace HeldInterface(@This());
67104
68 pub fn release(held: Held) void {
69 switch (@atomicRmw(State, &held.mutex.state, .Xchg, .unlocked, .Release)) {
70 .unlocked => unreachable,
71 .locked => {},
72 .waiting => held.mutex.unlockSlow(),
73 }
74 }
75 };
76
77 pub fn tryAcquire(m: *AtomicMutex) ?Held {
78 if (@cmpxchgStrong(
105 fn tryAcquireDirect(m: *AtomicMutex) bool {
106 return @cmpxchgStrong(
79107 State,
80108 &m.state,
81109 .unlocked,
82110 .locked,
83111 .Acquire,
84112 .Monotonic,
85 ) == null) {
86 return Held{ .mutex = m };
87 } else {
88 return null;
89 }
113 ) == null;
90114 }
91115
92 pub fn acquire(m: *AtomicMutex) Held {
116 fn acquireDirect(m: *AtomicMutex) void {
93117 switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) {
94118 .unlocked => {},
95119 else => |s| m.lockSlow(s),
96120 }
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 }
98129 }
99130
100131 fn lockSlow(m: *AtomicMutex, current_state: State) void {
......@@ -171,36 +202,20 @@ pub const AtomicMutex = struct {
171202pub const PthreadMutex = struct {
172203 pthread_mutex: std.c.pthread_mutex_t = .{},
173204
174 pub const Held = struct {
175 mutex: *PthreadMutex,
176
177 pub fn release(held: Held) void {
178 switch (std.c.pthread_mutex_unlock(&held.mutex.pthread_mutex)) {
179 .SUCCESS => return,
180 .INVAL => unreachable,
181 .AGAIN => unreachable,
182 .PERM => unreachable,
183 else => unreachable,
184 }
185 }
186 };
205 pub usingnamespace HeldInterface(@This());
187206
188 /// Try to acquire the mutex without blocking. Returns null if
189 /// the mutex is unavailable. Otherwise returns Held. Call
190 /// release on Held.
191 pub fn tryAcquire(m: *PthreadMutex) ?Held {
192 if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS) {
193 return Held{ .mutex = m };
194 } else {
195 return null;
196 }
207 /// Try to acquire the mutex without blocking. Returns true if
208 /// the mutex is unavailable. Otherwise returns false. Call
209 /// release when done.
210 fn tryAcquireDirect(m: *PthreadMutex) bool {
211 return std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS;
197212 }
198213
199214 /// Acquire the mutex. Will deadlock if the mutex is already
200215 /// held by the calling thread.
201 pub fn acquire(m: *PthreadMutex) Held {
216 fn acquireDirect(m: *PthreadMutex) void {
202217 switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) {
203 .SUCCESS => return Held{ .mutex = m },
218 .SUCCESS => {},
204219 .INVAL => unreachable,
205220 .BUSY => unreachable,
206221 .AGAIN => unreachable,
......@@ -209,6 +224,16 @@ pub const PthreadMutex = struct {
209224 else => unreachable,
210225 }
211226 }
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 }
212237};
213238
214239/// This has the sematics as `Mutex`, however it does not actually do any
......@@ -216,58 +241,50 @@ pub const PthreadMutex = struct {
216241pub const Dummy = struct {
217242 lock: @TypeOf(lock_init) = lock_init,
218243
219 const lock_init = if (std.debug.runtime_safety) false else {};
220
221 pub const Held = struct {
222 mutex: *Dummy,
244 pub usingnamespace HeldInterface(@This());
223245
224 pub fn release(held: Held) void {
225 if (std.debug.runtime_safety) {
226 held.mutex.lock = false;
227 }
228 }
229 };
246 const lock_init = if (std.debug.runtime_safety) false else {};
230247
231 /// Try to acquire the mutex without blocking. Returns null if
232 /// the mutex is unavailable. Otherwise returns Held. Call
233 /// release on Held.
234 pub fn tryAcquire(m: *Dummy) ?Held {
248 /// Try to acquire the mutex without blocking. Returns false if
249 /// the mutex is unavailable. Otherwise returns true.
250 fn tryAcquireDirect(m: *Dummy) bool {
235251 if (std.debug.runtime_safety) {
236 if (m.lock) return null;
252 if (m.lock) return false;
237253 m.lock = true;
238254 }
239 return Held{ .mutex = m };
255 return true;
240256 }
241257
242258 /// Acquire the mutex. Will deadlock if the mutex is already
243259 /// held by the calling thread.
244 pub fn acquire(m: *Dummy) Held {
245 return m.tryAcquire() orelse @panic("deadlock detected");
260 fn acquireDirect(m: *Dummy) void {
261 if (!m.tryAcquireDirect()) {
262 @panic("deadlock detected");
263 }
264 }
265
266 fn releaseDirect(m: *Dummy) void {
267 if (std.debug.runtime_safety) {
268 m.lock = false;
269 }
246270 }
247271};
248272
249273const WindowsMutex = struct {
250274 srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT,
251275
252 pub const Held = struct {
253 mutex: *WindowsMutex,
276 pub usingnamespace HeldInterface(@This());
254277
255 pub fn release(held: Held) void {
256 windows.kernel32.ReleaseSRWLockExclusive(&held.mutex.srwlock);
257 }
258 };
259
260 pub fn tryAcquire(m: *WindowsMutex) ?Held {
261 if (windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE) {
262 return Held{ .mutex = m };
263 } else {
264 return null;
265 }
278 fn tryAcquireDirect(m: *WindowsMutex) bool {
279 return windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE;
266280 }
267281
268 pub fn acquire(m: *WindowsMutex) Held {
282 fn acquireDirect(m: *WindowsMutex) void {
269283 windows.kernel32.AcquireSRWLockExclusive(&m.srwlock);
270 return Held{ .mutex = m };
284 }
285
286 fn releaseDirect(m: *WindowsMutex) void {
287 windows.kernel32.ReleaseSRWLockExclusive(&m.srwlock);
271288 }
272289};
273290
lib/std/array_hash_map.zig+10-10
......@@ -90,7 +90,7 @@ pub fn ArrayHashMap(
9090 /// Modifying the key is allowed only if it does not change the hash.
9191 /// Modifying the value is allowed.
9292 /// Entry pointers become invalid whenever this ArrayHashMap is modified,
93 /// unless `ensureCapacity` was previously used.
93 /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
9494 pub const Entry = Unmanaged.Entry;
9595
9696 /// A KV pair which has been copied out of the backing store
......@@ -110,7 +110,7 @@ pub fn ArrayHashMap(
110110 /// Modifying the key is allowed only if it does not change the hash.
111111 /// Modifying the value is allowed.
112112 /// Entry pointers become invalid whenever this ArrayHashMap is modified,
113 /// unless `ensureCapacity` was previously used.
113 /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
114114 pub const GetOrPutResult = Unmanaged.GetOrPutResult;
115115
116116 /// An Iterator over Entry pointers.
......@@ -478,7 +478,7 @@ pub fn ArrayHashMapUnmanaged(
478478 /// Modifying the key is allowed only if it does not change the hash.
479479 /// Modifying the value is allowed.
480480 /// Entry pointers become invalid whenever this ArrayHashMap is modified,
481 /// unless `ensureCapacity` was previously used.
481 /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
482482 pub const Entry = struct {
483483 key_ptr: *K,
484484 value_ptr: *V,
......@@ -509,7 +509,7 @@ pub fn ArrayHashMapUnmanaged(
509509 /// Modifying the key is allowed only if it does not change the hash.
510510 /// Modifying the value is allowed.
511511 /// Entry pointers become invalid whenever this ArrayHashMap is modified,
512 /// unless `ensureCapacity` was previously used.
512 /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
513513 pub const GetOrPutResult = struct {
514514 key_ptr: *K,
515515 value_ptr: *V,
......@@ -759,20 +759,20 @@ pub fn ArrayHashMapUnmanaged(
759759 }
760760 pub fn ensureTotalCapacityContext(self: *Self, allocator: *Allocator, new_capacity: usize, ctx: Context) !void {
761761 if (new_capacity <= linear_scan_max) {
762 try self.entries.ensureCapacity(allocator, new_capacity);
762 try self.entries.ensureTotalCapacity(allocator, new_capacity);
763763 return;
764764 }
765765
766766 if (self.index_header) |header| {
767767 if (new_capacity <= header.capacity()) {
768 try self.entries.ensureCapacity(allocator, new_capacity);
768 try self.entries.ensureTotalCapacity(allocator, new_capacity);
769769 return;
770770 }
771771 }
772772
773773 const new_bit_index = try IndexHeader.findBitIndex(new_capacity);
774774 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
777777 if (self.index_header) |old_header| old_header.free(allocator);
778778 self.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, new_header);
......@@ -1441,7 +1441,7 @@ pub fn ArrayHashMapUnmanaged(
14411441 unreachable;
14421442 }
14431443
1444 /// Must ensureCapacity before calling this.
1444 /// Must `ensureTotalCapacity`/`ensureUnusedCapacity` before calling this.
14451445 fn getOrPutInternal(self: *Self, key: anytype, ctx: anytype, header: *IndexHeader, comptime I: type) GetOrPutResult {
14461446 const slice = self.entries.slice();
14471447 const hashes_array = if (store_hash) slice.items(.hash) else {};
......@@ -1485,7 +1485,7 @@ pub fn ArrayHashMapUnmanaged(
14851485 }
14861486
14871487 // This pointer survives the following append because we call
1488 // entries.ensureCapacity before getOrPutInternal.
1488 // entries.ensureTotalCapacity before getOrPutInternal.
14891489 const hash_match = if (store_hash) h == hashes_array[slot_data.entry_index] else true;
14901490 if (hash_match and checkedEql(ctx, key, keys_array[slot_data.entry_index])) {
14911491 return .{
......@@ -1946,7 +1946,7 @@ test "iterator hash map" {
19461946 var reset_map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
19471947 defer reset_map.deinit();
19481948
1949 // test ensureCapacity with a 0 parameter
1949 // test ensureTotalCapacity with a 0 parameter
19501950 try reset_map.ensureTotalCapacity(0);
19511951
19521952 try reset_map.putNoClobber(0, 11);
lib/std/build.zig+7-6
......@@ -684,7 +684,11 @@ pub const Builder = struct {
684684 );
685685 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
689693 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
690694 const selected_target = CrossTarget.parse(.{
......@@ -2432,11 +2436,8 @@ pub const LibExeObjStep = struct {
24322436
24332437 if (populated_cpu_features.eql(cross.cpu.features)) {
24342438 // The CPU name alone is sufficient.
2435 // If it is the baseline CPU, no command line args are required.
2436 if (cross.cpu.model != std.Target.Cpu.baseline(cross.cpu.arch).model) {
2437 try zig_args.append("-mcpu");
2438 try zig_args.append(cross.cpu.model.name);
2439 }
2439 try zig_args.append("-mcpu");
2440 try zig_args.append(cross.cpu.model.name);
24402441 } else {
24412442 var mcpu_buffer = std.ArrayList(u8).init(builder.allocator);
24422443
lib/std/builtin.zig+11-1
......@@ -166,6 +166,15 @@ pub const CallingConvention = enum {
166166 SysV,
167167};
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
169178/// This data structure is used by the Zig language code generation and
170179/// therefore must be kept in sync with the compiler implementation.
171180pub const SourceLocation = struct {
......@@ -226,6 +235,7 @@ pub const TypeInfo = union(enum) {
226235 is_const: bool,
227236 is_volatile: bool,
228237 alignment: comptime_int,
238 address_space: AddressSpace,
229239 child: type,
230240 is_allowzero: bool,
231241
......@@ -700,7 +710,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
700710 },
701711 else => {
702712 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);
704714 },
705715 }
706716}
lib/std/c.zig+27
......@@ -252,6 +252,33 @@ pub extern "c" fn kevent(
252252 timeout: ?*const c.timespec,
253253) 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
255282pub extern "c" fn getaddrinfo(
256283 noalias node: ?[*:0]const u8,
257284 noalias service: ?[*:0]const u8,
lib/std/c/darwin.zig+1
......@@ -291,6 +291,7 @@ pub const sockaddr = extern struct {
291291 family: sa_family_t,
292292 data: [14]u8,
293293
294 pub const SS_MAXSIZE = 128;
294295 pub const storage = std.x.os.Socket.Address.Native.Storage;
295296 pub const in = extern struct {
296297 len: u8 = @sizeOf(in),
lib/std/c/dragonfly.zig+1
......@@ -465,6 +465,7 @@ pub const sockaddr = extern struct {
465465 family: u8,
466466 data: [14]u8,
467467
468 pub const SS_MAXSIZE = 128;
468469 pub const storage = std.x.os.Socket.Address.Native.Storage;
469470
470471 pub const in = extern struct {
lib/std/c/freebsd.zig+1
......@@ -323,6 +323,7 @@ pub const sockaddr = extern struct {
323323 /// actually longer; address value
324324 data: [14]u8,
325325
326 pub const SS_MAXSIZE = 128;
326327 pub const storage = std.x.os.Socket.Address.Native.Storage;
327328
328329 pub const in = extern struct {
lib/std/c/haiku.zig+1
......@@ -339,6 +339,7 @@ pub const sockaddr = extern struct {
339339 /// actually longer; address value
340340 data: [14]u8,
341341
342 pub const SS_MAXSIZE = 128;
342343 pub const storage = std.x.os.Socket.Address.Native.Storage;
343344
344345 pub const in = extern struct {
lib/std/c/netbsd.zig+1
......@@ -476,6 +476,7 @@ pub const sockaddr = extern struct {
476476 /// actually longer; address value
477477 data: [14]u8,
478478
479 pub const SS_MAXSIZE = 128;
479480 pub const storage = std.x.os.Socket.Address.Native.Storage;
480481
481482 pub const in = extern struct {
lib/std/c/openbsd.zig+1
......@@ -279,6 +279,7 @@ pub const sockaddr = extern struct {
279279 /// actually longer; address value
280280 data: [14]u8,
281281
282 pub const SS_MAXSIZE = 256;
282283 pub const storage = std.x.os.Socket.Address.Native.Storage;
283284
284285 pub const in = extern struct {
lib/std/c/solaris.zig+1909-11
......@@ -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
123pub const pthread_mutex_t = extern struct {
2 __pthread_mutex_flag1: u16 = 0,
3 __pthread_mutex_flag2: u8 = 0,
4 __pthread_mutex_ceiling: u8 = 0,
5 __pthread_mutex_type: u16 = 0,
6 __pthread_mutex_magic: u16 = 0x4d58,
7 __pthread_mutex_lock: u64 = 0,
8 __pthread_mutex_data: u64 = 0,
24 flag1: u16 = 0,
25 flag2: u8 = 0,
26 ceiling: u8 = 0,
27 @"type": u16 = 0,
28 magic: u16 = 0x4d58,
29 lock: u64 = 0,
30 data: u64 = 0,
931};
1032pub const pthread_cond_t = extern struct {
11 __pthread_cond_flag: u32 = 0,
12 __pthread_cond_type: u16 = 0,
13 __pthread_cond_magic: u16 = 0x4356,
14 __pthread_cond_data: u64 = 0,
33 flag: [4]u8 = [_]u8{0} ** 4,
34 @"type": u16 = 0,
35 magic: u16 = 0x4356,
36 data: u64 = 0,
1537};
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 {
195195 };
196196
197197 var dead_fds: usize = 0;
198 // We ask for ensureCapacity with this much extra space. This has more of an
198 // We ask for ensureTotalCapacity with this much extra space. This has more of an
199199 // effect on small reads because once the reads start to get larger the amount
200200 // of space an ArrayList will allocate grows exponentially.
201201 const bump_amt = 512;
......@@ -215,7 +215,7 @@ pub const ChildProcess = struct {
215215 if (poll_fds[0].revents & os.POLL.IN != 0) {
216216 // stdout is ready.
217217 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);
219219 const buf = stdout.unusedCapacitySlice();
220220 if (buf.len == 0) return error.StdoutStreamTooLong;
221221 const nread = try os.read(poll_fds[0].fd, buf);
......@@ -230,7 +230,7 @@ pub const ChildProcess = struct {
230230 if (poll_fds[1].revents & os.POLL.IN != 0) {
231231 // stderr is ready.
232232 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);
234234 const buf = stderr.unusedCapacitySlice();
235235 if (buf.len == 0) return error.StderrStreamTooLong;
236236 const nread = try os.read(poll_fds[1].fd, buf);
......@@ -276,7 +276,8 @@ pub const ChildProcess = struct {
276276
277277 // Windows Async IO requires an initial call to ReadFile before waiting on the handle
278278 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);
280281 const buf = outs[i].unusedCapacitySlice();
281282 _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]);
282283 wait_objects[wait_object_count] = handles[i];
......@@ -318,7 +319,7 @@ pub const ChildProcess = struct {
318319
319320 outs[i].items.len += read_bytes;
320321 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);
322323 const buf = outs[i].unusedCapacitySlice();
323324 if (buf.len == 0) return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong;
324325 _ = 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 {
277277 if (self.sections.items.len == self.coff_header.number_of_sections)
278278 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
282282 const in = self.in_file.reader();
283283
lib/std/compress/deflate.zig+26-6
......@@ -58,8 +58,10 @@ const Huffman = struct {
5858 }
5959
6060 // All zero.
61 if (self.count[0] == code_length.len)
61 if (self.count[0] == code_length.len) {
62 self.min_code_len = 0;
6263 return;
64 }
6365
6466 var left: isize = 1;
6567 for (self.count[1..]) |val| {
......@@ -280,7 +282,7 @@ pub fn InflateStream(comptime ReaderType: type) type {
280282 return self.bits & mask;
281283 }
282284 fn readBits(self: *Self, bits: usize) !u32 {
283 const val = self.peekBits(bits);
285 const val = try self.peekBits(bits);
284286 self.discardBits(bits);
285287 return val;
286288 }
......@@ -487,6 +489,8 @@ pub fn InflateStream(comptime ReaderType: type) type {
487489 // We can't read PREFIX_LUT_BITS as we don't want to read past the
488490 // deflate stream end, use an incremental approach instead.
489491 var code_len = h.min_code_len;
492 if (code_len == 0)
493 return error.OutOfCodes;
490494 while (true) {
491495 _ = try self.peekBits(code_len);
492496 // Small optimization win, use as many bits as possible in the
......@@ -658,11 +662,27 @@ test "lengths overflow" {
658662 // f dy hlit hdist hclen 16 17 18 0 (18) x138 (18) x138 (18) x39 (16) x6
659663 // 1 10 11101 11101 0000 010 010 010 010 (11) 1111111 (11) 1111111 (11) 0011100 (01) 11
660664 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 {
663683 var window: [0x8000]u8 = undefined;
684 const reader = std.io.fixedBufferStream(data).reader();
664685 var inflate = inflateStream(reader, &window);
665
666 var buf: [1]u8 = undefined;
667 try std.testing.expectError(error.InvalidLength, inflate.read(&buf));
686 var inflated = try inflate.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize));
687 defer std.testing.allocator.free(inflated);
668688}
lib/std/crypto/benchmark.zig+1-1
......@@ -1,4 +1,4 @@
1// zig run benchmark.zig --release-fast --zig-lib-dir ..
1// zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig
22
33const std = @import("../std.zig");
44const builtin = std.builtin;
lib/std/crypto/blake3.zig+4-4
......@@ -398,10 +398,10 @@ pub const Blake3 = struct {
398398 return Blake3.init_internal(context_key_words, DERIVE_KEY_MATERIAL);
399399 }
400400
401 pub fn hash(in: []const u8, out: []u8, options: Options) void {
402 var hasher = Blake3.init(options);
403 hasher.update(in);
404 hasher.final(out);
401 pub fn hash(b: []const u8, out: []u8, options: Options) void {
402 var d = Blake3.init(options);
403 d.update(b);
404 d.final(out);
405405 }
406406
407407 fn pushCv(self: *Blake3, cv: [8]u32) void {
lib/std/debug.zig+44-12
......@@ -228,9 +228,32 @@ pub fn assert(ok: bool) void {
228228
229229pub fn panic(comptime format: []const u8, args: anytype) noreturn {
230230 @setCold(true);
231 // TODO: remove conditional once wasi / LLVM defines __builtin_return_address
232 const first_trace_addr = if (native_os == .wasi) null else @returnAddress();
233 panicExtra(null, first_trace_addr, format, args);
231
232 panicExtra(null, format, args);
233}
234
235/// `panicExtra` is useful when you want to print out an `@errorReturnTrace`
236/// and also print out some values.
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);
234257}
235258
236259/// Non-zero whenever the program triggered a panic.
......@@ -244,7 +267,9 @@ var panic_mutex = std.Thread.Mutex{};
244267/// This is used to catch and handle panics triggered by the panic handler.
245268threadlocal 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 {
248273 @setCold(true);
249274
250275 if (enable_segfault_handler) {
......@@ -271,7 +296,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
271296 const current_thread_id = std.Thread.getCurrentId();
272297 stderr.print("thread {} panic: ", .{current_thread_id}) catch os.abort();
273298 }
274 stderr.print(format ++ "\n", args) catch os.abort();
299 stderr.print("{s}\n", .{msg}) catch os.abort();
275300 if (trace) |t| {
276301 dumpStackTrace(t.*);
277302 }
......@@ -654,6 +679,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
654679 .openbsd,
655680 .macos,
656681 .windows,
682 .solaris,
657683 => return DebugInfo.init(allocator),
658684 else => return error.UnsupportedDebugInfo,
659685 }
......@@ -1420,7 +1446,7 @@ pub const ModuleDebugInfo = switch (native_os) {
14201446 };
14211447 }
14221448 },
1423 .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku => struct {
1449 .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris => struct {
14241450 base_address: usize,
14251451 dwarf: DW.DwarfInfo,
14261452 mapped_memory: []const u8,
......@@ -1468,7 +1494,7 @@ fn getDebugInfoAllocator() *mem.Allocator {
14681494
14691495/// Whether or not the current target can print useful debug information when a segfault occurs.
14701496pub const have_segfault_handling_support = switch (native_os) {
1471 .linux, .netbsd => true,
1497 .linux, .netbsd, .solaris => true,
14721498 .windows => true,
14731499 .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"),
14741500 else => false,
......@@ -1535,6 +1561,7 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v
15351561 .freebsd => @ptrToInt(info.addr),
15361562 .netbsd => @ptrToInt(info.info.reason.fault.addr),
15371563 .openbsd => @ptrToInt(info.data.fault.addr),
1564 .solaris => @ptrToInt(info.reason.fault.addr),
15381565 else => unreachable,
15391566 };
15401567
......@@ -1559,13 +1586,13 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v
15591586 .x86_64 => {
15601587 const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
15611588 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]),
15631590 .freebsd => @intCast(usize, ctx.mcontext.rip),
15641591 .openbsd => @intCast(usize, ctx.sc_rip),
15651592 else => unreachable,
15661593 };
15671594 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]),
15691596 .openbsd => @intCast(usize, ctx.sc_rbp),
15701597 .freebsd => @intCast(usize, ctx.mcontext.rbp),
15711598 else => unreachable,
......@@ -1624,9 +1651,14 @@ fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: u
16241651 os.abort();
16251652 } else {
16261653 switch (msg) {
1627 0 => panicExtra(null, exception_address, format.?, .{}),
1628 1 => panicExtra(null, exception_address, "Segmentation fault at address 0x{x}", .{info.ExceptionRecord.ExceptionInformation[1]}),
1629 2 => panicExtra(null, exception_address, "Illegal Instruction", .{}),
1654 0 => panicImpl(null, exception_address, format.?),
1655 1 => {
1656 const format_item = "Segmentation fault at address 0x{x}";
1657 var buf: [format_item.len + 64]u8 = undefined; // 64 is arbitrary, but sufficiently large
1658 const to_print = std.fmt.bufPrint(buf[0..buf.len], format_item, .{info.ExceptionRecord.ExceptionInformation[1]}) catch unreachable;
1659 panicImpl(null, exception_address, to_print);
1660 },
1661 2 => panicImpl(null, exception_address, "Illegal Instruction"),
16301662 else => unreachable,
16311663 }
16321664 }
lib/std/dynamic_library.zig+1-1
......@@ -14,7 +14,7 @@ const max = std.math.max;
1414pub const DynLib = switch (builtin.os.tag) {
1515 .linux => if (builtin.link_libc) DlDynlib else ElfDynLib,
1616 .windows => WindowsDynLib,
17 .macos, .tvos, .watchos, .ios, .freebsd, .netbsd, .openbsd, .dragonfly => DlDynlib,
17 .macos, .tvos, .watchos, .ios, .freebsd, .netbsd, .openbsd, .dragonfly, .solaris => DlDynlib,
1818 else => void,
1919};
2020
lib/std/elf.zig+97
......@@ -1591,3 +1591,100 @@ pub const PF_MASKOS = 0x0ff00000;
15911591
15921592/// Bits for processor-specific semantics.
15931593pub 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(
119119 }
120120 }
121121
122 /// Deprecated: call `ensureUnusedCapacity` or `ensureTotalCapacity`.
123 pub const ensureCapacity = ensureTotalCapacity;
124
122125 /// 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 {
124127 if (self.buf.len >= size) return;
125128 if (buffer_type == .Dynamic) {
126129 self.realign();
......@@ -135,7 +138,7 @@ pub fn LinearFifo(
135138 pub fn ensureUnusedCapacity(self: *Self, size: usize) error{OutOfMemory}!void {
136139 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);
139142 }
140143
141144 /// Returns number of items currently in fifo
......@@ -471,7 +474,7 @@ test "LinearFifo(u8, .Dynamic)" {
471474 }
472475
473476 {
474 try fifo.ensureCapacity(1);
477 try fifo.ensureTotalCapacity(1);
475478 var in_fbs = std.io.fixedBufferStream("pump test");
476479 var out_buf: [50]u8 = undefined;
477480 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;
3535/// fit into a UTF-8 encoded array of this length.
3636/// The byte count includes room for a null sentinel byte.
3737pub 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,
3939 // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
4040 // If it would require 4 UTF-8 bytes, then there would be a surrogate
4141 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
......@@ -298,10 +298,10 @@ pub const Dir = struct {
298298 pub const Kind = File.Kind;
299299 };
300300
301 const IteratorError = error{AccessDenied} || os.UnexpectedError;
301 const IteratorError = error{ AccessDenied, SystemResources } || os.UnexpectedError;
302302
303303 pub const Iterator = switch (builtin.os.tag) {
304 .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd => struct {
304 .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris => struct {
305305 dir: Dir,
306306 seek: i64,
307307 buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)),
......@@ -318,6 +318,7 @@ pub const Dir = struct {
318318 switch (builtin.os.tag) {
319319 .macos, .ios => return self.nextDarwin(),
320320 .freebsd, .netbsd, .dragonfly, .openbsd => return self.nextBsd(),
321 .solaris => return self.nextSolaris(),
321322 else => @compileError("unimplemented"),
322323 }
323324 }
......@@ -372,6 +373,60 @@ pub const Dir = struct {
372373 }
373374 }
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
375430 fn nextBsd(self: *Self) !?Entry {
376431 start_over: while (true) {
377432 if (self.index >= self.end_index) {
......@@ -704,6 +759,7 @@ pub const Dir = struct {
704759 .netbsd,
705760 .dragonfly,
706761 .openbsd,
762 .solaris,
707763 => return Iterator{
708764 .dir = self,
709765 .seek = 0,
......@@ -1556,7 +1612,7 @@ pub const Dir = struct {
15561612 error.AccessDenied => |e| switch (builtin.os.tag) {
15571613 // non-Linux POSIX systems return EPERM when trying to delete a directory, so
15581614 // 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 => {
15601616 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them)
15611617 const fstat = os.fstatatZ(self.fd, sub_path_c, os.AT.SYMLINK_NOFOLLOW) catch return e;
15621618 const is_dir = fstat.mode & os.S.IFMT == os.S.IFDIR;
......@@ -2441,6 +2497,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
24412497 }
24422498 switch (builtin.os.tag) {
24432499 .linux => return os.readlinkZ("/proc/self/exe", out_buffer),
2500 .solaris => return os.readlinkZ("/proc/self/path/a.out", out_buffer),
24442501 .freebsd, .dragonfly => {
24452502 var mib = [4]c_int{ os.CTL.KERN, os.KERN.PROC, os.KERN.PROC_PATHNAME, -1 };
24462503 var out_len: usize = out_buffer.len;
lib/std/fs/file.zig+34-18
......@@ -41,6 +41,8 @@ pub const File = struct {
4141 File,
4242 UnixDomainSocket,
4343 Whiteout,
44 Door,
45 EventPort,
4446 Unknown,
4547 };
4648
......@@ -320,28 +322,40 @@ pub const File = struct {
320322 const atime = st.atime();
321323 const mtime = st.mtime();
322324 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
323354 return Stat{
324355 .inode = st.ino,
325356 .size = @bitCast(u64, st.size),
326357 .mode = st.mode,
327 .kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) {
328 .BLOCK_DEVICE => Kind.BlockDevice,
329 .CHARACTER_DEVICE => Kind.CharacterDevice,
330 .DIRECTORY => Kind.Directory,
331 .SYMBOLIC_LINK => Kind.SymLink,
332 .REGULAR_FILE => Kind.File,
333 .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket,
334 else => Kind.Unknown,
335 } else switch (st.mode & os.S.IFMT) {
336 os.S.IFBLK => Kind.BlockDevice,
337 os.S.IFCHR => Kind.CharacterDevice,
338 os.S.IFDIR => Kind.Directory,
339 os.S.IFIFO => Kind.NamedPipe,
340 os.S.IFLNK => Kind.SymLink,
341 os.S.IFREG => Kind.File,
342 os.S.IFSOCK => Kind.UnixDomainSocket,
343 else => Kind.Unknown,
344 },
358 .kind = kind,
345359 .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
346360 .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
347361 .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
......@@ -852,6 +866,7 @@ pub const File = struct {
852866
853867 pub const LockError = error{
854868 SystemResources,
869 FileLocksNotSupported,
855870 } || os.UnexpectedError;
856871
857872 /// Blocks when an incompatible lock is held by another process.
......@@ -914,6 +929,7 @@ pub const File = struct {
914929 return os.flock(file.handle, os.LOCK.UN) catch |err| switch (err) {
915930 error.WouldBlock => unreachable, // unlocking can't block
916931 error.SystemResources => unreachable, // We are deallocating resources.
932 error.FileLocksNotSupported => unreachable, // We already got the lock.
917933 error.Unexpected => unreachable, // Resource deallocation must succeed.
918934 };
919935 }
lib/std/fs/get_app_data_dir.zig+1-1
......@@ -44,7 +44,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
4444 };
4545 return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname });
4646 },
47 .linux, .freebsd, .netbsd, .dragonfly, .openbsd => {
47 .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris => {
4848 const home_dir = os.getenv("HOME") orelse {
4949 // TODO look in /etc/passwd
5050 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 {
188188
189189test "Dir.realpath smoke test" {
190190 switch (builtin.os.tag) {
191 .linux, .windows, .macos, .ios, .watchos, .tvos => {},
191 .linux, .windows, .macos, .ios, .watchos, .tvos, .solaris => {},
192192 else => return error.SkipZigTest,
193193 }
194194
lib/std/hash/benchmark.zig+1-1
......@@ -1,4 +1,4 @@
1// zig run benchmark.zig --release-fast --zig-lib-dir ..
1// zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig
22
33const builtin = std.builtin;
44const std = @import("std");
lib/std/hash_map.zig+9-9
......@@ -1568,11 +1568,11 @@ test "std.hash_map basic usage" {
15681568 try expectEqual(total, sum);
15691569}
15701570
1571test "std.hash_map ensureCapacity" {
1571test "std.hash_map ensureTotalCapacity" {
15721572 var map = AutoHashMap(i32, i32).init(std.testing.allocator);
15731573 defer map.deinit();
15741574
1575 try map.ensureCapacity(20);
1575 try map.ensureTotalCapacity(20);
15761576 const initial_capacity = map.capacity();
15771577 try testing.expect(initial_capacity >= 20);
15781578 var i: i32 = 0;
......@@ -1583,13 +1583,13 @@ test "std.hash_map ensureCapacity" {
15831583 try testing.expect(initial_capacity == map.capacity());
15841584}
15851585
1586test "std.hash_map ensureCapacity with tombstones" {
1586test "std.hash_map ensureUnusedCapacity with tombstones" {
15871587 var map = AutoHashMap(i32, i32).init(std.testing.allocator);
15881588 defer map.deinit();
15891589
15901590 var i: i32 = 0;
15911591 while (i < 100) : (i += 1) {
1592 try map.ensureCapacity(@intCast(u32, map.count() + 1));
1592 try map.ensureUnusedCapacity(1);
15931593 map.putAssumeCapacity(i, i);
15941594 // Remove to create tombstones that still count as load in the hashmap.
15951595 _ = map.remove(i);
......@@ -1669,7 +1669,7 @@ test "std.hash_map clone" {
16691669 try expectEqual(b.get(3).?, 3);
16701670}
16711671
1672test "std.hash_map ensureCapacity with existing elements" {
1672test "std.hash_map ensureTotalCapacity with existing elements" {
16731673 var map = AutoHashMap(u32, u32).init(std.testing.allocator);
16741674 defer map.deinit();
16751675
......@@ -1677,16 +1677,16 @@ test "std.hash_map ensureCapacity with existing elements" {
16771677 try expectEqual(map.count(), 1);
16781678 try expectEqual(map.capacity(), @TypeOf(map).Unmanaged.minimal_capacity);
16791679
1680 try map.ensureCapacity(65);
1680 try map.ensureTotalCapacity(65);
16811681 try expectEqual(map.count(), 1);
16821682 try expectEqual(map.capacity(), 128);
16831683}
16841684
1685test "std.hash_map ensureCapacity satisfies max load factor" {
1685test "std.hash_map ensureTotalCapacity satisfies max load factor" {
16861686 var map = AutoHashMap(u32, u32).init(std.testing.allocator);
16871687 defer map.deinit();
16881688
1689 try map.ensureCapacity(127);
1689 try map.ensureTotalCapacity(127);
16901690 try expectEqual(map.capacity(), 256);
16911691}
16921692
......@@ -1870,7 +1870,7 @@ test "std.hash_map putAssumeCapacity" {
18701870 var map = AutoHashMap(u32, u32).init(std.testing.allocator);
18711871 defer map.deinit();
18721872
1873 try map.ensureCapacity(20);
1873 try map.ensureTotalCapacity(20);
18741874 var i: u32 = 0;
18751875 while (i < 20) : (i += 1) {
18761876 map.putAssumeCapacityNoClobber(i, i);
lib/std/heap/general_purpose_allocator.zig+1-4
......@@ -746,10 +746,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
746746
747747 const new_aligned_size = math.max(len, ptr_align);
748748 if (new_aligned_size > largest_bucket_object_size) {
749 try self.large_allocations.ensureCapacity(
750 self.backing_allocator,
751 self.large_allocations.count() + 1,
752 );
749 try self.large_allocations.ensureUnusedCapacity(self.backing_allocator, 1);
753750
754751 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(
6161 array_list: *std.ArrayListAligned(u8, alignment),
6262 max_append_size: usize,
6363 ) !void {
64 try array_list.ensureCapacity(math.min(max_append_size, 4096));
64 try array_list.ensureTotalCapacity(math.min(max_append_size, 4096));
6565 const original_len = array_list.items.len;
6666 var start_index: usize = original_len;
6767 while (true) {
......@@ -81,7 +81,7 @@ pub fn Reader(
8181 }
8282
8383 // 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);
8585 }
8686 }
8787
lib/std/json.zig+1-1
......@@ -1838,7 +1838,7 @@ fn parseInternal(
18381838 else => {},
18391839 }
18401840
1841 try arraylist.ensureCapacity(arraylist.items.len + 1);
1841 try arraylist.ensureUnusedCapacity(1);
18421842 const v = try parseInternal(ptrInfo.child, tok, tokens, options);
18431843 arraylist.appendAssumeCapacity(v);
18441844 }
lib/std/leb128.zig+12-2
......@@ -76,6 +76,14 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {
7676 const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift));
7777 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;
7878 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 }
7987 }
8088
8189 value |= temp;
......@@ -215,6 +223,8 @@ test "deserialize signed LEB128" {
215223 try testing.expectError(error.Overflow, test_read_ileb128(i32, "\x80\x80\x80\x80\x40"));
216224 try testing.expectError(error.Overflow, test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40"));
217225 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
219229 // Decode SLEB128
220230 try testing.expect((try test_read_ileb128(i64, "\x00")) == 0);
......@@ -233,8 +243,8 @@ test "deserialize signed LEB128" {
233243 try testing.expect((try test_read_ileb128(i8, "\xff\x7f")) == -1);
234244 try testing.expect((try test_read_ileb128(i16, "\xff\xff\x7f")) == -1);
235245 try testing.expect((try test_read_ileb128(i32, "\xff\xff\xff\xff\x7f")) == -1);
236 try testing.expect((try test_read_ileb128(i32, "\x80\x80\x80\x80\x08")) == -0x80000000);
237 try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")) == @bitCast(i64, @intCast(u64, 0x8000000000000000)));
246 try testing.expect((try test_read_ileb128(i32, "\x80\x80\x80\x80\x78")) == -0x80000000);
247 try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7f")) == @bitCast(i64, @intCast(u64, 0x8000000000000000)));
238248 try testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x40")) == -0x4000000000000000);
239249 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 {
552552 r.positive = a.positive;
553553 }
554554
555 /// r = a | b
555 /// r = a | b under 2s complement semantics.
556556 /// r may alias with a or b.
557557 ///
558558 /// a and b are zero-extended to the longer of a or b.
559559 ///
560560 /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`.
561561 pub fn bitOr(r: *Mutable, a: Const, b: Const) void {
562 if (a.limbs.len > b.limbs.len) {
563 llor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
564 r.len = a.limbs.len;
562 // Trivial cases, llsignedor does not support zero.
563 if (a.eqZero()) {
564 r.copy(b);
565 return;
566 } else if (b.eqZero()) {
567 r.copy(a);
568 return;
569 }
570
571 if (a.limbs.len >= b.limbs.len) {
572 r.positive = llsignedor(r.limbs, a.limbs, a.positive, b.limbs, b.positive);
573 r.normalize(if (b.positive) a.limbs.len else b.limbs.len);
565574 } else {
566 llor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
567 r.len = b.limbs.len;
575 r.positive = llsignedor(r.limbs, b.limbs, b.positive, a.limbs, a.positive);
576 r.normalize(if (a.positive) b.limbs.len else a.limbs.len);
568577 }
569578 }
570579
571 /// r = a & b
580 /// r = a & b under 2s complement semantics.
572581 /// r may alias with a or b.
573582 ///
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`.
575586 pub fn bitAnd(r: *Mutable, a: Const, b: Const) void {
576 if (a.limbs.len > b.limbs.len) {
577 lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
578 r.normalize(b.limbs.len);
587 // Trivial cases, llsignedand does not support zero.
588 if (a.eqZero()) {
589 r.copy(a);
590 return;
591 } else if (b.eqZero()) {
592 r.copy(b);
593 return;
594 }
595
596 if (a.limbs.len >= b.limbs.len) {
597 r.positive = llsignedand(r.limbs, a.limbs, a.positive, b.limbs, b.positive);
598 r.normalize(if (a.positive or b.positive) b.limbs.len else a.limbs.len + 1);
579599 } else {
580 lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
581 r.normalize(a.limbs.len);
600 r.positive = llsignedand(r.limbs, b.limbs, b.positive, a.limbs, a.positive);
601 r.normalize(if (a.positive or b.positive) a.limbs.len else b.limbs.len + 1);
582602 }
583603 }
584604
585 /// r = a ^ b
605 /// r = a ^ b under 2s complement semantics.
586606 /// r may alias with a or b.
587607 ///
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`.
589611 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
590621 if (a.limbs.len > b.limbs.len) {
591 llxor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
592 r.normalize(a.limbs.len);
622 r.positive = llsignedxor(r.limbs, a.limbs, a.positive, b.limbs, b.positive);
623 r.normalize(a.limbs.len + @boolToInt(a.positive != b.positive));
593624 } else {
594 llxor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
595 r.normalize(b.limbs.len);
625 r.positive = llsignedxor(r.limbs, b.limbs, b.positive, a.limbs, a.positive);
626 r.normalize(b.limbs.len + @boolToInt(a.positive != b.positive));
596627 }
597628 }
598629
......@@ -1834,7 +1865,11 @@ pub const Managed = struct {
18341865
18351866 /// r = a & b
18361867 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);
18381873 var m = r.toMutable();
18391874 m.bitAnd(a.toConst(), b.toConst());
18401875 r.setMetadata(m.positive, m.len);
......@@ -1842,7 +1877,9 @@ pub const Managed = struct {
18421877
18431878 /// r = a ^ b
18441879 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
18461883 var m = r.toMutable();
18471884 m.bitXor(a.toConst(), b.toConst());
18481885 r.setMetadata(m.positive, m.len);
......@@ -2221,42 +2258,299 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
22212258 }
22222259}
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 {
22252268 @setRuntimeSafety(debug_safety);
22262269 assert(r.len >= a.len);
22272270 assert(a.len >= b.len);
22282271
2229 var i: usize = 0;
2230 while (i < b.len) : (i += 1) {
2231 r[i] = a[i] | b[i];
2232 }
2233 while (i < a.len) : (i += 1) {
2234 r[i] = a[i];
2272 if (a_positive and b_positive) {
2273 // Trivial case, result is positive.
2274 var i: usize = 0;
2275 while (i < b.len) : (i += 1) {
2276 r[i] = a[i] | b[i];
2277 }
2278 while (i < a.len) : (i += 1) {
2279 r[i] = a[i];
2280 }
2281
2282 return true;
2283 } else if (!a_positive and b_positive) {
2284 // Result is negative.
2285 // r = (--a) | b
2286 // = ~(-a - 1) | b
2287 // = ~(-a - 1) | ~~b
2288 // = ~((-a - 1) & ~b)
2289 // = -(((-a - 1) & ~b) + 1)
2290
2291 var i: usize = 0;
2292 var a_borrow: u1 = 1;
2293 var r_carry: u1 = 1;
2294
2295 while (i < b.len) : (i += 1) {
2296 var a_limb: Limb = undefined;
2297 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
2298
2299 r[i] = a_limb & ~b[i];
2300 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2301 }
2302
2303 // In order for r_carry to be nonzero at this point, ~b[i] would need to be
2304 // all ones, which would require b[i] to be zero. This cannot be when
2305 // b is normalized, so there cannot be a carry here.
2306 // Also, x & ~b can only clear bits, so (x & ~b) <= x, meaning (-a - 1) + 1 never overflows.
2307 assert(r_carry == 0);
2308
2309 // With b = 0, we get (-a - 1) & ~0 = -a - 1.
2310 // Note, if a_borrow is zero we do not need to compute anything for
2311 // the higher limbs so we can early return here.
2312 while (i < a.len and a_borrow == 1) : (i += 1) {
2313 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i]));
2314 }
2315
2316 assert(a_borrow == 0); // a was 0.
2317
2318 return false;
2319 } else if (a_positive and !b_positive) {
2320 // Result is negative.
2321 // r = a | (--b)
2322 // = a | ~(-b - 1)
2323 // = ~~a | ~(-b - 1)
2324 // = ~(~a & (-b - 1))
2325 // = -((~a & (-b - 1)) + 1)
2326
2327 var i: usize = 0;
2328 var b_borrow: u1 = 1;
2329 var r_carry: u1 = 1;
2330
2331 while (i < b.len) : (i += 1) {
2332 var b_limb: Limb = undefined;
2333 b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb));
2334
2335 r[i] = ~a[i] & b_limb;
2336 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2337 }
2338
2339 // b is at least 1, so this should never underflow.
2340 assert(b_borrow == 0); // b was 0
2341
2342 // x & ~a can only clear bits, so (x & ~a) <= x, meaning (-b - 1) + 1 never overflows.
2343 assert(r_carry == 0);
2344
2345 // With b = 0 and b_borrow = 0, we get ~a & (-0 - 0) = ~a & 0 = 0.
2346 // Omit setting the upper bytes, just deal with those when calling llsignedor.
2347
2348 return false;
2349 } else {
2350 // Result is negative.
2351 // r = (--a) | (--b)
2352 // = ~(-a - 1) | ~(-b - 1)
2353 // = ~((-a - 1) & (-b - 1))
2354 // = -(~(~((-a - 1) & (-b - 1))) + 1)
2355 // = -((-a - 1) & (-b - 1) + 1)
2356
2357 var i: usize = 0;
2358 var a_borrow: u1 = 1;
2359 var b_borrow: u1 = 1;
2360 var r_carry: u1 = 1;
2361
2362 while (i < b.len) : (i += 1) {
2363 var a_limb: Limb = undefined;
2364 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
2365
2366 var b_limb: Limb = undefined;
2367 b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb));
2368
2369 r[i] = a_limb & b_limb;
2370 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2371 }
2372
2373 // b is at least 1, so this should never underflow.
2374 assert(b_borrow == 0); // b was 0
2375
2376 // Can never overflow because in order for b_limb to be maxInt(Limb),
2377 // b_borrow would need to equal 1.
2378
2379 // x & y can only clear bits, meaning x & y <= x and x & y <= y. This implies that
2380 // for x = a - 1 and y = b - 1, the +1 term would never cause an overflow.
2381 assert(r_carry == 0);
2382
2383 // With b = 0 and b_borrow = 0 we get (-a - 1) & (-0 - 0) = (-a - 1) & 0 = 0.
2384 // Omit setting the upper bytes, just deal with those when calling llsignedor.
2385 return false;
22352386 }
22362387}
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 {
22392396 @setRuntimeSafety(debug_safety);
2240 assert(r.len >= b.len);
2397 assert(a.len != 0 and b.len != 0);
2398 assert(r.len >= a.len);
22412399 assert(a.len >= b.len);
22422400
2243 var i: usize = 0;
2244 while (i < b.len) : (i += 1) {
2245 r[i] = a[i] & b[i];
2401 if (a_positive and b_positive) {
2402 // Trivial case, result is positive.
2403 var i: usize = 0;
2404 while (i < b.len) : (i += 1) {
2405 r[i] = a[i] & b[i];
2406 }
2407
2408 // With b = 0 we have a & 0 = 0, so the upper bytes are zero.
2409 // Omit setting them here and simply discard them whenever
2410 // llsignedand is called.
2411
2412 return true;
2413 } else if (!a_positive and b_positive) {
2414 // Result is positive.
2415 // r = (--a) & b
2416 // = ~(-a - 1) & b
2417
2418 var i: usize = 0;
2419 var a_borrow: u1 = 1;
2420
2421 while (i < b.len) : (i += 1) {
2422 var a_limb: Limb = undefined;
2423 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
2424 r[i] = ~a_limb & b[i];
2425 }
2426
2427 // With b = 0 we have ~(a - 1) & 0 = 0, so the upper bytes are zero.
2428 // Omit setting them here and simply discard them whenever
2429 // llsignedand is called.
2430
2431 return true;
2432 } else if (a_positive and !b_positive) {
2433 // Result is positive.
2434 // r = a & (--b)
2435 // = a & ~(-b - 1)
2436
2437 var i: usize = 0;
2438 var b_borrow: u1 = 1;
2439
2440 while (i < b.len) : (i += 1) {
2441 var a_limb: Limb = undefined;
2442 b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &a_limb));
2443 r[i] = a[i] & ~a_limb;
2444 }
2445
2446 assert(b_borrow == 0); // b was 0
2447
2448 // With b = 0 and b_borrow = 0 we have a & ~(-0 - 0) = a & 0 = 0, so
2449 // the upper bytes are zero. Omit setting them here and simply discard
2450 // them whenever llsignedand is called.
2451
2452 return true;
2453 } else {
2454 // Result is negative.
2455 // r = (--a) & (--b)
2456 // = ~(-a - 1) & ~(-b - 1)
2457 // = ~((-a - 1) | (-b - 1))
2458 // = -(((-a - 1) | (-b - 1)) + 1)
2459
2460 var i: usize = 0;
2461 var a_borrow: u1 = 1;
2462 var b_borrow: u1 = 1;
2463 var r_carry: u1 = 1;
2464
2465 while (i < b.len) : (i += 1) {
2466 var a_limb: Limb = undefined;
2467 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
2468
2469 var b_limb: Limb = undefined;
2470 b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb));
2471
2472 r[i] = a_limb | b_limb;
2473 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2474 }
2475
2476 // b is at least 1, so this should never underflow.
2477 assert(b_borrow == 0); // b was 0
2478
2479 // With b = 0 and b_borrow = 0 we get (-a - 1) | (-0 - 0) = (-a - 1) | 0 = -a - 1.
2480 while (i < a.len) : (i += 1) {
2481 a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i]));
2482 r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
2483 }
2484
2485 assert(a_borrow == 0); // a was 0.
2486
2487 // The final addition can overflow here, so we need to keep that in mind.
2488 r[i] = r_carry;
2489
2490 return false;
22462491 }
22472492}
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);
22502503 assert(r.len >= a.len);
22512504 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
22532522 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
22542527 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]));
22562536 }
2537
22572538 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]));
22592541 }
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;
22602554}
22612555
22622556/// r MUST NOT alias x.
lib/std/math/big/int_test.zig+210
......@@ -5,6 +5,7 @@ const Managed = std.math.big.int.Managed;
55const Mutable = std.math.big.int.Mutable;
66const Limb = std.math.big.Limb;
77const DoubleLimb = std.math.big.DoubleLimb;
8const SignedDoubleLimb = std.math.big.SignedDoubleLimb;
89const maxInt = std.math.maxInt;
910const minInt = std.math.minInt;
1011
......@@ -1364,6 +1365,83 @@ test "big.int bitwise and multi-limb" {
13641365 try testing.expect((try a.to(u128)) == 0);
13651366}
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
13671445test "big.int bitwise xor simple" {
13681446 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
13691447 defer a.deinit();
......@@ -1386,6 +1464,72 @@ test "big.int bitwise xor multi-limb" {
13861464 try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb));
13871465}
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
13891533test "big.int bitwise or simple" {
13901534 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
13911535 defer a.deinit();
......@@ -1409,6 +1553,72 @@ test "big.int bitwise or multi-limb" {
14091553 try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));
14101554}
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
14121622test "big.int var args" {
14131623 var a = try Managed.initSet(testing.allocator, 5);
14141624 defer a.deinit();
lib/std/mem.zig+42
......@@ -147,6 +147,46 @@ test "mem.Allocator basics" {
147147 try testing.expectError(error.OutOfMemory, failAllocator.allocSentinel(u8, 1, 0));
148148}
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
150190/// Copy all of source into dest at position 0.
151191/// dest.len must be >= source.len.
152192/// 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
24722512 .is_volatile = info.is_volatile,
24732513 .is_allowzero = info.is_allowzero,
24742514 .alignment = info.alignment,
2515 .address_space = info.address_space,
24752516 .child = child,
24762517 .sentinel = null,
24772518 },
......@@ -2960,6 +3001,7 @@ fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: u29) typ
29603001 .is_volatile = info.is_volatile,
29613002 .is_allowzero = info.is_allowzero,
29623003 .alignment = new_alignment,
3004 .address_space = info.address_space,
29633005 .child = info.child,
29643006 .sentinel = null,
29653007 },
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
313313 const new_byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;
314314 const rc = try self.resizeFn(self, old_byte_slice, Slice.alignment, new_byte_count, 0, @returnAddress());
315315 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];
317317 return mem.bytesAsSlice(T, new_byte_slice);
318318}
319319
lib/std/meta.zig+3
......@@ -235,6 +235,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
235235 .is_const = info.is_const,
236236 .is_volatile = info.is_volatile,
237237 .alignment = info.alignment,
238 .address_space = info.address_space,
238239 .child = @Type(.{
239240 .Array = .{
240241 .len = array_info.len,
......@@ -254,6 +255,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
254255 .is_const = info.is_const,
255256 .is_volatile = info.is_volatile,
256257 .alignment = info.alignment,
258 .address_space = info.address_space,
257259 .child = info.child,
258260 .is_allowzero = info.is_allowzero,
259261 .sentinel = sentinel_val,
......@@ -271,6 +273,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
271273 .is_const = ptr_info.is_const,
272274 .is_volatile = ptr_info.is_volatile,
273275 .alignment = ptr_info.alignment,
276 .address_space = ptr_info.address_space,
274277 .child = ptr_info.child,
275278 .is_allowzero = ptr_info.is_allowzero,
276279 .sentinel = sentinel_val,
lib/std/multi_array_list.zig+2-2
......@@ -189,7 +189,7 @@ pub fn MultiArrayList(comptime S: type) type {
189189 /// sets the given index to the specified element. May reallocate
190190 /// and invalidate iterators.
191191 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);
193193 self.insertAssumeCapacity(index, elem);
194194 }
195195
......@@ -376,7 +376,7 @@ pub fn MultiArrayList(comptime S: type) type {
376376 pub fn clone(self: Self, gpa: *Allocator) !Self {
377377 var result = Self{};
378378 errdefer result.deinit(gpa);
379 try result.ensureCapacity(gpa, self.len);
379 try result.ensureTotalCapacity(gpa, self.len);
380380 result.len = self.len;
381381 const self_slice = self.slice();
382382 const result_slice = result.slice();
lib/std/net.zig+1-1
......@@ -157,7 +157,7 @@ pub const Address = extern union {
157157 unreachable;
158158 }
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)});
161161 },
162162 else => unreachable,
163163 }
lib/std/net/test.zig+13
......@@ -90,6 +90,19 @@ test "parse and render IPv4 addresses" {
9090 try testing.expectError(error.NonCanonical, net.Address.parseIp4("127.01.0.1", 0));
9191}
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
93106test "resolve DNS" {
94107 if (builtin.os.tag == .wasi) return error.SkipZigTest;
95108
lib/std/os.zig+24
......@@ -31,6 +31,7 @@ pub const freebsd = std.c;
3131pub const haiku = std.c;
3232pub const netbsd = std.c;
3333pub const openbsd = std.c;
34pub const solaris = std.c;
3435pub const linux = @import("os/linux.zig");
3536pub const uefi = @import("os/uefi.zig");
3637pub const wasi = @import("os/wasi.zig");
......@@ -64,8 +65,10 @@ else switch (builtin.os.tag) {
6465};
6566
6667pub const AF = system.AF;
68pub const AF_SUN = system.AF_SUN;
6769pub const ARCH = system.ARCH;
6870pub const AT = system.AT;
71pub const AT_SUN = system.AT_SUN;
6972pub const CLOCK = system.CLOCK;
7073pub const CPU_COUNT = system.CPU_COUNT;
7174pub const CTL = system.CTL;
......@@ -101,6 +104,7 @@ pub const RR = system.RR;
101104pub const S = system.S;
102105pub const SA = system.SA;
103106pub const SC = system.SC;
107pub const _SC = system._SC;
104108pub const SEEK = system.SEEK;
105109pub const SHUT = system.SHUT;
106110pub const SIG = system.SIG;
......@@ -143,6 +147,10 @@ pub const off_t = system.off_t;
143147pub const oflags_t = system.oflags_t;
144148pub const pid_t = system.pid_t;
145149pub 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;
146154pub const rights_t = system.rights_t;
147155pub const rlim_t = system.rlim_t;
148156pub const rlimit = system.rlimit;
......@@ -2038,6 +2046,7 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr
20382046 .NOTDIR => return error.NotDir,
20392047 .NOMEM => return error.SystemResources,
20402048 .ROFS => return error.ReadOnlyFileSystem,
2049 .EXIST => return error.DirNotEmpty,
20412050 .NOTEMPTY => return error.DirNotEmpty,
20422051
20432052 .INVAL => unreachable, // invalid flags, or pathname has . as last component
......@@ -4492,8 +4501,12 @@ pub const FlockError = error{
44924501
44934502 /// The kernel ran out of memory for allocating file locks
44944503 SystemResources,
4504
4505 /// The underlying filesystem does not support file locks
4506 FileLocksNotSupported,
44954507} || UnexpectedError;
44964508
4509/// Depending on the operating system `flock` may or may not interact with `fcntl` locks made by other processes.
44974510pub fn flock(fd: fd_t, operation: i32) FlockError!void {
44984511 while (true) {
44994512 const rc = system.flock(fd, operation);
......@@ -4504,6 +4517,7 @@ pub fn flock(fd: fd_t, operation: i32) FlockError!void {
45044517 .INVAL => unreachable, // invalid parameters
45054518 .NOLCK => return error.SystemResources,
45064519 .AGAIN => return error.WouldBlock, // TODO: integrate with async instead of just returning an error
4520 .OPNOTSUPP => return error.FileLocksNotSupported,
45074521 else => |err| return unexpectedErrno(err),
45084522 }
45094523 }
......@@ -4667,6 +4681,16 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
46674681 };
46684682 return target;
46694683 },
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 },
46704694 else => @compileError("querying for canonical path of a handle is unsupported on this host"),
46714695 }
46724696}
lib/std/os/linux.zig+9-7
......@@ -89,6 +89,7 @@ pub const user_desc = arch_bits.user_desc;
8989pub const tls = @import("linux/tls.zig");
9090pub const pie = @import("linux/start_pie.zig");
9191pub const BPF = @import("linux/bpf.zig");
92pub const IOCTL = @import("linux/ioctl.zig");
9293
9394pub const MAP = struct {
9495 pub usingnamespace arch_bits.MAP;
......@@ -2585,18 +2586,18 @@ pub const T = struct {
25852586 pub const IOCGSID = 0x5429;
25862587 pub const IOCGRS485 = 0x542E;
25872588 pub const IOCSRS485 = 0x542F;
2588 pub const IOCGPTN = 0x80045430;
2589 pub const IOCSPTLCK = 0x40045431;
2590 pub const IOCGDEV = 0x80045432;
2589 pub const IOCGPTN = IOCTL.IOR('T', 0x30, c_uint);
2590 pub const IOCSPTLCK = IOCTL.IOW('T', 0x31, c_int);
2591 pub const IOCGDEV = IOCTL.IOR('T', 0x32, c_uint);
25912592 pub const CGETX = 0x5432;
25922593 pub const CSETX = 0x5433;
25932594 pub const CSETXF = 0x5434;
25942595 pub const CSETXW = 0x5435;
2595 pub const IOCSIG = 0x40045436;
2596 pub const IOCSIG = IOCTL.IOW('T', 0x36, c_int);
25962597 pub const IOCVHANGUP = 0x5437;
2597 pub const IOCGPKT = 0x80045438;
2598 pub const IOCGPTLCK = 0x80045439;
2599 pub const IOCGEXCL = 0x80045440;
2598 pub const IOCGPKT = IOCTL.IOR('T', 0x38, c_int);
2599 pub const IOCGPTLCK = IOCTL.IOR('T', 0x39, c_int);
2600 pub const IOCGEXCL = IOCTL.IOR('T', 0x40, c_int);
26002601};
26012602
26022603pub const EPOLL = struct {
......@@ -2923,6 +2924,7 @@ pub const sockaddr = extern struct {
29232924 family: sa_family_t,
29242925 data: [14]u8,
29252926
2927 pub const SS_MAXSIZE = 128;
29262928 pub const storage = std.x.os.Socket.Address.Native.Storage;
29272929
29282930 /// IPv4 socket address
lib/std/os/linux/io_uring.zig+128
......@@ -404,6 +404,25 @@ pub const IO_Uring = struct {
404404 return sqe;
405405 }
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
407426 /// Queues (but does not submit) an SQE to perform a `pwritev()`.
408427 /// Returns a pointer to the SQE so that you can further modify the SQE for advanced use cases.
409428 /// 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 {
421440 return sqe;
422441 }
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
424462 /// Queues (but does not submit) an SQE to perform an `accept4(2)` on a socket.
425463 /// Returns a pointer to the SQE.
426464 pub fn accept(
......@@ -674,6 +712,29 @@ pub const IO_Uring = struct {
674712 try handle_registration_result(res);
675713 }
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
677738 fn handle_registration_result(res: usize) !void {
678739 switch (linux.getErrno(res)) {
679740 .SUCCESS => {},
......@@ -905,6 +966,16 @@ pub fn io_uring_prep_writev(
905966 io_uring_prep_rw(.WRITEV, sqe, fd, @ptrToInt(iovecs.ptr), iovecs.len, offset);
906967}
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
908979pub fn io_uring_prep_accept(
909980 sqe: *io_uring_sqe,
910981 fd: os.fd_t,
......@@ -1282,6 +1353,63 @@ test "write/read" {
12821353 try testing.expectEqualSlices(u8, buffer_write[0..], buffer_read[0..]);
12831354}
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
12851413test "openat" {
12861414 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 {
674674pub const off_t = i64;
675675pub const ino_t = u64;
676676pub 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
678682// The `stat64` definition used by the kernel.
679683pub 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 {
188188}
189189
190190test "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 }
192195 var cwd = fs.cwd();
193196
194197 cwd.deleteFile("example.txt") catch {};
......@@ -222,7 +225,10 @@ test "link with relative paths" {
222225}
223226
224227test "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 }
226232 var cwd = fs.cwd();
227233 var tmp = tmpDir(.{});
228234
......@@ -634,8 +640,10 @@ test "fcntl" {
634640}
635641
636642test "signalfd" {
637 if (native_os != .linux)
638 return error.SkipZigTest;
643 switch (native_os) {
644 .linux, .solaris => {},
645 else => return error.SkipZigTest,
646 }
639647 _ = std.os.signalfd;
640648}
641649
......@@ -658,8 +666,10 @@ test "sync" {
658666}
659667
660668test "fsync" {
661 if (native_os != .linux and native_os != .windows)
662 return error.SkipZigTest;
669 switch (native_os) {
670 .linux, .windows, .solaris => {},
671 else => return error.SkipZigTest,
672 }
663673
664674 var tmp = tmpDir(.{});
665675 defer tmp.cleanup();
......@@ -754,7 +764,10 @@ test "sigaction" {
754764}
755765
756766test "dup & dup2" {
757 if (native_os != .linux) return error.SkipZigTest;
767 switch (native_os) {
768 .linux, .solaris => {},
769 else => return error.SkipZigTest,
770 }
758771
759772 var tmp = tmpDir(.{});
760773 defer tmp.cleanup();
lib/std/os/windows/kernel32.zig+4
......@@ -152,6 +152,8 @@ pub extern "kernel32" fn GetCommandLineW() callconv(WINAPI) LPWSTR;
152152
153153pub extern "kernel32" fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) callconv(WINAPI) BOOL;
154154
155pub extern "kernel32" fn GetConsoleOutputCP() callconv(WINAPI) UINT;
156
155157pub extern "kernel32" fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) callconv(WINAPI) BOOL;
156158pub extern "kernel32" fn FillConsoleOutputCharacterA(hConsoleOutput: HANDLE, cCharacter: CHAR, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfCharsWritten: *DWORD) callconv(WINAPI) BOOL;
157159pub 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(
286288 Add: BOOL,
287289) callconv(WINAPI) BOOL;
288290
291pub extern "kernel32" fn SetConsoleOutputCP(wCodePageID: UINT) callconv(WINAPI) BOOL;
292
289293pub extern "kernel32" fn SetFileCompletionNotificationModes(
290294 FileHandle: HANDLE,
291295 Flags: UCHAR,
lib/std/os/windows/ws2_32.zig+1
......@@ -1105,6 +1105,7 @@ pub const sockaddr = extern struct {
11051105 family: ADDRESS_FAMILY,
11061106 data: [14]u8,
11071107
1108 pub const SS_MAXSIZE = 128;
11081109 pub const storage = std.x.os.Socket.Address.Native.Storage;
11091110
11101111 /// IPv4 socket address
lib/std/priority_dequeue.zig+14-5
......@@ -43,13 +43,13 @@ pub fn PriorityDequeue(comptime T: type) type {
4343
4444 /// Insert a new element, maintaining priority.
4545 pub fn add(self: *Self, elem: T) !void {
46 try ensureCapacity(self, self.len + 1);
46 try self.ensureUnusedCapacity(1);
4747 addUnchecked(self, elem);
4848 }
4949
5050 /// Add each element in `items` to the dequeue.
5151 pub fn addSlice(self: *Self, items: []const T) !void {
52 try self.ensureCapacity(self.len + items.len);
52 try self.ensureUnusedCapacity(items.len);
5353 for (items) |e| {
5454 self.addUnchecked(e);
5555 }
......@@ -359,7 +359,11 @@ pub fn PriorityDequeue(comptime T: type) type {
359359 return queue;
360360 }
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 {
363367 var better_capacity = self.capacity();
364368 if (better_capacity >= new_capacity) return;
365369 while (true) {
......@@ -369,6 +373,11 @@ pub fn PriorityDequeue(comptime T: type) type {
369373 self.items = try self.allocator.realloc(self.items, better_capacity);
370374 }
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
372381 /// Reduce allocated capacity to `new_len`.
373382 pub fn shrinkAndFree(self: *Self, new_len: usize) void {
374383 assert(new_len <= self.items.len);
......@@ -824,7 +833,7 @@ test "std.PriorityDequeue: shrinkAndFree" {
824833 var queue = PDQ.init(testing.allocator, lessThanComparison);
825834 defer queue.deinit();
826835
827 try queue.ensureCapacity(4);
836 try queue.ensureTotalCapacity(4);
828837 try expect(queue.capacity() >= 4);
829838
830839 try queue.add(1);
......@@ -940,7 +949,7 @@ fn fuzzTestMinMax(rng: *std.rand.Random, queue_size: usize) !void {
940949
941950fn generateRandomSlice(allocator: *std.mem.Allocator, rng: *std.rand.Random, size: usize) ![]u32 {
942951 var array = std.ArrayList(u32).init(allocator);
943 try array.ensureCapacity(size);
952 try array.ensureTotalCapacity(size);
944953
945954 var i: usize = 0;
946955 while (i < size) : (i += 1) {
lib/std/priority_queue.zig+13-4
......@@ -42,7 +42,7 @@ pub fn PriorityQueue(comptime T: type) type {
4242
4343 /// Insert a new element, maintaining priority.
4444 pub fn add(self: *Self, elem: T) !void {
45 try ensureCapacity(self, self.len + 1);
45 try self.ensureUnusedCapacity(1);
4646 addUnchecked(self, elem);
4747 }
4848
......@@ -69,7 +69,7 @@ pub fn PriorityQueue(comptime T: type) type {
6969
7070 /// Add each element in `items` to the queue.
7171 pub fn addSlice(self: *Self, items: []const T) !void {
72 try self.ensureCapacity(self.len + items.len);
72 try self.ensureUnusedCapacity(items.len);
7373 for (items) |e| {
7474 self.addUnchecked(e);
7575 }
......@@ -175,7 +175,11 @@ pub fn PriorityQueue(comptime T: type) type {
175175 return queue;
176176 }
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 {
179183 var better_capacity = self.capacity();
180184 if (better_capacity >= new_capacity) return;
181185 while (true) {
......@@ -185,6 +189,11 @@ pub fn PriorityQueue(comptime T: type) type {
185189 self.items = try self.allocator.realloc(self.items, better_capacity);
186190 }
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
188197 /// Reduce allocated capacity to `new_len`.
189198 pub fn shrinkAndFree(self: *Self, new_len: usize) void {
190199 assert(new_len <= self.items.len);
......@@ -483,7 +492,7 @@ test "std.PriorityQueue: shrinkAndFree" {
483492 var queue = PQ.init(testing.allocator, lessThan);
484493 defer queue.deinit();
485494
486 try queue.ensureCapacity(4);
495 try queue.ensureTotalCapacity(4);
487496 try expect(queue.capacity() >= 4);
488497
489498 try queue.add(1);
lib/std/process.zig+2-1
......@@ -625,7 +625,7 @@ pub const UserInfo = struct {
625625/// POSIX function which gets a uid from username.
626626pub fn getUserInfo(name: []const u8) !UserInfo {
627627 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),
629629 else => @compileError("Unsupported OS"),
630630 };
631631}
......@@ -753,6 +753,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]
753753 .netbsd,
754754 .dragonfly,
755755 .openbsd,
756 .solaris,
756757 => {
757758 var paths = List.init(allocator);
758759 errdefer {
lib/std/special/c.zig+43-1174
......@@ -1,177 +1,36 @@
1// This is Zig's multi-target implementation of libc.
2// When builtin.link_libc is true, we need to export all the functions and
3// provide an entire C API.
4// Otherwise, only the functions which LLVM generates calls to need to be generated,
5// such as memcpy, memset, and some math functions.
1//! This is Zig's multi-target implementation of libc.
2//! When builtin.link_libc is true, we need to export all the functions and
3//! provide an entire C API.
4//! Otherwise, only the functions which LLVM generates calls to need to be generated,
5//! such as memcpy, memset, and some math functions.
66
77const std = @import("std");
8const builtin = std.builtin;
9const maxInt = std.math.maxInt;
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;
8const builtin = @import("builtin");
9const native_os = builtin.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};
2711comptime {
28 if (is_freestanding and is_wasm and builtin.link_libc) {
29 @export(wasm_start, .{ .name = "_start", .linkage = .Strong });
30 }
31 if (builtin.link_libc) {
32 @export(strcmp, .{ .name = "strcmp", .linkage = .Strong });
33 @export(strncmp, .{ .name = "strncmp", .linkage = .Strong });
34 @export(strerror, .{ .name = "strerror", .linkage = .Strong });
35 @export(strlen, .{ .name = "strlen", .linkage = .Strong });
36 @export(strcpy, .{ .name = "strcpy", .linkage = .Strong });
37 @export(strncpy, .{ .name = "strncpy", .linkage = .Strong });
38 @export(strcat, .{ .name = "strcat", .linkage = .Strong });
39 @export(strncat, .{ .name = "strncat", .linkage = .Strong });
40 } else if (is_msvc) {
41 @export(_fltused, .{ .name = "_fltused", .linkage = .Strong });
42 }
43}
44
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;
12 // When the self-hosted compiler is further along, all the logic from c_stage1.zig will
13 // be migrated to this file and then c_stage1.zig will be deleted. Until then we have a
14 // simpler implementation of c.zig that only uses features already implemented in self-hosted.
15 if (builtin.zig_is_stage2) {
16 @export(memset, .{ .name = "memset", .linkage = .Strong });
17 @export(memcpy, .{ .name = "memcpy", .linkage = .Strong });
18 } else {
19 _ = @import("c_stage1.zig");
15320 }
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);
16721}
16822
16923// Avoid dragging in the runtime safety mechanisms into this .o file,
17024// 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);
17227 _ = error_return_trace;
28 if (builtin.zig_is_stage2) {
29 while (true) {
30 @breakpoint();
31 }
32 }
17333 if (builtin.is_test) {
174 @setCold(true);
17534 std.debug.panic("{s}", .{msg});
17635 }
17736 if (native_os != .freestanding and native_os != .other) {
......@@ -180,1028 +39,38 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn
18039 while (true) {}
18140}
18241
183export fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8 {
184 @setRuntimeSafety(false);
185
186 var index: usize = 0;
187 while (index != n) : (index += 1)
188 dest.?[index] = c;
189
190 return dest;
191}
192
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 {
42fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.C) ?[*]u8 {
21043 @setRuntimeSafety(false);
21144
212 if (@ptrToInt(dest) < @ptrToInt(src)) {
213 var index: usize = 0;
214 while (index != n) : (index += 1) {
215 dest.?[index] = src.?[index];
216 }
217 } else {
218 var index = n;
219 while (index != 0) {
220 index -= 1;
221 dest.?[index] = src.?[index];
45 if (len != 0) {
46 var d = dest.?;
47 var n = len;
48 while (true) {
49 d.* = c;
50 n -= 1;
51 if (n == 0) break;
52 d += 1;
22253 }
22354 }
22455
22556 return dest;
22657}
22758
228export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) c_int {
229 @setRuntimeSafety(false);
230
231 var index: usize = 0;
232 while (index != n) : (index += 1) {
233 const compare_val = @bitCast(i8, vl.?[index] -% vr.?[index]);
234 if (compare_val != 0) {
235 return compare_val;
236 }
237 }
238
239 return 0;
240}
241
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 {
59fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, len: usize) callconv(.C) ?[*]u8 {
77860 @setRuntimeSafety(false);
77961
780 const bits = @typeInfo(T).Float.bits;
781 const uint = std.meta.Int(.unsigned, bits);
782 const log2uint = math.Log2Int(uint);
783 const digits = if (T == f32) 23 else 52;
784 const exp_bits = if (T == f32) 9 else 12;
785 const bits_minus_1 = bits - 1;
786 const mask = if (T == f32) 0xff else 0x7ff;
787 var ux = @bitCast(uint, x);
788 var uy = @bitCast(uint, y);
789 var ex = @intCast(i32, (ux >> digits) & mask);
790 var ey = @intCast(i32, (uy >> digits) & mask);
791 const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1);
792 var i: uint = undefined;
793
794 if (uy << 1 == 0 or isNan(@bitCast(T, uy)) or ex == mask)
795 return (x * y) / (x * y);
796
797 if (ux << 1 <= uy << 1) {
798 if (ux << 1 == uy << 1)
799 return 0 * x;
800 return x;
801 }
802
803 // normalize x and y
804 if (ex == 0) {
805 i = ux << exp_bits;
806 while (i >> bits_minus_1 == 0) : ({
807 ex -= 1;
808 i <<= 1;
809 }) {}
810 ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1));
811 } else {
812 ux &= maxInt(uint) >> exp_bits;
813 ux |= 1 << digits;
814 }
815 if (ey == 0) {
816 i = uy << exp_bits;
817 while (i >> bits_minus_1 == 0) : ({
818 ey -= 1;
819 i <<= 1;
820 }) {}
821 uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1));
822 } else {
823 uy &= maxInt(uint) >> exp_bits;
824 uy |= 1 << digits;
825 }
826
827 // x mod y
828 while (ex > ey) : (ex -= 1) {
829 i = ux -% uy;
830 if (i >> bits_minus_1 == 0) {
831 if (i == 0)
832 return 0 * x;
833 ux = i;
834 }
835 ux <<= 1;
836 }
837 i = ux -% uy;
838 if (i >> bits_minus_1 == 0) {
839 if (i == 0)
840 return 0 * x;
841 ux = i;
842 }
843 while (ux >> digits == 0) : ({
844 ux <<= 1;
845 ex -= 1;
846 }) {}
847
848 // scale result up
849 if (ex > 0) {
850 ux -%= 1 << digits;
851 ux |= @as(uint, @bitCast(u32, ex)) << digits;
852 } else {
853 ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1));
854 }
855 if (T == f32) {
856 ux |= sx;
857 } else {
858 ux |= @intCast(uint, sx) << bits_minus_1;
859 }
860 return @bitCast(T, ux);
861}
862
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;
62 if (len != 0) {
63 var d = dest.?;
64 var s = src.?;
65 var n = len;
66 while (true) {
67 d.* = s.*;
68 n -= 1;
69 if (n == 0) break;
70 d += 1;
71 s += 1;
98172 }
982 m -= @intCast(i32, i) - 1;
983 ix0 |= ix1 >> @intCast(u5, 32 - i);
984 ix1 <<= @intCast(u5, i);
98573 }
98674
987 // unbias exponent
988 m -= 1023;
989 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
990 if (m & 1 != 0) {
991 ix0 += ix0 + (ix1 >> 31);
992 ix1 = ix1 +% ix1;
993 }
994 m >>= 1;
995
996 // sqrt(x) bit by bit
997 ix0 += ix0 + (ix1 >> 31);
998 ix1 = ix1 +% ix1;
999
1000 var q: u32 = 0;
1001 var q1: u32 = 0;
1002 var s0: u32 = 0;
1003 var s1: u32 = 0;
1004 var r: u32 = 0x00200000;
1005 var t: u32 = undefined;
1006 var t1: u32 = undefined;
1007
1008 while (r != 0) {
1009 t = s0 +% r;
1010 if (t <= ix0) {
1011 s0 = t + r;
1012 ix0 -= t;
1013 q += r;
1014 }
1015 ix0 = ix0 +% ix0 +% (ix1 >> 31);
1016 ix1 = ix1 +% ix1;
1017 r >>= 1;
1018 }
1019
1020 r = sign;
1021 while (r != 0) {
1022 t1 = s1 +% r;
1023 t = s0;
1024 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
1025 s1 = t1 +% r;
1026 if (t1 & sign == sign and s1 & sign == 0) {
1027 s0 += 1;
1028 }
1029 ix0 -= t;
1030 if (ix1 < t1) {
1031 ix0 -= 1;
1032 }
1033 ix1 = ix1 -% t1;
1034 q1 += r;
1035 }
1036 ix0 = ix0 +% ix0 +% (ix1 >> 31);
1037 ix1 = ix1 +% ix1;
1038 r >>= 1;
1039 }
1040
1041 // rounding direction
1042 if (ix0 | ix1 != 0) {
1043 var z = 1.0 - tiny; // raise inexact
1044 if (z >= 1.0) {
1045 z = 1.0 + tiny;
1046 if (q1 == 0xFFFFFFFF) {
1047 q1 = 0;
1048 q += 1;
1049 } else if (z > 1.0) {
1050 if (q1 == 0xFFFFFFFE) {
1051 q += 1;
1052 }
1053 q1 += 2;
1054 } else {
1055 q1 += q1 & 1;
1056 }
1057 }
1058 }
1059
1060 ix0 = (q >> 1) + 0x3FE00000;
1061 ix1 = q1 >> 1;
1062 if (q & 1 != 0) {
1063 ix1 |= 0x80000000;
1064 }
1065
1066 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
1067 // behaviour at least.
1068 var iix0 = @intCast(i32, ix0);
1069 iix0 = iix0 +% (m << 20);
1070
1071 const uz = (@intCast(u64, iix0) << 32) | ix1;
1072 return @bitCast(f64, uz);
1073}
1074
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))));
75 return dest;
120776}
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 @@
11const std = @import("std");
2const builtin = std.builtin;
2const builtin = @import("builtin");
33const is_test = builtin.is_test;
44const os_tag = std.Target.current.os.tag;
5const arch = std.Target.current.cpu.arch;
5const arch = builtin.stage2_arch;
66const abi = std.Target.current.abi;
77
88const is_gnu = abi.isGnu();
99const is_mingw = os_tag == .windows and is_gnu;
1010
11comptime {
12 const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak;
13 const strong_linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong;
14
15 switch (arch) {
16 .i386,
17 .x86_64,
18 => {
19 const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;
20 @export(zig_probe_stack, .{
21 .name = "__zig_probe_stack",
22 .linkage = linkage,
23 });
24 },
25
26 else => {},
27 }
11const linkage = if (is_test)
12 std.builtin.GlobalLinkage.Internal
13else
14 std.builtin.GlobalLinkage.Weak;
2815
29 // __clear_cache manages its own logic about whether to be exported or not.
30 _ = @import("compiler_rt/clear_cache.zig").clear_cache;
31
32 const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2;
33 @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage });
34 const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2;
35 @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage });
36 const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2;
37 @export(__letf2, .{ .name = "__letf2", .linkage = linkage });
38
39 const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2;
40 @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage });
41 const __gedf2 = @import("compiler_rt/compareXf2.zig").__gedf2;
42 @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage });
43 const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2;
44 @export(__getf2, .{ .name = "__getf2", .linkage = linkage });
45
46 if (!is_test) {
47 @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage });
48 @export(__ledf2, .{ .name = "__cmpdf2", .linkage = linkage });
49 @export(__letf2, .{ .name = "__cmptf2", .linkage = linkage });
50
51 const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2;
52 @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage });
53 const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2;
54 @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage });
55 @export(__letf2, .{ .name = "__eqtf2", .linkage = linkage });
56
57 const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2;
58 @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage });
59 const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2;
60 @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage });
61 @export(__letf2, .{ .name = "__lttf2", .linkage = linkage });
62
63 const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2;
64 @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage });
65 const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2;
66 @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage });
67 @export(__letf2, .{ .name = "__netf2", .linkage = linkage });
68
69 const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2;
70 @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage });
71 const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2;
72 @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage });
73 @export(__getf2, .{ .name = "__gttf2", .linkage = linkage });
74
75 const __extendhfsf2 = @import("compiler_rt/extendXfYf2.zig").__extendhfsf2;
76 @export(__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage });
77 const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2;
78 @export(__truncsfhf2, .{ .name = "__gnu_f2h_ieee", .linkage = linkage });
79 }
80
81 const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;
82 @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage });
83 const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2;
84 @export(__unorddf2, .{ .name = "__unorddf2", .linkage = linkage });
85 const __unordtf2 = @import("compiler_rt/compareXf2.zig").__unordtf2;
86 @export(__unordtf2, .{ .name = "__unordtf2", .linkage = linkage });
87
88 const __addsf3 = @import("compiler_rt/addXf3.zig").__addsf3;
89 @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage });
90 const __adddf3 = @import("compiler_rt/addXf3.zig").__adddf3;
91 @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage });
92 const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3;
93 @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage });
94 const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3;
95 @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage });
96 const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3;
97 @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage });
98 const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3;
99 @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage });
100
101 const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3;
102 @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage });
103 const __muldf3 = @import("compiler_rt/mulXf3.zig").__muldf3;
104 @export(__muldf3, .{ .name = "__muldf3", .linkage = linkage });
105 const __multf3 = @import("compiler_rt/mulXf3.zig").__multf3;
106 @export(__multf3, .{ .name = "__multf3", .linkage = linkage });
107
108 const __divsf3 = @import("compiler_rt/divsf3.zig").__divsf3;
109 @export(__divsf3, .{ .name = "__divsf3", .linkage = linkage });
110 const __divdf3 = @import("compiler_rt/divdf3.zig").__divdf3;
111 @export(__divdf3, .{ .name = "__divdf3", .linkage = linkage });
112 const __divtf3 = @import("compiler_rt/divtf3.zig").__divtf3;
113 @export(__divtf3, .{ .name = "__divtf3", .linkage = linkage });
114
115 const __ashldi3 = @import("compiler_rt/shift.zig").__ashldi3;
116 @export(__ashldi3, .{ .name = "__ashldi3", .linkage = linkage });
117 const __ashlti3 = @import("compiler_rt/shift.zig").__ashlti3;
118 @export(__ashlti3, .{ .name = "__ashlti3", .linkage = linkage });
119 const __ashrdi3 = @import("compiler_rt/shift.zig").__ashrdi3;
120 @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = linkage });
121 const __ashrti3 = @import("compiler_rt/shift.zig").__ashrti3;
122 @export(__ashrti3, .{ .name = "__ashrti3", .linkage = linkage });
123 const __lshrdi3 = @import("compiler_rt/shift.zig").__lshrdi3;
124 @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = linkage });
125 const __lshrti3 = @import("compiler_rt/shift.zig").__lshrti3;
126 @export(__lshrti3, .{ .name = "__lshrti3", .linkage = linkage });
127
128 const __floatsidf = @import("compiler_rt/floatsiXf.zig").__floatsidf;
129 @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage });
130 const __floatsisf = @import("compiler_rt/floatsiXf.zig").__floatsisf;
131 @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage });
132 const __floatdidf = @import("compiler_rt/floatdidf.zig").__floatdidf;
133 @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage });
134 const __floatsitf = @import("compiler_rt/floatsiXf.zig").__floatsitf;
135 @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage });
136
137 const __floatunsisf = @import("compiler_rt/floatunsisf.zig").__floatunsisf;
138 @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage });
139 const __floatundisf = @import("compiler_rt/floatundisf.zig").__floatundisf;
140 @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage });
141 const __floatunsidf = @import("compiler_rt/floatunsidf.zig").__floatunsidf;
142 @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage });
143 const __floatundidf = @import("compiler_rt/floatundidf.zig").__floatundidf;
144 @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage });
145
146 const __floatditf = @import("compiler_rt/floatditf.zig").__floatditf;
147 @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage });
148 const __floattitf = @import("compiler_rt/floattitf.zig").__floattitf;
149 @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage });
150 const __floattidf = @import("compiler_rt/floattidf.zig").__floattidf;
151 @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage });
152 const __floattisf = @import("compiler_rt/floatXisf.zig").__floattisf;
153 @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage });
154 const __floatdisf = @import("compiler_rt/floatXisf.zig").__floatdisf;
155 @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage });
156
157 const __floatunditf = @import("compiler_rt/floatunditf.zig").__floatunditf;
158 @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage });
159 const __floatunsitf = @import("compiler_rt/floatunsitf.zig").__floatunsitf;
160 @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage });
161
162 const __floatuntitf = @import("compiler_rt/floatuntitf.zig").__floatuntitf;
163 @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage });
164 const __floatuntidf = @import("compiler_rt/floatuntidf.zig").__floatuntidf;
165 @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage });
166 const __floatuntisf = @import("compiler_rt/floatuntisf.zig").__floatuntisf;
167 @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage });
16const strong_linkage = if (is_test)
17 std.builtin.GlobalLinkage.Internal
18else
19 std.builtin.GlobalLinkage.Strong;
16820
21comptime {
16922 const __extenddftf2 = @import("compiler_rt/extendXfYf2.zig").__extenddftf2;
17023 @export(__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage });
17124 const __extendsftf2 = @import("compiler_rt/extendXfYf2.zig").__extendsftf2;
......@@ -175,446 +28,611 @@ comptime {
17528 const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2;
17629 @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage });
17730
178 const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2;
179 @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage });
180 const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2;
181 @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage });
182 const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2;
183 @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage });
184 const __trunctfdf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfdf2;
185 @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage });
186 const __trunctfsf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfsf2;
187 @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage });
188
189 const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2;
190 @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage });
191
192 const __extendsfdf2 = @import("compiler_rt/extendXfYf2.zig").__extendsfdf2;
193 @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage });
194
195 const __fixunssfsi = @import("compiler_rt/fixunssfsi.zig").__fixunssfsi;
196 @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage });
197 const __fixunssfdi = @import("compiler_rt/fixunssfdi.zig").__fixunssfdi;
198 @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage });
199 const __fixunssfti = @import("compiler_rt/fixunssfti.zig").__fixunssfti;
200 @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage });
201
202 const __fixunsdfsi = @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi;
203 @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage });
204 const __fixunsdfdi = @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi;
205 @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage });
206 const __fixunsdfti = @import("compiler_rt/fixunsdfti.zig").__fixunsdfti;
207 @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage });
208
209 const __fixunstfsi = @import("compiler_rt/fixunstfsi.zig").__fixunstfsi;
210 @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage });
211 const __fixunstfdi = @import("compiler_rt/fixunstfdi.zig").__fixunstfdi;
212 @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage });
213 const __fixunstfti = @import("compiler_rt/fixunstfti.zig").__fixunstfti;
214 @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage });
215
216 const __fixdfdi = @import("compiler_rt/fixdfdi.zig").__fixdfdi;
217 @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage });
218 const __fixdfsi = @import("compiler_rt/fixdfsi.zig").__fixdfsi;
219 @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage });
220 const __fixdfti = @import("compiler_rt/fixdfti.zig").__fixdfti;
221 @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage });
222 const __fixsfdi = @import("compiler_rt/fixsfdi.zig").__fixsfdi;
223 @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage });
224 const __fixsfsi = @import("compiler_rt/fixsfsi.zig").__fixsfsi;
225 @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage });
226 const __fixsfti = @import("compiler_rt/fixsfti.zig").__fixsfti;
227 @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage });
228 const __fixtfdi = @import("compiler_rt/fixtfdi.zig").__fixtfdi;
229 @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage });
230 const __fixtfsi = @import("compiler_rt/fixtfsi.zig").__fixtfsi;
231 @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage });
232 const __fixtfti = @import("compiler_rt/fixtfti.zig").__fixtfti;
233 @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage });
234
235 const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4;
236 @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage });
237 const __popcountdi2 = @import("compiler_rt/popcountdi2.zig").__popcountdi2;
238 @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage });
239
240 const __mulsi3 = @import("compiler_rt/int.zig").__mulsi3;
241 @export(__mulsi3, .{ .name = "__mulsi3", .linkage = linkage });
242 const __muldi3 = @import("compiler_rt/muldi3.zig").__muldi3;
243 @export(__muldi3, .{ .name = "__muldi3", .linkage = linkage });
244 const __divmoddi4 = @import("compiler_rt/int.zig").__divmoddi4;
245 @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = linkage });
246 const __divsi3 = @import("compiler_rt/int.zig").__divsi3;
247 @export(__divsi3, .{ .name = "__divsi3", .linkage = linkage });
248 const __divdi3 = @import("compiler_rt/int.zig").__divdi3;
249 @export(__divdi3, .{ .name = "__divdi3", .linkage = linkage });
250 const __udivsi3 = @import("compiler_rt/int.zig").__udivsi3;
251 @export(__udivsi3, .{ .name = "__udivsi3", .linkage = linkage });
252 const __udivdi3 = @import("compiler_rt/int.zig").__udivdi3;
253 @export(__udivdi3, .{ .name = "__udivdi3", .linkage = linkage });
254 const __modsi3 = @import("compiler_rt/int.zig").__modsi3;
255 @export(__modsi3, .{ .name = "__modsi3", .linkage = linkage });
256 const __moddi3 = @import("compiler_rt/int.zig").__moddi3;
257 @export(__moddi3, .{ .name = "__moddi3", .linkage = linkage });
258 const __umodsi3 = @import("compiler_rt/int.zig").__umodsi3;
259 @export(__umodsi3, .{ .name = "__umodsi3", .linkage = linkage });
260 const __umoddi3 = @import("compiler_rt/int.zig").__umoddi3;
261 @export(__umoddi3, .{ .name = "__umoddi3", .linkage = linkage });
262 const __divmodsi4 = @import("compiler_rt/int.zig").__divmodsi4;
263 @export(__divmodsi4, .{ .name = "__divmodsi4", .linkage = linkage });
264 const __udivmodsi4 = @import("compiler_rt/int.zig").__udivmodsi4;
265 @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = linkage });
266
267 const __negsf2 = @import("compiler_rt/negXf2.zig").__negsf2;
268 @export(__negsf2, .{ .name = "__negsf2", .linkage = linkage });
269 const __negdf2 = @import("compiler_rt/negXf2.zig").__negdf2;
270 @export(__negdf2, .{ .name = "__negdf2", .linkage = linkage });
271
272 const __clzsi2 = @import("compiler_rt/count0bits.zig").__clzsi2;
273 @export(__clzsi2, .{ .name = "__clzsi2", .linkage = linkage });
274 const __clzdi2 = @import("compiler_rt/count0bits.zig").__clzdi2;
275 @export(__clzdi2, .{ .name = "__clzdi2", .linkage = linkage });
276 const __clzti2 = @import("compiler_rt/count0bits.zig").__clzti2;
277 @export(__clzti2, .{ .name = "__clzti2", .linkage = linkage });
278
279 if (builtin.link_libc and os_tag == .openbsd) {
280 const __emutls_get_address = @import("compiler_rt/emutls.zig").__emutls_get_address;
281 @export(__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = linkage });
282 }
31 if (!builtin.zig_is_stage2) {
32 switch (arch) {
33 .i386,
34 .x86_64,
35 => {
36 const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;
37 @export(zig_probe_stack, .{
38 .name = "__zig_probe_stack",
39 .linkage = linkage,
40 });
41 },
28342
284 if ((arch.isARM() or arch.isThumb()) and !is_test) {
285 const __aeabi_unwind_cpp_pr0 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0;
286 @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage });
287 const __aeabi_unwind_cpp_pr1 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1;
288 @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage });
289 const __aeabi_unwind_cpp_pr2 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr2;
290 @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage });
291
292 @export(__muldi3, .{ .name = "__aeabi_lmul", .linkage = linkage });
293
294 const __aeabi_ldivmod = @import("compiler_rt/arm.zig").__aeabi_ldivmod;
295 @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage });
296 const __aeabi_uldivmod = @import("compiler_rt/arm.zig").__aeabi_uldivmod;
297 @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage });
298
299 @export(__divsi3, .{ .name = "__aeabi_idiv", .linkage = linkage });
300 const __aeabi_idivmod = @import("compiler_rt/arm.zig").__aeabi_idivmod;
301 @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage });
302 @export(__udivsi3, .{ .name = "__aeabi_uidiv", .linkage = linkage });
303 const __aeabi_uidivmod = @import("compiler_rt/arm.zig").__aeabi_uidivmod;
304 @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage });
305
306 const __aeabi_memcpy = @import("compiler_rt/arm.zig").__aeabi_memcpy;
307 @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage });
308 @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy4", .linkage = linkage });
309 @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy8", .linkage = linkage });
310
311 const __aeabi_memmove = @import("compiler_rt/arm.zig").__aeabi_memmove;
312 @export(__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = linkage });
313 @export(__aeabi_memmove, .{ .name = "__aeabi_memmove4", .linkage = linkage });
314 @export(__aeabi_memmove, .{ .name = "__aeabi_memmove8", .linkage = linkage });
315
316 const __aeabi_memset = @import("compiler_rt/arm.zig").__aeabi_memset;
317 @export(__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = linkage });
318 @export(__aeabi_memset, .{ .name = "__aeabi_memset4", .linkage = linkage });
319 @export(__aeabi_memset, .{ .name = "__aeabi_memset8", .linkage = linkage });
320
321 const __aeabi_memclr = @import("compiler_rt/arm.zig").__aeabi_memclr;
322 @export(__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = linkage });
323 @export(__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage });
324 @export(__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage });
325
326 if (os_tag == .linux) {
327 const __aeabi_read_tp = @import("compiler_rt/arm.zig").__aeabi_read_tp;
328 @export(__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage });
43 else => {},
32944 }
33045
331 const __aeabi_f2d = @import("compiler_rt/extendXfYf2.zig").__aeabi_f2d;
332 @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage });
333 const __aeabi_i2d = @import("compiler_rt/floatsiXf.zig").__aeabi_i2d;
334 @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage });
335 const __aeabi_l2d = @import("compiler_rt/floatdidf.zig").__aeabi_l2d;
336 @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage });
337 const __aeabi_l2f = @import("compiler_rt/floatXisf.zig").__aeabi_l2f;
338 @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage });
339 const __aeabi_ui2d = @import("compiler_rt/floatunsidf.zig").__aeabi_ui2d;
340 @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage });
341 const __aeabi_ul2d = @import("compiler_rt/floatundidf.zig").__aeabi_ul2d;
342 @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage });
343 const __aeabi_ui2f = @import("compiler_rt/floatunsisf.zig").__aeabi_ui2f;
344 @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage });
345 const __aeabi_ul2f = @import("compiler_rt/floatundisf.zig").__aeabi_ul2f;
346 @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage });
347
348 const __aeabi_fneg = @import("compiler_rt/negXf2.zig").__aeabi_fneg;
349 @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage });
350 const __aeabi_dneg = @import("compiler_rt/negXf2.zig").__aeabi_dneg;
351 @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage });
352
353 const __aeabi_fmul = @import("compiler_rt/mulXf3.zig").__aeabi_fmul;
354 @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage });
355 const __aeabi_dmul = @import("compiler_rt/mulXf3.zig").__aeabi_dmul;
356 @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage });
357
358 const __aeabi_d2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2h;
359 @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage });
360
361 const __aeabi_f2ulz = @import("compiler_rt/fixunssfdi.zig").__aeabi_f2ulz;
362 @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage });
363 const __aeabi_d2ulz = @import("compiler_rt/fixunsdfdi.zig").__aeabi_d2ulz;
364 @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage });
365
366 const __aeabi_f2lz = @import("compiler_rt/fixsfdi.zig").__aeabi_f2lz;
367 @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage });
368 const __aeabi_d2lz = @import("compiler_rt/fixdfdi.zig").__aeabi_d2lz;
369 @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage });
370
371 const __aeabi_d2uiz = @import("compiler_rt/fixunsdfsi.zig").__aeabi_d2uiz;
372 @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage });
373
374 const __aeabi_h2f = @import("compiler_rt/extendXfYf2.zig").__aeabi_h2f;
375 @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage });
376 const __aeabi_f2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_f2h;
377 @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage });
378
379 const __aeabi_i2f = @import("compiler_rt/floatsiXf.zig").__aeabi_i2f;
380 @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage });
381 const __aeabi_d2f = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2f;
382 @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage });
383
384 const __aeabi_fadd = @import("compiler_rt/addXf3.zig").__aeabi_fadd;
385 @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage });
386 const __aeabi_dadd = @import("compiler_rt/addXf3.zig").__aeabi_dadd;
387 @export(__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = linkage });
388 const __aeabi_fsub = @import("compiler_rt/addXf3.zig").__aeabi_fsub;
389 @export(__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = linkage });
390 const __aeabi_dsub = @import("compiler_rt/addXf3.zig").__aeabi_dsub;
391 @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage });
392
393 const __aeabi_f2uiz = @import("compiler_rt/fixunssfsi.zig").__aeabi_f2uiz;
394 @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage });
395
396 const __aeabi_f2iz = @import("compiler_rt/fixsfsi.zig").__aeabi_f2iz;
397 @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage });
398 const __aeabi_d2iz = @import("compiler_rt/fixdfsi.zig").__aeabi_d2iz;
399 @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage });
400
401 const __aeabi_fdiv = @import("compiler_rt/divsf3.zig").__aeabi_fdiv;
402 @export(__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage });
403 const __aeabi_ddiv = @import("compiler_rt/divdf3.zig").__aeabi_ddiv;
404 @export(__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage });
405
406 const __aeabi_llsl = @import("compiler_rt/shift.zig").__aeabi_llsl;
407 @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = linkage });
408 const __aeabi_lasr = @import("compiler_rt/shift.zig").__aeabi_lasr;
409 @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = linkage });
410 const __aeabi_llsr = @import("compiler_rt/shift.zig").__aeabi_llsr;
411 @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = linkage });
412
413 const __aeabi_fcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpeq;
414 @export(__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage });
415 const __aeabi_fcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmplt;
416 @export(__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage });
417 const __aeabi_fcmple = @import("compiler_rt/compareXf2.zig").__aeabi_fcmple;
418 @export(__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage });
419 const __aeabi_fcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpge;
420 @export(__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage });
421 const __aeabi_fcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpgt;
422 @export(__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage });
423 const __aeabi_fcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpun;
424 @export(__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage });
425
426 const __aeabi_dcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpeq;
427 @export(__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage });
428 const __aeabi_dcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmplt;
429 @export(__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage });
430 const __aeabi_dcmple = @import("compiler_rt/compareXf2.zig").__aeabi_dcmple;
431 @export(__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage });
432 const __aeabi_dcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpge;
433 @export(__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage });
434 const __aeabi_dcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpgt;
435 @export(__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage });
436 const __aeabi_dcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpun;
437 @export(__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage });
438 }
46 // __clear_cache manages its own logic about whether to be exported or not.
47 _ = @import("compiler_rt/clear_cache.zig").clear_cache;
48
49 const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2;
50 @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage });
51 const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2;
52 @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage });
53 const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2;
54 @export(__letf2, .{ .name = "__letf2", .linkage = linkage });
55
56 const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2;
57 @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage });
58 const __gedf2 = @import("compiler_rt/compareXf2.zig").__gedf2;
59 @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage });
60 const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2;
61 @export(__getf2, .{ .name = "__getf2", .linkage = linkage });
62
63 if (!is_test) {
64 @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage });
65 @export(__ledf2, .{ .name = "__cmpdf2", .linkage = linkage });
66 @export(__letf2, .{ .name = "__cmptf2", .linkage = linkage });
67
68 const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2;
69 @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage });
70 const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2;
71 @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage });
72 @export(__letf2, .{ .name = "__eqtf2", .linkage = linkage });
73
74 const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2;
75 @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage });
76 const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2;
77 @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage });
78 @export(__letf2, .{ .name = "__lttf2", .linkage = linkage });
79
80 const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2;
81 @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage });
82 const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2;
83 @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage });
84 @export(__letf2, .{ .name = "__netf2", .linkage = linkage });
85
86 const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2;
87 @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage });
88 const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2;
89 @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage });
90 @export(__getf2, .{ .name = "__gttf2", .linkage = linkage });
91
92 @export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{
93 .name = "__gnu_h2f_ieee",
94 .linkage = linkage,
95 });
96 @export(@import("compiler_rt/truncXfYf2.zig").__truncsfhf2, .{
97 .name = "__gnu_f2h_ieee",
98 .linkage = linkage,
99 });
100 }
439101
440 if (arch == .i386 and abi == .msvc) {
441 // Don't let LLVM apply the stdcall name mangling on those MSVC builtins
442 const _alldiv = @import("compiler_rt/aulldiv.zig")._alldiv;
443 @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage });
444 const _aulldiv = @import("compiler_rt/aulldiv.zig")._aulldiv;
445 @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage });
446 const _allrem = @import("compiler_rt/aullrem.zig")._allrem;
447 @export(_allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage });
448 const _aullrem = @import("compiler_rt/aullrem.zig")._aullrem;
449 @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });
450 }
102 const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;
103 @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage });
104 const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2;
105 @export(__unorddf2, .{ .name = "__unorddf2", .linkage = linkage });
106 const __unordtf2 = @import("compiler_rt/compareXf2.zig").__unordtf2;
107 @export(__unordtf2, .{ .name = "__unordtf2", .linkage = linkage });
108
109 const __addsf3 = @import("compiler_rt/addXf3.zig").__addsf3;
110 @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage });
111 const __adddf3 = @import("compiler_rt/addXf3.zig").__adddf3;
112 @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage });
113 const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3;
114 @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage });
115 const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3;
116 @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage });
117 const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3;
118 @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage });
119 const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3;
120 @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage });
121
122 const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3;
123 @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage });
124 const __muldf3 = @import("compiler_rt/mulXf3.zig").__muldf3;
125 @export(__muldf3, .{ .name = "__muldf3", .linkage = linkage });
126 const __multf3 = @import("compiler_rt/mulXf3.zig").__multf3;
127 @export(__multf3, .{ .name = "__multf3", .linkage = linkage });
128
129 const __divsf3 = @import("compiler_rt/divsf3.zig").__divsf3;
130 @export(__divsf3, .{ .name = "__divsf3", .linkage = linkage });
131 const __divdf3 = @import("compiler_rt/divdf3.zig").__divdf3;
132 @export(__divdf3, .{ .name = "__divdf3", .linkage = linkage });
133 const __divtf3 = @import("compiler_rt/divtf3.zig").__divtf3;
134 @export(__divtf3, .{ .name = "__divtf3", .linkage = linkage });
135
136 const __ashldi3 = @import("compiler_rt/shift.zig").__ashldi3;
137 @export(__ashldi3, .{ .name = "__ashldi3", .linkage = linkage });
138 const __ashlti3 = @import("compiler_rt/shift.zig").__ashlti3;
139 @export(__ashlti3, .{ .name = "__ashlti3", .linkage = linkage });
140 const __ashrdi3 = @import("compiler_rt/shift.zig").__ashrdi3;
141 @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = linkage });
142 const __ashrti3 = @import("compiler_rt/shift.zig").__ashrti3;
143 @export(__ashrti3, .{ .name = "__ashrti3", .linkage = linkage });
144 const __lshrdi3 = @import("compiler_rt/shift.zig").__lshrdi3;
145 @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = linkage });
146 const __lshrti3 = @import("compiler_rt/shift.zig").__lshrti3;
147 @export(__lshrti3, .{ .name = "__lshrti3", .linkage = linkage });
148
149 const __floatsidf = @import("compiler_rt/floatsiXf.zig").__floatsidf;
150 @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage });
151 const __floatsisf = @import("compiler_rt/floatsiXf.zig").__floatsisf;
152 @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage });
153 const __floatdidf = @import("compiler_rt/floatdidf.zig").__floatdidf;
154 @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage });
155 const __floatsitf = @import("compiler_rt/floatsiXf.zig").__floatsitf;
156 @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage });
157
158 const __floatunsisf = @import("compiler_rt/floatunsisf.zig").__floatunsisf;
159 @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage });
160 const __floatundisf = @import("compiler_rt/floatundisf.zig").__floatundisf;
161 @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage });
162 const __floatunsidf = @import("compiler_rt/floatunsidf.zig").__floatunsidf;
163 @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage });
164 const __floatundidf = @import("compiler_rt/floatundidf.zig").__floatundidf;
165 @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage });
166
167 const __floatditf = @import("compiler_rt/floatditf.zig").__floatditf;
168 @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage });
169 const __floattitf = @import("compiler_rt/floattitf.zig").__floattitf;
170 @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage });
171 const __floattidf = @import("compiler_rt/floattidf.zig").__floattidf;
172 @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage });
173 const __floattisf = @import("compiler_rt/floatXisf.zig").__floattisf;
174 @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage });
175 const __floatdisf = @import("compiler_rt/floatXisf.zig").__floatdisf;
176 @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage });
177
178 const __floatunditf = @import("compiler_rt/floatunditf.zig").__floatunditf;
179 @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage });
180 const __floatunsitf = @import("compiler_rt/floatunsitf.zig").__floatunsitf;
181 @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage });
182
183 const __floatuntitf = @import("compiler_rt/floatuntitf.zig").__floatuntitf;
184 @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage });
185 const __floatuntidf = @import("compiler_rt/floatuntidf.zig").__floatuntidf;
186 @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage });
187 const __floatuntisf = @import("compiler_rt/floatuntisf.zig").__floatuntisf;
188 @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage });
451189
452 if (arch.isSPARC()) {
453 // SPARC systems use a different naming scheme
454 const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add;
455 @export(_Qp_add, .{ .name = "_Qp_add", .linkage = linkage });
456 const _Qp_div = @import("compiler_rt/sparc.zig")._Qp_div;
457 @export(_Qp_div, .{ .name = "_Qp_div", .linkage = linkage });
458 const _Qp_mul = @import("compiler_rt/sparc.zig")._Qp_mul;
459 @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage });
460 const _Qp_sub = @import("compiler_rt/sparc.zig")._Qp_sub;
461 @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage });
462
463 const _Qp_cmp = @import("compiler_rt/sparc.zig")._Qp_cmp;
464 @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage });
465 const _Qp_feq = @import("compiler_rt/sparc.zig")._Qp_feq;
466 @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage });
467 const _Qp_fne = @import("compiler_rt/sparc.zig")._Qp_fne;
468 @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage });
469 const _Qp_flt = @import("compiler_rt/sparc.zig")._Qp_flt;
470 @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage });
471 const _Qp_fle = @import("compiler_rt/sparc.zig")._Qp_fle;
472 @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage });
473 const _Qp_fgt = @import("compiler_rt/sparc.zig")._Qp_fgt;
474 @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage });
475 const _Qp_fge = @import("compiler_rt/sparc.zig")._Qp_fge;
476 @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage });
477
478 const _Qp_itoq = @import("compiler_rt/sparc.zig")._Qp_itoq;
479 @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage });
480 const _Qp_uitoq = @import("compiler_rt/sparc.zig")._Qp_uitoq;
481 @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage });
482 const _Qp_xtoq = @import("compiler_rt/sparc.zig")._Qp_xtoq;
483 @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage });
484 const _Qp_uxtoq = @import("compiler_rt/sparc.zig")._Qp_uxtoq;
485 @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage });
486 const _Qp_stoq = @import("compiler_rt/sparc.zig")._Qp_stoq;
487 @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage });
488 const _Qp_dtoq = @import("compiler_rt/sparc.zig")._Qp_dtoq;
489 @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage });
490 const _Qp_qtoi = @import("compiler_rt/sparc.zig")._Qp_qtoi;
491 @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage });
492 const _Qp_qtoui = @import("compiler_rt/sparc.zig")._Qp_qtoui;
493 @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage });
494 const _Qp_qtox = @import("compiler_rt/sparc.zig")._Qp_qtox;
495 @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage });
496 const _Qp_qtoux = @import("compiler_rt/sparc.zig")._Qp_qtoux;
497 @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage });
498 const _Qp_qtos = @import("compiler_rt/sparc.zig")._Qp_qtos;
499 @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage });
500 const _Qp_qtod = @import("compiler_rt/sparc.zig")._Qp_qtod;
501 @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });
502 }
190 const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2;
191 @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage });
192 const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2;
193 @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage });
194 const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2;
195 @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage });
196 const __trunctfdf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfdf2;
197 @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage });
198 const __trunctfsf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfsf2;
199 @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage });
200
201 const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2;
202 @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage });
203
204 const __extendsfdf2 = @import("compiler_rt/extendXfYf2.zig").__extendsfdf2;
205 @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage });
206
207 const __fixunssfsi = @import("compiler_rt/fixunssfsi.zig").__fixunssfsi;
208 @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage });
209 const __fixunssfdi = @import("compiler_rt/fixunssfdi.zig").__fixunssfdi;
210 @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage });
211 const __fixunssfti = @import("compiler_rt/fixunssfti.zig").__fixunssfti;
212 @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage });
213
214 const __fixunsdfsi = @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi;
215 @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage });
216 const __fixunsdfdi = @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi;
217 @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage });
218 const __fixunsdfti = @import("compiler_rt/fixunsdfti.zig").__fixunsdfti;
219 @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage });
220
221 const __fixunstfsi = @import("compiler_rt/fixunstfsi.zig").__fixunstfsi;
222 @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage });
223 const __fixunstfdi = @import("compiler_rt/fixunstfdi.zig").__fixunstfdi;
224 @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage });
225 const __fixunstfti = @import("compiler_rt/fixunstfti.zig").__fixunstfti;
226 @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage });
227
228 const __fixdfdi = @import("compiler_rt/fixdfdi.zig").__fixdfdi;
229 @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage });
230 const __fixdfsi = @import("compiler_rt/fixdfsi.zig").__fixdfsi;
231 @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage });
232 const __fixdfti = @import("compiler_rt/fixdfti.zig").__fixdfti;
233 @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage });
234 const __fixsfdi = @import("compiler_rt/fixsfdi.zig").__fixsfdi;
235 @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage });
236 const __fixsfsi = @import("compiler_rt/fixsfsi.zig").__fixsfsi;
237 @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage });
238 const __fixsfti = @import("compiler_rt/fixsfti.zig").__fixsfti;
239 @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage });
240 const __fixtfdi = @import("compiler_rt/fixtfdi.zig").__fixtfdi;
241 @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage });
242 const __fixtfsi = @import("compiler_rt/fixtfsi.zig").__fixtfsi;
243 @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage });
244 const __fixtfti = @import("compiler_rt/fixtfti.zig").__fixtfti;
245 @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage });
246
247 const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4;
248 @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage });
249 const __popcountdi2 = @import("compiler_rt/popcountdi2.zig").__popcountdi2;
250 @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage });
251
252 const __mulsi3 = @import("compiler_rt/int.zig").__mulsi3;
253 @export(__mulsi3, .{ .name = "__mulsi3", .linkage = linkage });
254 const __muldi3 = @import("compiler_rt/muldi3.zig").__muldi3;
255 @export(__muldi3, .{ .name = "__muldi3", .linkage = linkage });
256 const __divmoddi4 = @import("compiler_rt/int.zig").__divmoddi4;
257 @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = linkage });
258 const __divsi3 = @import("compiler_rt/int.zig").__divsi3;
259 @export(__divsi3, .{ .name = "__divsi3", .linkage = linkage });
260 const __divdi3 = @import("compiler_rt/int.zig").__divdi3;
261 @export(__divdi3, .{ .name = "__divdi3", .linkage = linkage });
262 const __udivsi3 = @import("compiler_rt/int.zig").__udivsi3;
263 @export(__udivsi3, .{ .name = "__udivsi3", .linkage = linkage });
264 const __udivdi3 = @import("compiler_rt/int.zig").__udivdi3;
265 @export(__udivdi3, .{ .name = "__udivdi3", .linkage = linkage });
266 const __modsi3 = @import("compiler_rt/int.zig").__modsi3;
267 @export(__modsi3, .{ .name = "__modsi3", .linkage = linkage });
268 const __moddi3 = @import("compiler_rt/int.zig").__moddi3;
269 @export(__moddi3, .{ .name = "__moddi3", .linkage = linkage });
270 const __umodsi3 = @import("compiler_rt/int.zig").__umodsi3;
271 @export(__umodsi3, .{ .name = "__umodsi3", .linkage = linkage });
272 const __umoddi3 = @import("compiler_rt/int.zig").__umoddi3;
273 @export(__umoddi3, .{ .name = "__umoddi3", .linkage = linkage });
274 const __divmodsi4 = @import("compiler_rt/int.zig").__divmodsi4;
275 @export(__divmodsi4, .{ .name = "__divmodsi4", .linkage = linkage });
276 const __udivmodsi4 = @import("compiler_rt/int.zig").__udivmodsi4;
277 @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = linkage });
278
279 const __negsf2 = @import("compiler_rt/negXf2.zig").__negsf2;
280 @export(__negsf2, .{ .name = "__negsf2", .linkage = linkage });
281 const __negdf2 = @import("compiler_rt/negXf2.zig").__negdf2;
282 @export(__negdf2, .{ .name = "__negdf2", .linkage = linkage });
283
284 const __clzsi2 = @import("compiler_rt/count0bits.zig").__clzsi2;
285 @export(__clzsi2, .{ .name = "__clzsi2", .linkage = linkage });
286 const __clzdi2 = @import("compiler_rt/count0bits.zig").__clzdi2;
287 @export(__clzdi2, .{ .name = "__clzdi2", .linkage = linkage });
288 const __clzti2 = @import("compiler_rt/count0bits.zig").__clzti2;
289 @export(__clzti2, .{ .name = "__clzti2", .linkage = linkage });
290
291 if (builtin.link_libc and os_tag == .openbsd) {
292 const __emutls_get_address = @import("compiler_rt/emutls.zig").__emutls_get_address;
293 @export(__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = linkage });
294 }
503295
504 if ((arch == .powerpc or arch.isPPC64()) and !is_test) {
505 @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage });
506 @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage });
507 @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage });
508 @export(__divtf3, .{ .name = "__divkf3", .linkage = linkage });
509 @export(__extendsftf2, .{ .name = "__extendsfkf2", .linkage = linkage });
510 @export(__extenddftf2, .{ .name = "__extenddfkf2", .linkage = linkage });
511 @export(__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = linkage });
512 @export(__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = linkage });
513 @export(__fixtfdi, .{ .name = "__fixkfdi", .linkage = linkage });
514 @export(__fixtfsi, .{ .name = "__fixkfsi", .linkage = linkage });
515 @export(__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = linkage });
516 @export(__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = linkage });
517 @export(__floatsitf, .{ .name = "__floatsikf", .linkage = linkage });
518 @export(__floatditf, .{ .name = "__floatdikf", .linkage = linkage });
519 @export(__floatunditf, .{ .name = "__floatundikf", .linkage = linkage });
520 @export(__floatunsitf, .{ .name = "__floatunsikf", .linkage = linkage });
521
522 @export(__letf2, .{ .name = "__eqkf2", .linkage = linkage });
523 @export(__letf2, .{ .name = "__nekf2", .linkage = linkage });
524 @export(__getf2, .{ .name = "__gekf2", .linkage = linkage });
525 @export(__letf2, .{ .name = "__ltkf2", .linkage = linkage });
526 @export(__letf2, .{ .name = "__lekf2", .linkage = linkage });
527 @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage });
528 @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage });
529 }
296 if ((arch.isARM() or arch.isThumb()) and !is_test) {
297 const __aeabi_unwind_cpp_pr0 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0;
298 @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage });
299 const __aeabi_unwind_cpp_pr1 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1;
300 @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage });
301 const __aeabi_unwind_cpp_pr2 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr2;
302 @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage });
303
304 @export(__muldi3, .{ .name = "__aeabi_lmul", .linkage = linkage });
305
306 const __aeabi_ldivmod = @import("compiler_rt/arm.zig").__aeabi_ldivmod;
307 @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage });
308 const __aeabi_uldivmod = @import("compiler_rt/arm.zig").__aeabi_uldivmod;
309 @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage });
310
311 @export(__divsi3, .{ .name = "__aeabi_idiv", .linkage = linkage });
312 const __aeabi_idivmod = @import("compiler_rt/arm.zig").__aeabi_idivmod;
313 @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage });
314 @export(__udivsi3, .{ .name = "__aeabi_uidiv", .linkage = linkage });
315 const __aeabi_uidivmod = @import("compiler_rt/arm.zig").__aeabi_uidivmod;
316 @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage });
317
318 const __aeabi_memcpy = @import("compiler_rt/arm.zig").__aeabi_memcpy;
319 @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage });
320 @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy4", .linkage = linkage });
321 @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy8", .linkage = linkage });
322
323 const __aeabi_memmove = @import("compiler_rt/arm.zig").__aeabi_memmove;
324 @export(__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = linkage });
325 @export(__aeabi_memmove, .{ .name = "__aeabi_memmove4", .linkage = linkage });
326 @export(__aeabi_memmove, .{ .name = "__aeabi_memmove8", .linkage = linkage });
327
328 const __aeabi_memset = @import("compiler_rt/arm.zig").__aeabi_memset;
329 @export(__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = linkage });
330 @export(__aeabi_memset, .{ .name = "__aeabi_memset4", .linkage = linkage });
331 @export(__aeabi_memset, .{ .name = "__aeabi_memset8", .linkage = linkage });
332
333 const __aeabi_memclr = @import("compiler_rt/arm.zig").__aeabi_memclr;
334 @export(__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = linkage });
335 @export(__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage });
336 @export(__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage });
337
338 if (os_tag == .linux) {
339 const __aeabi_read_tp = @import("compiler_rt/arm.zig").__aeabi_read_tp;
340 @export(__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage });
341 }
342
343 const __aeabi_f2d = @import("compiler_rt/extendXfYf2.zig").__aeabi_f2d;
344 @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage });
345 const __aeabi_i2d = @import("compiler_rt/floatsiXf.zig").__aeabi_i2d;
346 @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage });
347 const __aeabi_l2d = @import("compiler_rt/floatdidf.zig").__aeabi_l2d;
348 @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage });
349 const __aeabi_l2f = @import("compiler_rt/floatXisf.zig").__aeabi_l2f;
350 @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage });
351 const __aeabi_ui2d = @import("compiler_rt/floatunsidf.zig").__aeabi_ui2d;
352 @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage });
353 const __aeabi_ul2d = @import("compiler_rt/floatundidf.zig").__aeabi_ul2d;
354 @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage });
355 const __aeabi_ui2f = @import("compiler_rt/floatunsisf.zig").__aeabi_ui2f;
356 @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage });
357 const __aeabi_ul2f = @import("compiler_rt/floatundisf.zig").__aeabi_ul2f;
358 @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage });
359
360 const __aeabi_fneg = @import("compiler_rt/negXf2.zig").__aeabi_fneg;
361 @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage });
362 const __aeabi_dneg = @import("compiler_rt/negXf2.zig").__aeabi_dneg;
363 @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage });
364
365 const __aeabi_fmul = @import("compiler_rt/mulXf3.zig").__aeabi_fmul;
366 @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage });
367 const __aeabi_dmul = @import("compiler_rt/mulXf3.zig").__aeabi_dmul;
368 @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage });
369
370 const __aeabi_d2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2h;
371 @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage });
372
373 const __aeabi_f2ulz = @import("compiler_rt/fixunssfdi.zig").__aeabi_f2ulz;
374 @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage });
375 const __aeabi_d2ulz = @import("compiler_rt/fixunsdfdi.zig").__aeabi_d2ulz;
376 @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage });
377
378 const __aeabi_f2lz = @import("compiler_rt/fixsfdi.zig").__aeabi_f2lz;
379 @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage });
380 const __aeabi_d2lz = @import("compiler_rt/fixdfdi.zig").__aeabi_d2lz;
381 @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage });
382
383 const __aeabi_d2uiz = @import("compiler_rt/fixunsdfsi.zig").__aeabi_d2uiz;
384 @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage });
385
386 const __aeabi_h2f = @import("compiler_rt/extendXfYf2.zig").__aeabi_h2f;
387 @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage });
388 const __aeabi_f2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_f2h;
389 @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage });
390
391 const __aeabi_i2f = @import("compiler_rt/floatsiXf.zig").__aeabi_i2f;
392 @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage });
393 const __aeabi_d2f = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2f;
394 @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage });
395
396 const __aeabi_fadd = @import("compiler_rt/addXf3.zig").__aeabi_fadd;
397 @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage });
398 const __aeabi_dadd = @import("compiler_rt/addXf3.zig").__aeabi_dadd;
399 @export(__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = linkage });
400 const __aeabi_fsub = @import("compiler_rt/addXf3.zig").__aeabi_fsub;
401 @export(__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = linkage });
402 const __aeabi_dsub = @import("compiler_rt/addXf3.zig").__aeabi_dsub;
403 @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage });
404
405 const __aeabi_f2uiz = @import("compiler_rt/fixunssfsi.zig").__aeabi_f2uiz;
406 @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage });
407
408 const __aeabi_f2iz = @import("compiler_rt/fixsfsi.zig").__aeabi_f2iz;
409 @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage });
410 const __aeabi_d2iz = @import("compiler_rt/fixdfsi.zig").__aeabi_d2iz;
411 @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage });
412
413 const __aeabi_fdiv = @import("compiler_rt/divsf3.zig").__aeabi_fdiv;
414 @export(__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage });
415 const __aeabi_ddiv = @import("compiler_rt/divdf3.zig").__aeabi_ddiv;
416 @export(__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage });
417
418 const __aeabi_llsl = @import("compiler_rt/shift.zig").__aeabi_llsl;
419 @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = linkage });
420 const __aeabi_lasr = @import("compiler_rt/shift.zig").__aeabi_lasr;
421 @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = linkage });
422 const __aeabi_llsr = @import("compiler_rt/shift.zig").__aeabi_llsr;
423 @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = linkage });
424
425 const __aeabi_fcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpeq;
426 @export(__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage });
427 const __aeabi_fcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmplt;
428 @export(__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage });
429 const __aeabi_fcmple = @import("compiler_rt/compareXf2.zig").__aeabi_fcmple;
430 @export(__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage });
431 const __aeabi_fcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpge;
432 @export(__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage });
433 const __aeabi_fcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpgt;
434 @export(__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage });
435 const __aeabi_fcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpun;
436 @export(__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage });
437
438 const __aeabi_dcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpeq;
439 @export(__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage });
440 const __aeabi_dcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmplt;
441 @export(__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage });
442 const __aeabi_dcmple = @import("compiler_rt/compareXf2.zig").__aeabi_dcmple;
443 @export(__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage });
444 const __aeabi_dcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpge;
445 @export(__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage });
446 const __aeabi_dcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpgt;
447 @export(__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage });
448 const __aeabi_dcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpun;
449 @export(__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage });
450 }
530451
531 if (builtin.os.tag == .windows) {
532 // Default stack-probe functions emitted by LLVM
533 if (is_mingw) {
534 const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk;
535 @export(_chkstk, .{ .name = "_alloca", .linkage = strong_linkage });
536 const ___chkstk_ms = @import("compiler_rt/stack_probe.zig").___chkstk_ms;
537 @export(___chkstk_ms, .{ .name = "___chkstk_ms", .linkage = strong_linkage });
538 } else if (!builtin.link_libc) {
539 // This symbols are otherwise exported by MSVCRT.lib
540 const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk;
541 @export(_chkstk, .{ .name = "_chkstk", .linkage = strong_linkage });
542 const __chkstk = @import("compiler_rt/stack_probe.zig").__chkstk;
543 @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage });
452 if (arch == .i386 and abi == .msvc) {
453 // Don't let LLVM apply the stdcall name mangling on those MSVC builtins
454 const _alldiv = @import("compiler_rt/aulldiv.zig")._alldiv;
455 @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage });
456 const _aulldiv = @import("compiler_rt/aulldiv.zig")._aulldiv;
457 @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage });
458 const _allrem = @import("compiler_rt/aullrem.zig")._allrem;
459 @export(_allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage });
460 const _aullrem = @import("compiler_rt/aullrem.zig")._aullrem;
461 @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });
544462 }
545463
546 switch (arch) {
547 .i386 => {
548 const __divti3 = @import("compiler_rt/divti3.zig").__divti3;
549 @export(__divti3, .{ .name = "__divti3", .linkage = linkage });
464 if (arch.isSPARC()) {
465 // SPARC systems use a different naming scheme
466 const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add;
467 @export(_Qp_add, .{ .name = "_Qp_add", .linkage = linkage });
468 const _Qp_div = @import("compiler_rt/sparc.zig")._Qp_div;
469 @export(_Qp_div, .{ .name = "_Qp_div", .linkage = linkage });
470 const _Qp_mul = @import("compiler_rt/sparc.zig")._Qp_mul;
471 @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage });
472 const _Qp_sub = @import("compiler_rt/sparc.zig")._Qp_sub;
473 @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage });
474
475 const _Qp_cmp = @import("compiler_rt/sparc.zig")._Qp_cmp;
476 @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage });
477 const _Qp_feq = @import("compiler_rt/sparc.zig")._Qp_feq;
478 @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage });
479 const _Qp_fne = @import("compiler_rt/sparc.zig")._Qp_fne;
480 @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage });
481 const _Qp_flt = @import("compiler_rt/sparc.zig")._Qp_flt;
482 @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage });
483 const _Qp_fle = @import("compiler_rt/sparc.zig")._Qp_fle;
484 @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage });
485 const _Qp_fgt = @import("compiler_rt/sparc.zig")._Qp_fgt;
486 @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage });
487 const _Qp_fge = @import("compiler_rt/sparc.zig")._Qp_fge;
488 @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage });
489
490 const _Qp_itoq = @import("compiler_rt/sparc.zig")._Qp_itoq;
491 @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage });
492 const _Qp_uitoq = @import("compiler_rt/sparc.zig")._Qp_uitoq;
493 @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage });
494 const _Qp_xtoq = @import("compiler_rt/sparc.zig")._Qp_xtoq;
495 @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage });
496 const _Qp_uxtoq = @import("compiler_rt/sparc.zig")._Qp_uxtoq;
497 @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage });
498 const _Qp_stoq = @import("compiler_rt/sparc.zig")._Qp_stoq;
499 @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage });
500 const _Qp_dtoq = @import("compiler_rt/sparc.zig")._Qp_dtoq;
501 @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage });
502 const _Qp_qtoi = @import("compiler_rt/sparc.zig")._Qp_qtoi;
503 @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage });
504 const _Qp_qtoui = @import("compiler_rt/sparc.zig")._Qp_qtoui;
505 @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage });
506 const _Qp_qtox = @import("compiler_rt/sparc.zig")._Qp_qtox;
507 @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage });
508 const _Qp_qtoux = @import("compiler_rt/sparc.zig")._Qp_qtoux;
509 @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage });
510 const _Qp_qtos = @import("compiler_rt/sparc.zig")._Qp_qtos;
511 @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage });
512 const _Qp_qtod = @import("compiler_rt/sparc.zig")._Qp_qtod;
513 @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });
514 }
515
516 if ((arch == .powerpc or arch.isPPC64()) and !is_test) {
517 @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage });
518 @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage });
519 @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage });
520 @export(__divtf3, .{ .name = "__divkf3", .linkage = linkage });
521 @export(__extendsftf2, .{ .name = "__extendsfkf2", .linkage = linkage });
522 @export(__extenddftf2, .{ .name = "__extenddfkf2", .linkage = linkage });
523 @export(__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = linkage });
524 @export(__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = linkage });
525 @export(__fixtfdi, .{ .name = "__fixkfdi", .linkage = linkage });
526 @export(__fixtfsi, .{ .name = "__fixkfsi", .linkage = linkage });
527 @export(__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = linkage });
528 @export(__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = linkage });
529 @export(__floatsitf, .{ .name = "__floatsikf", .linkage = linkage });
530 @export(__floatditf, .{ .name = "__floatdikf", .linkage = linkage });
531 @export(__floatunditf, .{ .name = "__floatundikf", .linkage = linkage });
532 @export(__floatunsitf, .{ .name = "__floatunsikf", .linkage = linkage });
533
534 @export(__letf2, .{ .name = "__eqkf2", .linkage = linkage });
535 @export(__letf2, .{ .name = "__nekf2", .linkage = linkage });
536 @export(__getf2, .{ .name = "__gekf2", .linkage = linkage });
537 @export(__letf2, .{ .name = "__ltkf2", .linkage = linkage });
538 @export(__letf2, .{ .name = "__lekf2", .linkage = linkage });
539 @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage });
540 @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage });
541 }
542
543 if (builtin.os.tag == .windows) {
544 // Default stack-probe functions emitted by LLVM
545 if (is_mingw) {
546 const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk;
547 @export(_chkstk, .{ .name = "_alloca", .linkage = strong_linkage });
548 const ___chkstk_ms = @import("compiler_rt/stack_probe.zig").___chkstk_ms;
549 @export(___chkstk_ms, .{ .name = "___chkstk_ms", .linkage = strong_linkage });
550 } else if (!builtin.link_libc) {
551 // This symbols are otherwise exported by MSVCRT.lib
552 const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk;
553 @export(_chkstk, .{ .name = "_chkstk", .linkage = strong_linkage });
554 const __chkstk = @import("compiler_rt/stack_probe.zig").__chkstk;
555 @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage });
556 }
557
558 switch (arch) {
559 .i386 => {
560 const __divti3 = @import("compiler_rt/divti3.zig").__divti3;
561 @export(__divti3, .{ .name = "__divti3", .linkage = linkage });
562 const __modti3 = @import("compiler_rt/modti3.zig").__modti3;
563 @export(__modti3, .{ .name = "__modti3", .linkage = linkage });
564 const __multi3 = @import("compiler_rt/multi3.zig").__multi3;
565 @export(__multi3, .{ .name = "__multi3", .linkage = linkage });
566 const __udivti3 = @import("compiler_rt/udivti3.zig").__udivti3;
567 @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage });
568 const __udivmodti4 = @import("compiler_rt/udivmodti4.zig").__udivmodti4;
569 @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage });
570 const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3;
571 @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage });
572 },
573 .x86_64 => {
574 // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI
575 // that LLVM expects compiler-rt to have.
576 const __divti3_windows_x86_64 = @import("compiler_rt/divti3.zig").__divti3_windows_x86_64;
577 @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = linkage });
578 const __modti3_windows_x86_64 = @import("compiler_rt/modti3.zig").__modti3_windows_x86_64;
579 @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = linkage });
580 const __multi3_windows_x86_64 = @import("compiler_rt/multi3.zig").__multi3_windows_x86_64;
581 @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = linkage });
582 const __udivti3_windows_x86_64 = @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64;
583 @export(__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = linkage });
584 const __udivmodti4_windows_x86_64 = @import("compiler_rt/udivmodti4.zig").__udivmodti4_windows_x86_64;
585 @export(__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = linkage });
586 const __umodti3_windows_x86_64 = @import("compiler_rt/umodti3.zig").__umodti3_windows_x86_64;
587 @export(__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = linkage });
588 },
589 else => {},
590 }
591 if (arch.isAARCH64()) {
592 const __chkstk = @import("compiler_rt/stack_probe.zig").__chkstk;
593 @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage });
594 const __divti3_windows = @import("compiler_rt/divti3.zig").__divti3;
595 @export(__divti3_windows, .{ .name = "__divti3", .linkage = linkage });
550596 const __modti3 = @import("compiler_rt/modti3.zig").__modti3;
551597 @export(__modti3, .{ .name = "__modti3", .linkage = linkage });
552 const __multi3 = @import("compiler_rt/multi3.zig").__multi3;
553 @export(__multi3, .{ .name = "__multi3", .linkage = linkage });
554 const __udivti3 = @import("compiler_rt/udivti3.zig").__udivti3;
555 @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage });
556 const __udivmodti4 = @import("compiler_rt/udivmodti4.zig").__udivmodti4;
557 @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage });
598 const __udivti3_windows = @import("compiler_rt/udivti3.zig").__udivti3;
599 @export(__udivti3_windows, .{ .name = "__udivti3", .linkage = linkage });
558600 const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3;
559601 @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage });
560 },
561 .x86_64 => {
562 // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI
563 // that LLVM expects compiler-rt to have.
564 const __divti3_windows_x86_64 = @import("compiler_rt/divti3.zig").__divti3_windows_x86_64;
565 @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = linkage });
566 const __modti3_windows_x86_64 = @import("compiler_rt/modti3.zig").__modti3_windows_x86_64;
567 @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = linkage });
568 const __multi3_windows_x86_64 = @import("compiler_rt/multi3.zig").__multi3_windows_x86_64;
569 @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = linkage });
570 const __udivti3_windows_x86_64 = @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64;
571 @export(__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = linkage });
572 const __udivmodti4_windows_x86_64 = @import("compiler_rt/udivmodti4.zig").__udivmodti4_windows_x86_64;
573 @export(__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = linkage });
574 const __umodti3_windows_x86_64 = @import("compiler_rt/umodti3.zig").__umodti3_windows_x86_64;
575 @export(__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = linkage });
576 },
577 else => {},
578 }
579 if (arch.isAARCH64()) {
580 const __chkstk = @import("compiler_rt/stack_probe.zig").__chkstk;
581 @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage });
582 const __divti3_windows = @import("compiler_rt/divti3.zig").__divti3;
583 @export(__divti3_windows, .{ .name = "__divti3", .linkage = linkage });
602 }
603 } else {
604 const __divti3 = @import("compiler_rt/divti3.zig").__divti3;
605 @export(__divti3, .{ .name = "__divti3", .linkage = linkage });
584606 const __modti3 = @import("compiler_rt/modti3.zig").__modti3;
585607 @export(__modti3, .{ .name = "__modti3", .linkage = linkage });
586 const __udivti3_windows = @import("compiler_rt/udivti3.zig").__udivti3;
587 @export(__udivti3_windows, .{ .name = "__udivti3", .linkage = linkage });
608 const __multi3 = @import("compiler_rt/multi3.zig").__multi3;
609 @export(__multi3, .{ .name = "__multi3", .linkage = linkage });
610 const __udivti3 = @import("compiler_rt/udivti3.zig").__udivti3;
611 @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage });
612 const __udivmodti4 = @import("compiler_rt/udivmodti4.zig").__udivmodti4;
613 @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage });
588614 const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3;
589615 @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage });
590616 }
591 } else {
592 const __divti3 = @import("compiler_rt/divti3.zig").__divti3;
593 @export(__divti3, .{ .name = "__divti3", .linkage = linkage });
594 const __modti3 = @import("compiler_rt/modti3.zig").__modti3;
595 @export(__modti3, .{ .name = "__modti3", .linkage = linkage });
596 const __multi3 = @import("compiler_rt/multi3.zig").__multi3;
597 @export(__multi3, .{ .name = "__multi3", .linkage = linkage });
598 const __udivti3 = @import("compiler_rt/udivti3.zig").__udivti3;
599 @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage });
600 const __udivmodti4 = @import("compiler_rt/udivmodti4.zig").__udivmodti4;
601 @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage });
602 const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3;
603 @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage });
604 }
605 const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4;
606 @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage });
607 const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4;
608 @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage });
617 const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4;
618 @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage });
619 const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4;
620 @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage });
609621
610 _ = @import("compiler_rt/atomics.zig");
622 _ = @import("compiler_rt/atomics.zig");
623 }
611624}
612625
613626// Avoid dragging in the runtime safety mechanisms into this .o file,
614627// 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 {
616629 _ = error_return_trace;
617630 @setCold(true);
631 if (builtin.zig_is_stage2) {
632 while (true) {
633 @breakpoint();
634 }
635 }
618636 if (is_test) {
619637 std.debug.panic("{s}", .{msg});
620638 } else {
lib/std/special/compiler_rt/extendXfYf2.zig+6-6
......@@ -3,23 +3,23 @@ const builtin = @import("builtin");
33const is_test = builtin.is_test;
44
55pub 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));
77}
88
99pub 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));
1111}
1212
1313pub 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));
1515}
1616
1717pub fn __extendhfsf2(a: u16) callconv(.C) f32 {
18 return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, a });
18 return extendXfYf2(f32, f16, a);
1919}
2020
2121pub fn __extendhftf2(a: u16) callconv(.C) f128 {
22 return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f16, a });
22 return extendXfYf2(f128, f16, a);
2323}
2424
2525pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 {
......@@ -34,7 +34,7 @@ pub fn __aeabi_f2d(arg: f32) callconv(.AAPCS) f64 {
3434
3535const 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 {
3838 @setRuntimeSafety(builtin.is_test);
3939
4040 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 {
5353 },
5454 else => {},
5555 }
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
7057 unreachable;
7158}
......@@ -118,6 +105,21 @@ fn win_probe_stack_only() void {
118105 },
119106 else => {},
120107 }
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
122124 unreachable;
123125}
......@@ -199,7 +201,9 @@ pub fn _chkstk() callconv(.Naked) void {
199201}
200202pub fn __chkstk() callconv(.Naked) void {
201203 @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) {
203207 .i386 => @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{}),
204208 .x86_64 => @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{}),
205209 else => unreachable,
lib/std/special/test_runner.zig+16-2
......@@ -13,6 +13,12 @@ fn processArgs() void {
1313 const args = std.process.argsAlloc(&args_allocator.allocator) catch {
1414 @panic("Too many bytes passed over the CLI to the test runner");
1515 };
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 }
1622 std.testing.zig_exe_path = args[1];
1723}
1824
......@@ -56,7 +62,7 @@ pub fn main() void {
5662 .evented => blk: {
5763 if (async_frame_buffer.len < size) {
5864 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");
6066 }
6167 const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func);
6268 break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{});
......@@ -123,8 +129,16 @@ pub fn log(
123129}
124130
125131pub fn main2() anyerror!void {
132 var bad = false;
126133 // Simpler main(), exercising fewer language features, so that stage2 can handle it.
127134 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;
129143 }
130144}
lib/std/start.zig+17
......@@ -28,6 +28,8 @@ comptime {
2828 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
2929 @export(main2, .{ .name = "main" });
3030 }
31 } else if (builtin.os.tag == .windows) {
32 @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
3133 } else {
3234 if (!@hasDecl(root, "_start")) {
3335 @export(_start2, .{ .name = "_start" });
......@@ -87,6 +89,16 @@ fn main2() callconv(.C) c_int {
8789}
8890
8991fn _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 {
90102 root.main();
91103 exit2(0);
92104}
......@@ -143,11 +155,16 @@ fn exit2(code: usize) noreturn {
143155 },
144156 else => @compileError("TODO"),
145157 },
158 .windows => {
159 ExitProcess(@truncate(u32, code));
160 },
146161 else => @compileError("TODO"),
147162 }
148163 unreachable;
149164}
150165
166extern "kernel32" fn ExitProcess(exit_code: u32) callconv(.C) noreturn;
167
151168////////////////////////////////////////////////////////////////////////////////
152169
153170fn _DllMainCRTStartup(
lib/std/target.zig+9-3
......@@ -235,7 +235,6 @@ pub const Target = struct {
235235 .fuchsia,
236236 .kfreebsd,
237237 .lv2,
238 .solaris,
239238 .zos,
240239 .haiku,
241240 .minix,
......@@ -310,6 +309,12 @@ pub const Target = struct {
310309 .max = .{ .major = 6, .minor = 0 },
311310 },
312311 },
312 .solaris => return .{
313 .semver = .{
314 .min = .{ .major = 5, .minor = 11 },
315 .max = .{ .major = 5, .minor = 11 },
316 },
317 },
313318
314319 .linux => return .{
315320 .linux = .{
......@@ -353,6 +358,7 @@ pub const Target = struct {
353358 .netbsd,
354359 .openbsd,
355360 .dragonfly,
361 .solaris,
356362 => return TaggedVersionRange{ .semver = self.version_range.semver },
357363
358364 else => return .none,
......@@ -385,6 +391,7 @@ pub const Target = struct {
385391 .dragonfly,
386392 .openbsd,
387393 .haiku,
394 .solaris,
388395 => true,
389396
390397 .linux,
......@@ -395,7 +402,6 @@ pub const Target = struct {
395402 .fuchsia,
396403 .kfreebsd,
397404 .lv2,
398 .solaris,
399405 .zos,
400406 .minix,
401407 .rtems,
......@@ -1523,6 +1529,7 @@ pub const Target = struct {
15231529 .netbsd => return copy(&result, "/libexec/ld.elf_so"),
15241530 .openbsd => return copy(&result, "/usr/libexec/ld.so"),
15251531 .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"),
1532 .solaris => return copy(&result, "/lib/64/ld.so.1"),
15261533 .linux => switch (self.cpu.arch) {
15271534 .i386,
15281535 .sparc,
......@@ -1642,7 +1649,6 @@ pub const Target = struct {
16421649 .fuchsia,
16431650 .kfreebsd,
16441651 .lv2,
1645 .solaris,
16461652 .zos,
16471653 .minix,
16481654 .rtems,
lib/std/unicode.zig+22-4
......@@ -553,8 +553,9 @@ fn testDecode(bytes: []const u8) !u21 {
553553/// Caller must free returned memory.
554554pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
555555 var result = std.ArrayList(u8).init(allocator);
556 errdefer result.deinit();
556557 // optimistically guess that it will all be ascii.
557 try result.ensureCapacity(utf16le.len);
558 try result.ensureTotalCapacity(utf16le.len);
558559 var out_index: usize = 0;
559560 var it = Utf16LeIterator.init(utf16le);
560561 while (try it.nextCodepoint()) |codepoint| {
......@@ -569,9 +570,10 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8
569570
570571/// Caller must free returned memory.
571572pub 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();
573575 // optimistically guess that it will all be ascii.
574 try result.ensureCapacity(utf16le.len);
576 try result.ensureTotalCapacity(utf16le.len);
575577 var out_index: usize = 0;
576578 var it = Utf16LeIterator.init(utf16le);
577579 while (try it.nextCodepoint()) |codepoint| {
......@@ -653,12 +655,20 @@ test "utf16leToUtf8" {
653655 defer std.testing.allocator.free(utf8);
654656 try testing.expect(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));
655657 }
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 }
656665}
657666
658667pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![:0]u16 {
659668 var result = std.ArrayList(u16).init(allocator);
669 errdefer result.deinit();
660670 // optimistically guess that it will not require surrogate pairs
661 try result.ensureCapacity(utf8.len + 1);
671 try result.ensureTotalCapacity(utf8.len + 1);
662672
663673 const view = try Utf8View.init(utf8);
664674 var it = view.iterator();
......@@ -718,6 +728,10 @@ test "utf8ToUtf16Le" {
718728 try testing.expectEqual(@as(usize, 2), length);
719729 try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16le[0..]));
720730 }
731 {
732 const result = utf8ToUtf16Le(utf16le[0..], "\xf4\x90\x80\x80");
733 try testing.expectError(error.InvalidUtf8, result);
734 }
721735}
722736
723737test "utf8ToUtf16LeWithNull" {
......@@ -733,6 +747,10 @@ test "utf8ToUtf16LeWithNull" {
733747 try testing.expectEqualSlices(u8, "\xff\xdb\xff\xdf", mem.sliceAsBytes(utf16[0..]));
734748 try testing.expect(utf16[2] == 0);
735749 }
750 {
751 const result = utf8ToUtf16LeWithNull(testing.allocator, "\xf4\x90\x80\x80");
752 try testing.expectError(error.InvalidUtf8, result);
753 }
736754}
737755
738756/// 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 {
2727 return rc;
2828 }
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);
3131 defer os.closeSocket(fd);
3232
3333 var f: os.ifreq = undefined;
......@@ -566,21 +566,17 @@ test "ipv6: parse & format" {
566566
567567test "ipv6: parse & format addresses with scope ids" {
568568 if (!have_ifnamesize) return error.SkipZigTest;
569
570 const inputs = [_][]const u8{
571 "FF01::FB%lo",
572 };
573
574 const outputs = [_][]const u8{
575 "ff01::fb%1",
569 const iface = if (native_os.tag == .linux)
570 "lo"
571 else
572 "lo0";
573 const input = "FF01::FB%" ++ iface;
574 const output = "ff01::fb%1";
575
576 const parsed = IPv6.parse(input) catch |err| switch (err) {
577 error.InterfaceNotFound => return,
578 else => return err,
576579 };
577580
578 for (inputs) |input, i| {
579 const parsed = IPv6.parse(input) catch |err| switch (err) {
580 error.InterfaceNotFound => continue,
581 else => return err,
582 };
583
584 try testing.expectFmt(outputs[i], "{}", .{parsed});
585 }
581 try testing.expectFmt(output, "{}", .{parsed});
586582}
lib/std/x/os/socket.zig+1-1
......@@ -37,7 +37,7 @@ pub const Socket = struct {
3737
3838 /// POSIX `sockaddr.storage`. The expected size and alignment is specified in IETF RFC 2553.
3939 pub const Storage = extern struct {
40 pub const expected_size = 128;
40 pub const expected_size = os.sockaddr.SS_MAXSIZE;
4141 pub const expected_alignment = 8;
4242
4343 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 {
262262 token_tags[parse_error.token].symbol(),
263263 });
264264 },
265 .extra_addrspace_qualifier => {
266 return stream.writeAll("extra addrspace qualifier");
267 },
265268 .extra_align_qualifier => {
266269 return stream.writeAll("extra align qualifier");
267270 },
......@@ -392,14 +395,18 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
392395 .assign_mod,
393396 .assign_add,
394397 .assign_sub,
395 .assign_bit_shift_left,
396 .assign_bit_shift_right,
398 .assign_shl,
399 .assign_shl_sat,
400 .assign_shr,
397401 .assign_bit_and,
398402 .assign_bit_xor,
399403 .assign_bit_or,
400404 .assign_mul_wrap,
401405 .assign_add_wrap,
402406 .assign_sub_wrap,
407 .assign_mul_sat,
408 .assign_add_sat,
409 .assign_sub_sat,
403410 .assign,
404411 .merge_error_sets,
405412 .mul,
......@@ -407,13 +414,17 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
407414 .mod,
408415 .array_mult,
409416 .mul_wrap,
417 .mul_sat,
410418 .add,
411419 .sub,
412420 .array_cat,
413421 .add_wrap,
414422 .sub_wrap,
415 .bit_shift_left,
416 .bit_shift_right,
423 .add_sat,
424 .sub_sat,
425 .shl,
426 .shl_sat,
427 .shr,
417428 .bit_and,
418429 .bit_xor,
419430 .bit_or,
......@@ -648,14 +659,18 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
648659 .assign_mod,
649660 .assign_add,
650661 .assign_sub,
651 .assign_bit_shift_left,
652 .assign_bit_shift_right,
662 .assign_shl,
663 .assign_shl_sat,
664 .assign_shr,
653665 .assign_bit_and,
654666 .assign_bit_xor,
655667 .assign_bit_or,
656668 .assign_mul_wrap,
657669 .assign_add_wrap,
658670 .assign_sub_wrap,
671 .assign_mul_sat,
672 .assign_add_sat,
673 .assign_sub_sat,
659674 .assign,
660675 .merge_error_sets,
661676 .mul,
......@@ -663,13 +678,17 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
663678 .mod,
664679 .array_mult,
665680 .mul_wrap,
681 .mul_sat,
666682 .add,
667683 .sub,
668684 .array_cat,
669685 .add_wrap,
670686 .sub_wrap,
671 .bit_shift_left,
672 .bit_shift_right,
687 .add_sat,
688 .sub_sat,
689 .shl,
690 .shl_sat,
691 .shr,
673692 .bit_and,
674693 .bit_xor,
675694 .bit_or,
......@@ -1021,7 +1040,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
10211040 },
10221041 .fn_proto_one => {
10231042 const extra = tree.extraData(datas[n].lhs, Node.FnProtoOne);
1024 // linksection, callconv, align can appear in any order, so we
1043 // addrspace, linksection, callconv, align can appear in any order, so we
10251044 // find the last one here.
10261045 var max_node: Node.Index = datas[n].rhs;
10271046 var max_start = token_starts[main_tokens[max_node]];
......@@ -1034,6 +1053,14 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
10341053 max_offset = 1; // for the rparen
10351054 }
10361055 }
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 }
10371064 if (extra.section_expr != 0) {
10381065 const start = token_starts[main_tokens[extra.section_expr]];
10391066 if (start > max_start) {
......@@ -1055,7 +1082,7 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
10551082 },
10561083 .fn_proto => {
10571084 const extra = tree.extraData(datas[n].lhs, Node.FnProto);
1058 // linksection, callconv, align can appear in any order, so we
1085 // addrspace, linksection, callconv, align can appear in any order, so we
10591086 // find the last one here.
10601087 var max_node: Node.Index = datas[n].rhs;
10611088 var max_start = token_starts[main_tokens[max_node]];
......@@ -1068,6 +1095,14 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
10681095 max_offset = 1; // for the rparen
10691096 }
10701097 }
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 }
10711106 if (extra.section_expr != 0) {
10721107 const start = token_starts[main_tokens[extra.section_expr]];
10731108 if (start > max_start) {
......@@ -1138,6 +1173,7 @@ pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
11381173 return tree.fullVarDecl(.{
11391174 .type_node = extra.type_node,
11401175 .align_node = extra.align_node,
1176 .addrspace_node = extra.addrspace_node,
11411177 .section_node = extra.section_node,
11421178 .init_node = data.rhs,
11431179 .mut_token = tree.nodes.items(.main_token)[node],
......@@ -1151,6 +1187,7 @@ pub fn localVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
11511187 return tree.fullVarDecl(.{
11521188 .type_node = extra.type_node,
11531189 .align_node = extra.align_node,
1190 .addrspace_node = 0,
11541191 .section_node = 0,
11551192 .init_node = data.rhs,
11561193 .mut_token = tree.nodes.items(.main_token)[node],
......@@ -1163,6 +1200,7 @@ pub fn simpleVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
11631200 return tree.fullVarDecl(.{
11641201 .type_node = data.lhs,
11651202 .align_node = 0,
1203 .addrspace_node = 0,
11661204 .section_node = 0,
11671205 .init_node = data.rhs,
11681206 .mut_token = tree.nodes.items(.main_token)[node],
......@@ -1175,6 +1213,7 @@ pub fn alignedVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
11751213 return tree.fullVarDecl(.{
11761214 .type_node = 0,
11771215 .align_node = data.lhs,
1216 .addrspace_node = 0,
11781217 .section_node = 0,
11791218 .init_node = data.rhs,
11801219 .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.
12491288 .return_type = data.rhs,
12501289 .params = params,
12511290 .align_expr = 0,
1291 .addrspace_expr = 0,
12521292 .section_expr = 0,
12531293 .callconv_expr = 0,
12541294 });
......@@ -1265,6 +1305,7 @@ pub fn fnProtoMulti(tree: Tree, node: Node.Index) full.FnProto {
12651305 .return_type = data.rhs,
12661306 .params = params,
12671307 .align_expr = 0,
1308 .addrspace_expr = 0,
12681309 .section_expr = 0,
12691310 .callconv_expr = 0,
12701311 });
......@@ -1282,6 +1323,7 @@ pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnP
12821323 .return_type = data.rhs,
12831324 .params = params,
12841325 .align_expr = extra.align_expr,
1326 .addrspace_expr = extra.addrspace_expr,
12851327 .section_expr = extra.section_expr,
12861328 .callconv_expr = extra.callconv_expr,
12871329 });
......@@ -1298,6 +1340,7 @@ pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto {
12981340 .return_type = data.rhs,
12991341 .params = params,
13001342 .align_expr = extra.align_expr,
1343 .addrspace_expr = extra.addrspace_expr,
13011344 .section_expr = extra.section_expr,
13021345 .callconv_expr = extra.callconv_expr,
13031346 });
......@@ -1453,6 +1496,7 @@ pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType {
14531496 return tree.fullPtrType(.{
14541497 .main_token = tree.nodes.items(.main_token)[node],
14551498 .align_node = data.lhs,
1499 .addrspace_node = 0,
14561500 .sentinel = 0,
14571501 .bit_range_start = 0,
14581502 .bit_range_end = 0,
......@@ -1466,6 +1510,7 @@ pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType {
14661510 return tree.fullPtrType(.{
14671511 .main_token = tree.nodes.items(.main_token)[node],
14681512 .align_node = 0,
1513 .addrspace_node = 0,
14691514 .sentinel = data.lhs,
14701515 .bit_range_start = 0,
14711516 .bit_range_end = 0,
......@@ -1480,6 +1525,7 @@ pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType {
14801525 return tree.fullPtrType(.{
14811526 .main_token = tree.nodes.items(.main_token)[node],
14821527 .align_node = extra.align_node,
1528 .addrspace_node = extra.addrspace_node,
14831529 .sentinel = extra.sentinel,
14841530 .bit_range_start = 0,
14851531 .bit_range_end = 0,
......@@ -1494,6 +1540,7 @@ pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType {
14941540 return tree.fullPtrType(.{
14951541 .main_token = tree.nodes.items(.main_token)[node],
14961542 .align_node = extra.align_node,
1543 .addrspace_node = extra.addrspace_node,
14971544 .sentinel = extra.sentinel,
14981545 .bit_range_start = extra.bit_range_start,
14991546 .bit_range_end = extra.bit_range_end,
......@@ -2063,6 +2110,7 @@ pub const full = struct {
20632110 mut_token: TokenIndex,
20642111 type_node: Node.Index,
20652112 align_node: Node.Index,
2113 addrspace_node: Node.Index,
20662114 section_node: Node.Index,
20672115 init_node: Node.Index,
20682116 };
......@@ -2130,6 +2178,7 @@ pub const full = struct {
21302178 return_type: Node.Index,
21312179 params: []const Node.Index,
21322180 align_expr: Node.Index,
2181 addrspace_expr: Node.Index,
21332182 section_expr: Node.Index,
21342183 callconv_expr: Node.Index,
21352184 };
......@@ -2288,6 +2337,7 @@ pub const full = struct {
22882337 pub const Components = struct {
22892338 main_token: TokenIndex,
22902339 align_node: Node.Index,
2340 addrspace_node: Node.Index,
22912341 sentinel: Node.Index,
22922342 bit_range_start: Node.Index,
22932343 bit_range_end: Node.Index,
......@@ -2397,6 +2447,7 @@ pub const Error = struct {
23972447 expected_var_decl_or_fn,
23982448 expected_loop_payload,
23992449 expected_container,
2450 extra_addrspace_qualifier,
24002451 extra_align_qualifier,
24012452 extra_allowzero_qualifier,
24022453 extra_const_qualifier,
......@@ -2489,9 +2540,11 @@ pub const Node = struct {
24892540 /// `lhs -= rhs`. main_token is op.
24902541 assign_sub,
24912542 /// `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,
24932546 /// `lhs >>= rhs`. main_token is op.
2494 assign_bit_shift_right,
2547 assign_shr,
24952548 /// `lhs &= rhs`. main_token is op.
24962549 assign_bit_and,
24972550 /// `lhs ^= rhs`. main_token is op.
......@@ -2504,6 +2557,12 @@ pub const Node = struct {
25042557 assign_add_wrap,
25052558 /// `lhs -%= rhs`. main_token is op.
25062559 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,
25072566 /// `lhs = rhs`. main_token is op.
25082567 assign,
25092568 /// `lhs || rhs`. main_token is the `||`.
......@@ -2518,6 +2577,8 @@ pub const Node = struct {
25182577 array_mult,
25192578 /// `lhs *% rhs`. main_token is the `*%`.
25202579 mul_wrap,
2580 /// `lhs *| rhs`. main_token is the `*|`.
2581 mul_sat,
25212582 /// `lhs + rhs`. main_token is the `+`.
25222583 add,
25232584 /// `lhs - rhs`. main_token is the `-`.
......@@ -2528,10 +2589,16 @@ pub const Node = struct {
25282589 add_wrap,
25292590 /// `lhs -% rhs`. main_token is the `-%`.
25302591 sub_wrap,
2592 /// `lhs +| rhs`. main_token is the `+|`.
2593 add_sat,
2594 /// `lhs -| rhs`. main_token is the `-|`.
2595 sub_sat,
25312596 /// `lhs << rhs`. main_token is the `<<`.
2532 bit_shift_left,
2597 shl,
2598 /// `lhs <<| rhs`. main_token is the `<<|`.
2599 shl_sat,
25332600 /// `lhs >> rhs`. main_token is the `>>`.
2534 bit_shift_right,
2601 shr,
25352602 /// `lhs & rhs`. main_token is the `&`.
25362603 bit_and,
25372604 /// `lhs ^ rhs`. main_token is the `^`.
......@@ -2723,13 +2790,13 @@ pub const Node = struct {
27232790 /// main_token is the `fn` keyword.
27242791 /// extern function declarations use this tag.
27252792 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]`.
27272794 /// zero or one parameters.
27282795 /// anytype and ... parameters are omitted from the AST tree.
27292796 /// main_token is the `fn` keyword.
27302797 /// extern function declarations use this tag.
27312798 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]`.
27332800 /// anytype and ... parameters are omitted from the AST tree.
27342801 /// main_token is the `fn` keyword.
27352802 /// extern function declarations use this tag.
......@@ -2893,11 +2960,13 @@ pub const Node = struct {
28932960 pub const PtrType = struct {
28942961 sentinel: Index,
28952962 align_node: Index,
2963 addrspace_node: Index,
28962964 };
28972965
28982966 pub const PtrTypeBitRange = struct {
28992967 sentinel: Index,
29002968 align_node: Index,
2969 addrspace_node: Index,
29012970 bit_range_start: Index,
29022971 bit_range_end: Index,
29032972 };
......@@ -2920,8 +2989,13 @@ pub const Node = struct {
29202989 };
29212990
29222991 pub const GlobalVarDecl = struct {
2992 /// Populated if there is an explicit type ascription.
29232993 type_node: Index,
2994 /// Populated if align(A) is present.
29242995 align_node: Index,
2996 /// Populated if addrspace(A) is present.
2997 addrspace_node: Index,
2998 /// Populated if linksection(A) is present.
29252999 section_node: Index,
29263000 };
29273001
......@@ -2953,6 +3027,8 @@ pub const Node = struct {
29533027 param: Index,
29543028 /// Populated if align(A) is present.
29553029 align_expr: Index,
3030 /// Populated if addrspace(A) is present.
3031 addrspace_expr: Index,
29563032 /// Populated if linksection(A) is present.
29573033 section_expr: Index,
29583034 /// Populated if callconv(A) is present.
......@@ -2964,6 +3040,8 @@ pub const Node = struct {
29643040 params_end: Index,
29653041 /// Populated if align(A) is present.
29663042 align_expr: Index,
3043 /// Populated if addrspace(A) is present.
3044 addrspace_expr: Index,
29673045 /// Populated if linksection(A) is present.
29683046 section_expr: Index,
29693047 /// 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 {
325325 .is_const = ptr.is_const,
326326 .is_volatile = ptr.is_volatile,
327327 .alignment = @alignOf(ElementType),
328 .address_space = .generic,
328329 .child = ElementType,
329330 .is_allowzero = true,
330331 .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 {
1717
1818 // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count.
1919 const estimated_token_count = source.len / 8;
20 try tokens.ensureCapacity(gpa, estimated_token_count);
20 try tokens.ensureTotalCapacity(gpa, estimated_token_count);
2121
2222 var tokenizer = std.zig.Tokenizer.init(source);
2323 while (true) {
......@@ -48,7 +48,7 @@ pub fn parse(gpa: *Allocator, source: [:0]const u8) Allocator.Error!Ast {
4848 // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes.
4949 // Make sure at least 1 so we can use appendAssumeCapacity on the root node below.
5050 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
5353 // Root node must be index 0.
5454 // Root <- skip ContainerMembers eof
......@@ -138,7 +138,7 @@ const Parser = struct {
138138
139139 fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {
140140 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);
142142 const result = @intCast(u32, p.extra_data.items.len);
143143 inline for (fields) |field| {
144144 comptime assert(field.field_type == Node.Index);
......@@ -629,7 +629,7 @@ const Parser = struct {
629629 };
630630 }
631631
632 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
632 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
633633 fn parseFnProto(p: *Parser) !Node.Index {
634634 const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
635635
......@@ -639,6 +639,7 @@ const Parser = struct {
639639 _ = p.eatToken(.identifier);
640640 const params = try p.parseParamDeclList();
641641 const align_expr = try p.parseByteAlign();
642 const addrspace_expr = try p.parseAddrSpace();
642643 const section_expr = try p.parseLinkSection();
643644 const callconv_expr = try p.parseCallconv();
644645 _ = p.eatToken(.bang);
......@@ -650,7 +651,7 @@ const Parser = struct {
650651 try p.warn(.expected_return_type);
651652 }
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) {
654655 switch (params) {
655656 .zero_or_one => |param| return p.setNode(fn_proto_index, .{
656657 .tag = .fn_proto_simple,
......@@ -683,6 +684,7 @@ const Parser = struct {
683684 .lhs = try p.addExtra(Node.FnProtoOne{
684685 .param = param,
685686 .align_expr = align_expr,
687 .addrspace_expr = addrspace_expr,
686688 .section_expr = section_expr,
687689 .callconv_expr = callconv_expr,
688690 }),
......@@ -698,6 +700,7 @@ const Parser = struct {
698700 .params_start = span.start,
699701 .params_end = span.end,
700702 .align_expr = align_expr,
703 .addrspace_expr = addrspace_expr,
701704 .section_expr = section_expr,
702705 .callconv_expr = callconv_expr,
703706 }),
......@@ -708,7 +711,7 @@ const Parser = struct {
708711 }
709712 }
710713
711 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
714 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON
712715 fn parseVarDecl(p: *Parser) !Node.Index {
713716 const mut_token = p.eatToken(.keyword_const) orelse
714717 p.eatToken(.keyword_var) orelse
......@@ -717,9 +720,10 @@ const Parser = struct {
717720 _ = try p.expectToken(.identifier);
718721 const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr();
719722 const align_node = try p.parseByteAlign();
723 const addrspace_node = try p.parseAddrSpace();
720724 const section_node = try p.parseLinkSection();
721725 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) {
723727 if (align_node == 0) {
724728 return p.addNode(.{
725729 .tag = .simple_var_decl,
......@@ -759,6 +763,7 @@ const Parser = struct {
759763 .lhs = try p.addExtra(Node.GlobalVarDecl{
760764 .type_node = type_node,
761765 .align_node = align_node,
766 .addrspace_node = addrspace_node,
762767 .section_node = section_node,
763768 }),
764769 .rhs = init_node,
......@@ -1263,14 +1268,18 @@ const Parser = struct {
12631268 .percent_equal => .assign_mod,
12641269 .plus_equal => .assign_add,
12651270 .minus_equal => .assign_sub,
1266 .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left,
1267 .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right,
1271 .angle_bracket_angle_bracket_left_equal => .assign_shl,
1272 .angle_bracket_angle_bracket_left_pipe_equal => .assign_shl_sat,
1273 .angle_bracket_angle_bracket_right_equal => .assign_shr,
12681274 .ampersand_equal => .assign_bit_and,
12691275 .caret_equal => .assign_bit_xor,
12701276 .pipe_equal => .assign_bit_or,
12711277 .asterisk_percent_equal => .assign_mul_wrap,
12721278 .plus_percent_equal => .assign_add_wrap,
12731279 .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,
12741283 .equal => .assign,
12751284 else => return expr,
12761285 };
......@@ -1337,14 +1346,17 @@ const Parser = struct {
13371346 .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" },
13381347 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },
13391348
1340 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left },
1341 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right },
1349 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .shl },
1350 .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .shl_sat },
1351 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .shr },
13421352
13431353 .plus = .{ .prec = 60, .tag = .add },
13441354 .minus = .{ .prec = 60, .tag = .sub },
13451355 .plus_plus = .{ .prec = 60, .tag = .array_cat },
13461356 .plus_percent = .{ .prec = 60, .tag = .add_wrap },
13471357 .minus_percent = .{ .prec = 60, .tag = .sub_wrap },
1358 .plus_pipe = .{ .prec = 60, .tag = .add_sat },
1359 .minus_pipe = .{ .prec = 60, .tag = .sub_sat },
13481360
13491361 .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets },
13501362 .asterisk = .{ .prec = 70, .tag = .mul },
......@@ -1352,6 +1364,7 @@ const Parser = struct {
13521364 .percent = .{ .prec = 70, .tag = .mod },
13531365 .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult },
13541366 .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap },
1367 .asterisk_pipe = .{ .prec = 70, .tag = .mul_sat },
13551368 });
13561369
13571370 fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index {
......@@ -1440,8 +1453,8 @@ const Parser = struct {
14401453 /// PrefixTypeOp
14411454 /// <- QUESTIONMARK
14421455 /// / KEYWORD_anyframe MINUSRARROW
1443 /// / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
1444 /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
1456 /// / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
1457 /// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
14451458 /// / ArrayTypeStart
14461459 /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET
14471460 /// PtrTypeStart
......@@ -1474,29 +1487,43 @@ const Parser = struct {
14741487 const asterisk = p.nextToken();
14751488 const mods = try p.parsePtrModifiers();
14761489 const elem_type = try p.expectTypeExpr();
1477 if (mods.bit_range_start == 0) {
1490 if (mods.bit_range_start != 0) {
14781491 return p.addNode(.{
1479 .tag = .ptr_type_aligned,
1492 .tag = .ptr_type_bit_range,
14801493 .main_token = asterisk,
14811494 .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 }),
14831502 .rhs = elem_type,
14841503 },
14851504 });
1486 } else {
1505 } else if (mods.addrspace_node != 0) {
14871506 return p.addNode(.{
1488 .tag = .ptr_type_bit_range,
1507 .tag = .ptr_type,
14891508 .main_token = asterisk,
14901509 .data = .{
1491 .lhs = try p.addExtra(Node.PtrTypeBitRange{
1510 .lhs = try p.addExtra(Node.PtrType{
14921511 .sentinel = 0,
14931512 .align_node = mods.align_node,
1494 .bit_range_start = mods.bit_range_start,
1495 .bit_range_end = mods.bit_range_end,
1513 .addrspace_node = mods.addrspace_node,
14961514 }),
14971515 .rhs = elem_type,
14981516 },
14991517 });
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 });
15001527 }
15011528 },
15021529 .asterisk_asterisk => {
......@@ -1504,29 +1531,43 @@ const Parser = struct {
15041531 const mods = try p.parsePtrModifiers();
15051532 const elem_type = try p.expectTypeExpr();
15061533 const inner: Node.Index = inner: {
1507 if (mods.bit_range_start == 0) {
1534 if (mods.bit_range_start != 0) {
15081535 break :inner try p.addNode(.{
1509 .tag = .ptr_type_aligned,
1536 .tag = .ptr_type_bit_range,
15101537 .main_token = asterisk,
15111538 .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 }),
15131546 .rhs = elem_type,
15141547 },
15151548 });
1516 } else {
1549 } else if (mods.addrspace_node != 0) {
15171550 break :inner try p.addNode(.{
1518 .tag = .ptr_type_bit_range,
1551 .tag = .ptr_type,
15191552 .main_token = asterisk,
15201553 .data = .{
1521 .lhs = try p.addExtra(Node.PtrTypeBitRange{
1554 .lhs = try p.addExtra(Node.PtrType{
15221555 .sentinel = 0,
15231556 .align_node = mods.align_node,
1524 .bit_range_start = mods.bit_range_start,
1525 .bit_range_end = mods.bit_range_end,
1557 .addrspace_node = mods.addrspace_node,
15261558 }),
15271559 .rhs = elem_type,
15281560 },
15291561 });
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 });
15301571 }
15311572 };
15321573 return p.addNode(.{
......@@ -1543,24 +1584,19 @@ const Parser = struct {
15431584 _ = p.nextToken();
15441585 const asterisk = p.nextToken();
15451586 var sentinel: Node.Index = 0;
1546 prefix: {
1547 if (p.eatToken(.identifier)) |ident| {
1548 const token_slice = p.source[p.token_starts[ident]..][0..2];
1549 if (!std.mem.eql(u8, token_slice, "c]")) {
1550 p.tok_i -= 1;
1551 } else {
1552 break :prefix;
1553 }
1554 }
1555 if (p.eatToken(.colon)) |_| {
1556 sentinel = try p.expectExpr();
1587 if (p.eatToken(.identifier)) |ident| {
1588 const ident_slice = p.source[p.token_starts[ident]..p.token_starts[ident + 1]];
1589 if (!std.mem.eql(u8, std.mem.trimRight(u8, ident_slice, &std.ascii.spaces), "c")) {
1590 p.tok_i -= 1;
15571591 }
1592 } else if (p.eatToken(.colon)) |_| {
1593 sentinel = try p.expectExpr();
15581594 }
15591595 _ = try p.expectToken(.r_bracket);
15601596 const mods = try p.parsePtrModifiers();
15611597 const elem_type = try p.expectTypeExpr();
15621598 if (mods.bit_range_start == 0) {
1563 if (sentinel == 0) {
1599 if (sentinel == 0 and mods.addrspace_node == 0) {
15641600 return p.addNode(.{
15651601 .tag = .ptr_type_aligned,
15661602 .main_token = asterisk,
......@@ -1569,7 +1605,7 @@ const Parser = struct {
15691605 .rhs = elem_type,
15701606 },
15711607 });
1572 } else if (mods.align_node == 0) {
1608 } else if (mods.align_node == 0 and mods.addrspace_node == 0) {
15731609 return p.addNode(.{
15741610 .tag = .ptr_type_sentinel,
15751611 .main_token = asterisk,
......@@ -1586,6 +1622,7 @@ const Parser = struct {
15861622 .lhs = try p.addExtra(Node.PtrType{
15871623 .sentinel = sentinel,
15881624 .align_node = mods.align_node,
1625 .addrspace_node = mods.addrspace_node,
15891626 }),
15901627 .rhs = elem_type,
15911628 },
......@@ -1599,6 +1636,7 @@ const Parser = struct {
15991636 .lhs = try p.addExtra(Node.PtrTypeBitRange{
16001637 .sentinel = sentinel,
16011638 .align_node = mods.align_node,
1639 .addrspace_node = mods.addrspace_node,
16021640 .bit_range_start = mods.bit_range_start,
16031641 .bit_range_end = mods.bit_range_end,
16041642 }),
......@@ -1624,7 +1662,7 @@ const Parser = struct {
16241662 .token = p.nodes.items(.main_token)[mods.bit_range_start],
16251663 });
16261664 }
1627 if (sentinel == 0) {
1665 if (sentinel == 0 and mods.addrspace_node == 0) {
16281666 return p.addNode(.{
16291667 .tag = .ptr_type_aligned,
16301668 .main_token = lbracket,
......@@ -1633,7 +1671,7 @@ const Parser = struct {
16331671 .rhs = elem_type,
16341672 },
16351673 });
1636 } else if (mods.align_node == 0) {
1674 } else if (mods.align_node == 0 and mods.addrspace_node == 0) {
16371675 return p.addNode(.{
16381676 .tag = .ptr_type_sentinel,
16391677 .main_token = lbracket,
......@@ -1650,6 +1688,7 @@ const Parser = struct {
16501688 .lhs = try p.addExtra(Node.PtrType{
16511689 .sentinel = sentinel,
16521690 .align_node = mods.align_node,
1691 .addrspace_node = mods.addrspace_node,
16531692 }),
16541693 .rhs = elem_type,
16551694 },
......@@ -1661,6 +1700,7 @@ const Parser = struct {
16611700 .keyword_const,
16621701 .keyword_volatile,
16631702 .keyword_allowzero,
1703 .keyword_addrspace,
16641704 => return p.fail(.ptr_mod_on_array_child_type),
16651705 else => {},
16661706 }
......@@ -2879,6 +2919,15 @@ const Parser = struct {
28792919 return expr_node;
28802920 }
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
28822931 /// ParamDecl
28832932 /// <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
28842933 /// / DOT3
......@@ -3011,6 +3060,7 @@ const Parser = struct {
30113060
30123061 const PtrModifiers = struct {
30133062 align_node: Node.Index,
3063 addrspace_node: Node.Index,
30143064 bit_range_start: Node.Index,
30153065 bit_range_end: Node.Index,
30163066 };
......@@ -3018,12 +3068,14 @@ const Parser = struct {
30183068 fn parsePtrModifiers(p: *Parser) !PtrModifiers {
30193069 var result: PtrModifiers = .{
30203070 .align_node = 0,
3071 .addrspace_node = 0,
30213072 .bit_range_start = 0,
30223073 .bit_range_end = 0,
30233074 };
30243075 var saw_const = false;
30253076 var saw_volatile = false;
30263077 var saw_allowzero = false;
3078 var saw_addrspace = false;
30273079 while (true) {
30283080 switch (p.token_tags[p.tok_i]) {
30293081 .keyword_align => {
......@@ -3063,6 +3115,12 @@ const Parser = struct {
30633115 p.tok_i += 1;
30643116 saw_allowzero = true;
30653117 },
3118 .keyword_addrspace => {
3119 if (saw_addrspace) {
3120 try p.warn(.extra_addrspace_qualifier);
3121 }
3122 result.addrspace_node = try p.parseAddrSpace();
3123 },
30663124 else => return result,
30673125 }
30683126 }
lib/std/zig/parser_test.zig+52-10
......@@ -404,6 +404,10 @@ test "zig fmt: trailing comma in fn parameter list" {
404404 \\pub fn f(
405405 \\ a: i32,
406406 \\ b: i32,
407 \\) addrspace(.generic) i32 {}
408 \\pub fn f(
409 \\ a: i32,
410 \\ b: i32,
407411 \\) linksection(".text") i32 {}
408412 \\pub fn f(
409413 \\ a: i32,
......@@ -553,8 +557,8 @@ test "zig fmt: sentinel-terminated slice type" {
553557test "zig fmt: pointer-to-one with modifiers" {
554558 try testCanonical(
555559 \\const x: *u32 = undefined;
556 \\const y: *allowzero align(8) const volatile u32 = undefined;
557 \\const z: *allowzero align(8:4:2) const volatile u32 = undefined;
560 \\const y: *allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
561 \\const z: *allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
558562 \\
559563 );
560564}
......@@ -562,8 +566,8 @@ test "zig fmt: pointer-to-one with modifiers" {
562566test "zig fmt: pointer-to-many with modifiers" {
563567 try testCanonical(
564568 \\const x: [*]u32 = undefined;
565 \\const y: [*]allowzero align(8) const volatile u32 = undefined;
566 \\const z: [*]allowzero align(8:4:2) const volatile u32 = undefined;
569 \\const y: [*]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
570 \\const z: [*]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
567571 \\
568572 );
569573}
......@@ -571,8 +575,8 @@ test "zig fmt: pointer-to-many with modifiers" {
571575test "zig fmt: sentinel pointer with modifiers" {
572576 try testCanonical(
573577 \\const x: [*:42]u32 = undefined;
574 \\const y: [*:42]allowzero align(8) const volatile u32 = undefined;
575 \\const y: [*:42]allowzero align(8:4:2) const volatile u32 = undefined;
578 \\const y: [*:42]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
579 \\const y: [*:42]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
576580 \\
577581 );
578582}
......@@ -580,8 +584,8 @@ test "zig fmt: sentinel pointer with modifiers" {
580584test "zig fmt: c pointer with modifiers" {
581585 try testCanonical(
582586 \\const x: [*c]u32 = undefined;
583 \\const y: [*c]allowzero align(8) const volatile u32 = undefined;
584 \\const z: [*c]allowzero align(8:4:2) const volatile u32 = undefined;
587 \\const y: [*c]allowzero align(8) addrspace(.generic) const volatile u32 = undefined;
588 \\const z: [*c]allowzero align(8:4:2) addrspace(.generic) const volatile u32 = undefined;
585589 \\
586590 );
587591}
......@@ -589,7 +593,7 @@ test "zig fmt: c pointer with modifiers" {
589593test "zig fmt: slice with modifiers" {
590594 try testCanonical(
591595 \\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;
593597 \\
594598 );
595599}
......@@ -597,7 +601,7 @@ test "zig fmt: slice with modifiers" {
597601test "zig fmt: sentinel slice with modifiers" {
598602 try testCanonical(
599603 \\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;
601605 \\
602606 );
603607}
......@@ -1129,6 +1133,16 @@ test "zig fmt: linksection" {
11291133 );
11301134}
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
11321146test "zig fmt: correctly space struct fields with doc comments" {
11331147 try testTransform(
11341148 \\pub const S = struct {
......@@ -4725,6 +4739,26 @@ test "zig fmt: assignment with inline for and inline while" {
47254739 );
47264740}
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
47284762test "zig fmt: insert trailing comma if there are comments between switch values" {
47294763 try testTransform(
47304764 \\const a = switch (b) {
......@@ -5225,6 +5259,14 @@ test "recovery: nonfinal varargs" {
52255259 });
52265260}
52275261
5262test "recovery: eof in c pointer" {
5263 try testError(
5264 \\const Ptr = [*c
5265 , &[_]Error{
5266 .expected_token,
5267 });
5268}
5269
52285270const std = @import("std");
52295271const mem = std.mem;
52305272const 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,
333333
334334 .add,
335335 .add_wrap,
336 .add_sat,
336337 .array_cat,
337338 .array_mult,
338339 .assign,
339340 .assign_bit_and,
340341 .assign_bit_or,
341 .assign_bit_shift_left,
342 .assign_bit_shift_right,
342 .assign_shl,
343 .assign_shl_sat,
344 .assign_shr,
343345 .assign_bit_xor,
344346 .assign_div,
345347 .assign_sub,
346348 .assign_sub_wrap,
349 .assign_sub_sat,
347350 .assign_mod,
348351 .assign_add,
349352 .assign_add_wrap,
353 .assign_add_sat,
350354 .assign_mul,
351355 .assign_mul_wrap,
356 .assign_mul_sat,
352357 .bang_equal,
353358 .bit_and,
354359 .bit_or,
355 .bit_shift_left,
356 .bit_shift_right,
360 .shl,
361 .shl_sat,
362 .shr,
357363 .bit_xor,
358364 .bool_and,
359365 .bool_or,
......@@ -367,8 +373,10 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
367373 .mod,
368374 .mul,
369375 .mul_wrap,
376 .mul_sat,
370377 .sub,
371378 .sub_wrap,
379 .sub_sat,
372380 .@"orelse",
373381 => {
374382 const infix = datas[node];
......@@ -797,6 +805,14 @@ fn renderPtrType(
797805 }
798806 }
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
800816 if (ptr_type.const_token) |const_token| {
801817 try renderToken(ais, tree, const_token, .space);
802818 }
......@@ -921,6 +937,7 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe
921937
922938 const name_space = if (var_decl.ast.type_node == 0 and
923939 (var_decl.ast.align_node != 0 or
940 var_decl.ast.addrspace_node != 0 or
924941 var_decl.ast.section_node != 0 or
925942 var_decl.ast.init_node != 0))
926943 Space.space
......@@ -930,8 +947,8 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe
930947
931948 if (var_decl.ast.type_node != 0) {
932949 try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // :
933 if (var_decl.ast.align_node != 0 or var_decl.ast.section_node != 0 or
934 var_decl.ast.init_node != 0)
950 if (var_decl.ast.align_node != 0 or var_decl.ast.addrspace_node != 0 or
951 var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0)
935952 {
936953 try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space);
937954 } else {
......@@ -948,6 +965,23 @@ fn renderVarDecl(gpa: *Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDe
948965 try renderToken(ais, tree, align_kw, Space.none); // align
949966 try renderToken(ais, tree, lparen, Space.none); // (
950967 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);
951985 if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) {
952986 try renderToken(ais, tree, rparen, .space); // )
953987 } else {
......@@ -1267,6 +1301,14 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro
12671301 smallest_start = start;
12681302 }
12691303 }
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 }
12701312 if (fn_proto.ast.section_expr != 0) {
12711313 const tok = tree.firstToken(fn_proto.ast.section_expr) - 3;
12721314 const start = token_starts[tok];
......@@ -1407,6 +1449,16 @@ fn renderFnProto(gpa: *Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnPro
14071449 try renderToken(ais, tree, align_rparen, .space); // )
14081450 }
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
14101462 if (fn_proto.ast.section_expr != 0) {
14111463 const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;
14121464 const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;
......@@ -2476,8 +2528,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
24762528 .assign,
24772529 .assign_bit_and,
24782530 .assign_bit_or,
2479 .assign_bit_shift_left,
2480 .assign_bit_shift_right,
2531 .assign_shl,
2532 .assign_shr,
24812533 .assign_bit_xor,
24822534 .assign_div,
24832535 .assign_sub,
......@@ -2490,8 +2542,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
24902542 .bang_equal,
24912543 .bit_and,
24922544 .bit_or,
2493 .bit_shift_left,
2494 .bit_shift_right,
2545 .shl,
2546 .shr,
24952547 .bit_xor,
24962548 .bool_and,
24972549 .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
2929 const slice = bytes[1..];
3030
3131 const prev_len = buf.items.len;
32 try buf.ensureCapacity(prev_len + slice.len - 1);
32 try buf.ensureUnusedCapacity(slice.len - 1);
3333 errdefer buf.shrinkRetainingCapacity(prev_len);
3434
3535 const State = enum {
lib/std/zig/system.zig+23
......@@ -101,6 +101,17 @@ pub const NativePaths = struct {
101101 return self;
102102 }
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
104115 if (native_target.os.tag != .windows) {
105116 const triple = try native_target.linuxTriple(allocator);
106117 const qual = native_target.cpu.arch.ptrBitWidth();
......@@ -243,6 +254,18 @@ pub const NativeTargetInfo = struct {
243254 error.InvalidVersion => {},
244255 }
245256 },
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 },
246269 .windows => {
247270 const detected_version = windows.detectRuntimeVersion();
248271 os.version_range.windows.min = detected_version;
lib/std/zig/tokenizer.zig+115-1
......@@ -11,6 +11,7 @@ pub const Token = struct {
1111 };
1212
1313 pub const keywords = std.ComptimeStringMap(Tag, .{
14 .{ "addrspace", .keyword_addrspace },
1415 .{ "align", .keyword_align },
1516 .{ "allowzero", .keyword_allowzero },
1617 .{ "and", .keyword_and },
......@@ -102,15 +103,21 @@ pub const Token = struct {
102103 plus_equal,
103104 plus_percent,
104105 plus_percent_equal,
106 plus_pipe,
107 plus_pipe_equal,
105108 minus,
106109 minus_equal,
107110 minus_percent,
108111 minus_percent_equal,
112 minus_pipe,
113 minus_pipe_equal,
109114 asterisk,
110115 asterisk_equal,
111116 asterisk_asterisk,
112117 asterisk_percent,
113118 asterisk_percent_equal,
119 asterisk_pipe,
120 asterisk_pipe_equal,
114121 arrow,
115122 colon,
116123 slash,
......@@ -123,6 +130,8 @@ pub const Token = struct {
123130 angle_bracket_left_equal,
124131 angle_bracket_angle_bracket_left,
125132 angle_bracket_angle_bracket_left_equal,
133 angle_bracket_angle_bracket_left_pipe,
134 angle_bracket_angle_bracket_left_pipe_equal,
126135 angle_bracket_right,
127136 angle_bracket_right_equal,
128137 angle_bracket_angle_bracket_right,
......@@ -132,6 +141,7 @@ pub const Token = struct {
132141 float_literal,
133142 doc_comment,
134143 container_doc_comment,
144 keyword_addrspace,
135145 keyword_align,
136146 keyword_allowzero,
137147 keyword_and,
......@@ -225,15 +235,21 @@ pub const Token = struct {
225235 .plus_equal => "+=",
226236 .plus_percent => "+%",
227237 .plus_percent_equal => "+%=",
238 .plus_pipe => "+|",
239 .plus_pipe_equal => "+|=",
228240 .minus => "-",
229241 .minus_equal => "-=",
230242 .minus_percent => "-%",
231243 .minus_percent_equal => "-%=",
244 .minus_pipe => "-|",
245 .minus_pipe_equal => "-|=",
232246 .asterisk => "*",
233247 .asterisk_equal => "*=",
234248 .asterisk_asterisk => "**",
235249 .asterisk_percent => "*%",
236250 .asterisk_percent_equal => "*%=",
251 .asterisk_pipe => "*|",
252 .asterisk_pipe_equal => "*|=",
237253 .arrow => "->",
238254 .colon => ":",
239255 .slash => "/",
......@@ -246,11 +262,14 @@ pub const Token = struct {
246262 .angle_bracket_left_equal => "<=",
247263 .angle_bracket_angle_bracket_left => "<<",
248264 .angle_bracket_angle_bracket_left_equal => "<<=",
265 .angle_bracket_angle_bracket_left_pipe => "<<|",
266 .angle_bracket_angle_bracket_left_pipe_equal => "<<|=",
249267 .angle_bracket_right => ">",
250268 .angle_bracket_right_equal => ">=",
251269 .angle_bracket_angle_bracket_right => ">>",
252270 .angle_bracket_angle_bracket_right_equal => ">>=",
253271 .tilde => "~",
272 .keyword_addrspace => "addrspace",
254273 .keyword_align => "align",
255274 .keyword_allowzero => "allowzero",
256275 .keyword_and => "and",
......@@ -349,8 +368,10 @@ pub const Tokenizer = struct {
349368 pipe,
350369 minus,
351370 minus_percent,
371 minus_pipe,
352372 asterisk,
353373 asterisk_percent,
374 asterisk_pipe,
354375 slash,
355376 line_comment_start,
356377 line_comment,
......@@ -379,8 +400,10 @@ pub const Tokenizer = struct {
379400 percent,
380401 plus,
381402 plus_percent,
403 plus_pipe,
382404 angle_bracket_left,
383405 angle_bracket_angle_bracket_left,
406 angle_bracket_angle_bracket_left_pipe,
384407 angle_bracket_right,
385408 angle_bracket_angle_bracket_right,
386409 period,
......@@ -581,6 +604,9 @@ pub const Tokenizer = struct {
581604 '%' => {
582605 state = .asterisk_percent;
583606 },
607 '|' => {
608 state = .asterisk_pipe;
609 },
584610 else => {
585611 result.tag = .asterisk;
586612 break;
......@@ -599,6 +625,18 @@ pub const Tokenizer = struct {
599625 },
600626 },
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
602640 .percent => switch (c) {
603641 '=' => {
604642 result.tag = .percent_equal;
......@@ -625,6 +663,9 @@ pub const Tokenizer = struct {
625663 '%' => {
626664 state = .plus_percent;
627665 },
666 '|' => {
667 state = .plus_pipe;
668 },
628669 else => {
629670 result.tag = .plus;
630671 break;
......@@ -643,6 +684,18 @@ pub const Tokenizer = struct {
643684 },
644685 },
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
646699 .caret => switch (c) {
647700 '=' => {
648701 result.tag = .caret_equal;
......@@ -700,7 +753,7 @@ pub const Tokenizer = struct {
700753 },
701754
702755 .string_literal_backslash => switch (c) {
703 '\n' => {
756 0, '\n' => {
704757 result.tag = .invalid;
705758 break;
706759 },
......@@ -769,6 +822,10 @@ pub const Tokenizer = struct {
769822 },
770823
771824 .char_literal_unicode_escape_saw_u => switch (c) {
825 0 => {
826 result.tag = .invalid;
827 break;
828 },
772829 '{' => {
773830 state = .char_literal_unicode_escape;
774831 },
......@@ -779,6 +836,10 @@ pub const Tokenizer = struct {
779836 },
780837
781838 .char_literal_unicode_escape => switch (c) {
839 0 => {
840 result.tag = .invalid;
841 break;
842 },
782843 '0'...'9', 'a'...'f', 'A'...'F' => {},
783844 '}' => {
784845 state = .char_literal_end; // too many/few digits handled later
......@@ -892,6 +953,9 @@ pub const Tokenizer = struct {
892953 '%' => {
893954 state = .minus_percent;
894955 },
956 '|' => {
957 state = .minus_pipe;
958 },
895959 else => {
896960 result.tag = .minus;
897961 break;
......@@ -909,6 +973,17 @@ pub const Tokenizer = struct {
909973 break;
910974 },
911975 },
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
913988 .angle_bracket_left => switch (c) {
914989 '<' => {
......@@ -931,12 +1006,27 @@ pub const Tokenizer = struct {
9311006 self.index += 1;
9321007 break;
9331008 },
1009 '|' => {
1010 state = .angle_bracket_angle_bracket_left_pipe;
1011 },
9341012 else => {
9351013 result.tag = .angle_bracket_angle_bracket_left;
9361014 break;
9371015 },
9381016 },
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
9401030 .angle_bracket_right => switch (c) {
9411031 '>' => {
9421032 state = .angle_bracket_angle_bracket_right;
......@@ -1919,6 +2009,30 @@ test "tokenizer - invalid builtin identifiers" {
19192009 try testTokenize("@0()", &.{ .invalid, .integer_literal, .l_paren, .r_paren });
19202010}
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
19222036fn testTokenize(source: [:0]const u8, expected_tokens: []const Token.Tag) !void {
19232037 var tokenizer = Tokenizer.init(source);
19242038 for (expected_tokens) |expected_token_id| {
src/Air.zig+150-8
......@@ -44,6 +44,11 @@ pub const Inst = struct {
4444 /// is the same as both operands.
4545 /// Uses the `bin_op` field.
4646 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,
4752 /// Float or integer subtraction. For integers, wrapping is undefined behavior.
4853 /// Both operands are guaranteed to be the same type, and the result type
4954 /// is the same as both operands.
......@@ -54,6 +59,11 @@ pub const Inst = struct {
5459 /// is the same as both operands.
5560 /// Uses the `bin_op` field.
5661 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,
5767 /// Float or integer multiplication. For integers, wrapping is undefined behavior.
5868 /// Both operands are guaranteed to be the same type, and the result type
5969 /// is the same as both operands.
......@@ -64,15 +74,26 @@ pub const Inst = struct {
6474 /// is the same as both operands.
6575 /// Uses the `bin_op` field.
6676 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,
6782 /// Integer or float division. For integers, wrapping is undefined behavior.
6883 /// Both operands are guaranteed to be the same type, and the result type
6984 /// is the same as both operands.
7085 /// Uses the `bin_op` field.
7186 div,
72 /// Integer or float remainder.
73 /// Both operands are guaranteed to be the same type, and the result type is the same as both operands.
87 /// Integer or float remainder division.
88 /// Both operands are guaranteed to be the same type, and the result type
89 /// is the same as both operands.
7490 /// Uses the `bin_op` field.
7591 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,
7697 /// Add an offset to a pointer, returning a new pointer.
7798 /// The offset is in element type units, not bytes.
7899 /// Wrapping is undefined behavior.
......@@ -104,6 +125,14 @@ pub const Inst = struct {
104125 /// Shift left. `<<`
105126 /// Uses the `bin_op` field.
106127 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,
107136 /// Bitwise XOR. `^`
108137 /// Uses the `bin_op` field.
109138 xor,
......@@ -131,6 +160,15 @@ pub const Inst = struct {
131160 /// Result type is the return type of the function being called.
132161 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.
133162 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
134172 /// `<`. Result type is always bool.
135173 /// Uses the `bin_op` field.
136174 cmp_lt,
......@@ -149,6 +187,7 @@ pub const Inst = struct {
149187 /// `!=`. Result type is always bool.
150188 /// Uses the `bin_op` field.
151189 cmp_neq,
190
152191 /// Conditional branch.
153192 /// Result type is always noreturn; no instructions in a block follow this one.
154193 /// Uses the `pl_op` field. Operand is the condition. Payload is `CondBr`.
......@@ -225,9 +264,12 @@ pub const Inst = struct {
225264 /// Indicates the program counter will never get to this instruction.
226265 /// Result type is always noreturn; no instructions in a block follow this one.
227266 unreach,
228 /// Convert from one float type to another.
267 /// Convert from a float type to a smaller one.
229268 /// 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,
231273 /// Returns an integer with a different type than the operand. The new type may have
232274 /// fewer, the same, or more bits than the operand type. However, the instruction
233275 /// guarantees that the same integer value fits in both types.
......@@ -265,19 +307,29 @@ pub const Inst = struct {
265307 /// wrap from E to E!T
266308 /// Uses the `ty_op` field.
267309 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.
269311 /// Uses the `ty_pl` field, payload is `StructField`.
312 /// TODO rename to `agg_field_ptr`.
270313 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.
272315 /// The field index is the number at the end of the name.
273316 /// Uses `ty_op` field.
317 /// TODO rename to `agg_field_ptr_index_X`
274318 struct_field_ptr_index_0,
275319 struct_field_ptr_index_1,
276320 struct_field_ptr_index_2,
277321 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.
279323 /// Uses the `ty_pl` field, payload is `StructField`.
324 /// TODO rename to `agg_field_val`
280325 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,
281333 /// Given a slice value, return the length.
282334 /// Result type is always usize.
283335 /// Uses the `ty_op` field.
......@@ -309,10 +361,52 @@ pub const Inst = struct {
309361 /// Given a pointer to an array, return a slice.
310362 /// Uses the `ty_op` field.
311363 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
312384 /// Uses the `ty_pl` field with payload `Cmpxchg`.
313385 cmpxchg_weak,
314386 /// Uses the `ty_pl` field with payload `Cmpxchg`.
315387 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
317411 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
318412 return switch (op) {
......@@ -380,6 +474,11 @@ pub const Inst = struct {
380474 line: u32,
381475 column: u32,
382476 },
477 fence: std.builtin.AtomicOrder,
478 atomic_load: struct {
479 ptr: Ref,
480 order: std.builtin.AtomicOrder,
481 },
383482
384483 // Make sure we don't accidentally add a field to make this union
385484 // bigger than expected. Note that in Debug builds, Zig is allowed
......@@ -464,6 +563,21 @@ pub const Cmpxchg = struct {
464563 }
465564};
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
467581pub fn getMainBody(air: Air) []const Air.Inst.Index {
468582 const body_index = air.extra[@enumToInt(ExtraIndex.main_block)];
469583 const extra = air.extraData(Block, body_index);
......@@ -485,12 +599,16 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
485599
486600 .add,
487601 .addwrap,
602 .add_sat,
488603 .sub,
489604 .subwrap,
605 .sub_sat,
490606 .mul,
491607 .mulwrap,
608 .mul_sat,
492609 .div,
493610 .rem,
611 .mod,
494612 .bit_and,
495613 .bit_or,
496614 .xor,
......@@ -498,6 +616,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
498616 .ptr_sub,
499617 .shr,
500618 .shl,
619 .shl_exact,
620 .shl_sat,
501621 => return air.typeOf(datas[inst].bin_op.lhs),
502622
503623 .cmp_lt,
......@@ -535,7 +655,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
535655 .not,
536656 .bitcast,
537657 .load,
538 .floatcast,
658 .fpext,
659 .fptrunc,
539660 .intcast,
540661 .trunc,
541662 .optional_payload,
......@@ -553,6 +674,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
553674 .struct_field_ptr_index_2,
554675 .struct_field_ptr_index_3,
555676 .array_to_slice,
677 .float_to_int,
678 .int_to_float,
679 .get_union_tag,
680 .clz,
681 .ctz,
556682 => return air.getRefType(datas[inst].ty_op.ty),
557683
558684 .loop,
......@@ -566,6 +692,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
566692 .breakpoint,
567693 .dbg_stmt,
568694 .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,
569703 => return Type.initTag(.void),
570704
571705 .ptrtoint,
......@@ -588,6 +722,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
588722 const inner_ptr_ty = outer_ptr_ty.elemType();
589723 return inner_ptr_ty.elemType();
590724 },
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 },
591733 }
592734}
593735
src/AstGen.zig+691-261
......@@ -56,6 +56,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
5656 u32 => @field(extra, field.name),
5757 Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
5858 i32 => @bitCast(u32, @field(extra, field.name)),
59 Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),
5960 else => @compileError("bad field type"),
6061 });
6162 }
......@@ -124,7 +125,7 @@ pub fn generate(gpa: *Allocator, tree: Ast) Allocator.Error!Zir {
124125 container_decl,
125126 .Auto,
126127 )) |struct_decl_ref| {
127 astgen.extra.items[@enumToInt(Zir.ExtraIndex.main_struct)] = @enumToInt(struct_decl_ref);
128 assert(refToIndex(struct_decl_ref).? == 0);
128129 } else |err| switch (err) {
129130 error.OutOfMemory => return error.OutOfMemory,
130131 error.AnalysisFail => {}, // Handled via compile_errors below.
......@@ -197,7 +198,7 @@ pub const ResultLoc = union(enum) {
197198 none_or_ref,
198199 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
199200 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,
201202 /// so no `as` instruction needs to be emitted.
202203 coerced_ty: Zir.Inst.Ref,
203204 /// 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
316317 .assign,
317318 .assign_bit_and,
318319 .assign_bit_or,
319 .assign_bit_shift_left,
320 .assign_bit_shift_right,
320 .assign_shl,
321 .assign_shl_sat,
322 .assign_shr,
321323 .assign_bit_xor,
322324 .assign_div,
323325 .assign_sub,
324326 .assign_sub_wrap,
327 .assign_sub_sat,
325328 .assign_mod,
326329 .assign_add,
327330 .assign_add_wrap,
331 .assign_add_sat,
328332 .assign_mul,
329333 .assign_mul_wrap,
334 .assign_mul_sat,
330335 .add,
331336 .add_wrap,
337 .add_sat,
332338 .sub,
333339 .sub_wrap,
340 .sub_sat,
334341 .mul,
335342 .mul_wrap,
343 .mul_sat,
336344 .div,
337345 .mod,
338346 .bit_and,
339347 .bit_or,
340 .bit_shift_left,
341 .bit_shift_right,
348 .shl,
349 .shl_sat,
350 .shr,
342351 .bit_xor,
343352 .bang_equal,
344353 .equal_equal,
......@@ -479,7 +488,7 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
479488 return expr(gz, scope, .ref, node);
480489}
481490
482/// Turn Zig AST into untyped ZIR istructions.
491/// Turn Zig AST into untyped ZIR instructions.
483492/// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
484493/// result instruction can be used to inspect whether it is isNoReturn() but that is it,
485494/// it must otherwise not be used.
......@@ -521,11 +530,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
521530 return rvalue(gz, rl, .void_value, node);
522531 },
523532
524 .assign_bit_shift_left => {
533 .assign_shl => {
525534 try assignShift(gz, scope, node, .shl);
526535 return rvalue(gz, rl, .void_value, node);
527536 },
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 => {
529542 try assignShift(gz, scope, node, .shr);
530543 return rvalue(gz, rl, .void_value, node);
531544 },
......@@ -554,6 +567,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
554567 try assignOp(gz, scope, node, .subwrap);
555568 return rvalue(gz, rl, .void_value, node);
556569 },
570 .assign_sub_sat => {
571 try assignOp(gz, scope, node, .sub_sat);
572 return rvalue(gz, rl, .void_value, node);
573 },
557574 .assign_mod => {
558575 try assignOp(gz, scope, node, .mod_rem);
559576 return rvalue(gz, rl, .void_value, node);
......@@ -566,6 +583,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
566583 try assignOp(gz, scope, node, .addwrap);
567584 return rvalue(gz, rl, .void_value, node);
568585 },
586 .assign_add_sat => {
587 try assignOp(gz, scope, node, .add_sat);
588 return rvalue(gz, rl, .void_value, node);
589 },
569590 .assign_mul => {
570591 try assignOp(gz, scope, node, .mul);
571592 return rvalue(gz, rl, .void_value, node);
......@@ -574,19 +595,28 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
574595 try assignOp(gz, scope, node, .mulwrap);
575596 return rvalue(gz, rl, .void_value, node);
576597 },
598 .assign_mul_sat => {
599 try assignOp(gz, scope, node, .mul_sat);
600 return rvalue(gz, rl, .void_value, node);
601 },
577602
578603 // zig fmt: off
579 .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
580 .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
604 .shl => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
605 .shr => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
581606
582607 .add => return simpleBinOp(gz, scope, rl, node, .add),
583608 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),
609 .add_sat => return simpleBinOp(gz, scope, rl, node, .add_sat),
584610 .sub => return simpleBinOp(gz, scope, rl, node, .sub),
585611 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),
612 .sub_sat => return simpleBinOp(gz, scope, rl, node, .sub_sat),
586613 .mul => return simpleBinOp(gz, scope, rl, node, .mul),
587614 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),
615 .mul_sat => return simpleBinOp(gz, scope, rl, node, .mul_sat),
588616 .div => return simpleBinOp(gz, scope, rl, node, .div),
589617 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),
618 .shl_sat => return simpleBinOp(gz, scope, rl, node, .shl_sat),
619
590620 .bit_and => {
591621 const current_ampersand_token = main_tokens[node];
592622 if (token_tags[current_ampersand_token + 1] == .ampersand) {
......@@ -1116,6 +1146,11 @@ fn fnProtoExpr(
11161146 const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
11171147 break :inst try expr(gz, scope, align_rl, fn_proto.ast.align_expr);
11181148 };
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
11191154 if (fn_proto.ast.section_expr != 0) {
11201155 return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{});
11211156 }
......@@ -1352,7 +1387,14 @@ fn structInitExpr(
13521387 const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) {
13531388 .array_type => tree.arrayType(struct_init.ast.type_expr),
13541389 .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 },
13561398 };
13571399 const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
13581400 // This intentionally does not support `@"_"` syntax.
......@@ -1414,8 +1456,8 @@ fn structInitExpr(
14141456 const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init);
14151457 return rvalue(gz, rl, result, node);
14161458 },
1417 .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst),
1418 .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, node, struct_init, block_gz.rl_ptr),
1459 .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst),
1460 .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr),
14191461 }
14201462}
14211463
......@@ -1454,6 +1496,26 @@ fn structInitExprRlNone(
14541496}
14551497
14561498fn 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(
14571519 gz: *GenZir,
14581520 scope: *Scope,
14591521 node: Ast.Node.Index,
......@@ -1467,9 +1529,6 @@ fn structInitExprRlPtr(
14671529 const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len);
14681530 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
14731532 for (struct_init.ast.fields) |field_init, i| {
14741533 const name_token = tree.firstToken(field_init) - 2;
14751534 const str_index = try astgen.identAsString(name_token);
......@@ -1484,7 +1543,7 @@ fn structInitExprRlPtr(
14841543 .body_len = @intCast(u32, field_ptr_list.len),
14851544 });
14861545 try astgen.extra.appendSlice(gpa, field_ptr_list);
1487 return .void_value;
1546 return Zir.Inst.Ref.void_value;
14881547}
14891548
14901549fn structInitExprRlTy(
......@@ -1868,8 +1927,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
18681927
18691928 .assign => try assign(gz, scope, statement),
18701929
1871 .assign_bit_shift_left => try assignShift(gz, scope, statement, .shl),
1872 .assign_bit_shift_right => try assignShift(gz, scope, statement, .shr),
1930 .assign_shl => try assignShift(gz, scope, statement, .shl),
1931 .assign_shr => try assignShift(gz, scope, statement, .shr),
18731932
18741933 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),
18751934 .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
19051964 // in the above while loop.
19061965 const zir_tags = gz.astgen.instructions.items(.tag);
19071966 switch (zir_tags[inst]) {
1908 // For some instructions, swap in a slightly different ZIR tag
1967 // For some instructions, modify the zir data
19091968 // so we can avoid a separate ensure_result_used instruction.
1910 .call_chkused => unreachable,
19111969 .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);
19131975 break :b true;
19141976 },
19151977
19161978 // ZIR instructions that might be a type other than `noreturn` or `void`.
19171979 .add,
19181980 .addwrap,
1981 .add_sat,
19191982 .param,
19201983 .param_comptime,
19211984 .param_anytype,
......@@ -1947,9 +2010,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
19472010 .bool_br_and,
19482011 .bool_br_or,
19492012 .bool_not,
1950 .call_compile_time,
1951 .call_nosuspend,
1952 .call_async,
19532013 .cmp_lt,
19542014 .cmp_lte,
19552015 .cmp_eq,
......@@ -1967,8 +2027,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
19672027 .elem_val_node,
19682028 .field_ptr,
19692029 .field_val,
2030 .field_call_bind,
19702031 .field_ptr_named,
19712032 .field_val_named,
2033 .field_call_bind_named,
19722034 .func,
19732035 .func_inferred,
19742036 .int,
......@@ -1983,13 +2045,15 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
19832045 .mod_rem,
19842046 .mul,
19852047 .mulwrap,
1986 .param_type,
2048 .mul_sat,
19872049 .ref,
19882050 .shl,
2051 .shl_sat,
19892052 .shr,
19902053 .str,
19912054 .sub,
19922055 .subwrap,
2056 .sub_sat,
19932057 .negate,
19942058 .negate_wrap,
19952059 .typeof,
......@@ -2049,9 +2113,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
20492113 .union_init_ptr,
20502114 .field_type,
20512115 .field_type_ref,
2052 .opaque_decl,
2053 .opaque_decl_anon,
2054 .opaque_decl_func,
20552116 .error_set_decl,
20562117 .error_set_decl_anon,
20572118 .error_set_decl_func,
......@@ -2118,14 +2179,11 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
21182179 .select,
21192180 .atomic_load,
21202181 .atomic_rmw,
2121 .atomic_store,
21222182 .mul_add,
21232183 .builtin_call,
21242184 .field_ptr_type,
21252185 .field_parent_ptr,
21262186 .maximum,
2127 .memcpy,
2128 .memset,
21292187 .minimum,
21302188 .builtin_async_call,
21312189 .c_import,
......@@ -2134,6 +2192,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
21342192 .await_nosuspend,
21352193 .ret_err_value_code,
21362194 .extended,
2195 .closure_get,
21372196 => break :b false,
21382197
21392198 // ZIR instructions that are always `noreturn`.
......@@ -2162,8 +2221,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
21622221 .ensure_result_used,
21632222 .ensure_result_non_error,
21642223 .@"export",
2224 .export_value,
21652225 .set_eval_branch_quota,
21662226 .ensure_err_payload_void,
2227 .atomic_store,
21672228 .store,
21682229 .store_node,
21692230 .store_to_block_ptr,
......@@ -2175,6 +2236,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
21752236 .set_cold,
21762237 .set_float_mode,
21772238 .set_runtime_safety,
2239 .closure_capture,
2240 .memcpy,
2241 .memset,
21782242 => break :b true,
21792243 }
21802244 } else switch (maybe_unused_result) {
......@@ -2371,6 +2435,7 @@ fn varDecl(
23712435 const gpa = astgen.gpa;
23722436 const tree = astgen.tree;
23732437 const token_tags = tree.tokens.items(.tag);
2438 const main_tokens = tree.nodes.items(.main_token);
23742439
23752440 const name_token = var_decl.ast.mut_token + 1;
23762441 const ident_name_raw = tree.tokenSlice(name_token);
......@@ -2385,6 +2450,14 @@ fn varDecl(
23852450 return astgen.failNode(node, "variables must be initialized", .{});
23862451 }
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
23882461 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
23892462 try expr(gz, scope, align_rl, var_decl.ast.align_node)
23902463 else
......@@ -2494,7 +2567,11 @@ fn varDecl(
24942567 for (init_scope.instructions.items) |src_inst| {
24952568 if (zir_tags[src_inst] == .store_to_block_ptr) {
24962569 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 }
24982575 }
24992576 }
25002577 parent_zir.appendAssumeCapacity(src_inst);
......@@ -2668,6 +2745,24 @@ fn assignShift(
26682745 _ = try gz.addBin(.store, lhs_ptr, result);
26692746}
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
26712766fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
26722767 const astgen = gz.astgen;
26732768 const tree = astgen.tree;
......@@ -2714,6 +2809,7 @@ fn ptrType(
27142809 const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
27152810
27162811 const simple = ptr_info.ast.align_node == 0 and
2812 ptr_info.ast.addrspace_node == 0 and
27172813 ptr_info.ast.sentinel == 0 and
27182814 ptr_info.ast.bit_range_start == 0;
27192815
......@@ -2732,6 +2828,7 @@ fn ptrType(
27322828
27332829 var sentinel_ref: Zir.Inst.Ref = .none;
27342830 var align_ref: Zir.Inst.Ref = .none;
2831 var addrspace_ref: Zir.Inst.Ref = .none;
27352832 var bit_start_ref: Zir.Inst.Ref = .none;
27362833 var bit_end_ref: Zir.Inst.Ref = .none;
27372834 var trailing_count: u32 = 0;
......@@ -2744,6 +2841,10 @@ fn ptrType(
27442841 align_ref = try expr(gz, scope, align_rl, ptr_info.ast.align_node);
27452842 trailing_count += 1;
27462843 }
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 }
27472848 if (ptr_info.ast.bit_range_start != 0) {
27482849 assert(ptr_info.ast.bit_range_end != 0);
27492850 bit_start_ref = try expr(gz, scope, .none, ptr_info.ast.bit_range_start);
......@@ -2764,6 +2865,9 @@ fn ptrType(
27642865 if (align_ref != .none) {
27652866 gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref));
27662867 }
2868 if (addrspace_ref != .none) {
2869 gz.astgen.extra.appendAssumeCapacity(@enumToInt(addrspace_ref));
2870 }
27672871 if (bit_start_ref != .none) {
27682872 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
27692873 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
......@@ -2779,6 +2883,7 @@ fn ptrType(
27792883 .is_volatile = ptr_info.volatile_token != null,
27802884 .has_sentinel = sentinel_ref != .none,
27812885 .has_align = align_ref != .none,
2886 .has_addrspace = addrspace_ref != .none,
27822887 .has_bit_range = bit_start_ref != .none,
27832888 },
27842889 .size = ptr_info.size,
......@@ -2847,7 +2952,7 @@ const WipDecls = struct {
28472952 is_pub: bool,
28482953 is_export: bool,
28492954 has_align: bool,
2850 has_section: bool,
2955 has_section_or_addrspace: bool,
28512956 ) Allocator.Error!void {
28522957 if (wip_decls.decl_index % fields_per_u32 == 0 and wip_decls.decl_index != 0) {
28532958 try wip_decls.bit_bag.append(gpa, wip_decls.cur_bit_bag);
......@@ -2857,7 +2962,7 @@ const WipDecls = struct {
28572962 (@as(u32, @boolToInt(is_pub)) << 28) |
28582963 (@as(u32, @boolToInt(is_export)) << 29) |
28592964 (@as(u32, @boolToInt(has_align)) << 30) |
2860 (@as(u32, @boolToInt(has_section)) << 31);
2965 (@as(u32, @boolToInt(has_section_or_addrspace)) << 31);
28612966 wip_decls.decl_index += 1;
28622967 }
28632968
......@@ -2922,7 +3027,8 @@ fn fnDecl(
29223027 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
29233028 break :blk token_tags[maybe_inline_token] == .keyword_inline;
29243029 };
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
29273033 var params_scope = &fn_gz.base;
29283034 const is_var_args = is_var_args: {
......@@ -3011,6 +3117,9 @@ fn fnDecl(
30113117 const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
30123118 break :inst try expr(&decl_gz, params_scope, align_rl, fn_proto.ast.align_expr);
30133119 };
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 };
30143123 const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
30153124 break :inst try comptimeExpr(&decl_gz, params_scope, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr);
30163125 };
......@@ -3112,7 +3221,7 @@ fn fnDecl(
31123221 _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
31133222 try decl_gz.setBlockBody(block_inst);
31143223
3115 try wip_decls.payload.ensureUnusedCapacity(gpa, 9);
3224 try wip_decls.payload.ensureUnusedCapacity(gpa, 10);
31163225 {
31173226 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
31183227 const casted = @bitCast([4]u32, contents_hash);
......@@ -3127,8 +3236,10 @@ fn fnDecl(
31273236 if (align_inst != .none) {
31283237 wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));
31293238 }
3130 if (section_inst != .none) {
3239
3240 if (has_section_or_addrspace) {
31313241 wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst));
3242 wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst));
31323243 }
31333244}
31343245
......@@ -3175,10 +3286,14 @@ fn globalVarDecl(
31753286 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: {
31763287 break :inst try expr(&block_scope, &block_scope.base, align_rl, var_decl.ast.align_node);
31773288 };
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 };
31783292 const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: {
31793293 break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .ty = .const_slice_u8_type }, var_decl.ast.section_node);
31803294 };
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
31833298 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
31843299 if (!is_mutable) {
......@@ -3256,7 +3371,7 @@ fn globalVarDecl(
32563371 _ = try block_scope.addBreak(.break_inline, block_inst, var_inst);
32573372 try block_scope.setBlockBody(block_inst);
32583373
3259 try wip_decls.payload.ensureUnusedCapacity(gpa, 9);
3374 try wip_decls.payload.ensureUnusedCapacity(gpa, 10);
32603375 {
32613376 const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
32623377 const casted = @bitCast([4]u32, contents_hash);
......@@ -3271,8 +3386,9 @@ fn globalVarDecl(
32713386 if (align_inst != .none) {
32723387 wip_decls.payload.appendAssumeCapacity(@enumToInt(align_inst));
32733388 }
3274 if (section_inst != .none) {
3389 if (has_section_or_addrspace) {
32753390 wip_decls.payload.appendAssumeCapacity(@enumToInt(section_inst));
3391 wip_decls.payload.appendAssumeCapacity(@enumToInt(addrspace_inst));
32763392 }
32773393}
32783394
......@@ -3474,8 +3590,9 @@ fn structDeclInner(
34743590 container_decl: Ast.full.ContainerDecl,
34753591 layout: std.builtin.TypeInfo.ContainerLayout,
34763592) InnerError!Zir.Inst.Ref {
3593 const decl_inst = try gz.reserveInstructionIndex();
3594
34773595 if (container_decl.ast.members.len == 0) {
3478 const decl_inst = try gz.reserveInstructionIndex();
34793596 try gz.setStruct(decl_inst, .{
34803597 .src_node = node,
34813598 .layout = layout,
......@@ -3493,11 +3610,19 @@ fn structDeclInner(
34933610 const node_tags = tree.nodes.items(.tag);
34943611 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
34963621 // The struct_decl instruction introduces a scope in which the decls of the struct
34973622 // are in scope, so that field types, alignments, and default value expressions
34983623 // can refer to decls within the struct itself.
34993624 var block_scope: GenZir = .{
3500 .parent = scope,
3625 .parent = &namespace.base,
35013626 .decl_node_index = node,
35023627 .decl_line = gz.calcLine(node),
35033628 .astgen = astgen,
......@@ -3506,16 +3631,13 @@ fn structDeclInner(
35063631 };
35073632 defer block_scope.instructions.deinit(gpa);
35083633
3509 var namespace: Scope.Namespace = .{ .parent = scope, .node = node };
3510 defer namespace.decls.deinit(gpa);
3511
35123634 try astgen.scanDecls(&namespace, container_decl.ast.members);
35133635
35143636 var wip_decls: WipDecls = .{};
35153637 defer wip_decls.deinit(gpa);
35163638
35173639 // We don't know which members are fields until we iterate, so cannot do
3518 // an accurate ensureCapacity yet.
3640 // an accurate ensureTotalCapacity yet.
35193641 var fields_data = ArrayListUnmanaged(u32){};
35203642 defer fields_data.deinit(gpa);
35213643
......@@ -3713,7 +3835,6 @@ fn structDeclInner(
37133835 }
37143836 }
37153837
3716 const decl_inst = try gz.reserveInstructionIndex();
37173838 if (block_scope.instructions.items.len != 0) {
37183839 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
37193840 }
......@@ -3727,11 +3848,18 @@ fn structDeclInner(
37273848 .known_has_bits = known_has_bits,
37283849 });
37293850
3730 try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len +
3731 @boolToInt(field_index != 0) + fields_data.items.len +
3851 // zig fmt: off
3852 try astgen.extra.ensureUnusedCapacity(gpa,
3853 bit_bag.items.len +
3854 @boolToInt(wip_decls.decl_index != 0) +
3855 wip_decls.payload.items.len +
37323856 block_scope.instructions.items.len +
3733 wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +
3734 wip_decls.payload.items.len);
3857 wip_decls.bit_bag.items.len +
3858 @boolToInt(field_index != 0) +
3859 fields_data.items.len
3860 );
3861 // zig fmt: on
3862
37353863 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.
37363864 if (wip_decls.decl_index != 0) {
37373865 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
......@@ -3758,17 +3886,27 @@ fn unionDeclInner(
37583886 arg_node: Ast.Node.Index,
37593887 have_auto_enum: bool,
37603888) InnerError!Zir.Inst.Ref {
3889 const decl_inst = try gz.reserveInstructionIndex();
3890
37613891 const astgen = gz.astgen;
37623892 const gpa = astgen.gpa;
37633893 const tree = astgen.tree;
37643894 const node_tags = tree.nodes.items(.tag);
37653895 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
37673905 // The union_decl instruction introduces a scope in which the decls of the union
37683906 // are in scope, so that field types, alignments, and default value expressions
37693907 // can refer to decls within the union itself.
37703908 var block_scope: GenZir = .{
3771 .parent = scope,
3909 .parent = &namespace.base,
37723910 .decl_node_index = node,
37733911 .decl_line = gz.calcLine(node),
37743912 .astgen = astgen,
......@@ -3777,13 +3915,10 @@ fn unionDeclInner(
37773915 };
37783916 defer block_scope.instructions.deinit(gpa);
37793917
3780 var namespace: Scope.Namespace = .{ .parent = scope, .node = node };
3781 defer namespace.decls.deinit(gpa);
3782
37833918 try astgen.scanDecls(&namespace, members);
37843919
37853920 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)
37873922 else
37883923 .none;
37893924
......@@ -3791,7 +3926,7 @@ fn unionDeclInner(
37913926 defer wip_decls.deinit(gpa);
37923927
37933928 // We don't know which members are fields until we iterate, so cannot do
3794 // an accurate ensureCapacity yet.
3929 // an accurate ensureTotalCapacity yet.
37953930 var fields_data = ArrayListUnmanaged(u32){};
37963931 defer fields_data.deinit(gpa);
37973932
......@@ -3996,7 +4131,6 @@ fn unionDeclInner(
39964131 }
39974132 }
39984133
3999 const decl_inst = try gz.reserveInstructionIndex();
40004134 if (block_scope.instructions.items.len != 0) {
40014135 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
40024136 }
......@@ -4011,11 +4145,18 @@ fn unionDeclInner(
40114145 .auto_enum_tag = have_auto_enum,
40124146 });
40134147
4014 try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len +
4015 1 + fields_data.items.len +
4148 // zig fmt: off
4149 try astgen.extra.ensureUnusedCapacity(gpa,
4150 bit_bag.items.len +
4151 @boolToInt(wip_decls.decl_index != 0) +
4152 wip_decls.payload.items.len +
40164153 block_scope.instructions.items.len +
4017 wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +
4018 wip_decls.payload.items.len);
4154 wip_decls.bit_bag.items.len +
4155 1 + // cur_bit_bag
4156 fields_data.items.len
4157 );
4158 // zig fmt: on
4159
40194160 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.
40204161 if (wip_decls.decl_index != 0) {
40214162 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
......@@ -4178,10 +4319,20 @@ fn containerDecl(
41784319 // how structs are handled above.
41794320 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
41814332 // The enum_decl instruction introduces a scope in which the decls of the enum
41824333 // are in scope, so that tag values can refer to decls within the enum itself.
41834334 var block_scope: GenZir = .{
4184 .parent = scope,
4335 .parent = &namespace.base,
41854336 .decl_node_index = node,
41864337 .decl_line = gz.calcLine(node),
41874338 .astgen = astgen,
......@@ -4190,13 +4341,10 @@ fn containerDecl(
41904341 };
41914342 defer block_scope.instructions.deinit(gpa);
41924343
4193 var namespace: Scope.Namespace = .{ .parent = scope, .node = node };
4194 defer namespace.decls.deinit(gpa);
4195
41964344 try astgen.scanDecls(&namespace, container_decl.ast.members);
41974345
41984346 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)
42004348 else
42014349 .none;
42024350
......@@ -4391,7 +4539,6 @@ fn containerDecl(
43914539 }
43924540 }
43934541
4394 const decl_inst = try gz.reserveInstructionIndex();
43954542 if (block_scope.instructions.items.len != 0) {
43964543 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
43974544 }
......@@ -4405,11 +4552,18 @@ fn containerDecl(
44054552 .decls_len = @intCast(u32, wip_decls.decl_index),
44064553 });
44074554
4408 try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len +
4409 1 + fields_data.items.len +
4555 // zig fmt: off
4556 try astgen.extra.ensureUnusedCapacity(gpa,
4557 bit_bag.items.len +
4558 @boolToInt(wip_decls.decl_index != 0) +
4559 wip_decls.payload.items.len +
44104560 block_scope.instructions.items.len +
4411 wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +
4412 wip_decls.payload.items.len);
4561 wip_decls.bit_bag.items.len +
4562 1 + // cur_bit_bag
4563 fields_data.items.len
4564 );
4565 // zig fmt: on
4566
44134567 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.
44144568 if (wip_decls.decl_index != 0) {
44154569 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
......@@ -4426,8 +4580,15 @@ fn containerDecl(
44264580 .keyword_opaque => {
44274581 assert(container_decl.ast.arg == 0);
44284582
4429 var namespace: Scope.Namespace = .{ .parent = scope, .node = node };
4430 defer namespace.decls.deinit(gpa);
4583 const decl_inst = try gz.reserveInstructionIndex();
4584
4585 var namespace: Scope.Namespace = .{
4586 .parent = scope,
4587 .node = node,
4588 .inst = decl_inst,
4589 .declaring_gz = gz,
4590 };
4591 defer namespace.deinit(gpa);
44314592
44324593 try astgen.scanDecls(&namespace, container_decl.ast.members);
44334594
......@@ -4565,21 +4726,20 @@ fn containerDecl(
45654726 wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field);
45664727 }
45674728 }
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 +
4577 wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +
4578 wip_decls.payload.items.len);
4579 const zir_datas = astgen.instructions.items(.data);
4580 zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{
4730 try gz.setOpaque(decl_inst, .{
4731 .src_node = node,
45814732 .decls_len = @intCast(u32, wip_decls.decl_index),
45824733 });
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
45834743 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.
45844744 if (wip_decls.decl_index != 0) {
45854745 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
......@@ -4865,6 +5025,21 @@ fn fieldAccess(
48655025 scope: *Scope,
48665026 rl: ResultLoc,
48675027 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,
48685043) InnerError!Zir.Inst.Ref {
48695044 const astgen = gz.astgen;
48705045 const tree = astgen.tree;
......@@ -4875,16 +5050,11 @@ fn fieldAccess(
48755050 const dot_token = main_tokens[node];
48765051 const field_ident = dot_token + 1;
48775052 const str_index = try astgen.identAsString(field_ident);
4878 switch (rl) {
4879 .ref => return gz.addPlNode(.field_ptr, node, Zir.Inst.Field{
4880 .lhs = try expr(gz, scope, .ref, object_node),
4881 .field_name_start = str_index,
4882 }),
4883 else => return rvalue(gz, rl, try gz.addPlNode(.field_val, node, Zir.Inst.Field{
4884 .lhs = try expr(gz, scope, .none_or_ref, object_node),
4885 .field_name_start = str_index,
4886 }), node),
4887 }
5053
5054 return gz.addPlNode(tag, node, Zir.Inst.Field{
5055 .lhs = try expr(gz, scope, lhs_rl, object_node),
5056 .field_name_start = str_index,
5057 });
48885058}
48895059
48905060fn arrayAccess(
......@@ -6320,6 +6490,7 @@ fn identifier(
63206490
63216491 const astgen = gz.astgen;
63226492 const tree = astgen.tree;
6493 const gpa = astgen.gpa;
63236494 const main_tokens = tree.nodes.items(.main_token);
63246495
63256496 const ident_token = main_tokens[ident];
......@@ -6366,16 +6537,28 @@ fn identifier(
63666537 const name_str_index = try astgen.identAsString(ident_token);
63676538 var s = scope;
63686539 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;
63706542 while (true) switch (s.tag) {
63716543 .local_val => {
63726544 const local_val = s.cast(Scope.LocalVal).?;
63736545
63746546 if (local_val.name == name_str_index) {
6375 local_val.used = true;
63766547 // Locals cannot shadow anything, so we do not need to look for ambiguous
63776548 // 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);
63796562 }
63806563 s = local_val.parent;
63816564 },
......@@ -6383,16 +6566,29 @@ fn identifier(
63836566 const local_ptr = s.cast(Scope.LocalPtr).?;
63846567 if (local_ptr.name == name_str_index) {
63856568 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) {
63876572 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
63886573 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", .{}),
63906575 });
63916576 }
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
63926588 switch (rl) {
6393 .ref, .none_or_ref => return local_ptr.ptr,
6589 .ref, .none_or_ref => return ptr_inst,
63946590 else => {
6395 const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident);
6591 const loaded = try gz.addUnNode(.load, ptr_inst, ident);
63966592 return rvalue(gz, rl, loaded, ident);
63976593 },
63986594 }
......@@ -6413,7 +6609,8 @@ fn identifier(
64136609 // We found a match but must continue looking for ambiguous references to decls.
64146610 found_already = i;
64156611 }
6416 hit_namespace = ns.node;
6612 num_namespaces_out += 1;
6613 capturing_namespace = ns;
64176614 s = ns.parent;
64186615 },
64196616 .top => break,
......@@ -6433,6 +6630,37 @@ fn identifier(
64336630 }
64346631}
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
64366664fn stringLiteral(
64376665 gz: *GenZir,
64386666 rl: ResultLoc,
......@@ -6866,35 +7094,13 @@ fn asRlPtr(
68667094 operand_node: Ast.Node.Index,
68677095 dest_type: Zir.Inst.Ref,
68687096) 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.
68727097 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);
68757100 defer as_scope.instructions.deinit(astgen.gpa);
68767101
6877 as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
68787102 const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node);
6879 const parent_zir = &parent_gz.instructions;
6880 if (as_scope.rvalue_rl_count == 1) {
6881 // Busted! This expression didn't actually need a pointer.
6882 const zir_tags = astgen.instructions.items(.tag);
6883 const zir_datas = astgen.instructions.items(.data);
6884 try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len);
6885 for (as_scope.instructions.items) |src_inst| {
6886 if (indexToRef(src_inst) == as_scope.rl_ptr) continue;
6887 if (zir_tags[src_inst] == .store_to_block_ptr) {
6888 if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
6889 }
6890 parent_zir.appendAssumeCapacity(src_inst);
6891 }
6892 const casted_result = try parent_gz.addBin(.as, dest_type, result);
6893 return rvalue(parent_gz, rl, casted_result, operand_node);
6894 } else {
6895 try parent_zir.appendSlice(astgen.gpa, as_scope.instructions.items);
6896 return result;
6897 }
7103 return as_scope.finishCoercion(parent_gz, rl, operand_node, result, dest_type);
68987104}
68997105
69007106fn bitCast(
......@@ -7030,16 +7236,15 @@ fn builtinCall(
70307236 return rvalue(gz, rl, result, node);
70317237 },
70327238 .field => {
7033 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
70347239 if (rl == .ref) {
70357240 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
70367241 .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]),
70387243 });
70397244 }
70407245 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
70417246 .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]),
70437248 });
70447249 return rvalue(gz, rl, result, node);
70457250 },
......@@ -7047,7 +7252,7 @@ fn builtinCall(
70477252 .bit_cast => return bitCast( gz, scope, rl, node, params[0], params[1]),
70487253 .TypeOf => return typeOf( gz, scope, rl, node, params),
70497254 .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
70527257 .@"export" => {
70537258 const node_tags = tree.nodes.items(.tag);
......@@ -7060,32 +7265,55 @@ fn builtinCall(
70607265 .identifier => {
70617266 const ident_token = main_tokens[params[0]];
70627267 decl_name = try astgen.identAsString(ident_token);
7063 {
7064 var s = scope;
7065 while (true) switch (s.tag) {
7066 .local_val => {
7067 const local_val = s.cast(Scope.LocalVal).?;
7068 if (local_val.name == decl_name) {
7069 local_val.used = true;
7070 break;
7071 }
7072 s = local_val.parent;
7073 },
7074 .local_ptr => {
7075 const local_ptr = s.cast(Scope.LocalPtr).?;
7076 if (local_ptr.name == decl_name) {
7077 if (!local_ptr.maybe_comptime)
7078 return astgen.failNode(params[0], "unable to export runtime-known value", .{});
7079 local_ptr.used = true;
7080 break;
7268
7269 var s = scope;
7270 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
7271 while (true) switch (s.tag) {
7272 .local_val => {
7273 const local_val = s.cast(Scope.LocalVal).?;
7274 if (local_val.name == decl_name) {
7275 local_val.used = true;
7276 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
7277 .operand = local_val.inst,
7278 .options = try comptimeExpr(gz, scope, .{ .coerced_ty = .export_options_type }, params[1]),
7279 });
7280 return rvalue(gz, rl, .void_value, node);
7281 }
7282 s = local_val.parent;
7283 },
7284 .local_ptr => {
7285 const local_ptr = s.cast(Scope.LocalPtr).?;
7286 if (local_ptr.name == decl_name) {
7287 if (!local_ptr.maybe_comptime)
7288 return astgen.failNode(params[0], "unable to export runtime-known value", .{});
7289 local_ptr.used = true;
7290 const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
7291 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
7292 .operand = loaded,
7293 .options = try comptimeExpr(gz, scope, .{ .coerced_ty = .export_options_type }, params[1]),
7294 });
7295 return rvalue(gz, rl, .void_value, node);
7296 }
7297 s = local_ptr.parent;
7298 },
7299 .gen_zir => s = s.cast(GenZir).?.parent,
7300 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
7301 .namespace => {
7302 const ns = s.cast(Scope.Namespace).?;
7303 if (ns.decls.get(decl_name)) |i| {
7304 if (found_already) |f| {
7305 return astgen.failNodeNotes(node, "ambiguous reference", .{}, &.{
7306 try astgen.errNoteNode(f, "declared here", .{}),
7307 try astgen.errNoteNode(i, "also declared here", .{}),
7308 });
70817309 }
7082 s = local_ptr.parent;
7083 },
7084 .gen_zir => s = s.cast(GenZir).?.parent,
7085 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
7086 .namespace, .top => break,
7087 };
7088 }
7310 // We found a match but must continue looking for ambiguous references to decls.
7311 found_already = i;
7312 }
7313 s = ns.parent;
7314 },
7315 .top => break,
7316 };
70897317 },
70907318 .field_access => {
70917319 const namespace_node = node_datas[params[0]].lhs;
......@@ -7116,9 +7344,13 @@ fn builtinCall(
71167344 });
71177345 return rvalue(gz, rl, result, node);
71187346 },
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
71207353 .breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint),
7121 .fence => return simpleNoOpVoid(gz, rl, node, .fence),
71227354
71237355 .This => return rvalue(gz, rl, try gz.addNodeExtended(.this, node), node),
71247356 .return_address => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr, node), node),
......@@ -7230,6 +7462,7 @@ fn builtinCall(
72307462 return rvalue(gz, rl, result, node);
72317463 },
72327464 .c_define => {
7465 if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{});
72337466 const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[0]);
72347467 const value = try comptimeExpr(gz, scope, .none, params[1]);
72357468 const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{
......@@ -7305,13 +7538,9 @@ fn builtinCall(
73057538 return rvalue(gz, rl, result, node);
73067539 },
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
73137541 .atomic_load => {
73147542 const int_type = try typeExpr(gz, scope, params[0]);
7543 // TODO allow this pointer type to be volatile
73157544 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
73167545 .ptr_type_simple = .{
73177546 .is_allowzero = false,
......@@ -7321,16 +7550,17 @@ fn builtinCall(
73217550 .elem_type = int_type,
73227551 },
73237552 } });
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]);
73267553 const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.Bin{
7327 .lhs = ptr,
7328 .rhs = ordering,
7554 // zig fmt: off
7555 .lhs = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]),
7556 .rhs = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]),
7557 // zig fmt: on
73297558 });
73307559 return rvalue(gz, rl, result, node);
73317560 },
73327561 .atomic_rmw => {
73337562 const int_type = try typeExpr(gz, scope, params[0]);
7563 // TODO allow this pointer type to be volatile
73347564 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
73357565 .ptr_type_simple = .{
73367566 .is_allowzero = false,
......@@ -7340,20 +7570,19 @@ fn builtinCall(
73407570 .elem_type = int_type,
73417571 },
73427572 } });
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]);
73477573 const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
7348 .ptr = ptr,
7349 .operation = operation,
7350 .operand = operand,
7351 .ordering = ordering,
7574 // zig fmt: off
7575 .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]),
7576 .operation = try expr(gz, scope, .{ .coerced_ty = .atomic_rmw_op_type }, params[2]),
7577 .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]),
7578 .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]),
7579 // zig fmt: on
73527580 });
73537581 return rvalue(gz, rl, result, node);
73547582 },
73557583 .atomic_store => {
73567584 const int_type = try typeExpr(gz, scope, params[0]);
7585 // TODO allow this pointer type to be volatile
73577586 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
73587587 .ptr_type_simple = .{
73597588 .is_allowzero = false,
......@@ -7363,13 +7592,12 @@ fn builtinCall(
73637592 .elem_type = int_type,
73647593 },
73657594 } });
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]);
73697595 const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
7370 .ptr = ptr,
7371 .operand = operand,
7372 .ordering = ordering,
7596 // zig fmt: off
7597 .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]),
7598 .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]),
7599 .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[3]),
7600 // zig fmt: on
73737601 });
73747602 return rvalue(gz, rl, result, node);
73757603 },
......@@ -7387,7 +7615,7 @@ fn builtinCall(
73877615 },
73887616 .call => {
73897617 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]);
73917619 const args = try expr(gz, scope, .none, params[2]);
73927620 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
73937621 .options = options,
......@@ -7409,17 +7637,17 @@ fn builtinCall(
74097637 },
74107638 .memcpy => {
74117639 const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
7412 .dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]),
7413 .source = try expr(gz, scope, .{ .ty = .manyptr_const_u8_type }, params[1]),
7414 .byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]),
7640 .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
7641 .source = try expr(gz, scope, .{ .coerced_ty = .manyptr_const_u8_type }, params[1]),
7642 .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
74157643 });
74167644 return rvalue(gz, rl, result, node);
74177645 },
74187646 .memset => {
74197647 const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
7420 .dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]),
7421 .byte = try expr(gz, scope, .{ .ty = .u8_type }, params[1]),
7422 .byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]),
7648 .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
7649 .byte = try expr(gz, scope, .{ .coerced_ty = .u8_type }, params[1]),
7650 .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
74237651 });
74247652 return rvalue(gz, rl, result, node);
74257653 },
......@@ -7452,12 +7680,11 @@ fn builtinCall(
74527680 },
74537681 .Vector => {
74547682 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]),
74567684 .rhs = try typeExpr(gz, scope, params[1]),
74577685 });
74587686 return rvalue(gz, rl, result, node);
74597687 },
7460
74617688 }
74627689 // zig fmt: on
74637690}
......@@ -7603,6 +7830,8 @@ fn simpleCBuiltin(
76037830 operand_node: Ast.Node.Index,
76047831 tag: Zir.Inst.Extended,
76057832) 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});
76067835 const operand = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, operand_node);
76077836 _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{
76087837 .node = gz.nodeIndexToRelative(node),
......@@ -7651,7 +7880,6 @@ fn shiftOp(
76517880fn cImport(
76527881 gz: *GenZir,
76537882 scope: *Scope,
7654 rl: ResultLoc,
76557883 node: Ast.Node.Index,
76567884 body_node: Ast.Node.Index,
76577885) InnerError!Zir.Inst.Ref {
......@@ -7660,6 +7888,7 @@ fn cImport(
76607888
76617889 var block_scope = gz.makeSubBlock(scope);
76627890 block_scope.force_comptime = true;
7891 block_scope.c_import = true;
76637892 defer block_scope.instructions.deinit(gpa);
76647893
76657894 const block_inst = try gz.addBlock(.c_import, node);
......@@ -7670,7 +7899,7 @@ fn cImport(
76707899 try block_scope.setBlockBody(block_inst);
76717900 try gz.instructions.append(gpa, block_inst);
76727901
7673 return rvalue(gz, rl, .void_value, node);
7902 return indexToRef(block_inst);
76747903}
76757904
76767905fn overflowArithmetic(
......@@ -7703,24 +7932,6 @@ fn overflowArithmetic(
77037932 return rvalue(gz, rl, result, node);
77047933}
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
77247935fn callExpr(
77257936 gz: *GenZir,
77267937 scope: *Scope,
......@@ -7729,20 +7940,16 @@ fn callExpr(
77297940 call: Ast.full.Call,
77307941) InnerError!Zir.Inst.Ref {
77317942 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
77347946 const args = try astgen.gpa.alloc(Zir.Inst.Ref, call.ast.params.len);
77357947 defer astgen.gpa.free(args);
77367948
77377949 for (call.ast.params) |param_node, i| {
7738 const param_type = try gz.add(.{
7739 .tag = .param_type,
7740 .data = .{ .param_type = .{
7741 .callee = lhs,
7742 .param_index = @intCast(u32, i),
7743 } },
7744 });
7745 args[i] = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node);
7950 // Parameters are always temporary values, they have no
7951 // meaningful result location. Sema will coerce them.
7952 args[i] = try expr(gz, scope, .none, param_node);
77467953 }
77477954
77487955 const modifier: std.builtin.CallOptions.Modifier = blk: {
......@@ -7757,20 +7964,72 @@ fn callExpr(
77577964 }
77587965 break :blk .auto;
77597966 };
7760 const result: Zir.Inst.Ref = res: {
7761 const tag: Zir.Inst.Tag = switch (modifier) {
7762 .auto => .call,
7763 .async_kw => .call_async,
7764 .never_tail => unreachable,
7765 .never_inline => unreachable,
7766 .no_async => .call_nosuspend,
7767 .always_tail => unreachable,
7768 .always_inline => unreachable,
7769 .compile_time => .call_compile_time,
7770 };
7771 break :res try gz.addCall(tag, lhs, args, node);
7772 };
7773 return rvalue(gz, rl, result, node); // TODO function call with result location
7967 const call_inst = try gz.addCall(modifier, callee, args, node);
7968 return rvalue(gz, rl, call_inst, node); // TODO function call with result location
7969}
7970
7971/// calleeExpr generates the function part of a call expression (f in f(x)), or the
7972/// callee argument to the @call() builtin. If the lhs is a field access or the
7973/// @field() builtin, we need to generate a special field_call_bind instruction
7974/// instead of the normal field_val or field_ptr. If this is a inst.func() call,
7975/// this instruction will capture the value of the first argument before evaluating
7976/// the other arguments. We need to use .ref here to guarantee we will be able to
7977/// promote an lvalue to an address if the first parameter requires it. This
7978/// unfortunately also means we need to take a reference to any types on the lhs.
7979fn calleeExpr(
7980 gz: *GenZir,
7981 scope: *Scope,
7982 node: Ast.Node.Index,
7983) InnerError!Zir.Inst.Ref {
7984 const astgen = gz.astgen;
7985 const tree = astgen.tree;
7986
7987 const tag = tree.nodes.items(.tag)[node];
7988 switch (tag) {
7989 .field_access => return addFieldAccess(.field_call_bind, gz, scope, .ref, node),
7990
7991 .builtin_call_two,
7992 .builtin_call_two_comma,
7993 .builtin_call,
7994 .builtin_call_comma,
7995 => {
7996 const node_datas = tree.nodes.items(.data);
7997 const main_tokens = tree.nodes.items(.main_token);
7998 const builtin_token = main_tokens[node];
7999 const builtin_name = tree.tokenSlice(builtin_token);
8000
8001 var inline_params: [2]Ast.Node.Index = undefined;
8002 var params: []Ast.Node.Index = switch (tag) {
8003 .builtin_call,
8004 .builtin_call_comma,
8005 => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs],
8006
8007 .builtin_call_two,
8008 .builtin_call_two_comma,
8009 => blk: {
8010 inline_params = .{ node_datas[node].lhs, node_datas[node].rhs };
8011 const len: usize = if (inline_params[0] == 0) @as(usize, 0) else if (inline_params[1] == 0) @as(usize, 1) else @as(usize, 2);
8012 break :blk inline_params[0..len];
8013 },
8014
8015 else => unreachable,
8016 };
8017
8018 // If anything is wrong, fall back to builtinCall.
8019 // It will emit any necessary compile errors and notes.
8020 if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) {
8021 const lhs = try expr(gz, scope, .ref, params[0]);
8022 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
8023 return gz.addPlNode(.field_call_bind_named, node, Zir.Inst.FieldNamed{
8024 .lhs = lhs,
8025 .field_name = field_name,
8026 });
8027 }
8028
8029 return builtinCall(gz, scope, .none, node, params);
8030 },
8031 else => return expr(gz, scope, .none, node),
8032 }
77748033}
77758034
77768035pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{
......@@ -7892,27 +8151,33 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
78928151 .asm_simple,
78938152 .add,
78948153 .add_wrap,
8154 .add_sat,
78958155 .array_cat,
78968156 .array_mult,
78978157 .assign,
78988158 .assign_bit_and,
78998159 .assign_bit_or,
7900 .assign_bit_shift_left,
7901 .assign_bit_shift_right,
8160 .assign_shl,
8161 .assign_shl_sat,
8162 .assign_shr,
79028163 .assign_bit_xor,
79038164 .assign_div,
79048165 .assign_sub,
79058166 .assign_sub_wrap,
8167 .assign_sub_sat,
79068168 .assign_mod,
79078169 .assign_add,
79088170 .assign_add_wrap,
8171 .assign_add_sat,
79098172 .assign_mul,
79108173 .assign_mul_wrap,
8174 .assign_mul_sat,
79118175 .bang_equal,
79128176 .bit_and,
79138177 .bit_or,
7914 .bit_shift_left,
7915 .bit_shift_right,
8178 .shl,
8179 .shl_sat,
8180 .shr,
79168181 .bit_xor,
79178182 .bool_and,
79188183 .bool_or,
......@@ -7927,10 +8192,12 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
79278192 .mod,
79288193 .mul,
79298194 .mul_wrap,
8195 .mul_sat,
79308196 .switch_range,
79318197 .field_access,
79328198 .sub,
79338199 .sub_wrap,
8200 .sub_sat,
79348201 .slice,
79358202 .slice_open,
79368203 .slice_sentinel,
......@@ -8008,17 +8275,31 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
80088275 }
80098276 },
80108277
8011 .builtin_call,
8012 .builtin_call_comma,
8013 .builtin_call_two,
8014 .builtin_call_two_comma,
8015 => {
8278 .builtin_call_two, .builtin_call_two_comma => {
8279 const builtin_token = main_tokens[node];
8280 const builtin_name = tree.tokenSlice(builtin_token);
8281 // If the builtin is an invalid name, we don't cause an error here; instead
8282 // let it pass, and the error will be "invalid builtin function" later.
8283 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
8284 switch (builtin_info.needs_mem_loc) {
8285 .never => return false,
8286 .always => return true,
8287 .forward1 => node = node_datas[node].rhs,
8288 }
8289 },
8290
8291 .builtin_call, .builtin_call_comma => {
8292 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
80168293 const builtin_token = main_tokens[node];
80178294 const builtin_name = tree.tokenSlice(builtin_token);
80188295 // If the builtin is an invalid name, we don't cause an error here; instead
80198296 // let it pass, and the error will be "invalid builtin function" later.
80208297 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 }
80228303 },
80238304 }
80248305 }
......@@ -8125,27 +8406,33 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
81258406 .tagged_union_enum_tag_trailing,
81268407 .add,
81278408 .add_wrap,
8409 .add_sat,
81288410 .array_cat,
81298411 .array_mult,
81308412 .assign,
81318413 .assign_bit_and,
81328414 .assign_bit_or,
8133 .assign_bit_shift_left,
8134 .assign_bit_shift_right,
8415 .assign_shl,
8416 .assign_shl_sat,
8417 .assign_shr,
81358418 .assign_bit_xor,
81368419 .assign_div,
81378420 .assign_sub,
81388421 .assign_sub_wrap,
8422 .assign_sub_sat,
81398423 .assign_mod,
81408424 .assign_add,
81418425 .assign_add_wrap,
8426 .assign_add_sat,
81428427 .assign_mul,
81438428 .assign_mul_wrap,
8429 .assign_mul_sat,
81448430 .bang_equal,
81458431 .bit_and,
81468432 .bit_or,
8147 .bit_shift_left,
8148 .bit_shift_right,
8433 .shl,
8434 .shl_sat,
8435 .shr,
81498436 .bit_xor,
81508437 .bool_and,
81518438 .bool_or,
......@@ -8160,9 +8447,11 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
81608447 .mod,
81618448 .mul,
81628449 .mul_wrap,
8450 .mul_sat,
81638451 .switch_range,
81648452 .sub,
81658453 .sub_wrap,
8454 .sub_sat,
81668455 .slice,
81678456 .slice_open,
81688457 .slice_sentinel,
......@@ -8297,27 +8586,33 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
82978586 .asm_simple,
82988587 .add,
82998588 .add_wrap,
8589 .add_sat,
83008590 .array_cat,
83018591 .array_mult,
83028592 .assign,
83038593 .assign_bit_and,
83048594 .assign_bit_or,
8305 .assign_bit_shift_left,
8306 .assign_bit_shift_right,
8595 .assign_shl,
8596 .assign_shl_sat,
8597 .assign_shr,
83078598 .assign_bit_xor,
83088599 .assign_div,
83098600 .assign_sub,
83108601 .assign_sub_wrap,
8602 .assign_sub_sat,
83118603 .assign_mod,
83128604 .assign_add,
83138605 .assign_add_wrap,
8606 .assign_add_sat,
83148607 .assign_mul,
83158608 .assign_mul_wrap,
8609 .assign_mul_sat,
83168610 .bang_equal,
83178611 .bit_and,
83188612 .bit_or,
8319 .bit_shift_left,
8320 .bit_shift_right,
8613 .shl,
8614 .shl_sat,
8615 .shr,
83218616 .bit_xor,
83228617 .bool_and,
83238618 .bool_or,
......@@ -8332,10 +8627,12 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
83328627 .mod,
83338628 .mul,
83348629 .mul_wrap,
8630 .mul_sat,
83358631 .switch_range,
83368632 .field_access,
83378633 .sub,
83388634 .sub_wrap,
8635 .sub_sat,
83398636 .slice,
83408637 .slice_open,
83418638 .slice_sentinel,
......@@ -8894,6 +9191,17 @@ const Scope = struct {
88949191 return @fieldParentPtr(T, "base", base);
88959192 }
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
88979205 const Tag = enum {
88989206 gen_zir,
88999207 local_val,
......@@ -8919,7 +9227,7 @@ const Scope = struct {
89199227 const LocalVal = struct {
89209228 const base_tag: Tag = .local_val;
89219229 base: Scope = Scope{ .tag = base_tag },
8922 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`.
9230 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
89239231 parent: *Scope,
89249232 gen_zir: *GenZir,
89259233 inst: Zir.Inst.Ref,
......@@ -8938,7 +9246,7 @@ const Scope = struct {
89389246 const LocalPtr = struct {
89399247 const base_tag: Tag = .local_ptr;
89409248 base: Scope = Scope{ .tag = base_tag },
8941 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`.
9249 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
89429250 parent: *Scope,
89439251 gen_zir: *GenZir,
89449252 ptr: Zir.Inst.Ref,
......@@ -8956,7 +9264,7 @@ const Scope = struct {
89569264
89579265 const Defer = struct {
89589266 base: Scope,
8959 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`.
9267 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
89609268 parent: *Scope,
89619269 defer_node: Ast.Node.Index,
89629270 };
......@@ -8967,11 +9275,27 @@ const Scope = struct {
89679275 const base_tag: Tag = .namespace;
89689276 base: Scope = Scope{ .tag = base_tag },
89699277
9278 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
89709279 parent: *Scope,
89719280 /// Maps string table index to the source location of declaration,
89729281 /// for the purposes of reporting name shadowing compile errors.
89739282 decls: std.AutoHashMapUnmanaged(u32, Ast.Node.Index) = .{},
89749283 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 }
89759299 };
89769300
89779301 const Top = struct {
......@@ -8987,12 +9311,14 @@ const GenZir = struct {
89879311 base: Scope = Scope{ .tag = base_tag },
89889312 force_comptime: bool,
89899313 in_defer: bool,
9314 c_import: bool = false,
89909315 /// How decls created in this scope should be named.
89919316 anon_name_strategy: Zir.Inst.NameStrategy = .anon,
89929317 /// The containing decl AST node.
89939318 decl_node_index: Ast.Node.Index,
89949319 /// The containing decl line index, absolute.
89959320 decl_line: u32,
9321 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
89969322 parent: *Scope,
89979323 /// All `GenZir` scopes for the same ZIR share this.
89989324 astgen: *AstGen,
......@@ -9028,10 +9354,17 @@ const GenZir = struct {
90289354 suspend_node: Ast.Node.Index = 0,
90299355 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
90319363 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
90329364 return .{
90339365 .force_comptime = gz.force_comptime,
90349366 .in_defer = gz.in_defer,
9367 .c_import = gz.c_import,
90359368 .decl_node_index = gz.decl_node_index,
90369369 .decl_line = gz.decl_line,
90379370 .parent = scope,
......@@ -9041,6 +9374,52 @@ const GenZir = struct {
90419374 };
90429375 }
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
90449423 const Label = struct {
90459424 token: Ast.TokenIndex,
90469425 block_inst: Zir.Inst.Index,
......@@ -9357,7 +9736,7 @@ const GenZir = struct {
93579736
93589737 fn addCall(
93599738 gz: *GenZir,
9360 tag: Zir.Inst.Tag,
9739 modifier: std.builtin.CallOptions.Modifier,
93619740 callee: Zir.Inst.Ref,
93629741 args: []const Zir.Inst.Ref,
93639742 /// Absolute node index. This function does the conversion to offset from Decl.
......@@ -9366,20 +9745,24 @@ const GenZir = struct {
93669745 assert(callee != .none);
93679746 assert(src_node != 0);
93689747 const gpa = gz.astgen.gpa;
9748 const Call = Zir.Inst.Call;
93699749 try gz.instructions.ensureUnusedCapacity(gpa, 1);
93709750 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 +
93729752 args.len);
93739753
9374 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Call{
9754 const payload_index = gz.astgen.addExtraAssumeCapacity(Call{
93759755 .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 },
93779760 });
93789761 gz.astgen.appendRefsAssumeCapacity(args);
93799762
93809763 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
93819764 gz.astgen.instructions.appendAssumeCapacity(.{
9382 .tag = tag,
9765 .tag = .call,
93839766 .data = .{ .pl_node = .{
93849767 .src_node = gz.nodeIndexToRelative(src_node),
93859768 .payload_index = payload_index,
......@@ -9695,6 +10078,22 @@ const GenZir = struct {
969510078 });
969610079 }
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
969810097 fn addNodeExtended(
969910098 gz: *GenZir,
970010099 opcode: Zir.Inst.Extended,
......@@ -9996,6 +10395,37 @@ const GenZir = struct {
999610395 });
999710396 }
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
999910429 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
1000010430 return indexToRef(try gz.addAsIndex(inst));
1000110431 }
src/BuiltinFn.zig+17-41
......@@ -2,7 +2,6 @@ const std = @import("std");
22
33pub const Tag = enum {
44 add_with_overflow,
5 add_with_saturation,
65 align_cast,
76 align_of,
87 as,
......@@ -66,7 +65,6 @@ pub const Tag = enum {
6665 wasm_memory_grow,
6766 mod,
6867 mul_with_overflow,
69 mul_with_saturation,
7068 panic,
7169 pop_count,
7270 ptr_cast,
......@@ -81,12 +79,10 @@ pub const Tag = enum {
8179 set_runtime_safety,
8280 shl_exact,
8381 shl_with_overflow,
84 shl_with_saturation,
8582 shr_exact,
8683 shuffle,
8784 size_of,
8885 splat,
89 sub_with_saturation,
9086 reduce,
9187 src,
9288 sqrt,
......@@ -114,10 +110,19 @@ pub const Tag = enum {
114110 Vector,
115111};
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
117122tag: Tag,
118123
119/// `true` if the builtin call can take advantage of a result location pointer.
120needs_mem_loc: bool = false,
124/// Info about the builtin call's ability to take advantage of a result location pointer.
125needs_mem_loc: MemLocRequirement = .never,
121126/// `true` if the builtin call can be the left-hand side of an expression (assigned to).
122127allows_lvalue: bool = false,
123128/// The number of parameters to this builtin function. `null` means variable number
......@@ -152,7 +157,7 @@ pub const list = list: {
152157 "@as",
153158 .{
154159 .tag = .as,
155 .needs_mem_loc = true,
160 .needs_mem_loc = .forward1,
156161 .param_count = 2,
157162 },
158163 },
......@@ -188,7 +193,7 @@ pub const list = list: {
188193 "@bitCast",
189194 .{
190195 .tag = .bit_cast,
191 .needs_mem_loc = true,
196 .needs_mem_loc = .forward1,
192197 .param_count = 2,
193198 },
194199 },
......@@ -252,7 +257,7 @@ pub const list = list: {
252257 "@call",
253258 .{
254259 .tag = .call,
255 .needs_mem_loc = true,
260 .needs_mem_loc = .always,
256261 .param_count = 3,
257262 },
258263 },
......@@ -414,7 +419,7 @@ pub const list = list: {
414419 "@field",
415420 .{
416421 .tag = .field,
417 .needs_mem_loc = true,
422 .needs_mem_loc = .always,
418423 .param_count = 2,
419424 .allows_lvalue = true,
420425 },
......@@ -531,34 +536,6 @@ pub const list = list: {
531536 .param_count = 2,
532537 },
533538 },
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 },
562539 .{
563540 "@memcpy",
564541 .{
......@@ -731,7 +708,6 @@ pub const list = list: {
731708 "@splat",
732709 .{
733710 .tag = .splat,
734 .needs_mem_loc = true,
735711 .param_count = 2,
736712 },
737713 },
......@@ -746,7 +722,7 @@ pub const list = list: {
746722 "@src",
747723 .{
748724 .tag = .src,
749 .needs_mem_loc = true,
725 .needs_mem_loc = .always,
750726 .param_count = 0,
751727 },
752728 },
......@@ -901,7 +877,7 @@ pub const list = list: {
901877 "@unionInit",
902878 .{
903879 .tag = .union_init,
904 .needs_mem_loc = true,
880 .needs_mem_loc = .always,
905881 .param_count = 3,
906882 },
907883 },
src/Compilation.zig+134-60
......@@ -812,7 +812,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
812812
813813 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.
816816 const wasi_exec_model = if (options.target.os.tag != .wasi) undefined else options.wasi_exec_model orelse .command;
817817
818818 const comp: *Compilation = comp: {
......@@ -849,10 +849,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
849849 if (options.use_llvm) |explicit|
850850 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
856852 // If we are outputting .c code we must use Zig backend.
857853 if (ofmt == .c)
858854 break :blk false;
......@@ -861,6 +857,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
861857 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)
862858 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
864864 // The stage1 compiler depends on the stage1 C++ LLVM backend
865865 // to compile zig code.
866866 if (use_stage1)
......@@ -876,9 +876,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
876876 if (options.use_llvm == true) {
877877 return error.ZigCompilerNotBuiltWithLLVMExtensions;
878878 }
879 if (options.machine_code_model != .default) {
880 return error.MachineCodeModelNotSupportedWithoutLlvm;
881 }
882879 if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {
883880 return error.EmittingLlvmModuleRequiresUsingLlvmBackend;
884881 }
......@@ -1100,7 +1097,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
11001097
11011098 if (feature.llvm_name) |llvm_name| {
11021099 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);
11041101 buf.appendAssumeCapacity(plus_or_minus);
11051102 buf.appendSliceAssumeCapacity(llvm_name);
11061103 buf.appendSliceAssumeCapacity(",");
......@@ -1180,6 +1177,8 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
11801177 }
11811178 hash.add(valgrind);
11821179 hash.add(single_threaded);
1180 hash.add(use_stage1);
1181 hash.add(use_llvm);
11831182 hash.add(dll_export_fns);
11841183 hash.add(options.is_test);
11851184 hash.add(options.skip_linker_dependencies);
......@@ -1350,7 +1349,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
13501349
13511350 var system_libs: std.StringArrayHashMapUnmanaged(void) = .{};
13521351 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);
13541353 for (options.system_libs) |lib_name| {
13551354 system_libs.putAssumeCapacity(lib_name, {});
13561355 }
......@@ -1486,7 +1485,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
14861485 errdefer comp.astgen_wait_group.deinit();
14871486
14881487 // 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);
14901489 for (options.c_source_files) |c_source_file| {
14911490 const c_object = try gpa.create(CObject);
14921491 errdefer gpa.destroy(c_object);
......@@ -1577,25 +1576,30 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
15771576 // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler
15781577 // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for
15791578 // stage1 but not stage2.
1580 if (comp.bin_file.options.use_stage1) {
1581 if (comp.bin_file.options.include_compiler_rt) {
1582 if (is_exe_or_dyn_lib) {
1583 try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });
1584 } else if (options.output_mode != .Obj) {
1585 // If build-obj with -fcompiler-rt is requested, that is handled specially
1586 // elsewhere. In this case we are making a static library, so we ask
1587 // for a compiler-rt object to put in it.
1588 try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });
1589 }
1579 const capable_of_building_compiler_rt = comp.bin_file.options.use_stage1 or
1580 comp.bin_file.options.use_llvm;
1581 const capable_of_building_zig_libc = comp.bin_file.options.use_stage1 or
1582 comp.bin_file.options.use_llvm;
1583 const capable_of_building_ssp = comp.bin_file.options.use_stage1;
1584
1585 if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) {
1586 if (is_exe_or_dyn_lib) {
1587 try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });
1588 } else if (options.output_mode != .Obj) {
1589 // If build-obj with -fcompiler-rt is requested, that is handled specially
1590 // elsewhere. In this case we are making a static library, so we ask
1591 // for a compiler-rt object to put in it.
1592 try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });
15901593 }
1591 if (needs_c_symbols) {
1592 // MinGW provides no libssp, use our own implementation.
1593 if (comp.getTarget().isMinGW()) {
1594 try comp.work_queue.writeItem(.{ .libssp = {} });
1595 }
1596 if (!comp.bin_file.options.link_libc) {
1597 try comp.work_queue.writeItem(.{ .zig_libc = {} });
1598 }
1594 }
1595 if (needs_c_symbols) {
1596 // MinGW provides no libssp, use our own implementation.
1597 if (comp.getTarget().isMinGW() and capable_of_building_ssp) {
1598 try comp.work_queue.writeItem(.{ .libssp = {} });
1599 }
1600
1601 if (!comp.bin_file.options.link_libc and capable_of_building_zig_libc) {
1602 try comp.work_queue.writeItem(.{ .zig_libc = {} });
15991603 }
16001604 }
16011605 }
......@@ -1647,6 +1651,9 @@ pub fn destroy(self: *Compilation) void {
16471651 if (self.compiler_rt_static_lib) |*crt_file| {
16481652 crt_file.deinit(gpa);
16491653 }
1654 if (self.compiler_rt_obj) |*crt_file| {
1655 crt_file.deinit(gpa);
1656 }
16501657 if (self.libssp_static_lib) |*crt_file| {
16511658 crt_file.deinit(gpa);
16521659 }
......@@ -1793,6 +1800,10 @@ pub fn update(self: *Compilation) !void {
17931800 }
17941801 }
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
17961807 // If there are any errors, we anticipate the source files being loaded
17971808 // to report error messages. Otherwise we unload all source files to save memory.
17981809 // The ZIR needs to stay loaded in memory because (1) Decl objects contain references
......@@ -1808,6 +1819,37 @@ pub fn update(self: *Compilation) !void {
18081819 }
18091820}
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
18111853/// Having the file open for writing is problematic as far as executing the
18121854/// binary is concerned. This will remove the write flag, or close the file,
18131855/// or whatever is needed so that it can be executed.
......@@ -2113,7 +2155,11 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
21132155 const module = self.bin_file.options.module.?;
21142156 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) {
21172163 error.AnalysisFail => {
21182164 assert(func.state != .in_progress);
21192165 continue;
......@@ -2175,16 +2221,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
21752221 const decl_emit_h = decl.getEmitH(module);
21762222 const fwd_decl = &decl_emit_h.fwd_decl;
21772223 fwd_decl.shrinkRetainingCapacity(0);
2224 var typedefs_arena = std.heap.ArenaAllocator.init(gpa);
2225 defer typedefs_arena.deinit();
21782226
21792227 var dg: c_codegen.DeclGen = .{
2228 .gpa = gpa,
21802229 .module = module,
21812230 .error_msg = null,
21822231 .decl = decl,
21832232 .fwd_decl = fwd_decl.toManaged(gpa),
2184 // we don't want to emit optionals and error unions to headers since they have no ABI
2185 .typedefs = undefined,
2233 .typedefs = c_codegen.TypedefMap.init(gpa),
2234 .typedefs_arena = &typedefs_arena.allocator,
21862235 };
21872236 defer dg.fwd_decl.deinit();
2237 defer dg.typedefs.deinit();
21882238
21892239 c_codegen.genHeader(&dg) catch |err| switch (err) {
21902240 error.AnalysisFail => {
......@@ -2612,7 +2662,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
26122662
26132663 const dep_basename = std.fs.path.basename(out_dep_path);
26142664 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
26172667 const digest = man.final();
26182668 const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
......@@ -2721,16 +2771,12 @@ fn reportRetryableAstGenError(
27212771 try Module.ErrorMsg.create(
27222772 gpa,
27232773 src_loc,
2724 "unable to load '{'}" ++ std.fs.path.sep_str ++ "{'}': {s}",
2725 .{
2726 std.zig.fmtEscapes(dir_path),
2727 std.zig.fmtEscapes(file.sub_file_path),
2728 @errorName(err),
2729 },
2774 "unable to load '{s}" ++ std.fs.path.sep_str ++ "{s}': {s}",
2775 .{ dir_path, file.sub_file_path, @errorName(err) },
27302776 )
27312777 else
2732 try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{'}': {s}", .{
2733 std.zig.fmtEscapes(file.sub_file_path), @errorName(err),
2778 try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{s}': {s}", .{
2779 file.sub_file_path, @errorName(err),
27342780 });
27352781 errdefer err_msg.destroy(gpa);
27362782
......@@ -2766,6 +2812,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
27662812 defer man.deinit();
27672813
27682814 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
27702819 try man.hashCSource(c_object.src);
27712820
......@@ -2789,16 +2838,29 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
27892838 comp.bin_file.options.root_name
27902839 else
27912840 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);
27972843 const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
27982844 var argv = std.ArrayList([]const u8).init(comp.gpa);
27992845 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.
28022864 const out_obj_path = try comp.tmpFilePath(arena, o_basename);
28032865 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
28042866 defer zig_cache_tmp_dir.close();
......@@ -2812,15 +2874,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
28122874 try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});
28132875 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);
28162878 switch (comp.clang_preprocessor_mode) {
28172879 .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
28182880 .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
28192881 .stdout => argv.appendAssumeCapacity("-E"),
28202882 }
2821
2822 try argv.append(c_object.src.src_path);
2823 try argv.appendSlice(c_object.src.extra_flags);
2883 if (comp.clang_passthrough_mode) {
2884 if (comp.emit_asm != null) {
2885 argv.appendAssumeCapacity("-S");
2886 } else if (comp.emit_llvm_ir != null) {
2887 argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" });
2888 } else if (comp.emit_llvm_bc != null) {
2889 argv.appendAssumeCapacity("-emit-llvm");
2890 }
2891 }
2892 argv.appendAssumeCapacity(c_object.src.src_path);
2893 argv.appendSliceAssumeCapacity(c_object.src.extra_flags);
28242894
28252895 if (comp.verbose_cc) {
28262896 dump_argv(argv.items);
......@@ -2840,8 +2910,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
28402910 switch (term) {
28412911 .Exited => |code| {
28422912 if (code != 0) {
2843 // TODO https://github.com/ziglang/zig/issues/6342
2844 std.process.exit(1);
2913 std.process.exit(code);
28452914 }
28462915 if (comp.clang_preprocessor_mode == .stdout)
28472916 std.process.exit(0);
......@@ -2857,9 +2926,6 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
28572926
28582927 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));
28632929 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
28642930
28652931 const term = child.wait() catch |err| {
......@@ -2909,6 +2975,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
29092975 break :blk digest;
29102976 };
29112977
2978 const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext });
2979
29122980 c_object.status = .{
29132981 .success = .{
29142982 .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{
......@@ -3032,7 +3100,7 @@ pub fn addCCArgs(
30323100
30333101 // It would be really nice if there was a more compact way to communicate this info to Clang.
30343102 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);
30363104 for (all_features_list) |feature, index_usize| {
30373105 const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize);
30383106 const is_enabled = target.cpu.features.isEnabled(index);
......@@ -3533,7 +3601,7 @@ fn detectLibCIncludeDirs(
35333601
35343602fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
35353603 var list = std.ArrayList([]const u8).init(arena);
3536 try list.ensureCapacity(4);
3604 try list.ensureTotalCapacity(4);
35373605
35383606 list.appendAssumeCapacity(lci.include_dir.?);
35393607
......@@ -3640,7 +3708,7 @@ fn setMiscFailure(
36403708 comptime format: []const u8,
36413709 args: anytype,
36423710) 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);
36443712 const msg = try std.fmt.allocPrint(comp.gpa, format, args);
36453713 comp.misc_failures.putAssumeCapacityNoClobber(tag, .{ .msg = msg });
36463714}
......@@ -3662,6 +3730,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
36623730 const target = comp.getTarget();
36633731 const generic_arch_name = target.cpu.arch.genericName();
36643732 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
36663736 @setEvalBranchQuota(4000);
36673737 try buffer.writer().print(
......@@ -3673,6 +3743,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
36733743 \\pub const zig_is_stage2 = {};
36743744 \\/// Temporary until self-hosted supports the `cpu.arch` value.
36753745 \\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 = {};
36763748 \\
36773749 \\pub const output_mode = std.builtin.OutputMode.{};
36783750 \\pub const link_mode = std.builtin.LinkMode.{};
......@@ -3688,6 +3760,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
36883760 build_options.version,
36893761 !use_stage1,
36903762 std.zig.fmtId(@tagName(target.cpu.arch)),
3763 stage2_x86_cx16,
36913764 std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)),
36923765 std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)),
36933766 comp.bin_file.options.is_test,
......@@ -3912,6 +3985,7 @@ fn buildOutputFromZig(
39123985 },
39133986 .root_src_path = src_basename,
39143987 };
3988 defer main_pkg.deinitTable(comp.gpa);
39153989 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];
39163990 const target = comp.getTarget();
39173991 const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{
......@@ -3970,7 +4044,7 @@ fn buildOutputFromZig(
39704044 defer if (!keep_errors) errors.deinit(sub_compilation.gpa);
39714045
39724046 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);
39744048 comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{
39754049 .msg = try std.fmt.allocPrint(comp.gpa, "sub-compilation of {s} failed", .{
39764050 @tagName(misc_task_tag),
......@@ -4402,7 +4476,7 @@ pub fn build_crt_file(
44024476
44034477 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
44074481 comp.crt_files.putAssumeCapacityNoClobber(basename, .{
44084482 .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,
3030/// The main tomb bits are still used and the extra ones are starting with the lsb of the
3131/// value here.
3232special: 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.
3434extra: []const u32,
3535
3636/// Trailing is the set of instructions whose lifetimes end at the start of the then branch,
......@@ -226,12 +226,16 @@ fn analyzeInst(
226226 switch (inst_tags[inst]) {
227227 .add,
228228 .addwrap,
229 .add_sat,
229230 .sub,
230231 .subwrap,
232 .sub_sat,
231233 .mul,
232234 .mulwrap,
235 .mul_sat,
233236 .div,
234237 .rem,
238 .mod,
235239 .ptr_add,
236240 .ptr_sub,
237241 .bit_and,
......@@ -251,7 +255,14 @@ fn analyzeInst(
251255 .ptr_elem_val,
252256 .ptr_ptr_elem_val,
253257 .shl,
258 .shl_exact,
259 .shl_sat,
254260 .shr,
261 .atomic_store_unordered,
262 .atomic_store_monotonic,
263 .atomic_store_release,
264 .atomic_store_seq_cst,
265 .set_union_tag,
255266 => {
256267 const o = inst_datas[inst].bin_op;
257268 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
......@@ -264,12 +275,14 @@ fn analyzeInst(
264275 .breakpoint,
265276 .dbg_stmt,
266277 .unreach,
278 .fence,
267279 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
268280
269281 .not,
270282 .bitcast,
271283 .load,
272 .floatcast,
284 .fpext,
285 .fptrunc,
273286 .intcast,
274287 .trunc,
275288 .optional_payload,
......@@ -288,6 +301,11 @@ fn analyzeInst(
288301 .struct_field_ptr_index_2,
289302 .struct_field_ptr_index_3,
290303 .array_to_slice,
304 .float_to_int,
305 .int_to_float,
306 .get_union_tag,
307 .clz,
308 .ctz,
291309 => {
292310 const o = inst_datas[inst].ty_op;
293311 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
......@@ -344,6 +362,20 @@ fn analyzeInst(
344362 const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data;
345363 return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value });
346364 },
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 },
347379 .br => {
348380 const br = inst_datas[inst].br;
349381 return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });
......@@ -440,7 +472,7 @@ fn analyzeInst(
440472 }
441473 // Now we have to correctly populate new_set.
442474 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()));
444476 var it = then_table.keyIterator();
445477 while (it.next()) |key| {
446478 _ = ns.putAssumeCapacity(key.*, {});
src/Module.zig+368-165
......@@ -64,11 +64,16 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
6464/// The set of all the generic function instantiations. This is used so that when a generic
6565/// function is called twice with the same comptime parameter arguments, both calls dispatch
6666/// to the same function.
67/// TODO: remove functions from this set when they are destroyed.
6768monomorphed_funcs: MonomorphedFuncsSet = .{},
68
6969/// The set of all comptime function calls that have been cached so that future calls
7070/// with the same parameters will get the same return value.
7171memoized_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
7378/// We optimize memory usage for a compilation with no compile errors by storing the
7479/// error messages and mapping outside of `Decl`.
......@@ -215,6 +220,13 @@ pub const MemoizedCall = struct {
215220 }
216221};
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
218230/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.
219231pub const GlobalEmitH = struct {
220232 /// Where to put the output.
......@@ -236,6 +248,9 @@ pub const Export = struct {
236248 link: link.File.Export,
237249 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
238250 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,
239254 /// The Decl being exported. Note this is *not* the Decl performing the export.
240255 exported_decl: *Decl,
241256 status: enum {
......@@ -249,8 +264,8 @@ pub const Export = struct {
249264
250265 pub fn getSrcLoc(exp: Export) SrcLoc {
251266 return .{
252 .file_scope = exp.owner_decl.namespace.file_scope,
253 .parent_decl_node = exp.owner_decl.src_node,
267 .file_scope = exp.src_decl.namespace.file_scope,
268 .parent_decl_node = exp.src_decl.src_node,
254269 .lazy = exp.src,
255270 };
256271 }
......@@ -263,6 +278,56 @@ pub const DeclPlusEmitH = struct {
263278 emit_h: EmitH,
264279};
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
266331pub const Decl = struct {
267332 /// Allocated with Module's allocator; outlives the ZIR code.
268333 name: [*:0]const u8,
......@@ -276,7 +341,9 @@ pub const Decl = struct {
276341 align_val: Value,
277342 /// Populated when `has_tv`.
278343 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.
280347 /// If this is `null` then there is no memory management needed.
281348 value_arena: ?*std.heap.ArenaAllocator.State = null,
282349 /// The direct parent namespace of the Decl.
......@@ -285,6 +352,11 @@ pub const Decl = struct {
285352 /// the namespace of the struct, since there is no parent.
286353 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
288360 /// An integer that can be checked against the corresponding incrementing
289361 /// generation field of Module. This is used to determine whether `complete` status
290362 /// represents pre- or post- re-analysis.
......@@ -339,7 +411,7 @@ pub const Decl = struct {
339411 /// to require re-analysis.
340412 outdated,
341413 },
342 /// Whether `typed_value`, `align_val`, and `linksection_val` are populated.
414 /// Whether `typed_value`, `align_val`, `linksection_val` and `addrspace` are populated.
343415 has_tv: bool,
344416 /// If `true` it means the `Decl` is the resource owner of the type/value associated
345417 /// with it. That means when `Decl` is destroyed, the cleanup code should additionally
......@@ -354,8 +426,8 @@ pub const Decl = struct {
354426 is_exported: bool,
355427 /// Whether the ZIR code provides an align instruction.
356428 has_align: bool,
357 /// Whether the ZIR code provides a linksection instruction.
358 has_linksection: bool,
429 /// Whether the ZIR code provides a linksection and address space instruction.
430 has_linksection_or_addrspace: bool,
359431 /// Flag used by garbage collection to mark and sweep.
360432 /// Decls which correspond to an AST node always have this field set to `true`.
361433 /// Anonymous Decls are initialized with this field set to `false` and then it
......@@ -477,14 +549,22 @@ pub const Decl = struct {
477549 if (!decl.has_align) return .none;
478550 assert(decl.zir_decl_index != 0);
479551 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]);
481553 }
482554
483555 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;
485565 assert(decl.zir_decl_index != 0);
486566 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;
488568 return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
489569 }
490570
......@@ -542,11 +622,11 @@ pub const Decl = struct {
542622 return decl.namespace.renderFullyQualifiedName(unqualified_name, writer);
543623 }
544624
545 pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![]u8 {
625 pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![:0]u8 {
546626 var buffer = std.ArrayList(u8).init(gpa);
547627 defer buffer.deinit();
548628 try decl.renderFullyQualifiedName(buffer.writer());
549 return buffer.toOwnedSlice();
629 return buffer.toOwnedSliceSentinel(0);
550630 }
551631
552632 pub fn typedValue(decl: Decl) error{AnalysisFail}!TypedValue {
......@@ -588,7 +668,7 @@ pub const Decl = struct {
588668
589669 /// If the Decl has a value and it is a function, return it,
590670 /// otherwise null.
591 pub fn getFunction(decl: *Decl) ?*Fn {
671 pub fn getFunction(decl: *const Decl) ?*Fn {
592672 if (!decl.owns_tv) return null;
593673 const func = (decl.val.castTag(.function) orelse return null).data;
594674 assert(func.owner_decl == decl);
......@@ -733,7 +813,7 @@ pub const Struct = struct {
733813 is_comptime: bool,
734814 };
735815
736 pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 {
816 pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![:0]u8 {
737817 return s.owner_decl.getFullyQualifiedName(gpa);
738818 }
739819
......@@ -779,6 +859,36 @@ pub const EnumSimple = struct {
779859 }
780860};
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
782892/// Represents the data that an enum declaration provides, when there is
783893/// at least one tag value explicitly specified, or at least one declaration.
784894pub const EnumFull = struct {
......@@ -788,16 +898,17 @@ pub const EnumFull = struct {
788898 /// Whether zig chooses this type or the user specifies it, it is stored here.
789899 tag_ty: Type,
790900 /// Set of field names in declaration order.
791 fields: std.StringArrayHashMapUnmanaged(void),
901 fields: NameMap,
792902 /// Maps integer tag value to field index.
793903 /// Entries are in declaration order, same as `fields`.
794904 /// If this hash map is empty, it means the enum tags are auto-numbered.
795905 values: ValueMap,
796 /// Represents the declarations inside this struct.
906 /// Represents the declarations inside this enum.
797907 namespace: Scope.Namespace,
798908 /// Offset from `owner_decl`, points to the enum decl AST node.
799909 node_offset: i32,
800910
911 pub const NameMap = std.StringArrayHashMapUnmanaged(void);
801912 pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false);
802913
803914 pub fn srcLoc(self: EnumFull) SrcLoc {
......@@ -853,6 +964,44 @@ pub const Union = struct {
853964 .lazy = .{ .node_offset = self.node_offset },
854965 };
855966 }
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 }
8561005};
8571006
8581007/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
......@@ -881,6 +1030,7 @@ pub const Fn = struct {
8811030
8821031 state: Analysis,
8831032 is_cold: bool = false,
1033 is_noinline: bool = false,
8841034
8851035 pub const Analysis = enum {
8861036 queued,
......@@ -936,19 +1086,21 @@ pub const Scope = struct {
9361086 return @fieldParentPtr(T, "base", base);
9371087 }
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 {
9401091 return switch (scope.tag) {
941 .block => scope.cast(Block).?.sema.owner_decl,
1092 .block => scope.cast(Block).?.src_decl,
9421093 .file => null,
943 .namespace => null,
1094 .namespace => scope.cast(Namespace).?.getDecl(),
9441095 };
9451096 }
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 {
9481100 return switch (scope.tag) {
949 .block => scope.cast(Block).?.src_decl,
1101 .block => scope.cast(Block).?.wip_capture_scope,
9501102 .file => null,
951 .namespace => scope.cast(Namespace).?.getDecl(),
1103 .namespace => scope.cast(Namespace).?.getDecl().src_scope,
9521104 };
9531105 }
9541106
......@@ -1288,6 +1440,9 @@ pub const Scope = struct {
12881440 instructions: ArrayListUnmanaged(Air.Inst.Index),
12891441 // `param` instructions are collected here to be used by the `func` instruction.
12901442 params: std.ArrayListUnmanaged(Param) = .{},
1443
1444 wip_capture_scope: *CaptureScope,
1445
12911446 label: ?*Label = null,
12921447 inlining: ?*Inlining,
12931448 /// If runtime_index is not 0 then one of these is guaranteed to be non null.
......@@ -1302,6 +1457,8 @@ pub const Scope = struct {
13021457 /// when null, it is determined by build mode, changed by @setRuntimeSafety
13031458 want_safety: ?bool = null,
13041459
1460 c_import_buf: ?*std.ArrayList(u8) = null,
1461
13051462 const Param = struct {
13061463 /// `noreturn` means `anytype`.
13071464 ty: Type,
......@@ -1347,6 +1504,7 @@ pub const Scope = struct {
13471504 .sema = parent.sema,
13481505 .src_decl = parent.src_decl,
13491506 .instructions = .{},
1507 .wip_capture_scope = parent.wip_capture_scope,
13501508 .label = null,
13511509 .inlining = parent.inlining,
13521510 .is_comptime = parent.is_comptime,
......@@ -1354,6 +1512,7 @@ pub const Scope = struct {
13541512 .runtime_loop = parent.runtime_loop,
13551513 .runtime_index = parent.runtime_index,
13561514 .want_safety = parent.want_safety,
1515 .c_import_buf = parent.c_import_buf,
13571516 };
13581517 }
13591518
......@@ -1453,6 +1612,40 @@ pub const Scope = struct {
14531612 });
14541613 }
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
14561649 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
14571650 return Air.indexToRef(try block.addInstAsIndex(inst));
14581651 }
......@@ -2092,39 +2285,39 @@ pub const LazySrcLoc = union(enum) {
20922285 node_offset_bin_op: i32,
20932286 /// The source location points to the LHS of a binary expression, found
20942287 /// 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.
20962289 /// The Decl is determined contextually.
20972290 node_offset_bin_lhs: i32,
20982291 /// The source location points to the RHS of a binary expression, found
20992292 /// 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.
21012294 /// The Decl is determined contextually.
21022295 node_offset_bin_rhs: i32,
21032296 /// The source location points to the operand of a switch expression, found
21042297 /// 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.
21062299 /// The Decl is determined contextually.
21072300 node_offset_switch_operand: i32,
21082301 /// The source location points to the else/`_` prong of a switch expression, found
21092302 /// 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.
21112304 /// The Decl is determined contextually.
21122305 node_offset_switch_special_prong: i32,
21132306 /// The source location points to all the ranges of a switch expression, found
21142307 /// by taking this AST node index offset from the containing Decl AST node,
2115 /// which points to a switch expression AST node. Next, nagivate to any of the
2308 /// which points to a switch expression AST node. Next, navigate to any of the
21162309 /// range nodes. The error applies to all of them.
21172310 /// The Decl is determined contextually.
21182311 node_offset_switch_range: i32,
21192312 /// The source location points to the calling convention of a function type
21202313 /// expression, found by taking this AST node index offset from the containing
2121 /// Decl AST node, which points to a function type AST node. Next, nagivate to
2314 /// Decl AST node, which points to a function type AST node. Next, navigate to
21222315 /// the calling convention node.
21232316 /// The Decl is determined contextually.
21242317 node_offset_fn_type_cc: i32,
21252318 /// The source location points to the return type of a function type
21262319 /// expression, found by taking this AST node index offset from the containing
2127 /// Decl AST node, which points to a function type AST node. Next, nagivate to
2320 /// Decl AST node, which points to a function type AST node. Next, navigate to
21282321 /// the return type node.
21292322 /// The Decl is determined contextually.
21302323 node_offset_fn_type_ret_ty: i32,
......@@ -2347,6 +2540,7 @@ pub fn deinit(mod: *Module) void {
23472540
23482541 mod.error_name_list.deinit(gpa);
23492542 mod.test_functions.deinit(gpa);
2543 mod.align_stack_fns.deinit(gpa);
23502544 mod.monomorphed_funcs.deinit(gpa);
23512545
23522546 {
......@@ -2362,6 +2556,7 @@ pub fn deinit(mod: *Module) void {
23622556fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
23632557 for (export_list) |exp| {
23642558 gpa.free(exp.options.name);
2559 if (exp.options.section) |s| gpa.free(s);
23652560 gpa.destroy(exp);
23662561 }
23672562 gpa.free(export_list);
......@@ -2372,7 +2567,7 @@ const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;
23722567// We need a better language feature for initializing a union with
23732568// a runtime known tag.
23742569const Stage1DataLayout = extern struct {
2375 data: [8]u8 align(8),
2570 data: [8]u8 align(@alignOf(Zir.Inst.Data)),
23762571 safety_tag: u8,
23772572};
23782573comptime {
......@@ -2873,12 +3068,10 @@ pub fn mapOldZirToNew(
28733068 var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{};
28743069 defer match_stack.deinit(gpa);
28753070
2876 const old_main_struct_inst = old_zir.getMainStruct();
2877 const new_main_struct_inst = new_zir.getMainStruct();
2878
3071 // Main struct inst is always the same
28793072 try match_stack.append(gpa, .{
2880 .old_inst = old_main_struct_inst,
2881 .new_inst = new_main_struct_inst,
3073 .old_inst = Zir.main_struct_inst,
3074 .new_inst = Zir.main_struct_inst,
28823075 });
28833076
28843077 var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa);
......@@ -3036,6 +3229,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
30363229 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);
30373230 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);
30383231 const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty);
3232 const ty_ty = comptime Type.initTag(.type);
30393233 struct_obj.* = .{
30403234 .owner_decl = undefined, // set below
30413235 .fields = .{},
......@@ -3050,7 +3244,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
30503244 .file_scope = file,
30513245 },
30523246 };
3053 const new_decl = try mod.allocateNewDecl(&struct_obj.namespace, 0);
3247 const new_decl = try mod.allocateNewDecl(&struct_obj.namespace, 0, null);
30543248 file.root_decl = new_decl;
30553249 struct_obj.owner_decl = new_decl;
30563250 new_decl.src_line = 0;
......@@ -3058,8 +3252,8 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
30583252 new_decl.is_pub = true;
30593253 new_decl.is_exported = false;
30603254 new_decl.has_align = false;
3061 new_decl.has_linksection = false;
3062 new_decl.ty = struct_ty;
3255 new_decl.has_linksection_or_addrspace = false;
3256 new_decl.ty = ty_ty;
30633257 new_decl.val = struct_val;
30643258 new_decl.has_tv = true;
30653259 new_decl.owns_tv = true;
......@@ -3069,7 +3263,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
30693263
30703264 if (file.status == .success_zir) {
30713265 assert(file.zir_loaded);
3072 const main_struct_inst = file.zir.getMainStruct();
3266 const main_struct_inst = Zir.main_struct_inst;
30733267 struct_obj.zir_index = main_struct_inst;
30743268
30753269 var sema_arena = std.heap.ArenaAllocator.init(gpa);
......@@ -3079,6 +3273,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
30793273 .mod = mod,
30803274 .gpa = gpa,
30813275 .arena = &sema_arena.allocator,
3276 .perm_arena = &new_decl_arena.allocator,
30823277 .code = file.zir,
30833278 .owner_decl = new_decl,
30843279 .namespace = &struct_obj.namespace,
......@@ -3087,10 +3282,15 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
30873282 .owner_func = null,
30883283 };
30893284 defer sema.deinit();
3285
3286 var wip_captures = try WipCaptureScope.init(gpa, &new_decl_arena.allocator, null);
3287 defer wip_captures.deinit();
3288
30903289 var block_scope: Scope.Block = .{
30913290 .parent = null,
30923291 .sema = &sema,
30933292 .src_decl = new_decl,
3293 .wip_capture_scope = wip_captures.scope,
30943294 .instructions = .{},
30953295 .inlining = null,
30963296 .is_comptime = true,
......@@ -3098,6 +3298,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
30983298 defer block_scope.instructions.deinit(gpa);
30993299
31003300 if (sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj)) |_| {
3301 try wip_captures.finalize();
31013302 new_decl.analysis = .complete;
31023303 } else |err| switch (err) {
31033304 error.OutOfMemory => return error.OutOfMemory,
......@@ -3127,6 +3328,10 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
31273328
31283329 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
31303335 var analysis_arena = std.heap.ArenaAllocator.init(gpa);
31313336 defer analysis_arena.deinit();
31323337
......@@ -3134,6 +3339,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
31343339 .mod = mod,
31353340 .gpa = gpa,
31363341 .arena = &analysis_arena.allocator,
3342 .perm_arena = &decl_arena.allocator,
31373343 .code = zir,
31383344 .owner_decl = decl,
31393345 .namespace = decl.namespace,
......@@ -3145,7 +3351,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
31453351
31463352 if (decl.isRoot()) {
31473353 log.debug("semaDecl root {*} ({s})", .{ decl, decl.name });
3148 const main_struct_inst = zir.getMainStruct();
3354 const main_struct_inst = Zir.main_struct_inst;
31493355 const struct_obj = decl.getStruct().?;
31503356 // This might not have gotten set in `semaFile` if the first time had
31513357 // a ZIR failure, so we set it here in case.
......@@ -3157,10 +3363,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
31573363 }
31583364 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
31603369 var block_scope: Scope.Block = .{
31613370 .parent = null,
31623371 .sema = &sema,
31633372 .src_decl = decl,
3373 .wip_capture_scope = wip_captures.scope,
31643374 .instructions = .{},
31653375 .inlining = null,
31663376 .is_comptime = true,
......@@ -3175,6 +3385,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
31753385 const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index);
31763386 const body = zir.extra[extra.end..][0..extra.data.body_len];
31773387 const break_index = try sema.analyzeBody(&block_scope, body);
3388 try wip_captures.finalize();
31783389 const result_ref = zir_datas[break_index].@"break".operand;
31793390 const src: LazySrcLoc = .{ .node_offset = 0 };
31803391 const decl_tv = try sema.resolveInstValue(&block_scope, src, result_ref);
......@@ -3188,14 +3399,29 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
31883399 if (linksection_ref == .none) break :blk Value.initTag(.null_value);
31893400 break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val;
31903401 };
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
31913420 // Note this resolves the type of the Decl, not the value; if this Decl
31923421 // is a struct, for example, this resolves `type` (which needs no resolution),
31933422 // not the struct itself.
31943423 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();
31993425 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
32003426
32013427 if (decl.is_usingnamespace) {
......@@ -3244,6 +3470,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
32443470 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
32453471 decl.align_val = try align_val.copy(&decl_arena.allocator);
32463472 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3473 decl.@"addrspace" = address_space;
32473474 decl.has_tv = true;
32483475 decl.owns_tv = owns_tv;
32493476 decl_arena_state.* = decl_arena.state;
......@@ -3271,7 +3498,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
32713498 return mod.fail(&block_scope.base, export_src, "export of inline function", .{});
32723499 }
32733500 // 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);
32753503 }
32763504 return type_changed or is_inline != prev_is_inline;
32773505 }
......@@ -3305,6 +3533,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
33053533 decl.val = try decl_tv.val.copy(&decl_arena.allocator);
33063534 decl.align_val = try align_val.copy(&decl_arena.allocator);
33073535 decl.linksection_val = try linksection_val.copy(&decl_arena.allocator);
3536 decl.@"addrspace" = address_space;
33083537 decl.has_tv = true;
33093538 decl_arena_state.* = decl_arena.state;
33103539 decl.value_arena = decl_arena_state;
......@@ -3329,7 +3558,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
33293558 if (decl.is_exported) {
33303559 const export_src = src; // TODO point to the export token
33313560 // 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);
33333563 }
33343564
33353565 return type_changed;
......@@ -3490,7 +3720,7 @@ pub fn scanNamespace(
34903720 const zir = namespace.file_scope.zir;
34913721
34923722 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
34953725 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
34963726 var extra_index = extra_start + bit_bags_count;
......@@ -3512,8 +3742,8 @@ pub fn scanNamespace(
35123742
35133743 const decl_sub_index = extra_index;
35143744 extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1)
3515 extra_index += @truncate(u1, flags >> 2);
3516 extra_index += @truncate(u1, flags >> 3);
3745 extra_index += @truncate(u1, flags >> 2); // Align
3746 extra_index += @as(u2, @truncate(u1, flags >> 3)) * 2; // Link section or address space, consists of 2 Refs
35173747
35183748 try scanDecl(&scan_decl_iter, decl_sub_index, flags);
35193749 }
......@@ -3539,10 +3769,10 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
35393769 const zir = namespace.file_scope.zir;
35403770
35413771 // zig fmt: off
3542 const is_pub = (flags & 0b0001) != 0;
3543 const export_bit = (flags & 0b0010) != 0;
3544 const has_align = (flags & 0b0100) != 0;
3545 const has_linksection = (flags & 0b1000) != 0;
3772 const is_pub = (flags & 0b0001) != 0;
3773 const export_bit = (flags & 0b0010) != 0;
3774 const has_align = (flags & 0b0100) != 0;
3775 const has_linksection_or_addrspace = (flags & 0b1000) != 0;
35463776 // zig fmt: on
35473777
35483778 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
35883818 // We create a Decl for it regardless of analysis status.
35893819 const gop = try namespace.decls.getOrPut(gpa, decl_name);
35903820 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);
35923822 if (is_usingnamespace) {
35933823 namespace.usingnamespace_set.putAssumeCapacity(new_decl, is_pub);
35943824 }
......@@ -3625,7 +3855,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
36253855 new_decl.is_exported = is_exported;
36263856 new_decl.is_usingnamespace = is_usingnamespace;
36273857 new_decl.has_align = has_align;
3628 new_decl.has_linksection = has_linksection;
3858 new_decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
36293859 new_decl.zir_decl_index = @intCast(u32, decl_sub_index);
36303860 new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive.
36313861 return;
......@@ -3642,7 +3872,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
36423872 decl.is_exported = is_exported;
36433873 decl.is_usingnamespace = is_usingnamespace;
36443874 decl.has_align = has_align;
3645 decl.has_linksection = has_linksection;
3875 decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
36463876 decl.zir_decl_index = @intCast(u32, decl_sub_index);
36473877 if (decl.getFunction()) |_| {
36483878 switch (mod.comp.bin_file.tag) {
......@@ -3660,7 +3890,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
36603890 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
36613891 },
36623892 .plan9 => {
3663 // TODO implement for plan9
3893 // TODO Look into detecting when this would be unnecessary by storing enough state
3894 // in `Decl` to notice that the line number did not change.
3895 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
36643896 },
36653897 .c, .wasm, .spirv => {},
36663898 }
......@@ -3706,13 +3938,6 @@ pub fn clearDecl(
37063938 dep.removeDependency(decl);
37073939 if (outdated_decls) |map| {
37083940 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));
37163941 }
37173942 }
37183943 decl.dependants.clearRetainingCapacity();
......@@ -3740,7 +3965,7 @@ pub fn clearDecl(
37403965 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
37413966 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
37423967 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
3743 .c => .{ .c = link.File.C.DeclBlock.empty },
3968 .c => .{ .c = {} },
37443969 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
37453970 .spirv => .{ .spirv = {} },
37463971 };
......@@ -3749,7 +3974,7 @@ pub fn clearDecl(
37493974 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
37503975 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
37513976 .plan9 => .{ .plan9 = {} },
3752 .c => .{ .c = link.File.C.FnBlock.empty },
3977 .c => .{ .c = {} },
37533978 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
37543979 .spirv => .{ .spirv = .{} },
37553980 };
......@@ -3779,10 +4004,13 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
37794004 // about the Decl in the first place.
37804005 // Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we
37814006 // must call `freeDecl` in the linker backend now.
3782 if (decl.has_tv) {
3783 if (decl.ty.hasCodeGenBits()) {
3784 mod.comp.bin_file.freeDecl(decl);
3785 }
4007 switch (mod.comp.bin_file.tag) {
4008 .c => {}, // this linker backend has already migrated to the new API
4009 else => if (decl.has_tv) {
4010 if (decl.ty.hasCodeGenBits()) {
4011 mod.comp.bin_file.freeDecl(decl);
4012 }
4013 },
37864014 }
37874015
37884016 const dependants = decl.dependants.keys();
......@@ -3800,7 +4028,7 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
38004028
38014029pub fn deleteAnonDecl(mod: *Module, scope: *Scope, decl: *Decl) void {
38024030 log.debug("deleteAnonDecl {*} ({s})", .{ decl, decl.name });
3803 const scope_decl = scope.ownerDecl().?;
4031 const scope_decl = scope.srcDecl().?;
38044032 assert(scope_decl.namespace.anon_decls.swapRemove(decl));
38054033 decl.destroy(mod);
38064034}
......@@ -3844,22 +4072,21 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {
38444072 mod.gpa.free(kv.value);
38454073}
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 {
38484076 const tracy = trace(@src());
38494077 defer tracy.end();
38504078
38514079 const gpa = mod.gpa;
38524080
3853 // Use the Decl's arena for function memory.
3854 var arena = decl.value_arena.?.promote(gpa);
3855 defer decl.value_arena.?.* = arena.state;
3856
3857 const fn_ty = decl.ty;
4081 // Use the Decl's arena for captured values.
4082 var decl_arena = decl.value_arena.?.promote(gpa);
4083 defer decl.value_arena.?.* = decl_arena.state;
38584084
38594085 var sema: Sema = .{
38604086 .mod = mod,
38614087 .gpa = gpa,
3862 .arena = &arena.allocator,
4088 .arena = arena,
4089 .perm_arena = &decl_arena.allocator,
38634090 .code = decl.namespace.file_scope.zir,
38644091 .owner_decl = decl,
38654092 .namespace = decl.namespace,
......@@ -3874,10 +4101,14 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
38744101 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
38754102 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
38774107 var inner_block: Scope.Block = .{
38784108 .parent = null,
38794109 .sema = &sema,
38804110 .src_decl = decl,
4111 .wip_capture_scope = wip_captures.scope,
38814112 .instructions = .{},
38824113 .inlining = null,
38834114 .is_comptime = false,
......@@ -3893,6 +4124,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
38934124 // This could be a generic function instantiation, however, in which case we need to
38944125 // map the comptime parameters to constant values and only emit arg AIR instructions
38954126 // for the runtime ones.
4127 const fn_ty = decl.ty;
38964128 const runtime_params_len = @intCast(u32, fn_ty.fnParamLen());
38974129 try inner_block.instructions.ensureTotalCapacity(gpa, runtime_params_len);
38984130 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 {
39524184 else => |e| return e,
39534185 };
39544186
4187 try wip_captures.finalize();
4188
39554189 // Copy the block into place and mark that as the main block.
39564190 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
39574191 inner_block.instructions.items.len);
......@@ -3977,6 +4211,12 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
39774211 if (mod.failed_decls.fetchSwapRemove(decl)) |kv| {
39784212 kv.value.destroy(mod.gpa);
39794213 }
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 }
39804220 if (mod.emit_h) |emit_h| {
39814221 if (emit_h.failed_decls.fetchSwapRemove(decl)) |kv| {
39824222 kv.value.destroy(mod.gpa);
......@@ -3986,7 +4226,7 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
39864226 decl.analysis = .outdated;
39874227}
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 {
39904230 // If we have emit-h then we must allocate a bigger structure to store the emit-h state.
39914231 const new_decl: *Decl = if (mod.emit_h != null) blk: {
39924232 const parent_struct = try mod.gpa.create(DeclPlusEmitH);
......@@ -4008,15 +4248,17 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
40084248 .val = undefined,
40094249 .align_val = undefined,
40104250 .linksection_val = undefined,
4251 .@"addrspace" = undefined,
40114252 .analysis = .unreferenced,
40124253 .deletion_flag = false,
40134254 .zir_decl_index = 0,
4255 .src_scope = src_scope,
40144256 .link = switch (mod.comp.bin_file.tag) {
40154257 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
40164258 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
40174259 .macho => .{ .macho = link.File.MachO.TextBlock.empty },
40184260 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
4019 .c => .{ .c = link.File.C.DeclBlock.empty },
4261 .c => .{ .c = {} },
40204262 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
40214263 .spirv => .{ .spirv = {} },
40224264 },
......@@ -4025,18 +4267,19 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: Ast.
40254267 .elf => .{ .elf = link.File.Elf.SrcFn.empty },
40264268 .macho => .{ .macho = link.File.MachO.SrcFn.empty },
40274269 .plan9 => .{ .plan9 = {} },
4028 .c => .{ .c = link.File.C.FnBlock.empty },
4270 .c => .{ .c = {} },
40294271 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
40304272 .spirv => .{ .spirv = .{} },
40314273 },
40324274 .generation = 0,
40334275 .is_pub = false,
40344276 .is_exported = false,
4035 .has_linksection = false,
4277 .has_linksection_or_addrspace = false,
40364278 .has_align = false,
40374279 .alive = false,
40384280 .is_usingnamespace = false,
40394281 };
4282
40404283 return new_decl;
40414284}
40424285
......@@ -4051,7 +4294,7 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged
40514294 }
40524295
40534296 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);
40554298 gop.key_ptr.* = try mod.gpa.dupe(u8, name);
40564299 gop.value_ptr.* = @intCast(ErrorInt, mod.error_name_list.items.len);
40574300 mod.error_name_list.appendAssumeCapacity(gop.key_ptr.*);
......@@ -4063,34 +4306,44 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged
40634306
40644307pub fn analyzeExport(
40654308 mod: *Module,
4066 scope: *Scope,
4309 block: *Scope.Block,
40674310 src: LazySrcLoc,
4068 borrowed_symbol_name: []const u8,
4311 borrowed_options: std.builtin.ExportOptions,
40694312 exported_decl: *Decl,
40704313) !void {
40714314 try mod.ensureDeclAnalyzed(exported_decl);
40724315 switch (exported_decl.ty.zigTypeTag()) {
40734316 .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}),
40754318 }
40764319
4077 try mod.decl_exports.ensureUnusedCapacity(mod.gpa, 1);
4078 try mod.export_owners.ensureUnusedCapacity(mod.gpa, 1);
4320 const gpa = mod.gpa;
4321
4322 try mod.decl_exports.ensureUnusedCapacity(gpa, 1);
4323 try mod.export_owners.ensureUnusedCapacity(gpa, 1);
4324
4325 const new_export = try gpa.create(Export);
4326 errdefer gpa.destroy(new_export);
40794327
4080 const new_export = try mod.gpa.create(Export);
4081 errdefer mod.gpa.destroy(new_export);
4328 const symbol_name = try gpa.dupe(u8, borrowed_options.name);
4329 errdefer gpa.free(symbol_name);
40824330
4083 const symbol_name = try mod.gpa.dupe(u8, borrowed_symbol_name);
4084 errdefer mod.gpa.free(symbol_name);
4331 const section: ?[]const u8 = if (borrowed_options.section) |s| try gpa.dupe(u8, s) else null;
4332 errdefer if (section) |s| gpa.free(s);
40854333
4086 const owner_decl = scope.ownerDecl().?;
4334 const src_decl = block.src_decl;
4335 const owner_decl = block.sema.owner_decl;
40874336
40884337 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,
40904339 });
40914340
40924341 new_export.* = .{
4093 .options = .{ .name = symbol_name },
4342 .options = .{
4343 .name = symbol_name,
4344 .linkage = borrowed_options.linkage,
4345 .section = section,
4346 },
40944347 .src = src,
40954348 .link = switch (mod.comp.bin_file.tag) {
40964349 .coff => .{ .coff = {} },
......@@ -4102,6 +4355,7 @@ pub fn analyzeExport(
41024355 .spirv => .{ .spirv = {} },
41034356 },
41044357 .owner_decl = owner_decl,
4358 .src_decl = src_decl,
41054359 .exported_decl = exported_decl,
41064360 .status = .in_progress,
41074361 };
......@@ -4111,18 +4365,18 @@ pub fn analyzeExport(
41114365 if (!eo_gop.found_existing) {
41124366 eo_gop.value_ptr.* = &[0]*Export{};
41134367 }
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);
41154369 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
41184372 // Add to exported_decl table.
41194373 const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl);
41204374 if (!de_gop.found_existing) {
41214375 de_gop.value_ptr.* = &[0]*Export{};
41224376 }
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);
41244378 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);
41264380}
41274381
41284382/// Takes ownership of `name` even if it returns an error.
......@@ -4132,39 +4386,43 @@ pub fn createAnonymousDeclNamed(
41324386 typed_value: TypedValue,
41334387 name: [:0]u8,
41344388) !*Decl {
4135 return mod.createAnonymousDeclFromDeclNamed(scope.ownerDecl().?, typed_value, name);
4389 return mod.createAnonymousDeclFromDeclNamed(scope.srcDecl().?, scope.srcScope(), typed_value, name);
41364390}
41374391
41384392pub 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);
41404394}
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 {
41434397 const name_index = mod.getNextAnonNameIndex();
41444398 const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{
4145 owner_decl.name, name_index,
4399 src_decl.name, name_index,
41464400 });
4147 return mod.createAnonymousDeclFromDeclNamed(owner_decl, tv, name);
4401 return mod.createAnonymousDeclFromDeclNamed(src_decl, src_scope, tv, name);
41484402}
41494403
41504404/// Takes ownership of `name` even if it returns an error.
41514405pub fn createAnonymousDeclFromDeclNamed(
41524406 mod: *Module,
4153 owner_decl: *Decl,
4407 src_decl: *Decl,
4408 src_scope: ?*CaptureScope,
41544409 typed_value: TypedValue,
41554410 name: [:0]u8,
41564411) !*Decl {
41574412 errdefer mod.gpa.free(name);
41584413
4159 const namespace = owner_decl.namespace;
4414 const namespace = src_decl.namespace;
41604415 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
41644419 new_decl.name = name;
4165 new_decl.src_line = owner_decl.src_line;
4420 new_decl.src_line = src_decl.src_line;
41664421 new_decl.ty = typed_value.ty;
41674422 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
41684426 new_decl.has_tv = true;
41694427 new_decl.analysis = .complete;
41704428 new_decl.generation = mod.generation;
......@@ -4305,61 +4563,6 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) Co
43054563 return error.AnalysisFail;
43064564}
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
43634566pub fn optionalType(arena: *Allocator, child_type: Type) Allocator.Error!Type {
43644567 switch (child_type.tag()) {
43654568 .single_const_pointer => return Type.Tag.optional_single_const_pointer.create(
......@@ -4689,7 +4892,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
46894892 const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file;
46904893 const builtin_namespace = builtin_file.root_decl.?.namespace;
46914894 const decl = builtin_namespace.decls.get("test_functions").?;
4692 var buf: Type.Payload.ElemType = undefined;
4895 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
46934896 const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType();
46944897
46954898 const array_decl = d: {
......@@ -4700,7 +4903,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
47004903 const arena = &new_decl_arena.allocator;
47014904
47024905 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, .{
47044907 .ty = try Type.Tag.array.create(arena, .{
47054908 .len = test_fn_vals.len,
47064909 .elem_type = try tmp_test_fn_ty.copy(arena),
......@@ -4713,7 +4916,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
47134916 var name_decl_arena = std.heap.ArenaAllocator.init(gpa);
47144917 errdefer name_decl_arena.deinit();
47154918 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, .{
47174920 .ty = try Type.Tag.array_u8.create(&name_decl_arena.allocator, bytes.len),
47184921 .val = try Value.Tag.bytes.create(&name_decl_arena.allocator, bytes),
47194922 });
src/Package.zig+10-7
......@@ -99,19 +99,22 @@ pub fn destroy(pkg: *Package, gpa: *Allocator) void {
9999 }
100100 }
101101
102 {
103 var it = pkg.table.keyIterator();
104 while (it.next()) |key| {
105 gpa.free(key.*);
106 }
102 pkg.deinitTable(gpa);
103 gpa.destroy(pkg);
104}
105
106/// Only frees memory associated with the table.
107pub fn deinitTable(pkg: *Package, gpa: *Allocator) void {
108 var it = pkg.table.keyIterator();
109 while (it.next()) |key| {
110 gpa.free(key.*);
107111 }
108112
109113 pkg.table.deinit(gpa);
110 gpa.destroy(pkg);
111114}
112115
113116pub 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);
115118 const name_dupe = try mem.dupe(gpa, u8, name);
116119 pkg.table.putAssumeCapacityNoClobber(name_dupe, package);
117120}
src/Sema.zig+2886-736
......@@ -8,8 +8,12 @@
88mod: *Module,
99/// Alias to `mod.gpa`.
1010gpa: *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.
1213arena: *Allocator,
14/// Points to the arena allocator for the owner_decl.
15/// This arena will persist until the decl is invalidated.
16perm_arena: *Allocator,
1317code: Zir,
1418air_instructions: std.MultiArrayList(Air.Inst) = .{},
1519air_extra: std.ArrayListUnmanaged(u32) = .{},
......@@ -80,9 +84,13 @@ const Scope = Module.Scope;
8084const CompileError = Module.CompileError;
8185const SemaError = Module.SemaError;
8286const Decl = Module.Decl;
87const CaptureScope = Module.CaptureScope;
88const WipCaptureScope = Module.WipCaptureScope;
8389const LazySrcLoc = Module.LazySrcLoc;
8490const RangeSet = @import("RangeSet.zig");
8591const target_util = @import("target.zig");
92const Package = @import("Package.zig");
93const crash_report = @import("crash_report.zig");
8694
8795pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref);
8896
......@@ -128,15 +136,34 @@ pub fn analyzeBody(
128136) CompileError!Zir.Inst.Index {
129137 // 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
131151 const map = &block.sema.inst_map;
132152 const tags = block.sema.code.instructions.items(.tag);
133153 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
135161 // We use a while(true) loop here to avoid a redundant way of breaking out of
136162 // the loop. The only way to break out of the loop is with a `noreturn`
137163 // instruction.
138164 var i: usize = 0;
139 while (true) {
165 const result = while (true) {
166 crash_info.setBodyIndex(i);
140167 const inst = body[i];
141168 const air_inst: Air.Inst.Ref = switch (tags[inst]) {
142169 // zig fmt: off
......@@ -164,11 +191,8 @@ pub fn analyzeBody(
164191 .bool_br_and => try sema.zirBoolBr(block, inst, false),
165192 .bool_br_or => try sema.zirBoolBr(block, inst, true),
166193 .c_import => try sema.zirCImport(block, inst),
167 .call => try sema.zirCall(block, inst, .auto, false),
168 .call_chkused => try sema.zirCall(block, inst, .auto, true),
169 .call_compile_time => try sema.zirCall(block, inst, .compile_time, false),
170 .call_nosuspend => try sema.zirCall(block, inst, .no_async, false),
171 .call_async => try sema.zirCall(block, inst, .async_kw, false),
194 .call => try sema.zirCall(block, inst),
195 .closure_get => try sema.zirClosureGet(block, inst),
172196 .cmp_lt => try sema.zirCmp(block, inst, .lt),
173197 .cmp_lte => try sema.zirCmp(block, inst, .lte),
174198 .cmp_eq => try sema.zirCmpEq(block, inst, .eq, .cmp_eq),
......@@ -201,6 +225,8 @@ pub fn analyzeBody(
201225 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
202226 .field_val => try sema.zirFieldVal(block, inst),
203227 .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),
204230 .func => try sema.zirFunc(block, inst, false),
205231 .func_inferred => try sema.zirFunc(block, inst, true),
206232 .import => try sema.zirImport(block, inst),
......@@ -222,12 +248,10 @@ pub fn analyzeBody(
222248 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),
223249 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),
224250 .optional_type => try sema.zirOptionalType(block, inst),
225 .param_type => try sema.zirParamType(block, inst),
226251 .ptr_type => try sema.zirPtrType(block, inst),
227252 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
228253 .ref => try sema.zirRef(block, inst),
229254 .ret_err_value_code => try sema.zirRetErrValueCode(block, inst),
230 .shl => try sema.zirShl(block, inst),
231255 .shr => try sema.zirShr(block, inst),
232256 .slice_end => try sema.zirSliceEnd(block, inst),
233257 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
......@@ -300,9 +324,6 @@ pub fn analyzeBody(
300324 .div_exact => try sema.zirDivExact(block, inst),
301325 .div_floor => try sema.zirDivFloor(block, inst),
302326 .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),
306327 .shr_exact => try sema.zirShrExact(block, inst),
307328 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),
308329 .offset_of => try sema.zirOffsetOf(block, inst),
......@@ -314,14 +335,11 @@ pub fn analyzeBody(
314335 .select => try sema.zirSelect(block, inst),
315336 .atomic_load => try sema.zirAtomicLoad(block, inst),
316337 .atomic_rmw => try sema.zirAtomicRmw(block, inst),
317 .atomic_store => try sema.zirAtomicStore(block, inst),
318338 .mul_add => try sema.zirMulAdd(block, inst),
319339 .builtin_call => try sema.zirBuiltinCall(block, inst),
320340 .field_ptr_type => try sema.zirFieldPtrType(block, inst),
321341 .field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
322342 .maximum => try sema.zirMaximum(block, inst),
323 .memcpy => try sema.zirMemcpy(block, inst),
324 .memset => try sema.zirMemset(block, inst),
325343 .minimum => try sema.zirMinimum(block, inst),
326344 .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
327345 .@"resume" => try sema.zirResume(block, inst),
......@@ -343,32 +361,38 @@ pub fn analyzeBody(
343361 .trunc => try sema.zirUnaryMath(block, inst),
344362 .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),
349364 .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent),
350365 .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon),
351366 .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func),
352367
353 .add => try sema.zirArithmetic(block, inst),
354 .addwrap => try sema.zirArithmetic(block, inst),
355 .div => try sema.zirArithmetic(block, inst),
356 .mod_rem => try sema.zirArithmetic(block, inst),
357 .mul => try sema.zirArithmetic(block, inst),
358 .mulwrap => try sema.zirArithmetic(block, inst),
359 .sub => try sema.zirArithmetic(block, inst),
360 .subwrap => try sema.zirArithmetic(block, inst),
368 .add => try sema.zirArithmetic(block, inst, .add),
369 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),
370 .add_sat => try sema.zirArithmetic(block, inst, .add_sat),
371 .div => try sema.zirArithmetic(block, inst, .div),
372 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
373 .mod => try sema.zirArithmetic(block, inst, .mod),
374 .rem => try sema.zirArithmetic(block, inst, .rem),
375 .mul => try sema.zirArithmetic(block, inst, .mul),
376 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),
377 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat),
378 .sub => try sema.zirArithmetic(block, inst, .sub),
379 .subwrap => try sema.zirArithmetic(block, inst, .subwrap),
380 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat),
381
382 .shl => try sema.zirShl(block, inst, .shl),
383 .shl_exact => try sema.zirShl(block, inst, .shl_exact),
384 .shl_sat => try sema.zirShl(block, inst, .shl_sat),
361385
362386 // Instructions that we know to *always* be noreturn based solely on their tag.
363387 // These functions match the return type of analyzeBody so that we can
364388 // tail call them here.
365 .compile_error => return sema.zirCompileError(block, inst),
366 .ret_coerce => return sema.zirRetCoerce(block, inst),
367 .ret_node => return sema.zirRetNode(block, inst),
368 .ret_load => return sema.zirRetLoad(block, inst),
369 .ret_err_value => return sema.zirRetErrValue(block, inst),
370 .@"unreachable" => return sema.zirUnreachable(block, inst),
371 .panic => return sema.zirPanic(block, inst),
389 .compile_error => break sema.zirCompileError(block, inst),
390 .ret_coerce => break sema.zirRetCoerce(block, inst),
391 .ret_node => break sema.zirRetNode(block, inst),
392 .ret_load => break sema.zirRetLoad(block, inst),
393 .ret_err_value => break sema.zirRetErrValue(block, inst),
394 .@"unreachable" => break sema.zirUnreachable(block, inst),
395 .panic => break sema.zirPanic(block, inst),
372396 // zig fmt: on
373397
374398 // Instructions that we know can *never* be noreturn based solely on
......@@ -377,7 +401,9 @@ pub fn analyzeBody(
377401 // We also know that they cannot be referenced later, so we avoid
378402 // putting them into the map.
379403 .breakpoint => {
380 try sema.zirBreakpoint(block, inst);
404 if (!block.is_comptime) {
405 _ = try block.addNoOp(.breakpoint);
406 }
381407 i += 1;
382408 continue;
383409 },
......@@ -411,6 +437,11 @@ pub fn analyzeBody(
411437 i += 1;
412438 continue;
413439 },
440 .atomic_store => {
441 try sema.zirAtomicStore(block, inst);
442 i += 1;
443 continue;
444 },
414445 .store => {
415446 try sema.zirStore(block, inst);
416447 i += 1;
......@@ -451,6 +482,11 @@ pub fn analyzeBody(
451482 i += 1;
452483 continue;
453484 },
485 .export_value => {
486 try sema.zirExportValue(block, inst);
487 i += 1;
488 continue;
489 },
454490 .set_align_stack => {
455491 try sema.zirSetAlignStack(block, inst);
456492 i += 1;
......@@ -491,34 +527,59 @@ pub fn analyzeBody(
491527 i += 1;
492528 continue;
493529 },
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
495546 // Special case instructions to handle comptime control flow.
496547 .@"break" => {
497548 if (block.is_comptime) {
498 return inst; // same as break_inline
549 break inst; // same as break_inline
499550 } else {
500 return sema.zirBreak(block, inst);
551 break sema.zirBreak(block, inst);
501552 }
502553 },
503 .break_inline => return inst,
554 .break_inline => break inst,
504555 .repeat => {
505556 if (block.is_comptime) {
506557 // Send comptime control flow back to the beginning of this block.
507558 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
508559 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 }
509565 i = 0;
510566 continue;
511567 } else {
512568 const src_node = sema.code.instructions.items(.data)[inst].node;
513569 const src: LazySrcLoc = .{ .node_offset = src_node };
514570 try sema.requireRuntimeBlock(block, src);
515 return always_noreturn;
571 break always_noreturn;
516572 }
517573 },
518574 .repeat_inline => {
519575 // Send comptime control flow back to the beginning of this block.
520576 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
521577 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 }
522583 i = 0;
523584 continue;
524585 },
......@@ -533,7 +594,7 @@ pub fn analyzeBody(
533594 if (inst == break_data.block_inst) {
534595 break :blk sema.resolveInst(break_data.operand);
535596 } else {
536 return break_inst;
597 break break_inst;
537598 }
538599 },
539600 .block => blk: {
......@@ -547,7 +608,7 @@ pub fn analyzeBody(
547608 if (inst == break_data.block_inst) {
548609 break :blk sema.resolveInst(break_data.operand);
549610 } else {
550 return break_inst;
611 break break_inst;
551612 }
552613 },
553614 .block_inline => blk: {
......@@ -560,11 +621,11 @@ pub fn analyzeBody(
560621 if (inst == break_data.block_inst) {
561622 break :blk sema.resolveInst(break_data.operand);
562623 } else {
563 return break_inst;
624 break break_inst;
564625 }
565626 },
566627 .condbr => blk: {
567 if (!block.is_comptime) return sema.zirCondbr(block, inst);
628 if (!block.is_comptime) break sema.zirCondbr(block, inst);
568629 // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220
569630 const inst_data = datas[inst].pl_node;
570631 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
......@@ -578,7 +639,7 @@ pub fn analyzeBody(
578639 if (inst == break_data.block_inst) {
579640 break :blk sema.resolveInst(break_data.operand);
580641 } else {
581 return break_inst;
642 break break_inst;
582643 }
583644 },
584645 .condbr_inline => blk: {
......@@ -594,15 +655,22 @@ pub fn analyzeBody(
594655 if (inst == break_data.block_inst) {
595656 break :blk sema.resolveInst(break_data.operand);
596657 } else {
597 return break_inst;
658 break break_inst;
598659 }
599660 },
600661 };
601662 if (sema.typeOf(air_inst).isNoReturn())
602 return always_noreturn;
663 break always_noreturn;
603664 try map.put(sema.gpa, inst, air_inst);
604665 i += 1;
666 } else unreachable;
667
668 if (!wip_captures.finalized) {
669 try wip_captures.finalize();
670 block.wip_capture_scope = parent_capture_scope;
605671 }
672
673 return result;
606674}
607675
608676fn 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
614682 .struct_decl => return sema.zirStructDecl( block, extended, inst),
615683 .enum_decl => return sema.zirEnumDecl( block, extended),
616684 .union_decl => return sema.zirUnionDecl( block, extended, inst),
685 .opaque_decl => return sema.zirOpaqueDecl( block, extended, inst),
617686 .ret_ptr => return sema.zirRetPtr( block, extended),
618687 .ret_type => return sema.zirRetType( block, extended),
619688 .this => return sema.zirThis( block, extended),
......@@ -636,10 +705,6 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
636705 .c_define => return sema.zirCDefine( block, extended),
637706 .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),
638707 .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),
643708 // zig fmt: on
644709 }
645710}
......@@ -828,9 +893,18 @@ fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) Compile
828893 return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{});
829894}
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
831904/// Appropriate to call when the coercion has already been done by result
832905/// location semantics. Asserts the value fits in the provided `Int` type.
833906/// Only supports `Int` types 64 bits or less.
907/// TODO don't ever call this since we're migrating towards ResultLoc.coerced_ty.
834908fn resolveAlreadyCoercedInt(
835909 sema: *Sema,
836910 block: *Scope.Block,
......@@ -847,6 +921,23 @@ fn resolveAlreadyCoercedInt(
847921 }
848922}
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
850941fn resolveInt(
851942 sema: *Sema,
852943 block: *Scope.Block,
......@@ -900,10 +991,38 @@ fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) C
900991}
901992
902993fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
903 _ = inst;
904994 const tracy = trace(@src());
905995 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;
9071026}
9081027
9091028pub fn analyzeStructDecl(
......@@ -981,7 +1100,6 @@ fn zirStructDecl(
9811100}
9821101
9831102fn createTypeName(sema: *Sema, block: *Scope.Block, name_strategy: Zir.Inst.NameStrategy) ![:0]u8 {
984 _ = block;
9851103 switch (name_strategy) {
9861104 .anon => {
9871105 // 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
9901108 // semantically analyzed.
9911109 const name_index = sema.mod.getNextAnonNameIndex();
9921110 return std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{
993 sema.owner_decl.name, name_index,
1111 block.src_decl.name, name_index,
9941112 });
9951113 },
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)),
9971115 .func => {
9981116 const name_index = sema.mod.getNextAnonNameIndex();
9991117 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,
10011119 });
10021120 log.warn("TODO: handle NameStrategy.func correctly instead of using anon name '{s}'", .{
10031121 name,
......@@ -1053,17 +1171,6 @@ fn zirEnumDecl(
10531171 var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
10541172 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
10671174 const enum_obj = try new_decl_arena.allocator.create(Module.EnumFull);
10681175 const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumFull);
10691176 enum_ty_payload.* = .{
......@@ -1082,7 +1189,7 @@ fn zirEnumDecl(
10821189
10831190 enum_obj.* = .{
10841191 .owner_decl = new_decl,
1085 .tag_ty = tag_ty,
1192 .tag_ty = Type.initTag(.@"null"),
10861193 .fields = .{},
10871194 .values = .{},
10881195 .node_offset = src.node_offset,
......@@ -1110,16 +1217,6 @@ fn zirEnumDecl(
11101217 const body_end = extra_index;
11111218 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
11231220 {
11241221 // We create a block for the field type instructions because they
11251222 // may need to reference Decls from inside the enum namespace.
......@@ -1142,10 +1239,14 @@ fn zirEnumDecl(
11421239 sema.func = null;
11431240 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
11451245 var enum_block: Scope.Block = .{
11461246 .parent = null,
11471247 .sema = sema,
11481248 .src_decl = new_decl,
1249 .wip_capture_scope = wip_captures.scope,
11491250 .instructions = .{},
11501251 .inlining = null,
11511252 .is_comptime = true,
......@@ -1155,7 +1256,30 @@ fn zirEnumDecl(
11551256 if (body.len != 0) {
11561257 _ = try sema.analyzeBody(&enum_block, body);
11571258 }
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 });
11581281 }
1282
11591283 var bit_bag_index: usize = body_end;
11601284 var cur_bit_bag: u32 = undefined;
11611285 var field_i: u32 = 0;
......@@ -1194,10 +1318,10 @@ fn zirEnumDecl(
11941318 // that points to this default value expression rather than the struct.
11951319 // But only resolve the source location if we need to emit a compile error.
11961320 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 });
11981322 } else if (any_values) {
11991323 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 });
12011325 }
12021326 }
12031327
......@@ -1237,7 +1361,13 @@ fn zirUnionDecl(
12371361 errdefer new_decl_arena.deinit();
12381362
12391363 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);
12411371 const union_val = try Value.Tag.ty.create(&new_decl_arena.allocator, union_ty);
12421372 const type_name = try sema.createTypeName(block, small.name_strategy);
12431373 const new_decl = try sema.mod.createAnonymousDeclNamed(&block.base, .{
......@@ -1275,20 +1405,14 @@ fn zirUnionDecl(
12751405fn zirOpaqueDecl(
12761406 sema: *Sema,
12771407 block: *Scope.Block,
1408 extended: Zir.Inst.Extended.InstData,
12781409 inst: Zir.Inst.Index,
1279 name_strategy: Zir.Inst.NameStrategy,
12801410) CompileError!Air.Inst.Ref {
12811411 const tracy = trace(@src());
12821412 defer tracy.end();
12831413
1284 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1285 const src = inst_data.src();
1286 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
1287
1288 _ = name_strategy;
1289 _ = inst_data;
1290 _ = src;
1291 _ = extra;
1414 _ = extended;
1415 _ = inst;
12921416 return sema.mod.fail(&block.base, sema.src, "TODO implement zirOpaqueDecl", .{});
12931417}
12941418
......@@ -1349,7 +1473,10 @@ fn zirRetPtr(
13491473 return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty);
13501474 }
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 });
13531480 return block.addTy(.alloc, ptr_type);
13541481}
13551482
......@@ -1466,7 +1593,42 @@ fn zirAllocExtended(
14661593) CompileError!Air.Inst.Ref {
14671594 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
14681595 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);
14701632}
14711633
14721634fn 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
14971659 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
14981660 const var_decl_src = inst_data.src();
14991661 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 });
15011666 try sema.requireRuntimeBlock(block, var_decl_src);
15021667 return block.addTy(.alloc, ptr_type);
15031668}
......@@ -1513,8 +1678,11 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
15131678 if (block.is_comptime) {
15141679 return sema.analyzeComptimeAlloc(block, var_type);
15151680 }
1516 try sema.validateVarType(block, ty_src, var_type);
1517 const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);
1681 try sema.validateVarType(block, ty_src, var_type, false);
1682 const ptr_type = try Type.ptr(sema.arena, .{
1683 .pointee_type = var_type,
1684 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
1685 });
15181686 try sema.requireRuntimeBlock(block, var_decl_src);
15191687 return block.addTy(.alloc, ptr_type);
15201688}
......@@ -1574,7 +1742,10 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
15741742 try sema.mod.declareDeclDependency(sema.owner_decl, decl);
15751743
15761744 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 });
15781749 const final_ptr_ty_inst = try sema.addType(final_ptr_ty);
15791750 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
15931764 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
15941765 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
15951766 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);
15971768 }
15981769 // 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 });
16001774 sema.air_instructions.set(ptr_inst, .{
16011775 .tag = .alloc,
16021776 .data = .{ .ty = final_ptr_ty },
......@@ -1609,19 +1783,82 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
16091783 const tracy = trace(@src());
16101784 defer tracy.end();
16111785
1612 const gpa = sema.gpa;
1613 const mod = sema.mod;
16141786 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();
16161788 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
16171789 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: {
1620 const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
1621 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
1622 const object_ptr = sema.resolveInst(field_ptr_extra.lhs);
1623 break :s sema.typeOf(object_ptr).elemType().castTag(.@"struct").?.data;
1624 };
1812fn validateUnionInitPtr(
1813 sema: *Sema,
1814 block: *Scope.Block,
1815 union_obj: *Module.Union,
1816 init_src: LazySrcLoc,
1817 instrs: []const Zir.Inst.Index,
1818 union_ptr: Air.Inst.Ref,
1819) CompileError!void {
1820 const mod = sema.mod;
1821
1822 if (instrs.len != 1) {
1823 // TODO add note for other field
1824 // TODO add note for union declared here
1825 return mod.fail(&block.base, init_src, "only one union field can be active at once", .{});
1826 }
1827
1828 const field_ptr = instrs[0];
1829 const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
1830 const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node };
1831 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
1832 const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start);
1833 const field_index_big = union_obj.fields.getIndex(field_name) orelse
1834 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
1835 const field_index = @intCast(u32, field_index_big);
1836
1837 // TODO here we need to go back and see if we need to convert the union
1838 // to a comptime-known value. This will involve editing the AIR code we have
1839 // generated so far - in particular deleting some runtime pointer bitcast
1840 // instructions which are not actually needed if the initialization expression
1841 // ends up being comptime-known.
1842
1843 // Otherwise, we set the new union tag now.
1844 const new_tag = try sema.addConstant(
1845 union_obj.tag_ty,
1846 try Value.Tag.enum_field_index.create(sema.arena, field_index),
1847 );
1848
1849 try sema.requireRuntimeBlock(block, init_src);
1850 _ = try block.addBinOp(.set_union_tag, union_ptr, new_tag);
1851}
1852
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
16261863 // Maps field index to field_ptr index of where it was already initialized.
16271864 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
16601897 const template = "missing struct field: {s}";
16611898 const args = .{field_name};
16621899 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);
16641901 } 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);
16661903 }
16671904 }
16681905 if (root_msg) |msg| {
......@@ -1750,7 +1987,11 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
17501987 }
17511988 const ptr = sema.resolveInst(bin_inst.lhs);
17521989 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 });
17541995 // TODO detect when this store should be done at compile-time. For example,
17551996 // if expressions should force it when the condition is compile-time known.
17561997 const src: LazySrcLoc = .unneeded;
......@@ -1797,7 +2038,10 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
17972038 // for the inferred allocation.
17982039 try inferred_alloc.data.stored_inst_list.append(sema.arena, operand);
17992040 // 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 });
18012045 const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr);
18022046 return sema.storePtr(block, src, bitcasted_ptr, operand);
18032047 }
......@@ -1834,45 +2078,6 @@ fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
18342078 return sema.storePtr(block, src, ptr, value);
18352079}
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
18762081fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
18772082 const tracy = trace(@src());
18782083 defer tracy.end();
......@@ -2070,10 +2275,78 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Com
20702275 const tracy = trace(@src());
20712276 defer tracy.end();
20722277
2073 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2074 const src = inst_data.src();
2278 const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
2279 const src = pl_node.src();
2280 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
2281 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
2282
2283 // we check this here to avoid undefined symbols
2284 if (!@import("build_options").have_llvm)
2285 return sema.mod.fail(&parent_block.base, src, "cannot do C import on Zig compiler not built with LLVM-extension", .{});
2286
2287 var c_import_buf = std.ArrayList(u8).init(sema.gpa);
2288 defer c_import_buf.deinit();
2289
2290 var child_block: Scope.Block = .{
2291 .parent = parent_block,
2292 .sema = sema,
2293 .src_decl = parent_block.src_decl,
2294 .wip_capture_scope = parent_block.wip_capture_scope,
2295 .instructions = .{},
2296 .inlining = parent_block.inlining,
2297 .is_comptime = parent_block.is_comptime,
2298 .c_import_buf = &c_import_buf,
2299 };
2300 defer child_block.instructions.deinit(sema.gpa);
2301
2302 _ = try sema.analyzeBody(&child_block, body);
2303
2304 const c_import_res = sema.mod.comp.cImport(c_import_buf.items) catch |err|
2305 return sema.mod.fail(&child_block.base, src, "C import failed: {s}", .{@errorName(err)});
2306
2307 if (c_import_res.errors.len != 0) {
2308 const msg = msg: {
2309 const msg = try sema.mod.errMsg(&child_block.base, src, "C import failed", .{});
2310 errdefer msg.destroy(sema.gpa);
2311
2312 if (!sema.mod.comp.bin_file.options.link_libc)
2313 try sema.mod.errNote(&child_block.base, src, msg, "libc headers not available; compilation does not link against libc", .{});
2314
2315 for (c_import_res.errors) |_| {
2316 // TODO integrate with LazySrcLoc
2317 // try sema.mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]});
2318 // if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)",
2319 // clang_err.line + 1,
2320 // clang_err.column + 1,
2321 }
2322 @import("clang.zig").Stage2ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len);
2323 break :msg msg;
2324 };
2325 return sema.mod.failWithOwnedErrorMsg(&child_block.base, msg);
2326 }
2327 const c_import_pkg = Package.create(
2328 sema.gpa,
2329 null,
2330 c_import_res.out_zig_path,
2331 ) catch |err| switch (err) {
2332 error.OutOfMemory => return error.OutOfMemory,
2333 else => unreachable, // we pass null for root_src_dir_path
2334 };
2335 const std_pkg = sema.mod.main_pkg.table.get("std").?;
2336 const builtin_pkg = sema.mod.main_pkg.table.get("builtin").?;
2337 try c_import_pkg.add(sema.gpa, "builtin", builtin_pkg);
2338 try c_import_pkg.add(sema.gpa, "std", std_pkg);
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);
20772350}
20782351
20792352fn zirSuspendBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -2118,6 +2391,7 @@ fn zirBlock(
21182391 .parent = parent_block,
21192392 .sema = sema,
21202393 .src_decl = parent_block.src_decl,
2394 .wip_capture_scope = parent_block.wip_capture_scope,
21212395 .instructions = .{},
21222396 .label = &label,
21232397 .inlining = parent_block.inlining,
......@@ -2142,8 +2416,12 @@ fn resolveBlockBody(
21422416 body: []const Zir.Inst.Index,
21432417 merges: *Scope.Block.Merges,
21442418) CompileError!Air.Inst.Ref {
2145 _ = try sema.analyzeBody(child_block, body);
2146 return sema.analyzeBlockBody(parent_block, src, child_block, merges);
2419 if (child_block.is_comptime) {
2420 return sema.resolveBody(child_block, body);
2421 } else {
2422 _ = try sema.analyzeBody(child_block, body);
2423 return sema.analyzeBlockBody(parent_block, src, child_block, merges);
2424 }
21472425}
21482426
21492427fn analyzeBlockBody(
......@@ -2256,36 +2534,66 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro
22562534 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
22572535 const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
22582536 const src = inst_data.src();
2259 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2260 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
2537 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2538 const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
22612539 const decl_name = sema.code.nullTerminatedString(extra.decl_name);
22622540 if (extra.namespace != .none) {
22632541 return sema.mod.fail(&block.base, src, "TODO: implement exporting with field access", .{});
22642542 }
2265 const decl = try sema.lookupIdentifier(block, lhs_src, decl_name);
2266 const options = try sema.resolveInstConst(block, rhs_src, extra.options);
2267 const struct_obj = options.ty.castTag(.@"struct").?.data;
2268 const fields = options.val.castTag(.@"struct").?.data[0..struct_obj.fields.count()];
2269 const name_index = struct_obj.fields.getIndex("name").?;
2270 const linkage_index = struct_obj.fields.getIndex("linkage").?;
2271 const section_index = struct_obj.fields.getIndex("section").?;
2272 const export_name = try fields[name_index].toAllocatedBytes(sema.arena);
2273 const linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage);
2543 const decl = try sema.lookupIdentifier(block, operand_src, decl_name);
2544 const options = try sema.resolveExportOptions(block, options_src, extra.options);
2545 try sema.mod.analyzeExport(block, src, options, decl);
2546}
22742547
2275 if (linkage != .Strong) {
2276 return sema.mod.fail(&block.base, src, "TODO: implement exporting with non-strong linkage", .{});
2277 }
2278 if (!fields[section_index].isNull()) {
2279 return sema.mod.fail(&block.base, src, "TODO: implement exporting with linksection", .{});
2280 }
2548fn zirExportValue(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
2549 const tracy = trace(@src());
2550 defer tracy.end();
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);
22832564}
22842565
22852566fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
2567 const mod = sema.mod;
22862568 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 };
22872570 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 };
22892597}
22902598
22912599fn 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
23082616 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand);
23092617}
23102618
2311fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
2312 const tracy = trace(@src());
2313 defer tracy.end();
2619fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
2620 if (block.is_comptime) return;
23142621
2315 const src_node = sema.code.instructions.items(.data)[inst].node;
2316 const src: LazySrcLoc = .{ .node_offset = src_node };
2317 try sema.requireRuntimeBlock(block, src);
2318 _ = try block.addNoOp(.breakpoint);
2319}
2622 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2623 const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
2624 const order = try sema.resolveAtomicOrder(block, order_src, inst_data.operand);
23202625
2321fn zirFence(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
2322 const src_node = sema.code.instructions.items(.data)[inst].node;
2323 const src: LazySrcLoc = .{ .node_offset = src_node };
2324 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirFence", .{});
2626 if (@enumToInt(order) < @enumToInt(std.builtin.AtomicOrder.Acquire)) {
2627 return sema.mod.fail(&block.base, order_src, "atomic ordering must be Acquire or stricter", .{});
2628 }
2629
2630 _ = try block.addInst(.{
2631 .tag = .fence,
2632 .data = .{ .fence = order },
2633 });
23252634}
23262635
23272636fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
......@@ -2489,8 +2798,6 @@ fn zirCall(
24892798 sema: *Sema,
24902799 block: *Scope.Block,
24912800 inst: Zir.Inst.Index,
2492 modifier: std.builtin.CallOptions.Modifier,
2493 ensure_result_used: bool,
24942801) CompileError!Air.Inst.Ref {
24952802 const tracy = trace(@src());
24962803 defer tracy.end();
......@@ -2499,14 +2806,31 @@ fn zirCall(
24992806 const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
25002807 const call_src = inst_data.src();
25012808 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);
2505 // TODO handle function calls of generic functions
2506 const resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len);
2507 for (args) |zir_arg, i| {
2508 // the args are already casted to the result of a param type instruction.
2509 resolved_args[i] = sema.resolveInst(zir_arg);
2817 const func_type = sema.typeOf(func);
2818
2819 // Desugar bound functions here
2820 if (func_type.tag() == .bound_fn) {
2821 const bound_func = try sema.resolveValue(block, func_src, func);
2822 const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data;
2823 func = bound_data.func_inst;
2824 resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len + 1);
2825 resolved_args[0] = bound_data.arg0_inst;
2826 for (args) |zir_arg, i| {
2827 resolved_args[i + 1] = sema.resolveInst(zir_arg);
2828 }
2829 } else {
2830 resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len);
2831 for (args) |zir_arg, i| {
2832 resolved_args[i] = sema.resolveInst(zir_arg);
2833 }
25102834 }
25112835
25122836 return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);
......@@ -2694,10 +3018,14 @@ fn analyzeCall(
26943018 sema.func = module_fn;
26953019 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
26973024 var child_block: Scope.Block = .{
26983025 .parent = null,
26993026 .sema = sema,
27003027 .src_decl = module_fn.owner_decl,
3028 .wip_capture_scope = wip_captures.scope,
27013029 .instructions = .{},
27023030 .label = null,
27033031 .inlining = &inlining,
......@@ -2835,6 +3163,8 @@ fn analyzeCall(
28353163
28363164 // TODO: check whether any external comptime memory was mutated by the
28373165 // 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.
28383168 {
28393169 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
28403170 errdefer arena_allocator.deinit();
......@@ -2845,7 +3175,7 @@ fn analyzeCall(
28453175 }
28463176
28473177 try mod.memoized_calls.put(gpa, memoized_call_key, .{
2848 .val = result_val,
3178 .val = try result_val.copy(arena),
28493179 .arena = arena_allocator.state,
28503180 });
28513181 delete_memoized_call_key = false;
......@@ -2860,6 +3190,9 @@ fn analyzeCall(
28603190
28613191 break :res2 result;
28623192 };
3193
3194 try wip_captures.finalize();
3195
28633196 break :res res2;
28643197 } else if (func_ty_info.is_generic) res: {
28653198 const func_val = try sema.resolveConstValue(block, func_src, func);
......@@ -2942,7 +3275,8 @@ fn analyzeCall(
29423275 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
29433276
29443277 // 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);
29463280 // TODO better names for generic function instantiations
29473281 const name_index = mod.getNextAnonNameIndex();
29483282 new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{
......@@ -2952,7 +3286,8 @@ fn analyzeCall(
29523286 new_decl.is_pub = module_fn.owner_decl.is_pub;
29533287 new_decl.is_exported = module_fn.owner_decl.is_exported;
29543288 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";
29563291 new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index;
29573292 new_decl.alive = true; // This Decl is called at runtime.
29583293 new_decl.has_tv = true;
......@@ -2973,6 +3308,7 @@ fn analyzeCall(
29733308 .mod = mod,
29743309 .gpa = gpa,
29753310 .arena = sema.arena,
3311 .perm_arena = &new_decl_arena.allocator,
29763312 .code = fn_zir,
29773313 .owner_decl = new_decl,
29783314 .namespace = namespace,
......@@ -2985,10 +3321,14 @@ fn analyzeCall(
29853321 };
29863322 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
29883327 var child_block: Scope.Block = .{
29893328 .parent = null,
29903329 .sema = &child_sema,
29913330 .src_decl = new_decl,
3331 .wip_capture_scope = wip_captures.scope,
29923332 .instructions = .{},
29933333 .inlining = null,
29943334 .is_comptime = true,
......@@ -3022,14 +3362,16 @@ fn analyzeCall(
30223362 }
30233363 const arg_src = call_src; // TODO: better source location
30243364 const arg = uncasted_args[arg_i];
3025 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
3026 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
3027 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
3028 } else if (is_comptime) {
3029 return sema.failWithNeededComptime(block, arg_src);
3365 if (is_comptime) {
3366 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
3367 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
3368 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
3369 } else {
3370 return sema.failWithNeededComptime(block, arg_src);
3371 }
30303372 } else if (is_anytype) {
30313373 // 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.
30333375 const child_arg = try child_block.addArg(sema.typeOf(arg), 0);
30343376 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
30353377 }
......@@ -3076,6 +3418,8 @@ fn analyzeCall(
30763418 arg_i += 1;
30773419 }
30783420
3421 try wip_captures.finalize();
3422
30793423 // Populate the Decl ty/val with the function and its type.
30803424 new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator);
30813425 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
34363780 },
34373781 .error_set => {
34383782 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);
34403784 for (lhs_set.names_ptr[0..lhs_set.names_len]) |name| {
34413785 set.putAssumeCapacityNoClobber(name, {});
34423786 }
......@@ -3450,7 +3794,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com
34503794 },
34513795 .error_set => {
34523796 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);
34543798 for (rhs_set.names_ptr[0..rhs_set.names_len]) |name| {
34553799 set.putAssumeCapacity(name, {});
34563800 }
......@@ -3606,7 +3950,11 @@ fn zirOptionalPayloadPtr(
36063950 }
36073951
36083952 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
36113959 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
36123960 if (try pointer_val.pointerDeref(sema.arena)) |val| {
......@@ -3721,7 +4069,11 @@ fn zirErrUnionPayloadPtr(
37214069 return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand_ty.elemType()});
37224070
37234071 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
37264078 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
37274079 if (try pointer_val.pointerDeref(sema.arena)) |val| {
......@@ -4236,6 +4588,19 @@ fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
42364588 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
42374589}
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
42394604fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
42404605 const tracy = trace(@src());
42414606 defer tracy.end();
......@@ -4262,6 +4627,19 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp
42624627 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
42634628}
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
42654643fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
42664644 const tracy = trace(@src());
42674645 defer tracy.end();
......@@ -4275,8 +4653,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
42754653 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
42764654 const operand = sema.resolveInst(extra.rhs);
42774655
4278 const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_type);
4279 _ = try sema.requireIntegerType(block, operand_src, sema.typeOf(operand));
4656 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_type);
4657 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
42804658
42814659 if (try sema.isComptimeKnown(block, operand_src, operand)) {
42824660 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
42844662 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_int'", .{});
42854663 }
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);
42884667}
42894668
42904669fn 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
43384717
43394718 if (try sema.isComptimeKnown(block, operand_src, operand)) {
43404719 return sema.coerce(block, dest_type, operand, operand_src);
4341 } else if (dest_is_comptime_float) {
4720 }
4721 if (dest_is_comptime_float) {
43424722 return sema.mod.fail(&block.base, src, "unable to cast runtime value to 'comptime_float'", .{});
43434723 }
4344
4345 return sema.mod.fail(&block.base, src, "TODO implement analyze widen or shorten float", .{});
4724 const target = sema.mod.getTarget();
4725 const src_bits = operand_ty.floatBits(target);
4726 const dst_bits = dest_type.floatBits(target);
4727 if (dst_bits >= src_bits) {
4728 return sema.coerce(block, dest_type, operand, operand_src);
4729 }
4730 try sema.requireRuntimeBlock(block, operand_src);
4731 return block.addTyOp(.fptrunc, dest_type, operand);
43464732}
43474733
43484734fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -4778,8 +5164,8 @@ fn analyzeSwitch(
47785164 var arena = std.heap.ArenaAllocator.init(gpa);
47795165 defer arena.deinit();
47805166
4781 const min_int = try operand_ty.minInt(&arena, mod.getTarget());
4782 const max_int = try operand_ty.maxInt(&arena, mod.getTarget());
5167 const min_int = try operand_ty.minInt(&arena.allocator, mod.getTarget());
5168 const max_int = try operand_ty.maxInt(&arena.allocator, mod.getTarget());
47835169 if (try range_set.spans(min_int, max_int, operand_ty)) {
47845170 if (special_prong == .@"else") {
47855171 return mod.fail(
......@@ -4971,6 +5357,7 @@ fn analyzeSwitch(
49715357 .parent = block,
49725358 .sema = sema,
49735359 .src_decl = block.src_decl,
5360 .wip_capture_scope = block.wip_capture_scope,
49745361 .instructions = .{},
49755362 .label = &label,
49765363 .inlining = block.inlining,
......@@ -5075,12 +5462,19 @@ fn analyzeSwitch(
50755462 const body = sema.code.extra[extra_index..][0..body_len];
50765463 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
50785468 case_block.instructions.shrinkRetainingCapacity(0);
5469 case_block.wip_capture_scope = wip_captures.scope;
5470
50795471 const item = sema.resolveInst(item_ref);
50805472 // `item` is already guaranteed to be constant known.
50815473
50825474 _ = try sema.analyzeBody(&case_block, body);
50835475
5476 try wip_captures.finalize();
5477
50845478 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
50855479 cases_extra.appendAssumeCapacity(1); // items_len
50865480 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
......@@ -5108,6 +5502,7 @@ fn analyzeSwitch(
51085502 extra_index += items_len;
51095503
51105504 case_block.instructions.shrinkRetainingCapacity(0);
5505 case_block.wip_capture_scope = child_block.wip_capture_scope;
51115506
51125507 var any_ok: Air.Inst.Ref = .none;
51135508
......@@ -5186,11 +5581,18 @@ fn analyzeSwitch(
51865581 var cond_body = case_block.instructions.toOwnedSlice(gpa);
51875582 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
51895587 case_block.instructions.shrinkRetainingCapacity(0);
5588 case_block.wip_capture_scope = wip_captures.scope;
5589
51905590 const body = sema.code.extra[extra_index..][0..body_len];
51915591 extra_index += body_len;
51925592 _ = try sema.analyzeBody(&case_block, body);
51935593
5594 try wip_captures.finalize();
5595
51945596 if (is_first) {
51955597 is_first = false;
51965598 first_else_body = cond_body;
......@@ -5216,9 +5618,16 @@ fn analyzeSwitch(
52165618
52175619 var final_else_body: []const Air.Inst.Index = &.{};
52185620 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
52195624 case_block.instructions.shrinkRetainingCapacity(0);
5625 case_block.wip_capture_scope = wip_captures.scope;
5626
52205627 _ = try sema.analyzeBody(&case_block, special.body);
52215628
5629 try wip_captures.finalize();
5630
52225631 if (is_first) {
52235632 final_else_body = case_block.instructions.items;
52245633 } else {
......@@ -5500,7 +5909,7 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro
55005909 try mod.semaFile(result.file);
55015910 const file_root_decl = result.file.root_decl.?;
55025911 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);
55045913}
55055914
55065915fn 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
55095918 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});
55105919}
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 {
55135927 const tracy = trace(@src());
55145928 defer tracy.end();
55155929
55165930 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5517 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
55185931 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
55195932 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
55205933 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
55215934 const lhs = sema.resolveInst(extra.lhs);
55225935 const rhs = sema.resolveInst(extra.rhs);
55235936
5524 if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
5525 if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
5526 if (lhs_val.isUndef() or rhs_val.isUndef()) {
5527 return sema.addConstUndef(sema.typeOf(lhs));
5528 }
5529 return sema.mod.fail(&block.base, src, "TODO implement comptime shl", .{});
5937 // TODO coerce rhs if air_tag is not shl_sat
5938
5939 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
5940 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
5941
5942 const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
5943 const lhs_ty = sema.typeOf(lhs);
5944
5945 if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
5946 const rhs_val = maybe_rhs_val orelse break :rs rhs_src;
5947 if (rhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
5948
5949 // If rhs is 0, return lhs without doing any calculations.
5950 if (rhs_val.compareWithZero(.eq)) {
5951 return sema.addConstant(lhs_ty, lhs_val);
55305952 }
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);
5534 return block.addBinOp(.shl, lhs, rhs);
5968 // TODO: insert runtime safety check for shl_exact
5969
5970 try sema.requireRuntimeBlock(block, runtime_src);
5971 return block.addBinOp(air_tag, lhs, rhs);
55355972}
55365973
55375974fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -5619,10 +6056,13 @@ fn zirBitwise(
56196056
56206057 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
56216058 if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
5622 if (lhs_val.isUndef() or rhs_val.isUndef()) {
5623 return sema.addConstUndef(resolved_type);
5624 }
5625 return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});
6059 const result_val = switch (air_tag) {
6060 .bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena),
6061 .bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena),
6062 .xor => try lhs_val.bitwiseXor(rhs_val, sema.arena),
6063 else => unreachable,
6064 };
6065 return sema.addConstant(scalar_type, result_val);
56266066 }
56276067 }
56286068
......@@ -5671,38 +6111,36 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
56716111 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
56726112 if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
56736113 const final_len = lhs_info.len + rhs_info.len;
5674 if (lhs_ty.zigTypeTag() == .Pointer) {
5675 var anon_decl = try block.startAnonDecl();
5676 defer anon_decl.deinit();
6114 const is_pointer = lhs_ty.zigTypeTag() == .Pointer;
6115 var anon_decl = try block.startAnonDecl();
6116 defer anon_decl.deinit();
56776117
5678 const lhs_sub_val = (try lhs_val.pointerDeref(anon_decl.arena())).?;
5679 const rhs_sub_val = (try rhs_val.pointerDeref(anon_decl.arena())).?;
5680 const buf = try anon_decl.arena().alloc(Value, final_len);
5681 {
5682 var i: u64 = 0;
5683 while (i < lhs_info.len) : (i += 1) {
5684 const val = try lhs_sub_val.elemValue(sema.arena, i);
5685 buf[i] = try val.copy(anon_decl.arena());
5686 }
6118 const lhs_sub_val = if (is_pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val;
6119 const rhs_sub_val = if (is_pointer) (try rhs_val.pointerDeref(anon_decl.arena())).? else rhs_val;
6120 const buf = try anon_decl.arena().alloc(Value, final_len);
6121 {
6122 var i: u64 = 0;
6123 while (i < lhs_info.len) : (i += 1) {
6124 const val = try lhs_sub_val.elemValue(sema.arena, i);
6125 buf[i] = try val.copy(anon_decl.arena());
56876126 }
5688 {
5689 var i: u64 = 0;
5690 while (i < rhs_info.len) : (i += 1) {
5691 const val = try rhs_sub_val.elemValue(sema.arena, i);
5692 buf[lhs_info.len + i] = try val.copy(anon_decl.arena());
5693 }
6127 }
6128 {
6129 var i: u64 = 0;
6130 while (i < rhs_info.len) : (i += 1) {
6131 const val = try rhs_sub_val.elemValue(sema.arena, i);
6132 buf[lhs_info.len + i] = try val.copy(anon_decl.arena());
56946133 }
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 ));
57046134 }
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));
57066144 } else {
57076145 return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_cat", .{});
57086146 }
......@@ -5742,32 +6180,30 @@ fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
57426180
57436181 const final_len = std.math.mul(u64, mulinfo.len, tomulby) catch return sema.mod.fail(&block.base, rhs_src, "operation results in overflow", .{});
57446182 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
5745 if (lhs_ty.zigTypeTag() == .Pointer) {
5746 var anon_decl = try block.startAnonDecl();
5747 defer anon_decl.deinit();
5748 const lhs_sub_val = (try lhs_val.pointerDeref(anon_decl.arena())).?;
5749
5750 const final_ty = if (mulinfo.sentinel) |sent|
5751 try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type, .sentinel = sent })
5752 else
5753 try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type });
5754
5755 const buf = try anon_decl.arena().alloc(Value, final_len);
5756 var i: u64 = 0;
5757 while (i < tomulby) : (i += 1) {
5758 var j: u64 = 0;
5759 while (j < mulinfo.len) : (j += 1) {
5760 const val = try lhs_sub_val.elemValue(sema.arena, j);
5761 buf[mulinfo.len * i + j] = try val.copy(anon_decl.arena());
5762 }
6183 var anon_decl = try block.startAnonDecl();
6184 defer anon_decl.deinit();
6185 const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val;
6186 const final_ty = if (mulinfo.sentinel) |sent|
6187 try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type, .sentinel = sent })
6188 else
6189 try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type });
6190 const buf = try anon_decl.arena().alloc(Value, final_len);
6191
6192 // the actual loop
6193 var i: u64 = 0;
6194 while (i < tomulby) : (i += 1) {
6195 var j: u64 = 0;
6196 while (j < mulinfo.len) : (j += 1) {
6197 const val = try lhs_sub_val.elemValue(sema.arena, j);
6198 buf[mulinfo.len * i + j] = try val.copy(anon_decl.arena());
57636199 }
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 ));
57696200 }
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 }
57716207 }
57726208 return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_mul", .{});
57736209}
......@@ -5791,11 +6227,15 @@ fn zirNegate(
57916227 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
57926228}
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 {
57956236 const tracy = trace(@src());
57966237 defer tracy.end();
57976238
5798 const tag_override = block.sema.code.instructions.items(.tag)[inst];
57996239 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
58006240 sema.src = .{ .node_offset_bin_op = inst_data.src_node };
58016241 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
58046244 const lhs = sema.resolveInst(extra.lhs);
58056245 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);
58086248}
58096249
58106250fn zirOverflowArithmetic(
......@@ -5821,22 +6261,10 @@ fn zirOverflowArithmetic(
58216261 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{});
58226262}
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
58376264fn analyzeArithmetic(
58386265 sema: *Sema,
58396266 block: *Scope.Block,
6267 /// TODO performance investigation: make this comptime?
58406268 zir_tag: Zir.Inst.Tag,
58416269 lhs: Air.Inst.Ref,
58426270 rhs: Air.Inst.Ref,
......@@ -5854,7 +6282,7 @@ fn analyzeArithmetic(
58546282 lhs_ty.arrayLen(), rhs_ty.arrayLen(),
58556283 });
58566284 }
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", .{});
58586286 } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) {
58596287 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
58606288 lhs_ty, rhs_ty,
......@@ -5897,7 +6325,9 @@ fn analyzeArithmetic(
58976325 };
58986326
58996327 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 });
59016331 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
59026332 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
59036333
......@@ -5917,132 +6347,627 @@ fn analyzeArithmetic(
59176347 });
59186348 }
59196349
5920 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
5921 if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
5922 if (lhs_val.isUndef() or rhs_val.isUndef()) {
5923 return sema.addConstUndef(resolved_type);
5924 }
5925 // incase rhs is 0, simply return lhs without doing any calculations
5926 // TODO Once division is implemented we should throw an error when dividing by 0.
5927 if (rhs_val.compareWithZero(.eq)) {
5928 switch (zir_tag) {
5929 .add, .addwrap, .sub, .subwrap => {
5930 return sema.addConstant(scalar_type, lhs_val);
5931 },
5932 else => {},
6350 const target = sema.mod.getTarget();
6351 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs);
6352 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs);
6353 const rs: struct { src: LazySrcLoc, air_tag: Air.Inst.Tag } = rs: {
6354 switch (zir_tag) {
6355 .add => {
6356 // For integers:
6357 // If either of the operands are zero, then the other operand is
6358 // returned, even if it is undefined.
6359 // If either of the operands are undefined, it's a compile error
6360 // because there is a possible value for which the addition would
6361 // overflow (max_int), causing illegal behavior.
6362 // For floats: either operand being undef makes the result undef.
6363 if (maybe_lhs_val) |lhs_val| {
6364 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
6365 return casted_rhs;
6366 }
59336367 }
5934 }
5935
5936 const value = switch (zir_tag) {
5937 .add => blk: {
5938 const val = if (is_int)
5939 try lhs_val.intAdd(rhs_val, sema.arena)
5940 else
5941 try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena);
5942 break :blk val;
5943 },
5944 .sub => blk: {
5945 const val = if (is_int)
5946 try lhs_val.intSub(rhs_val, sema.arena)
5947 else
5948 try lhs_val.floatSub(rhs_val, scalar_type, sema.arena);
5949 break :blk val;
5950 },
5951 .div => blk: {
5952 const val = if (is_int)
5953 try lhs_val.intDiv(rhs_val, sema.arena)
5954 else
5955 try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena);
5956 break :blk val;
5957 },
5958 .mul => blk: {
5959 const val = if (is_int)
5960 try lhs_val.intMul(rhs_val, sema.arena)
5961 else
5962 try lhs_val.floatMul(rhs_val, scalar_type, sema.arena);
5963 break :blk val;
5964 },
5965 else => return sema.mod.fail(&block.base, src, "TODO implement comptime arithmetic for operand '{s}'", .{@tagName(zir_tag)}),
5966 };
5967
5968 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });
5969
5970 return sema.addConstant(scalar_type, value);
5971 } else {
5972 try sema.requireRuntimeBlock(block, rhs_src);
5973 }
5974 } else {
5975 try sema.requireRuntimeBlock(block, lhs_src);
5976 }
5977
5978 if (zir_tag == .mod_rem) {
5979 const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isFloat();
5980 const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isFloat();
5981 if (dirty_lhs or dirty_rhs) {
5982 return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });
5983 }
5984 }
5985
5986 const air_tag: Air.Inst.Tag = switch (zir_tag) {
5987 .add => .add,
5988 .addwrap => .addwrap,
5989 .sub => .sub,
5990 .subwrap => .subwrap,
5991 .mul => .mul,
5992 .mulwrap => .mulwrap,
5993 .div => .div,
5994 .mod_rem => .rem,
5995 .rem => .rem,
5996 else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}'", .{@tagName(zir_tag)}),
5997 };
5998
5999 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
6000}
6001
6002fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6003 const tracy = trace(@src());
6004 defer tracy.end();
6005
6006 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
6007 const src = inst_data.src();
6008 const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node };
6009 const ptr = sema.resolveInst(inst_data.operand);
6010 return sema.analyzeLoad(block, src, ptr, ptr_src);
6011}
6012
6013fn zirAsm(
6014 sema: *Sema,
6015 block: *Scope.Block,
6016 extended: Zir.Inst.Extended.InstData,
6017 inst: Zir.Inst.Index,
6018) CompileError!Air.Inst.Ref {
6019 const tracy = trace(@src());
6020 defer tracy.end();
6021
6022 const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
6023 const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
6024 const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node };
6025 const outputs_len = @truncate(u5, extended.small);
6026 const inputs_len = @truncate(u5, extended.small >> 5);
6027 const clobbers_len = @truncate(u5, extended.small >> 10);
6028
6029 if (outputs_len > 1) {
6030 return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with more than 1 output", .{});
6031 }
6032
6033 var extra_i = extra.end;
6034 var output_type_bits = extra.data.output_type_bits;
6035
6036 const Output = struct { constraint: []const u8, ty: Type };
6037 const output: ?Output = if (outputs_len == 0) null else blk: {
6038 const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i);
6039 extra_i = output.end;
6040
6041 const is_type = @truncate(u1, output_type_bits) != 0;
6042 output_type_bits >>= 1;
6368 if (maybe_rhs_val) |rhs_val| {
6369 if (rhs_val.isUndef()) {
6370 if (is_int) {
6371 return sema.failWithUseOfUndef(block, rhs_src);
6372 } else {
6373 return sema.addConstUndef(scalar_type);
6374 }
6375 }
6376 if (rhs_val.compareWithZero(.eq)) {
6377 return casted_lhs;
6378 }
6379 }
6380 if (maybe_lhs_val) |lhs_val| {
6381 if (lhs_val.isUndef()) {
6382 if (is_int) {
6383 return sema.failWithUseOfUndef(block, lhs_src);
6384 } else {
6385 return sema.addConstUndef(scalar_type);
6386 }
6387 }
6388 if (maybe_rhs_val) |rhs_val| {
6389 if (is_int) {
6390 return sema.addConstant(
6391 scalar_type,
6392 try lhs_val.intAdd(rhs_val, sema.arena),
6393 );
6394 } else {
6395 return sema.addConstant(
6396 scalar_type,
6397 try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena),
6398 );
6399 }
6400 } else break :rs .{ .src = rhs_src, .air_tag = .add };
6401 } else break :rs .{ .src = lhs_src, .air_tag = .add };
6402 },
6403 .addwrap => {
6404 // Integers only; floats are checked above.
6405 // If either of the operands are zero, the other operand is returned.
6406 // If either of the operands are undefined, the result is undefined.
6407 if (maybe_lhs_val) |lhs_val| {
6408 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
6409 return casted_rhs;
6410 }
6411 }
6412 if (maybe_rhs_val) |rhs_val| {
6413 if (rhs_val.isUndef()) {
6414 return sema.addConstUndef(scalar_type);
6415 }
6416 if (rhs_val.compareWithZero(.eq)) {
6417 return casted_lhs;
6418 }
6419 if (maybe_lhs_val) |lhs_val| {
6420 return sema.addConstant(
6421 scalar_type,
6422 try lhs_val.numberAddWrap(rhs_val, scalar_type, sema.arena, target),
6423 );
6424 } else break :rs .{ .src = lhs_src, .air_tag = .addwrap };
6425 } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };
6426 },
6427 .add_sat => {
6428 // Integers only; floats are checked above.
6429 // If either of the operands are zero, then the other operand is returned.
6430 // If either of the operands are undefined, the result is undefined.
6431 if (maybe_lhs_val) |lhs_val| {
6432 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
6433 return casted_rhs;
6434 }
6435 }
6436 if (maybe_rhs_val) |rhs_val| {
6437 if (rhs_val.isUndef()) {
6438 return sema.addConstUndef(scalar_type);
6439 }
6440 if (rhs_val.compareWithZero(.eq)) {
6441 return casted_lhs;
6442 }
6443 if (maybe_lhs_val) |lhs_val| {
6444 return sema.addConstant(
6445 scalar_type,
6446 try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target),
6447 );
6448 } else break :rs .{ .src = lhs_src, .air_tag = .add_sat };
6449 } else break :rs .{ .src = rhs_src, .air_tag = .add_sat };
6450 },
6451 .sub => {
6452 // For integers:
6453 // If the rhs is zero, then the other operand is
6454 // returned, even if it is undefined.
6455 // If either of the operands are undefined, it's a compile error
6456 // because there is a possible value for which the subtraction would
6457 // overflow, causing illegal behavior.
6458 // For floats: either operand being undef makes the result undef.
6459 if (maybe_rhs_val) |rhs_val| {
6460 if (rhs_val.isUndef()) {
6461 if (is_int) {
6462 return sema.failWithUseOfUndef(block, rhs_src);
6463 } else {
6464 return sema.addConstUndef(scalar_type);
6465 }
6466 }
6467 if (rhs_val.compareWithZero(.eq)) {
6468 return casted_lhs;
6469 }
6470 }
6471 if (maybe_lhs_val) |lhs_val| {
6472 if (lhs_val.isUndef()) {
6473 if (is_int) {
6474 return sema.failWithUseOfUndef(block, lhs_src);
6475 } else {
6476 return sema.addConstUndef(scalar_type);
6477 }
6478 }
6479 if (maybe_rhs_val) |rhs_val| {
6480 if (is_int) {
6481 return sema.addConstant(
6482 scalar_type,
6483 try lhs_val.intSub(rhs_val, sema.arena),
6484 );
6485 } else {
6486 return sema.addConstant(
6487 scalar_type,
6488 try lhs_val.floatSub(rhs_val, scalar_type, sema.arena),
6489 );
6490 }
6491 } else break :rs .{ .src = rhs_src, .air_tag = .sub };
6492 } else break :rs .{ .src = lhs_src, .air_tag = .sub };
6493 },
6494 .subwrap => {
6495 // Integers only; floats are checked above.
6496 // If the RHS is zero, then the other operand is returned, even if it is undefined.
6497 // If either of the operands are undefined, the result is undefined.
6498 if (maybe_rhs_val) |rhs_val| {
6499 if (rhs_val.isUndef()) {
6500 return sema.addConstUndef(scalar_type);
6501 }
6502 if (rhs_val.compareWithZero(.eq)) {
6503 return casted_lhs;
6504 }
6505 }
6506 if (maybe_lhs_val) |lhs_val| {
6507 if (lhs_val.isUndef()) {
6508 return sema.addConstUndef(scalar_type);
6509 }
6510 if (maybe_rhs_val) |rhs_val| {
6511 return sema.addConstant(
6512 scalar_type,
6513 try lhs_val.numberSubWrap(rhs_val, scalar_type, sema.arena, target),
6514 );
6515 } else break :rs .{ .src = rhs_src, .air_tag = .subwrap };
6516 } else break :rs .{ .src = lhs_src, .air_tag = .subwrap };
6517 },
6518 .sub_sat => {
6519 // Integers only; floats are checked above.
6520 // If the RHS is zero, result is LHS.
6521 // If either of the operands are undefined, result is undefined.
6522 if (maybe_rhs_val) |rhs_val| {
6523 if (rhs_val.isUndef()) {
6524 return sema.addConstUndef(scalar_type);
6525 }
6526 if (rhs_val.compareWithZero(.eq)) {
6527 return casted_lhs;
6528 }
6529 }
6530 if (maybe_lhs_val) |lhs_val| {
6531 if (lhs_val.isUndef()) {
6532 return sema.addConstUndef(scalar_type);
6533 }
6534 if (maybe_rhs_val) |rhs_val| {
6535 return sema.addConstant(
6536 scalar_type,
6537 try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target),
6538 );
6539 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };
6540 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
6541 },
6542 .div => {
6543 // For integers:
6544 // If the lhs is zero, then zero is returned regardless of rhs.
6545 // If the rhs is zero, compile error for division by zero.
6546 // If the rhs is undefined, compile error because there is a possible
6547 // value (zero) for which the division would be illegal behavior.
6548 // If the lhs is undefined:
6549 // * if lhs type is signed:
6550 // * if rhs is comptime-known and not -1, result is undefined
6551 // * if rhs is -1 or runtime-known, compile error because there is a
6552 // possible value (-min_int * -1) for which division would be
6553 // illegal behavior.
6554 // * if lhs type is unsigned, undef is returned regardless of rhs.
6555 // For floats:
6556 // If the rhs is zero, compile error for division by zero.
6557 // If the rhs is undefined, compile error because there is a possible
6558 // value (zero) for which the division would be illegal behavior.
6559 // If the lhs is undefined, result is undefined.
6560 if (maybe_lhs_val) |lhs_val| {
6561 if (!lhs_val.isUndef()) {
6562 if (lhs_val.compareWithZero(.eq)) {
6563 return sema.addConstant(scalar_type, Value.zero);
6564 }
6565 }
6566 }
6567 if (maybe_rhs_val) |rhs_val| {
6568 if (rhs_val.isUndef()) {
6569 return sema.failWithUseOfUndef(block, rhs_src);
6570 }
6571 if (rhs_val.compareWithZero(.eq)) {
6572 return sema.failWithDivideByZero(block, rhs_src);
6573 }
6574 }
6575 if (maybe_lhs_val) |lhs_val| {
6576 if (lhs_val.isUndef()) {
6577 if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) {
6578 if (maybe_rhs_val) |rhs_val| {
6579 if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) {
6580 return sema.addConstUndef(scalar_type);
6581 }
6582 }
6583 return sema.failWithUseOfUndef(block, rhs_src);
6584 }
6585 return sema.addConstUndef(scalar_type);
6586 }
60436587
6044 if (!is_type) {
6045 return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with non `->` output", .{});
6588 if (maybe_rhs_val) |rhs_val| {
6589 if (is_int) {
6590 return sema.addConstant(
6591 scalar_type,
6592 try lhs_val.intDiv(rhs_val, sema.arena),
6593 );
6594 } else {
6595 return sema.addConstant(
6596 scalar_type,
6597 try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena),
6598 );
6599 }
6600 } else break :rs .{ .src = rhs_src, .air_tag = .div };
6601 } else break :rs .{ .src = lhs_src, .air_tag = .div };
6602 },
6603 .mul => {
6604 // For integers:
6605 // If either of the operands are zero, the result is zero.
6606 // If either of the operands are one, the result is the other
6607 // operand, even if it is undefined.
6608 // If either of the operands are undefined, it's a compile error
6609 // because there is a possible value for which the addition would
6610 // overflow (max_int), causing illegal behavior.
6611 // For floats: either operand being undef makes the result undef.
6612 if (maybe_lhs_val) |lhs_val| {
6613 if (!lhs_val.isUndef()) {
6614 if (lhs_val.compareWithZero(.eq)) {
6615 return sema.addConstant(scalar_type, Value.zero);
6616 }
6617 if (lhs_val.compare(.eq, Value.one, scalar_type)) {
6618 return casted_rhs;
6619 }
6620 }
6621 }
6622 if (maybe_rhs_val) |rhs_val| {
6623 if (rhs_val.isUndef()) {
6624 if (is_int) {
6625 return sema.failWithUseOfUndef(block, rhs_src);
6626 } else {
6627 return sema.addConstUndef(scalar_type);
6628 }
6629 }
6630 if (rhs_val.compareWithZero(.eq)) {
6631 return sema.addConstant(scalar_type, Value.zero);
6632 }
6633 if (rhs_val.compare(.eq, Value.one, scalar_type)) {
6634 return casted_lhs;
6635 }
6636 if (maybe_lhs_val) |lhs_val| {
6637 if (lhs_val.isUndef()) {
6638 if (is_int) {
6639 return sema.failWithUseOfUndef(block, lhs_src);
6640 } else {
6641 return sema.addConstUndef(scalar_type);
6642 }
6643 }
6644 if (is_int) {
6645 return sema.addConstant(
6646 scalar_type,
6647 try lhs_val.intMul(rhs_val, sema.arena),
6648 );
6649 } else {
6650 return sema.addConstant(
6651 scalar_type,
6652 try lhs_val.floatMul(rhs_val, scalar_type, sema.arena),
6653 );
6654 }
6655 } else break :rs .{ .src = lhs_src, .air_tag = .mul };
6656 } else break :rs .{ .src = rhs_src, .air_tag = .mul };
6657 },
6658 .mulwrap => {
6659 // Integers only; floats are handled above.
6660 // If either of the operands are zero, result is zero.
6661 // If either of the operands are one, result is the other operand.
6662 // If either of the operands are undefined, result is undefined.
6663 if (maybe_lhs_val) |lhs_val| {
6664 if (!lhs_val.isUndef()) {
6665 if (lhs_val.compareWithZero(.eq)) {
6666 return sema.addConstant(scalar_type, Value.zero);
6667 }
6668 if (lhs_val.compare(.eq, Value.one, scalar_type)) {
6669 return casted_rhs;
6670 }
6671 }
6672 }
6673 if (maybe_rhs_val) |rhs_val| {
6674 if (rhs_val.isUndef()) {
6675 return sema.addConstUndef(scalar_type);
6676 }
6677 if (rhs_val.compareWithZero(.eq)) {
6678 return sema.addConstant(scalar_type, Value.zero);
6679 }
6680 if (rhs_val.compare(.eq, Value.one, scalar_type)) {
6681 return casted_lhs;
6682 }
6683 if (maybe_lhs_val) |lhs_val| {
6684 if (lhs_val.isUndef()) {
6685 return sema.addConstUndef(scalar_type);
6686 }
6687 return sema.addConstant(
6688 scalar_type,
6689 try lhs_val.numberMulWrap(rhs_val, scalar_type, sema.arena, target),
6690 );
6691 } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap };
6692 } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };
6693 },
6694 .mul_sat => {
6695 // Integers only; floats are checked above.
6696 // If either of the operands are zero, result is zero.
6697 // If either of the operands are one, result is the other operand.
6698 // If either of the operands are undefined, result is undefined.
6699 if (maybe_lhs_val) |lhs_val| {
6700 if (!lhs_val.isUndef()) {
6701 if (lhs_val.compareWithZero(.eq)) {
6702 return sema.addConstant(scalar_type, Value.zero);
6703 }
6704 if (lhs_val.compare(.eq, Value.one, scalar_type)) {
6705 return casted_rhs;
6706 }
6707 }
6708 }
6709 if (maybe_rhs_val) |rhs_val| {
6710 if (rhs_val.isUndef()) {
6711 return sema.addConstUndef(scalar_type);
6712 }
6713 if (rhs_val.compareWithZero(.eq)) {
6714 return sema.addConstant(scalar_type, Value.zero);
6715 }
6716 if (rhs_val.compare(.eq, Value.one, scalar_type)) {
6717 return casted_lhs;
6718 }
6719 if (maybe_lhs_val) |lhs_val| {
6720 if (lhs_val.isUndef()) {
6721 return sema.addConstUndef(scalar_type);
6722 }
6723 return sema.addConstant(
6724 scalar_type,
6725 try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target),
6726 );
6727 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };
6728 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };
6729 },
6730 .mod_rem => {
6731 // For integers:
6732 // Either operand being undef is a compile error because there exists
6733 // a possible value (TODO what is it?) that would invoke illegal behavior.
6734 // TODO: can lhs zero be handled better?
6735 // TODO: can lhs undef be handled better?
6736 //
6737 // For floats:
6738 // If the rhs is zero, compile error for division by zero.
6739 // If the rhs is undefined, compile error because there is a possible
6740 // value (zero) for which the division would be illegal behavior.
6741 // If the lhs is undefined, result is undefined.
6742 //
6743 // For either one: if the result would be different between @mod and @rem,
6744 // then emit a compile error saying you have to pick one.
6745 if (is_int) {
6746 if (maybe_lhs_val) |lhs_val| {
6747 if (lhs_val.isUndef()) {
6748 return sema.failWithUseOfUndef(block, lhs_src);
6749 }
6750 if (lhs_val.compareWithZero(.lt)) {
6751 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
6752 }
6753 } else if (lhs_ty.isSignedInt()) {
6754 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
6755 }
6756 if (maybe_rhs_val) |rhs_val| {
6757 if (rhs_val.isUndef()) {
6758 return sema.failWithUseOfUndef(block, rhs_src);
6759 }
6760 if (rhs_val.compareWithZero(.eq)) {
6761 return sema.failWithDivideByZero(block, rhs_src);
6762 }
6763 if (rhs_val.compareWithZero(.lt)) {
6764 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
6765 }
6766 if (maybe_lhs_val) |lhs_val| {
6767 return sema.addConstant(
6768 scalar_type,
6769 try lhs_val.intRem(rhs_val, sema.arena),
6770 );
6771 }
6772 break :rs .{ .src = lhs_src, .air_tag = .rem };
6773 } else if (rhs_ty.isSignedInt()) {
6774 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
6775 } else {
6776 break :rs .{ .src = rhs_src, .air_tag = .rem };
6777 }
6778 }
6779 // float operands
6780 if (maybe_rhs_val) |rhs_val| {
6781 if (rhs_val.isUndef()) {
6782 return sema.failWithUseOfUndef(block, rhs_src);
6783 }
6784 if (rhs_val.compareWithZero(.eq)) {
6785 return sema.failWithDivideByZero(block, rhs_src);
6786 }
6787 if (rhs_val.compareWithZero(.lt)) {
6788 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
6789 }
6790 if (maybe_lhs_val) |lhs_val| {
6791 if (lhs_val.isUndef() or lhs_val.compareWithZero(.lt)) {
6792 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
6793 }
6794 return sema.addConstant(
6795 scalar_type,
6796 try lhs_val.floatRem(rhs_val, sema.arena),
6797 );
6798 } else {
6799 return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
6800 }
6801 } else {
6802 return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
6803 }
6804 },
6805 .rem => {
6806 // For integers:
6807 // Either operand being undef is a compile error because there exists
6808 // a possible value (TODO what is it?) that would invoke illegal behavior.
6809 // TODO: can lhs zero be handled better?
6810 // TODO: can lhs undef be handled better?
6811 //
6812 // For floats:
6813 // If the rhs is zero, compile error for division by zero.
6814 // If the rhs is undefined, compile error because there is a possible
6815 // value (zero) for which the division would be illegal behavior.
6816 // If the lhs is undefined, result is undefined.
6817 if (is_int) {
6818 if (maybe_lhs_val) |lhs_val| {
6819 if (lhs_val.isUndef()) {
6820 return sema.failWithUseOfUndef(block, lhs_src);
6821 }
6822 }
6823 if (maybe_rhs_val) |rhs_val| {
6824 if (rhs_val.isUndef()) {
6825 return sema.failWithUseOfUndef(block, rhs_src);
6826 }
6827 if (rhs_val.compareWithZero(.eq)) {
6828 return sema.failWithDivideByZero(block, rhs_src);
6829 }
6830 if (maybe_lhs_val) |lhs_val| {
6831 return sema.addConstant(
6832 scalar_type,
6833 try lhs_val.intRem(rhs_val, sema.arena),
6834 );
6835 }
6836 break :rs .{ .src = lhs_src, .air_tag = .rem };
6837 } else {
6838 break :rs .{ .src = rhs_src, .air_tag = .rem };
6839 }
6840 }
6841 // float operands
6842 if (maybe_rhs_val) |rhs_val| {
6843 if (rhs_val.isUndef()) {
6844 return sema.failWithUseOfUndef(block, rhs_src);
6845 }
6846 if (rhs_val.compareWithZero(.eq)) {
6847 return sema.failWithDivideByZero(block, rhs_src);
6848 }
6849 }
6850 if (maybe_lhs_val) |lhs_val| {
6851 if (lhs_val.isUndef()) {
6852 return sema.addConstUndef(scalar_type);
6853 }
6854 if (maybe_rhs_val) |rhs_val| {
6855 return sema.addConstant(
6856 scalar_type,
6857 try lhs_val.floatRem(rhs_val, sema.arena),
6858 );
6859 } else break :rs .{ .src = rhs_src, .air_tag = .rem };
6860 } else break :rs .{ .src = lhs_src, .air_tag = .rem };
6861 },
6862 .mod => {
6863 // For integers:
6864 // Either operand being undef is a compile error because there exists
6865 // a possible value (TODO what is it?) that would invoke illegal behavior.
6866 // TODO: can lhs zero be handled better?
6867 // TODO: can lhs undef be handled better?
6868 //
6869 // For floats:
6870 // If the rhs is zero, compile error for division by zero.
6871 // If the rhs is undefined, compile error because there is a possible
6872 // value (zero) for which the division would be illegal behavior.
6873 // If the lhs is undefined, result is undefined.
6874 if (is_int) {
6875 if (maybe_lhs_val) |lhs_val| {
6876 if (lhs_val.isUndef()) {
6877 return sema.failWithUseOfUndef(block, lhs_src);
6878 }
6879 }
6880 if (maybe_rhs_val) |rhs_val| {
6881 if (rhs_val.isUndef()) {
6882 return sema.failWithUseOfUndef(block, rhs_src);
6883 }
6884 if (rhs_val.compareWithZero(.eq)) {
6885 return sema.failWithDivideByZero(block, rhs_src);
6886 }
6887 if (maybe_lhs_val) |lhs_val| {
6888 return sema.addConstant(
6889 scalar_type,
6890 try lhs_val.intMod(rhs_val, sema.arena),
6891 );
6892 }
6893 break :rs .{ .src = lhs_src, .air_tag = .mod };
6894 } else {
6895 break :rs .{ .src = rhs_src, .air_tag = .mod };
6896 }
6897 }
6898 // float operands
6899 if (maybe_rhs_val) |rhs_val| {
6900 if (rhs_val.isUndef()) {
6901 return sema.failWithUseOfUndef(block, rhs_src);
6902 }
6903 if (rhs_val.compareWithZero(.eq)) {
6904 return sema.failWithDivideByZero(block, rhs_src);
6905 }
6906 }
6907 if (maybe_lhs_val) |lhs_val| {
6908 if (lhs_val.isUndef()) {
6909 return sema.addConstUndef(scalar_type);
6910 }
6911 if (maybe_rhs_val) |rhs_val| {
6912 return sema.addConstant(
6913 scalar_type,
6914 try lhs_val.floatMod(rhs_val, sema.arena),
6915 );
6916 } else break :rs .{ .src = rhs_src, .air_tag = .mod };
6917 } else break :rs .{ .src = lhs_src, .air_tag = .mod };
6918 },
6919 else => unreachable,
6920 }
6921 };
6922
6923 try sema.requireRuntimeBlock(block, rs.src);
6924 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
6925}
6926
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", .{});
60466971 }
60476972
60486973 const constraint = sema.code.nullTerminatedString(output.data.constraint);
......@@ -6134,10 +7059,11 @@ fn zirCmpEq(
61347059 const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty;
61357060 return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type});
61367061 }
6137 if (((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
6138 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
6139 {
6140 return mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
7062 if (lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) {
7063 return sema.analyzeCmpUnionTag(block, rhs, rhs_src, lhs, lhs_src, op);
7064 }
7065 if (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union) {
7066 return sema.analyzeCmpUnionTag(block, lhs, lhs_src, rhs, rhs_src, op);
61417067 }
61427068 if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
61437069 const runtime_src: LazySrcLoc = src: {
......@@ -6178,6 +7104,28 @@ fn zirCmpEq(
61787104 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true);
61797105}
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
61817129/// Only called for non-equality operators. See also `zirCmpEq`.
61827130fn zirCmp(
61837131 sema: *Sema,
......@@ -6224,10 +7172,21 @@ fn analyzeCmp(
62247172 @tagName(op), resolved_type,
62257173 });
62267174 }
6227
62287175 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
62297176 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);
62317190 const runtime_src: LazySrcLoc = src: {
62327191 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
62337192 if (lhs_val.isUndef()) return sema.addConstUndef(resolved_type);
......@@ -6294,10 +7253,42 @@ fn runtimeBoolCmp(
62947253
62957254fn zirSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
62967255 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
7256 const src = inst_data.src();
62977257 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
62987258 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
7259 try sema.resolveTypeLayout(block, src, operand_ty);
62997260 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 };
63017292 return sema.addIntUnsigned(Type.initTag(.comptime_int), abi_size);
63027293}
63037294
......@@ -6315,8 +7306,45 @@ fn zirThis(
63157306 block: *Scope.Block,
63167307 extended: Zir.Inst.Extended.InstData,
63177308) CompileError!Air.Inst.Ref {
7309 const this_decl = block.base.namespace().getDecl();
63187310 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);
63207348}
63217349
63227350fn zirRetAddr(
......@@ -6345,19 +7373,80 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
63457373 const target = sema.mod.getTarget();
63467374
63477375 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 ),
63487439 .Fn => {
7440 const info = ty.fnInfo();
63497441 const field_values = try sema.arena.alloc(Value, 6);
63507442 // calling_convention: CallingConvention,
6351 field_values[0] = try Value.Tag.enum_field_index.create(
6352 sema.arena,
6353 @enumToInt(ty.fnCallingConvention()),
6354 );
7443 field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.cc));
63557444 // alignment: comptime_int,
63567445 field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(target));
63577446 // is_generic: bool,
6358 field_values[2] = Value.initTag(.bool_false); // TODO
7447 field_values[2] = if (info.is_generic) Value.initTag(.bool_true) else Value.initTag(.bool_false);
63597448 // is_var_args: bool,
6360 field_values[3] = Value.initTag(.bool_false); // TODO
7449 field_values[3] = if (info.is_var_args) Value.initTag(.bool_true) else Value.initTag(.bool_false);
63617450 // return_type: ?type,
63627451 field_values[4] = try Value.Tag.ty.create(sema.arena, ty.fnReturnType());
63637452 // args: []const FnArg,
......@@ -6366,10 +7455,7 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
63667455 return sema.addConstant(
63677456 type_info_ty,
63687457 try Value.Tag.@"union".create(sema.arena, .{
6369 .tag = try Value.Tag.enum_field_index.create(
6370 sema.arena,
6371 @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Fn),
6372 ),
7458 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Fn)),
63737459 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
63747460 }),
63757461 );
......@@ -6388,10 +7474,92 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
63887474 return sema.addConstant(
63897475 type_info_ty,
63907476 try Value.Tag.@"union".create(sema.arena, .{
6391 .tag = try Value.Tag.enum_field_index.create(
6392 sema.arena,
6393 @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Int),
6394 ),
7477 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Int)),
7478 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
7479 }),
7480 );
7481 },
7482 .Float => {
7483 const field_values = try sema.arena.alloc(Value, 1);
7484 // bits: comptime_int,
7485 field_values[0] = try Value.Tag.int_u64.create(sema.arena, ty.bitSize(target));
7486
7487 return sema.addConstant(
7488 type_info_ty,
7489 try Value.Tag.@"union".create(sema.arena, .{
7490 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Float)),
7491 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
7492 }),
7493 );
7494 },
7495 .Pointer => {
7496 const info = ty.ptrInfo().data;
7497 const field_values = try sema.arena.alloc(Value, 7);
7498 // size: Size,
7499 field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.size));
7500 // is_const: bool,
7501 field_values[1] = if (!info.mutable) Value.initTag(.bool_true) else Value.initTag(.bool_false);
7502 // is_volatile: bool,
7503 field_values[2] = if (info.@"volatile") Value.initTag(.bool_true) else Value.initTag(.bool_false);
7504 // alignment: comptime_int,
7505 field_values[3] = try Value.Tag.int_u64.create(sema.arena, info.@"align");
7506 // child: type,
7507 field_values[4] = try Value.Tag.ty.create(sema.arena, info.pointee_type);
7508 // is_allowzero: bool,
7509 field_values[5] = if (info.@"allowzero") Value.initTag(.bool_true) else Value.initTag(.bool_false);
7510 // sentinel: anytype,
7511 field_values[6] = if (info.sentinel) |some| try Value.Tag.opt_payload.create(sema.arena, some) else Value.initTag(.null_value);
7512
7513 return sema.addConstant(
7514 type_info_ty,
7515 try Value.Tag.@"union".create(sema.arena, .{
7516 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Pointer)),
7517 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
7518 }),
7519 );
7520 },
7521 .Array => {
7522 const info = ty.arrayInfo();
7523 const field_values = try sema.arena.alloc(Value, 3);
7524 // len: comptime_int,
7525 field_values[0] = try Value.Tag.int_u64.create(sema.arena, info.len);
7526 // child: type,
7527 field_values[1] = try Value.Tag.ty.create(sema.arena, info.elem_type);
7528 // sentinel: anytype,
7529 field_values[2] = if (info.sentinel) |some| try Value.Tag.opt_payload.create(sema.arena, some) else Value.initTag(.null_value);
7530
7531 return sema.addConstant(
7532 type_info_ty,
7533 try Value.Tag.@"union".create(sema.arena, .{
7534 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Array)),
7535 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
7536 }),
7537 );
7538 },
7539 .Optional => {
7540 const field_values = try sema.arena.alloc(Value, 1);
7541 // child: type,
7542 field_values[0] = try Value.Tag.ty.create(sema.arena, try ty.optionalChildAlloc(sema.arena));
7543
7544 return sema.addConstant(
7545 type_info_ty,
7546 try Value.Tag.@"union".create(sema.arena, .{
7547 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Optional)),
7548 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
7549 }),
7550 );
7551 },
7552 .ErrorUnion => {
7553 const field_values = try sema.arena.alloc(Value, 2);
7554 // error_set: type,
7555 field_values[0] = try Value.Tag.ty.create(sema.arena, ty.errorUnionSet());
7556 // payload: type,
7557 field_values[1] = try Value.Tag.ty.create(sema.arena, ty.errorUnionPayload());
7558
7559 return sema.addConstant(
7560 type_info_ty,
7561 try Value.Tag.@"union".create(sema.arena, .{
7562 .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ErrorUnion)),
63957563 .val = try Value.Tag.@"struct".create(sema.arena, field_values),
63967564 }),
63977565 );
......@@ -6808,7 +7976,7 @@ fn analyzeRet(
68087976fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
68097977 // extend this swich as additional operators are implemented
68107978 return switch (tag) {
6811 .add, .sub, .mul, .div => true,
7979 .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true,
68127980 else => false,
68137981 };
68147982}
......@@ -6819,18 +7987,14 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp
68197987
68207988 const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple;
68217989 const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type);
6822 const ty = try Module.ptrType(
6823 sema.arena,
6824 elem_type,
6825 null,
6826 0,
6827 0,
6828 0,
6829 inst_data.is_mutable,
6830 inst_data.is_allowzero,
6831 inst_data.is_volatile,
6832 inst_data.size,
6833 );
7990 const ty = try Type.ptr(sema.arena, .{
7991 .pointee_type = elem_type,
7992 .@"addrspace" = .generic,
7993 .mutable = inst_data.is_mutable,
7994 .@"allowzero" = inst_data.is_allowzero,
7995 .@"volatile" = inst_data.is_volatile,
7996 .size = inst_data.size,
7997 });
68347998 return sema.addType(ty);
68357999}
68368000
......@@ -6856,6 +8020,12 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
68568020 break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32);
68578021 } 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
68598029 const bit_start = if (inst_data.flags.has_bit_range) blk: {
68608030 const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
68618031 extra_i += 1;
......@@ -6873,18 +8043,18 @@ fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
68738043
68748044 const elem_type = try sema.resolveType(block, .unneeded, extra.data.elem_type);
68758045
6876 const ty = try Module.ptrType(
6877 sema.arena,
6878 elem_type,
6879 sentinel,
6880 abi_align,
6881 bit_start,
6882 bit_end,
6883 inst_data.flags.is_mutable,
6884 inst_data.flags.is_allowzero,
6885 inst_data.flags.is_volatile,
6886 inst_data.size,
6887 );
8046 const ty = try Type.ptr(sema.arena, .{
8047 .pointee_type = elem_type,
8048 .sentinel = sentinel,
8049 .@"align" = abi_align,
8050 .@"addrspace" = address_space,
8051 .bit_offset = bit_start,
8052 .host_size = bit_end,
8053 .mutable = inst_data.flags.is_mutable,
8054 .@"allowzero" = inst_data.flags.is_allowzero,
8055 .@"volatile" = inst_data.flags.is_volatile,
8056 .size = inst_data.size,
8057 });
68888058 return sema.addType(ty);
68898059}
68908060
......@@ -7059,8 +8229,49 @@ fn zirArrayInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
70598229 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
70608230 const src = inst_data.src();
70618231
7062 _ = is_ref;
7063 return sema.mod.fail(&block.base, src, "TODO: Sema.zirArrayInit", .{});
8232 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
8233 const args = sema.code.refSlice(extra.end, extra.data.operands_len);
8234
8235 var resolved_args = try sema.mod.gpa.alloc(Air.Inst.Ref, args.len);
8236 for (args) |arg, i| resolved_args[i] = sema.resolveInst(arg);
8237
8238 var all_args_comptime = for (resolved_args) |arg| {
8239 if ((try sema.resolveMaybeUndefVal(block, src, arg)) == null) break false;
8240 } else true;
8241
8242 if (all_args_comptime) {
8243 var anon_decl = try block.startAnonDecl();
8244 defer anon_decl.deinit();
8245 assert(!(resolved_args.len == 0));
8246 const final_ty = try Type.Tag.array.create(anon_decl.arena(), .{ .len = resolved_args.len, .elem_type = sema.typeOf(resolved_args[0]) });
8247 const buf = try anon_decl.arena().alloc(Value, resolved_args.len);
8248 for (resolved_args) |arg, i| {
8249 buf[i] = (try sema.resolveMaybeUndefVal(block, src, arg)).?;
8250 }
8251
8252 const val = try Value.Tag.array.create(anon_decl.arena(), buf);
8253 if (is_ref)
8254 return sema.analyzeDeclRef(try anon_decl.finish(final_ty, val))
8255 else
8256 return sema.analyzeDeclVal(block, .unneeded, try anon_decl.finish(final_ty, val));
8257 }
8258
8259 assert(!(resolved_args.len == 0));
8260 const array_ty = try Type.Tag.array.create(sema.arena, .{ .len = resolved_args.len, .elem_type = sema.typeOf(resolved_args[0]) });
8261 const final_ty = try Type.ptr(sema.arena, .{
8262 .pointee_type = array_ty,
8263 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
8264 });
8265 const alloc = try block.addTy(.alloc, final_ty);
8266
8267 for (resolved_args) |arg, i| {
8268 const pointer_to_array_at_index = try block.addBinOp(.ptr_elem_ptr, alloc, try sema.addIntUnsigned(Type.initTag(.u64), i));
8269 _ = try block.addBinOp(.store, pointer_to_array_at_index, arg);
8270 }
8271 return if (is_ref)
8272 alloc
8273 else
8274 try sema.analyzeLoad(block, .unneeded, alloc, .unneeded);
70648275}
70658276
70668277fn zirArrayInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
......@@ -7132,8 +8343,11 @@ fn zirFrameAddress(
71328343
71338344fn zirAlignOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
71348345 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
7135 const src = inst_data.src();
7136 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAlignOf", .{});
8346 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
8347 const ty = try sema.resolveType(block, operand_src, inst_data.operand);
8348 const target = sema.mod.getTarget();
8349 const abi_align = ty.abiAlignment(target);
8350 return sema.addIntUnsigned(Type.comptime_int, abi_align);
71378351}
71388352
71398353fn 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
72468460fn zirFloatToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
72478461 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
72488462 const src = inst_data.src();
8463 // TODO don't forget the safety check!
72498464 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFloatToInt", .{});
72508465}
72518466
72528467fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
72538468 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7254 const src = inst_data.src();
7255 return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToFloat", .{});
8469 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
8470 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
8471 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
8472 const dest_ty = try sema.resolveType(block, ty_src, extra.lhs);
8473 const operand = sema.resolveInst(extra.rhs);
8474 const operand_ty = sema.typeOf(operand);
8475
8476 _ = try sema.checkIntType(block, ty_src, dest_ty);
8477 try sema.checkFloatType(block, operand_src, operand_ty);
8478
8479 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
8480 const target = sema.mod.getTarget();
8481 const result_val = try val.intToFloat(sema.arena, dest_ty, target);
8482 return sema.addConstant(dest_ty, result_val);
8483 }
8484
8485 try sema.requireRuntimeBlock(block, operand_src);
8486 return block.addTyOp(.int_to_float, dest_ty, operand);
72568487}
72578488
72588489fn 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
73518582 const operand = sema.resolveInst(extra.rhs);
73528583 const operand_ty = sema.typeOf(operand);
73538584 const mod = sema.mod;
7354 const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_ty);
7355 const src_is_comptime_int = try sema.requireIntegerType(block, operand_src, operand_ty);
8585 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
8586 const src_is_comptime_int = try sema.checkIntType(block, operand_src, operand_ty);
73568587
73578588 if (dest_is_comptime_int) {
73588589 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
74108641
74118642fn zirClz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
74128643 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
7413 const src = inst_data.src();
7414 return sema.mod.fail(&block.base, src, "TODO: Sema.zirClz", .{});
8644 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
8645 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
8646 const operand = sema.resolveInst(inst_data.operand);
8647 const operand_ty = sema.typeOf(operand);
8648 // TODO implement support for vectors
8649 if (operand_ty.zigTypeTag() != .Int) {
8650 return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{
8651 operand_ty,
8652 });
8653 }
8654 const target = sema.mod.getTarget();
8655 const bits = operand_ty.intInfo(target).bits;
8656 if (bits == 0) return Air.Inst.Ref.zero;
8657
8658 const result_ty = try Type.smallestUnsignedInt(sema.arena, bits);
8659
8660 const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
8661 if (val.isUndef()) return sema.addConstUndef(result_ty);
8662 return sema.addIntUnsigned(result_ty, val.clz(operand_ty, target));
8663 } else operand_src;
8664
8665 try sema.requireRuntimeBlock(block, runtime_src);
8666 return block.addTyOp(.clz, result_ty, operand);
74158667}
74168668
74178669fn zirCtz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
74188670 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
7419 const src = inst_data.src();
7420 return sema.mod.fail(&block.base, src, "TODO: Sema.zirCtz", .{});
8671 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
8672 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
8673 const operand = sema.resolveInst(inst_data.operand);
8674 const operand_ty = sema.typeOf(operand);
8675 // TODO implement support for vectors
8676 if (operand_ty.zigTypeTag() != .Int) {
8677 return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{
8678 operand_ty,
8679 });
8680 }
8681 const target = sema.mod.getTarget();
8682 const bits = operand_ty.intInfo(target).bits;
8683 if (bits == 0) return Air.Inst.Ref.zero;
8684
8685 const result_ty = try Type.smallestUnsignedInt(sema.arena, bits);
8686
8687 const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
8688 if (val.isUndef()) return sema.addConstUndef(result_ty);
8689 return sema.mod.fail(&block.base, operand_src, "TODO: implement comptime @ctz", .{});
8690 } else operand_src;
8691
8692 try sema.requireRuntimeBlock(block, runtime_src);
8693 return block.addTyOp(.ctz, result_ty, operand);
74218694}
74228695
74238696fn 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
74508723 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivFloor", .{});
74518724}
74528725
7453fn 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 {
8726fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
74708727 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
74718728 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", .{});
74738730}
74748731
74758732fn 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
74908747 return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});
74918748}
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
74938773fn checkAtomicOperandType(
74948774 sema: *Sema,
74958775 block: *Scope.Block,
......@@ -7501,7 +8781,7 @@ fn checkAtomicOperandType(
75018781 const max_atomic_bits = target_util.largestAtomicBits(target);
75028782 const int_ty = switch (ty.zigTypeTag()) {
75038783 .Int => ty,
7504 .Enum => ty.enumTagType(&buffer),
8784 .Enum => ty.intTagType(&buffer),
75058785 .Float => {
75068786 const bit_count = ty.floatBits(target);
75078787 if (bit_count > max_atomic_bits) {
......@@ -7515,12 +8795,16 @@ fn checkAtomicOperandType(
75158795 return;
75168796 },
75178797 .Bool => return, // Will be treated as `u8`.
7518 else => return sema.mod.fail(
7519 &block.base,
7520 ty_src,
7521 "expected bool, integer, float, enum, or pointer type; found {}",
7522 .{ty},
7523 ),
8798 else => {
8799 if (ty.isPtrAtRuntime()) return;
8800
8801 return sema.mod.fail(
8802 &block.base,
8803 ty_src,
8804 "expected bool, integer, float, enum, or pointer type; found {}",
8805 .{ty},
8806 );
8807 },
75248808 };
75258809 const bit_count = int_ty.intInfo(target).bits;
75268810 if (bit_count > max_atomic_bits) {
......@@ -7533,6 +8817,31 @@ fn checkAtomicOperandType(
75338817 }
75348818}
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
75368845fn resolveAtomicOrder(
75378846 sema: *Sema,
75388847 block: *Scope.Block,
......@@ -7546,6 +8855,19 @@ fn resolveAtomicOrder(
75468855 return val.toEnum(std.builtin.AtomicOrder);
75478856}
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
75498871fn zirCmpxchg(
75508872 sema: *Sema,
75518873 block: *Scope.Block,
......@@ -7604,6 +8926,8 @@ fn zirCmpxchg(
76048926 if (try sema.resolveMaybeUndefVal(block, expected_src, expected_value)) |expected_val| {
76058927 if (try sema.resolveMaybeUndefVal(block, new_value_src, new_value)) |new_val| {
76068928 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
76078931 return sema.addConstUndef(result_ty);
76088932 }
76098933 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
76618985
76628986fn zirAtomicLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
76638987 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
7664 const src = inst_data.src();
7665 return sema.mod.fail(&block.base, src, "TODO: Sema.zirAtomicLoad", .{});
8988 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
8989 // zig fmt: off
8990 const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
8991 const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
8992 const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
8993 // zig fmt: on
8994 const ptr = sema.resolveInst(extra.lhs);
8995 const elem_ty = sema.typeOf(ptr).elemType();
8996 try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
8997 const order = try sema.resolveAtomicOrder(block, order_src, extra.rhs);
8998
8999 switch (order) {
9000 .Release, .AcqRel => {
9001 return sema.mod.fail(
9002 &block.base,
9003 order_src,
9004 "@atomicLoad atomic ordering must not be Release or AcqRel",
9005 .{},
9006 );
9007 },
9008 else => {},
9009 }
9010
9011 if (try sema.typeHasOnePossibleValue(block, elem_ty_src, elem_ty)) |val| {
9012 return sema.addConstant(elem_ty, val);
9013 }
9014
9015 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
9016 if (try ptr_val.pointerDeref(sema.arena)) |elem_val| {
9017 return sema.addConstant(elem_ty, elem_val);
9018 }
9019 }
9020
9021 try sema.requireRuntimeBlock(block, ptr_src);
9022 return block.addInst(.{
9023 .tag = .atomic_load,
9024 .data = .{ .atomic_load = .{
9025 .ptr = ptr,
9026 .order = order,
9027 } },
9028 });
76669029}
76679030
76689031fn zirAtomicRmw(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9032 const mod = sema.mod;
76699033 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;
76709035 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 });
76729108}
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 {
76759111 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;
76769113 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);
76789142}
76799143
76809144fn 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
77079171 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{});
77089172}
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 {
77119175 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;
77129177 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 });
77149237}
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 {
77179240 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;
77189242 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 });
77209283}
77219284
77229285fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -7761,7 +9324,6 @@ fn zirVarExtended(
77619324 const mut_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at mut token
77629325 const init_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at init expr
77639326 const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small);
7764 const var_ty = try sema.resolveType(block, ty_src, extra.data.var_type);
77659327
77669328 var extra_index: usize = extra.end;
77679329
......@@ -7781,19 +9343,29 @@ fn zirVarExtended(
77819343 // break :blk align_tv.val;
77829344 //} 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: {
77859347 const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
77869348 extra_index += 1;
7787 const init_air_inst = sema.resolveInst(init_ref);
7788 break :blk (try sema.resolveMaybeUndefVal(block, init_src, init_air_inst)) orelse
9349 break :blk sema.resolveInst(init_ref);
9350 } else .none;
9351
9352 const have_ty = extra.data.var_type != .none;
9353 const var_ty = if (have_ty)
9354 try sema.resolveType(block, ty_src, extra.data.var_type)
9355 else
9356 sema.typeOf(uncasted_init);
9357
9358 const init_val = if (uncasted_init != .none) blk: {
9359 const init = if (have_ty)
9360 try sema.coerce(block, var_ty, uncasted_init, init_src)
9361 else
9362 uncasted_init;
9363
9364 break :blk (try sema.resolveMaybeUndefVal(block, init_src, init)) orelse
77899365 return sema.failWithNeededComptime(block, init_src);
77909366 } else Value.initTag(.unreachable_value);
77919367
7792 if (!var_ty.isValidVarType(small.is_extern)) {
7793 return sema.mod.fail(&block.base, mut_src, "variable of type '{}' must be const", .{
7794 var_ty,
7795 });
7796 }
9368 try sema.validateVarType(block, mut_src, var_ty, small.is_extern);
77979369
77989370 if (lib_name != null) {
77999371 // Look at the sema code for functions which has this logic, it just needs to
......@@ -7890,7 +9462,10 @@ fn zirCUndef(
78909462) CompileError!Air.Inst.Ref {
78919463 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
78929464 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;
78949469}
78959470
78969471fn zirCInclude(
......@@ -7900,7 +9475,10 @@ fn zirCInclude(
79009475) CompileError!Air.Inst.Ref {
79019476 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
79029477 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;
79049482}
79059483
79069484fn zirCDefine(
......@@ -7910,7 +9488,15 @@ fn zirCDefine(
79109488) CompileError!Air.Inst.Ref {
79119489 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
79129490 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;
79149500}
79159501
79169502fn zirWasmMemorySize(
......@@ -7956,17 +9542,54 @@ fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void
79569542 try sema.requireFunctionBlock(block, src);
79579543}
79589544
7959fn requireIntegerType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !bool {
7960 switch (ty.zigTypeTag()) {
7961 .ComptimeInt => return true,
7962 .Int => return false,
7963 else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}),
7964 }
7965}
9545/// Emit a compile error if type cannot be used for a runtime variable.
9546fn validateVarType(
9547 sema: *Sema,
9548 block: *Scope.Block,
9549 src: LazySrcLoc,
9550 var_ty: Type,
9551 is_extern: bool,
9552) CompileError!void {
9553 var ty = var_ty;
9554 const ok: bool = while (true) switch (ty.zigTypeTag()) {
9555 .Bool,
9556 .Int,
9557 .Float,
9558 .ErrorSet,
9559 .Enum,
9560 .Frame,
9561 .AnyFrame,
9562 => break true,
9563
9564 .BoundFn,
9565 .ComptimeFloat,
9566 .ComptimeInt,
9567 .EnumLiteral,
9568 .NoReturn,
9569 .Type,
9570 .Void,
9571 .Undefined,
9572 .Null,
9573 => break false,
9574
9575 .Opaque => break is_extern,
9576
9577 .Optional => {
9578 var buf: Type.Payload.ElemType = undefined;
9579 const child_ty = ty.optionalChild(&buf);
9580 return validateVarType(sema, block, src, child_ty, is_extern);
9581 },
9582 .Pointer, .Array, .Vector => ty = ty.elemType(),
9583 .ErrorUnion => ty = ty.errorUnionPayload(),
79669584
7967fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {
7968 if (!ty.isValidVarType(false)) {
7969 return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty});
9585 .Fn => @panic("TODO fn validateVarType"),
9586 .Struct, .Union => {
9587 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
9588 break !resolved_ty.requiresComptime();
9589 },
9590 } else unreachable; // TODO should not need else unreachable
9591 if (!ok) {
9592 return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{var_ty});
79709593 }
79719594}
79729595
......@@ -7990,6 +9613,7 @@ fn addSafetyCheck(
79909613 var fail_block: Scope.Block = .{
79919614 .parent = parent_block,
79929615 .sema = sema,
9616 .wip_capture_scope = parent_block.wip_capture_scope,
79939617 .src_decl = parent_block.src_decl,
79949618 .instructions = .{},
79959619 .inlining = parent_block.inlining,
......@@ -8067,7 +9691,10 @@ fn panicWithMsg(
80679691 const panic_fn = try sema.getBuiltin(block, src, "panic");
80689692 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
80699693 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 });
80719698 const null_stack_trace = try sema.addConstant(
80729699 try Module.optionalType(arena, ptr_stack_trace_ty),
80739700 Value.initTag(.null_value),
......@@ -8083,7 +9710,7 @@ fn safetyPanic(
80839710 block: *Scope.Block,
80849711 src: LazySrcLoc,
80859712 panic_id: PanicId,
8086) !Zir.Inst.Index {
9713) CompileError!Zir.Inst.Index {
80879714 const msg = switch (panic_id) {
80889715 .unreach => "reached unreachable code",
80899716 .unwrap_null => "attempt to use null value",
......@@ -8151,7 +9778,7 @@ fn fieldVal(
81519778 .Pointer => switch (object_ty.ptrSize()) {
81529779 .Slice => {
81539780 if (mem.eql(u8, field_name, "ptr")) {
8154 const buf = try arena.create(Type.Payload.ElemType);
9781 const buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
81559782 const result_ty = object_ty.slicePtrFieldType(buf);
81569783 if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
81579784 if (val.isUndef()) return sema.addConstUndef(result_ty);
......@@ -8185,21 +9812,32 @@ fn fieldVal(
81859812 }
81869813 },
81879814 .One => {
8188 const elem_ty = object_ty.elemType();
8189 if (elem_ty.zigTypeTag() == .Array) {
8190 if (mem.eql(u8, field_name, "len")) {
8191 return sema.addConstant(
8192 Type.initTag(.comptime_int),
8193 try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()),
8194 );
8195 } else {
8196 return mod.fail(
8197 &block.base,
8198 field_name_src,
8199 "no member named '{s}' in '{}'",
8200 .{ field_name, object_ty },
8201 );
8202 }
9815 const ptr_child = object_ty.elemType();
9816 switch (ptr_child.zigTypeTag()) {
9817 .Array => {
9818 if (mem.eql(u8, field_name, "len")) {
9819 return sema.addConstant(
9820 Type.initTag(.comptime_int),
9821 try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
9822 );
9823 } else {
9824 return mod.fail(
9825 &block.base,
9826 field_name_src,
9827 "no member named '{s}' in '{}'",
9828 .{ field_name, object_ty },
9829 );
9830 }
9831 },
9832 .Struct => {
9833 const struct_ptr_deref = try sema.analyzeLoad(block, src, object, object_src);
9834 return sema.unionFieldVal(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child);
9835 },
9836 .Union => {
9837 const union_ptr_deref = try sema.analyzeLoad(block, src, object, object_src);
9838 return sema.unionFieldVal(block, src, union_ptr_deref, field_name, field_name_src, ptr_child);
9839 },
9840 else => {},
82039841 }
82049842 },
82059843 .Many, .C => {},
......@@ -8323,9 +9961,8 @@ fn fieldPtr(
83239961 );
83249962 }
83259963 },
8326 .Pointer => {
8327 const ptr_child = object_ty.elemType();
8328 if (ptr_child.isSlice()) {
9964 .Pointer => switch (object_ty.ptrSize()) {
9965 .Slice => {
83299966 // Here for the ptr and len fields what we need to do is the situation
83309967 // when a temporary has its address taken, e.g. `&a[c..d].len`.
83319968 // This value may be known at compile-time or runtime. In the former
......@@ -8355,26 +9992,39 @@ fn fieldPtr(
83559992 .{ field_name, object_ty },
83569993 );
83579994 }
8358 } else switch (ptr_child.zigTypeTag()) {
8359 .Array => {
8360 if (mem.eql(u8, field_name, "len")) {
8361 var anon_decl = try block.startAnonDecl();
8362 defer anon_decl.deinit();
8363 return sema.analyzeDeclRef(try anon_decl.finish(
8364 Type.initTag(.comptime_int),
8365 try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
8366 ));
8367 } else {
8368 return mod.fail(
8369 &block.base,
8370 field_name_src,
8371 "no member named '{s}' in '{}'",
8372 .{ field_name, object_ty },
8373 );
8374 }
8375 },
8376 else => {},
8377 }
9995 },
9996 .One => {
9997 const ptr_child = object_ty.elemType();
9998 switch (ptr_child.zigTypeTag()) {
9999 .Array => {
10000 if (mem.eql(u8, field_name, "len")) {
10001 var anon_decl = try block.startAnonDecl();
10002 defer anon_decl.deinit();
10003 return sema.analyzeDeclRef(try anon_decl.finish(
10004 Type.initTag(.comptime_int),
10005 try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
10006 ));
10007 } else {
10008 return mod.fail(
10009 &block.base,
10010 field_name_src,
10011 "no member named '{s}' in '{}'",
10012 .{ field_name, object_ty },
10013 );
10014 }
10015 },
10016 .Struct => {
10017 const struct_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
10018 return sema.structFieldPtr(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child);
10019 },
10020 .Union => {
10021 const union_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
10022 return sema.unionFieldPtr(block, src, union_ptr_deref, field_name, field_name_src, ptr_child);
10023 },
10024 else => {},
10025 }
10026 },
10027 .Many, .C => {},
837810028 },
837910029 .Type => {
838010030 _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);
......@@ -8460,11 +10110,153 @@ fn fieldPtr(
846010110 else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),
846110111 }
846210112 },
8463 .Struct => return sema.structFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty),
8464 .Union => return sema.unionFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty),
10113 .Struct => return sema.structFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty),
10114 .Union => return sema.unionFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty),
10115 else => {},
10116 }
10117 return mod.fail(&block.base, src, "type '{}' does not support field access", .{object_ty});
10118}
10119
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 },
846510256 else => {},
846610257 }
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 });
846810260}
846910261
847010262fn namespaceLookup(
......@@ -8516,13 +10308,19 @@ fn structFieldPtr(
851610308 const arena = sema.arena;
851710309 assert(unresolved_struct_ty.zigTypeTag() == .Struct);
851810310
10311 const struct_ptr_ty = sema.typeOf(struct_ptr);
851910312 const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);
852010313 const struct_obj = struct_ty.castTag(.@"struct").?.data;
852110314
8522 const field_index = struct_obj.fields.getIndex(field_name) orelse
10315 const field_index_big = struct_obj.fields.getIndex(field_name) orelse
852310316 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
10317 const field_index = @intCast(u32, field_index_big);
852410318 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
852710325 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
852810326 return sema.addConstant(
......@@ -8535,31 +10333,7 @@ fn structFieldPtr(
853510333 }
853610334
853710335 try sema.requireRuntimeBlock(block, src);
8538 const tag: Air.Inst.Tag = switch (field_index) {
8539 0 => .struct_field_ptr_index_0,
8540 1 => .struct_field_ptr_index_1,
8541 2 => .struct_field_ptr_index_2,
8542 3 => .struct_field_ptr_index_3,
8543 else => {
8544 return block.addInst(.{
8545 .tag = .struct_field_ptr,
8546 .data = .{ .ty_pl = .{
8547 .ty = try sema.addType(ptr_field_ty),
8548 .payload = try sema.addExtra(Air.StructField{
8549 .struct_operand = struct_ptr,
8550 .field_index = @intCast(u32, field_index),
8551 }),
8552 } },
8553 });
8554 },
8555 };
8556 return block.addInst(.{
8557 .tag = tag,
8558 .data = .{ .ty_op = .{
8559 .ty = try sema.addType(ptr_field_ty),
8560 .operand = struct_ptr,
8561 } },
8562 });
10336 return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
856310337}
856410338
856510339fn structFieldVal(
......@@ -8609,18 +10383,23 @@ fn unionFieldPtr(
860910383 field_name_src: LazySrcLoc,
861010384 unresolved_union_ty: Type,
861110385) CompileError!Air.Inst.Ref {
8612 const mod = sema.mod;
861310386 const arena = sema.arena;
861410387 assert(unresolved_union_ty.zigTypeTag() == .Union);
861510388
10389 const union_ptr_ty = sema.typeOf(union_ptr);
861610390 const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
861710391 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
861810392
8619 const field_index = union_obj.fields.getIndex(field_name) orelse
10393 const field_index_big = union_obj.fields.getIndex(field_name) orelse
862010394 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
10395 const field_index = @intCast(u32, field_index_big);
862110396
862210397 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
862510404 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {
862610405 // TODO detect inactive union field and emit compile error
......@@ -8634,7 +10413,7 @@ fn unionFieldPtr(
863410413 }
863510414
863610415 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);
863810417}
863910418
864010419fn unionFieldVal(
......@@ -8796,10 +10575,11 @@ fn elemPtrArray(
879610575) CompileError!Air.Inst.Ref {
879710576 const array_ptr_ty = sema.typeOf(array_ptr);
879810577 const pointee_type = array_ptr_ty.elemType().elemType();
8799 const result_ty = if (array_ptr_ty.ptrIsMutable())
8800 try Type.Tag.single_mut_pointer.create(sema.arena, pointee_type)
8801 else
8802 try Type.Tag.single_const_pointer.create(sema.arena, pointee_type);
10578 const result_ty = try Type.ptr(sema.arena, .{
10579 .pointee_type = pointee_type,
10580 .mutable = array_ptr_ty.ptrIsMutable(),
10581 .@"addrspace" = array_ptr_ty.ptrAddressSpace(),
10582 });
880310583
880410584 if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
880510585 if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {
......@@ -8845,14 +10625,14 @@ fn coerce(
884510625 if (dest_type.eql(inst_ty))
884610626 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());
884910632 if (in_memory_result == .ok) {
885010633 return sema.bitcast(block, dest_type, inst, inst_src);
885110634 }
885210635
8853 const mod = sema.mod;
8854 const arena = sema.arena;
8855
885610636 // undefined to anything
885710637 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
885810638 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
......@@ -8887,11 +10667,13 @@ fn coerce(
888710667 const array_type = inst_ty.elemType();
888810668 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
888910669 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;
889110672 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
889310675 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())) {
889510677 .ok => {},
889610678 .no_match => break :src_array_ptr,
889710679 }
......@@ -8932,7 +10714,7 @@ fn coerce(
893210714 const src_info = inst_ty.intInfo(target);
893310715 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
893410716 // 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))
893610718 {
893710719 try sema.requireRuntimeBlock(block, inst_src);
893810720 return block.addTyOp(.intcast, dest_type, inst);
......@@ -8948,13 +10730,13 @@ fn coerce(
894810730 const dst_bits = dest_type.floatBits(target);
894910731 if (dst_bits >= src_bits) {
895010732 try sema.requireRuntimeBlock(block, inst_src);
8951 return block.addTyOp(.floatcast, dest_type, inst);
10733 return block.addTyOp(.fpext, dest_type, inst);
895210734 }
895310735 }
895410736 },
8955 .Enum => {
8956 // enum literal to enum
8957 if (inst_ty.zigTypeTag() == .EnumLiteral) {
10737 .Enum => switch (inst_ty.zigTypeTag()) {
10738 .EnumLiteral => {
10739 // enum literal to enum
895810740 const val = try sema.resolveConstValue(block, inst_src, inst);
895910741 const bytes = val.castTag(.enum_literal).?.data;
896010742 const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type);
......@@ -8981,7 +10763,15 @@ fn coerce(
898110763 resolved_dest_type,
898210764 try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)),
898310765 );
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 => {},
898510775 },
898610776 .ErrorUnion => {
898710777 // T to E!T or E to E!T
......@@ -8998,10 +10788,92 @@ const InMemoryCoercionResult = enum {
899810788 no_match,
899910789};
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 {
900210803 if (dest_type.eql(src_type))
900310804 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
900510877 // TODO: implement more of this function
900610878
900710879 return .no_match;
......@@ -9021,33 +10893,56 @@ fn coerceNum(
902110893
902210894 const target = sema.mod.getTarget();
902310895
9024 if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) {
9025 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
9026 if (val.floatHasFraction()) {
9027 return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst_ty });
9028 }
9029 return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});
9030 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
9031 if (!val.intFitsInType(dest_type, target)) {
9032 return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });
9033 }
9034 return try sema.addConstant(dest_type, val);
9035 }
9036 } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) {
9037 if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) {
9038 const res = val.floatCast(sema.arena, dest_type) catch |err| switch (err) {
9039 error.Overflow => return sema.mod.fail(
9040 &block.base,
9041 inst_src,
9042 "cast of value {} to type '{}' loses information",
9043 .{ val, dest_type },
9044 ),
9045 error.OutOfMemory => return error.OutOfMemory,
9046 };
9047 return try sema.addConstant(dest_type, res);
9048 } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) {
9049 return sema.mod.fail(&block.base, inst_src, "TODO int to float", .{});
9050 }
10896 switch (dst_zig_tag) {
10897 .ComptimeInt, .Int => switch (src_zig_tag) {
10898 .Float, .ComptimeFloat => {
10899 if (val.floatHasFraction()) {
10900 return sema.mod.fail(&block.base, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_type });
10901 }
10902 return sema.mod.fail(&block.base, inst_src, "TODO float to int", .{});
10903 },
10904 .Int, .ComptimeInt => {
10905 if (!val.intFitsInType(dest_type, target)) {
10906 return sema.mod.fail(&block.base, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });
10907 }
10908 return try sema.addConstant(dest_type, val);
10909 },
10910 else => {},
10911 },
10912 .ComptimeFloat, .Float => switch (src_zig_tag) {
10913 .ComptimeFloat => {
10914 const result_val = try val.floatCast(sema.arena, dest_type);
10915 return try sema.addConstant(dest_type, result_val);
10916 },
10917 .Float => {
10918 const result_val = try val.floatCast(sema.arena, dest_type);
10919 if (!val.eql(result_val, dest_type)) {
10920 return sema.mod.fail(
10921 &block.base,
10922 inst_src,
10923 "type {} cannot represent float value {}",
10924 .{ dest_type, val },
10925 );
10926 }
10927 return try sema.addConstant(dest_type, result_val);
10928 },
10929 .Int, .ComptimeInt => {
10930 const result_val = try val.intToFloat(sema.arena, dest_type, target);
10931 // TODO implement this compile error
10932 //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
10933 //if (!int_again_val.eql(val, inst_ty)) {
10934 // return sema.mod.fail(
10935 // &block.base,
10936 // inst_src,
10937 // "type {} cannot represent integer value {}",
10938 // .{ dest_type, val },
10939 // );
10940 //}
10941 return try sema.addConstant(dest_type, result_val);
10942 },
10943 else => {},
10944 },
10945 else => {},
905110946 }
905210947 return null;
905310948}
......@@ -9067,66 +10962,97 @@ fn coerceVarArgParam(
906710962 return inst;
906810963}
906910964
10965// TODO migrate callsites to use storePtr2 instead.
907010966fn storePtr(
907110967 sema: *Sema,
907210968 block: *Scope.Block,
907310969 src: LazySrcLoc,
907410970 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,
907610985) !void {
907710986 const ptr_ty = sema.typeOf(ptr);
907810987 if (ptr_ty.isConstPtr())
907910988 return sema.mod.fail(&block.base, src, "cannot assign to constant", .{});
908010989
908110990 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);
908310992 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
908410993 return;
908510994
9086 if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| {
9087 if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| {
9088 const const_val = (try sema.resolveMaybeUndefVal(block, src, value)) orelse
9089 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
9090
9091 if (decl_ref_mut.data.runtime_index < block.runtime_index) {
9092 if (block.runtime_cond) |cond_src| {
9093 const msg = msg: {
9094 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
9095 errdefer msg.destroy(sema.gpa);
9096 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
9097 break :msg msg;
9098 };
9099 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
9100 }
9101 if (block.runtime_loop) |loop_src| {
9102 const msg = msg: {
9103 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
9104 errdefer msg.destroy(sema.gpa);
9105 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
9106 break :msg msg;
9107 };
9108 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
9109 }
9110 unreachable;
9111 }
9112 var new_arena = std.heap.ArenaAllocator.init(sema.gpa);
9113 errdefer new_arena.deinit();
9114 const new_ty = try elem_ty.copy(&new_arena.allocator);
9115 const new_val = try const_val.copy(&new_arena.allocator);
9116 const decl = decl_ref_mut.data.decl;
9117 var old_arena = decl.value_arena.?.promote(sema.gpa);
9118 decl.value_arena = null;
9119 try decl.finalizeNewArena(&new_arena);
9120 decl.ty = new_ty;
9121 decl.val = new_val;
9122 old_arena.deinit();
10995 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
10996 const operand_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse
10997 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
10998 if (ptr_val.tag() == .decl_ref_mut) {
10999 try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
912311000 return;
912411001 }
9125 }
11002 break :rs operand_src;
11003 } else ptr_src;
11004
912611005 // TODO handle if the element type requires comptime
912711006
9128 try sema.requireRuntimeBlock(block, src);
9129 _ = try block.addBinOp(.store, ptr, value);
11007 try sema.requireRuntimeBlock(block, runtime_src);
11008 _ = try block.addBinOp(air_tag, ptr, operand);
11009}
11010
11011/// Call when you have Value objects rather than Air instructions, and you want to
11012/// assert the store must be done at comptime.
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 }
913011056}
913111057
913211058fn bitcast(
......@@ -9171,7 +11097,8 @@ fn coerceArrayPtrToMany(
917111097 // The comptime Value representation is compatible with both types.
917211098 return sema.addConstant(dest_type, val);
917311099 }
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);
917511102}
917611103
917711104fn analyzeDeclVal(
......@@ -9211,11 +11138,19 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
921111138 const decl_tv = try decl.typedValue();
921211139 if (decl_tv.val.castTag(.variable)) |payload| {
921311140 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 });
921511146 return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl));
921611147 }
921711148 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 }),
921911154 try Value.Tag.decl_ref.create(sema.arena, decl),
922011155 );
922111156}
......@@ -9232,14 +11167,22 @@ fn analyzeRef(
923211167 var anon_decl = try block.startAnonDecl();
923311168 defer anon_decl.deinit();
923411169 return sema.analyzeDeclRef(try anon_decl.finish(
9235 operand_ty,
11170 try operand_ty.copy(anon_decl.arena()),
923611171 try val.copy(anon_decl.arena()),
923711172 ));
923811173 }
923911174
924011175 try sema.requireRuntimeBlock(block, src);
9241 const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
9242 const mut_ptr_type = try Module.simplePtrType(sema.arena, operand_ty, true, .One);
11176 const address_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local);
11177 const ptr_type = try Type.ptr(sema.arena, .{
11178 .pointee_type = operand_ty,
11179 .mutable = false,
11180 .@"addrspace" = address_space,
11181 });
11182 const mut_ptr_type = try Type.ptr(sema.arena, .{
11183 .pointee_type = operand_ty,
11184 .@"addrspace" = address_space,
11185 });
924311186 const alloc = try block.addTy(.alloc, mut_ptr_type);
924411187 try sema.storePtr(block, src, alloc, operand);
924511188
......@@ -9395,18 +11338,16 @@ fn analyzeSlice(
939511338 }
939611339 }
939711340 }
9398 const return_type = try Module.ptrType(
9399 sema.arena,
9400 return_elem_type,
9401 if (end_opt == .none) slice_sentinel else null,
9402 0, // TODO alignment
9403 0,
9404 0,
9405 !ptr_child.isConstPtr(),
9406 ptr_child.isAllowzeroPtr(),
9407 ptr_child.isVolatilePtr(),
9408 return_ptr_size,
9409 );
11341 const return_type = try Type.ptr(sema.arena, .{
11342 .pointee_type = return_elem_type,
11343 .sentinel = if (end_opt == .none) slice_sentinel else null,
11344 .@"align" = 0, // TODO alignment
11345 .@"addrspace" = if (ptr_child.zigTypeTag() == .Pointer) ptr_child.ptrAddressSpace() else .generic,
11346 .mutable = !ptr_child.isConstPtr(),
11347 .@"allowzero" = ptr_child.isAllowzeroPtr(),
11348 .@"volatile" = ptr_child.isVolatilePtr(),
11349 .size = return_ptr_size,
11350 });
941011351 _ = return_type;
941111352
941211353 return sema.mod.fail(&block.base, src, "TODO implement analysis of slice", .{});
......@@ -9508,11 +11449,11 @@ fn cmpNumeric(
950811449 const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
950911450 lhs_val.compareWithZero(.lt)
951011451 else
9511 (lhs_ty.isFloat() or lhs_ty.isSignedInt());
11452 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt());
951211453 const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
951311454 rhs_val.compareWithZero(.lt)
951411455 else
9515 (rhs_ty.isFloat() or rhs_ty.isSignedInt());
11456 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt());
951611457 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
951711458
951811459 var dest_float_type: ?Type = null;
......@@ -9692,6 +11633,20 @@ fn wrapErrorUnion(
969211633 }
969311634}
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
969511650fn resolvePeerTypes(
969611651 sema: *Sema,
969711652 block: *Scope.Block,
......@@ -9738,7 +11693,7 @@ fn resolvePeerTypes(
973811693 }
973911694 continue;
974011695 }
9741 if (chosen_ty.isFloat() and candidate_ty.isFloat()) {
11696 if (chosen_ty.isRuntimeFloat() and candidate_ty.isRuntimeFloat()) {
974211697 if (chosen_ty.floatBits(target) < candidate_ty.floatBits(target)) {
974311698 chosen = candidate;
974411699 chosen_i = candidate_i + 1;
......@@ -9756,13 +11711,13 @@ fn resolvePeerTypes(
975611711 continue;
975711712 }
975811713
9759 if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isFloat()) {
11714 if (chosen_ty.zigTypeTag() == .ComptimeFloat and candidate_ty.isRuntimeFloat()) {
976011715 chosen = candidate;
976111716 chosen_i = candidate_i + 1;
976211717 continue;
976311718 }
976411719
9765 if (chosen_ty.isFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {
11720 if (chosen_ty.isRuntimeFloat() and candidate_ty.zigTypeTag() == .ComptimeFloat) {
976611721 continue;
976711722 }
976811723
......@@ -9813,9 +11768,6 @@ pub fn resolveTypeLayout(
981311768 ty: Type,
981411769) CompileError!void {
981511770 switch (ty.zigTypeTag()) {
9816 .Pointer => {
9817 return sema.resolveTypeLayout(block, src, ty.elemType());
9818 },
981911771 .Struct => {
982011772 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
982111773 const struct_obj = resolved_ty.castTag(.@"struct").?.data;
......@@ -9863,6 +11815,10 @@ pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty:
986311815 sema.namespace = &struct_obj.namespace;
986411816 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
986611822 struct_obj.status = .field_types_wip;
986711823 try sema.analyzeStructFields(block, struct_obj);
986811824 struct_obj.status = .have_field_types;
......@@ -9881,6 +11837,10 @@ pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty:
988111837 sema.namespace = &union_obj.namespace;
988211838 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
988411844 union_obj.status = .field_types_wip;
988511845 try sema.analyzeUnionFields(block, union_obj);
988611846 union_obj.status = .have_field_types;
......@@ -9907,6 +11867,7 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type
990711867 .atomic_order => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrder"),
990811868 .atomic_rmw_op => return sema.resolveBuiltinTypeFields(block, src, "AtomicRmwOp"),
990911869 .calling_convention => return sema.resolveBuiltinTypeFields(block, src, "CallingConvention"),
11870 .address_space => return sema.resolveBuiltinTypeFields(block, src, "AddressSpace"),
991011871 .float_mode => return sema.resolveBuiltinTypeFields(block, src, "FloatMode"),
991111872 .reduce_op => return sema.resolveBuiltinTypeFields(block, src, "ReduceOp"),
991211873 .call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"),
......@@ -10081,9 +12042,11 @@ fn analyzeUnionFields(
1008112042 const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset };
1008212043 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]);
1008512047 extra_index += 1;
10086 }
12048 break :blk ty_ref;
12049 } else .none;
1008712050
1008812051 const body_len = if (small.has_body_len) blk: {
1008912052 const body_len = zir.extra[extra_index];
......@@ -10118,11 +12081,33 @@ fn analyzeUnionFields(
1011812081 var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa);
1011912082 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
1012312086 if (body.len != 0) {
1012412087 _ = try sema.analyzeBody(block, body);
1012512088 }
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
1012712112 const bits_per_field = 4;
1012812113 const fields_per_u32 = 32 / bits_per_field;
......@@ -10161,12 +12146,25 @@ fn analyzeUnionFields(
1016112146 break :blk align_ref;
1016212147 } 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]);
1016512151 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 });
1016612160 }
1016712161
1016812162 // This string needs to outlive the ZIR code.
1016912163 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
1017012168 const field_ty: Type = if (field_type_ref == .none)
1017112169 Type.initTag(.void)
1017212170 else
......@@ -10188,10 +12186,84 @@ fn analyzeUnionFields(
1018812186 // But only resolve the source location if we need to emit a compile error.
1018912187 const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val;
1019012188 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);
1019112191 }
1019212192 }
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_ref
12258 enum_obj.* = .{
12259 .owner_decl = new_decl,
12260 .fields = .{},
12261 .node_offset = 0,
12262 };
12263 // Here we pre-allocate the maps using the decl arena.
12264 try enum_obj.fields.ensureTotalCapacity(&new_decl_arena.allocator, fields_len);
12265 try new_decl.finalizeNewArena(&new_decl_arena);
12266 return enum_ty;
1019512267}
1019612268
1019712269fn getBuiltin(
......@@ -10231,7 +12303,7 @@ fn getBuiltinType(
1023112303}
1023212304
1023312305/// There is another implementation of this in `Type.onePossibleValue`. This one
10234/// in `Sema` is for calling during semantic analysis, and peforms field resolution
12306/// in `Sema` is for calling during semantic analysis, and performs field resolution
1023512307/// to get the answer. The one in `Type` is for calling during codegen and asserts
1023612308/// that the types are already resolved.
1023712309fn typeHasOnePossibleValue(
......@@ -10301,6 +12373,7 @@ fn typeHasOnePossibleValue(
1030112373 .atomic_order,
1030212374 .atomic_rmw_op,
1030312375 .calling_convention,
12376 .address_space,
1030412377 .float_mode,
1030512378 .reduce_op,
1030612379 .call_options,
......@@ -10316,6 +12389,7 @@ fn typeHasOnePossibleValue(
1031612389 .single_const_pointer,
1031712390 .single_mut_pointer,
1031812391 .pointer,
12392 .bound_fn,
1031912393 => return null,
1032012394
1032112395 .@"struct" => {
......@@ -10328,11 +12402,28 @@ fn typeHasOnePossibleValue(
1032812402 }
1032912403 return Value.initTag(.empty_struct_value);
1033012404 },
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 },
1033112418 .enum_full => {
1033212419 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
10333 const enum_full = resolved_ty.castTag(.enum_full).?.data;
10334 if (enum_full.fields.count() == 1) {
10335 return enum_full.values.keys()[0];
12420 const enum_obj = resolved_ty.castTag(.enum_full).?.data;
12421 if (enum_obj.fields.count() == 1) {
12422 if (enum_obj.values.count() == 0) {
12423 return Value.initTag(.zero); // auto-numbered
12424 } else {
12425 return enum_obj.values.keys()[0];
12426 }
1033612427 } else {
1033712428 return null;
1033812429 }
......@@ -10486,6 +12577,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
1048612577 .atomic_order => return .atomic_order_type,
1048712578 .atomic_rmw_op => return .atomic_rmw_op_type,
1048812579 .calling_convention => return .calling_convention_type,
12580 .address_space => return .address_space_type,
1048912581 .float_mode => return .float_mode_type,
1049012582 .reduce_op => return .reduce_op_type,
1049112583 .call_options => return .call_options_type,
......@@ -10581,7 +12673,10 @@ fn analyzeComptimeAlloc(
1058112673 block: *Scope.Block,
1058212674 var_type: Type,
1058312675) 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
1058612681 var anon_decl = try block.startAnonDecl();
1058712682 defer anon_decl.deinit();
......@@ -10597,3 +12692,58 @@ fn analyzeComptimeAlloc(
1059712692 .decl = decl,
1059812693 }));
1059912694}
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 {
4949};
5050
5151pub const ExtraIndex = enum(u32) {
52 /// Ref. The main struct decl for this file.
53 main_struct,
5452 /// If this is 0, no compile errors. Otherwise there is a `CompileErrors`
5553 /// payload at this index.
5654 compile_errors,
......@@ -61,11 +59,6 @@ pub const ExtraIndex = enum(u32) {
6159 _,
6260};
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
6962/// Returns the requested data, as well as the new index which is at the start of the
7063/// trailers for the object.
7164pub 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
7770 u32 => code.extra[i],
7871 Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
7972 i32 => @bitCast(i32, code.extra[i]),
73 Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]),
8074 else => @compileError("bad field type"),
8175 };
8276 i += 1;
......@@ -112,50 +106,9 @@ pub fn deinit(code: *Zir, gpa: *Allocator) void {
112106 code.* = undefined;
113107}
114108
115/// Write human-readable, debug formatted ZIR code to a file.
116pub fn renderAsTextToFile(
117 gpa: *Allocator,
118 scope_file: *Module.Scope.File,
119 fs_file: std.fs.File,
120) !void {
121 var arena = std.heap.ArenaAllocator.init(gpa);
122 defer arena.deinit();
123
124 var writer: Writer = .{
125 .gpa = gpa,
126 .arena = &arena.allocator,
127 .file = scope_file,
128 .code = scope_file.zir,
129 .indent = 0,
130 .parent_decl_node = 0,
131 };
132
133 const main_struct_inst = scope_file.zir.getMainStruct();
134 try fs_file.writer().print("%{d} ", .{main_struct_inst});
135 try writer.writeInstToStream(fs_file.writer(), main_struct_inst);
136 try fs_file.writeAll("\n");
137 const imports_index = scope_file.zir.extra[@enumToInt(ExtraIndex.imports)];
138 if (imports_index != 0) {
139 try fs_file.writeAll("Imports:\n");
140
141 const extra = scope_file.zir.extraData(Inst.Imports, imports_index);
142 var import_i: u32 = 0;
143 var extra_index = extra.end;
144
145 while (import_i < extra.data.imports_len) : (import_i += 1) {
146 const item = scope_file.zir.extraData(Inst.Imports.Item, extra_index);
147 extra_index = item.end;
148
149 const src: LazySrcLoc = .{ .token_abs = item.data.token };
150 const import_path = scope_file.zir.nullTerminatedString(item.data.name);
151 try fs_file.writer().print(" @import(\"{}\") ", .{
152 std.zig.fmtEscapes(import_path),
153 });
154 try writer.writeSrc(fs_file.writer(), src);
155 try fs_file.writer().writeAll("\n");
156 }
157 }
158}
109/// ZIR is structured so that the outermost "main" struct of any file
110/// is always at index 0.
111pub const main_struct_inst: Inst.Index = 0;
159112
160113/// These are untyped instructions generated from an Abstract Syntax Tree.
161114/// The data here is immutable because it is possible to have multiple
......@@ -173,6 +126,64 @@ pub const Inst = struct {
173126 /// Twos complement wrapping integer addition.
174127 /// Uses the `pl_node` union field. Payload is `Bin`.
175128 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
176187 /// Declares a parameter of the current function. Used for:
177188 /// * debug info
178189 /// * checking shadowing against declarations in the current namespace
......@@ -270,17 +281,9 @@ pub const Inst = struct {
270281 break_inline,
271282 /// Uses the `node` union field.
272283 breakpoint,
273 /// Function call with modifier `.auto`.
284 /// Function call.
274285 /// Uses `pl_node`. AST node is the function call. Payload is `Call`.
275286 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,
284287 /// `<`
285288 /// Uses the `pl_node` union field. Payload is `Bin`.
286289 cmp_lt,
......@@ -312,11 +315,6 @@ pub const Inst = struct {
312315 /// only the taken branch is analyzed. The then block and else block must
313316 /// terminate with an "inline" variant of a noreturn instruction.
314317 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,
320318 /// An error set type definition. Contains a list of field names.
321319 /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.
322320 error_set_decl,
......@@ -364,9 +362,13 @@ pub const Inst = struct {
364362 /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
365363 error_value,
366364 /// 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.
368366 /// Uses the `pl_node` union field. Payload is `Export`.
369367 @"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,
370372 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
371373 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
372374 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
......@@ -376,6 +378,15 @@ pub const Inst = struct {
376378 /// This instruction also accepts a pointer.
377379 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
378380 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,
379390 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
380391 /// to the named field. The field name is a comptime instruction. Used by @field.
381392 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
......@@ -384,6 +395,15 @@ pub const Inst = struct {
384395 /// The field name is a comptime instruction. Used by @field.
385396 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
386397 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,
387407 /// Returns a function type, or a function instance, depending on whether
388408 /// the body_len is 0. Calling convention is auto.
389409 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.
......@@ -433,25 +453,6 @@ pub const Inst = struct {
433453 /// Merge two error sets into one, `E1 || E2`.
434454 /// Uses the `pl_node` field with payload `Bin`.
435455 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,
455456 /// Turns an R-Value into a const L-Value. In other words, it takes a value,
456457 /// stores it in a memory location, and returns a const pointer to it. If the value
457458 /// is `comptime`, the memory location is global static constant data. Otherwise,
......@@ -488,10 +489,10 @@ pub const Inst = struct {
488489 /// this instruction; a following 'ret' instruction will do the diversion.
489490 /// Uses the `str_tok` union field.
490491 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.
492493 /// Uses the `ptr_type_simple` union field.
493494 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.
495496 /// Uses the `ptr_type` union field.
496497 ptr_type,
497498 /// Slice operation `lhs[rhs..]`. No sentinel and no end offset.
......@@ -528,12 +529,6 @@ pub const Inst = struct {
528529 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
529530 /// Uses the `str` union field.
530531 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,
537532 /// Arithmetic negation. Asserts no integer overflow.
538533 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.
539534 /// Uses `un_node`.
......@@ -731,7 +726,7 @@ pub const Inst = struct {
731726 size_of,
732727 /// Implements the `@bitSizeOf` builtin. Uses `un_node`.
733728 bit_size_of,
734 /// Implements the `@fence` builtin. Uses `node`.
729 /// Implements the `@fence` builtin. Uses `un_node`.
735730 fence,
736731
737732 /// Implement builtin `@ptrToInt`. Uses `un_node`.
......@@ -859,35 +854,6 @@ pub const Inst = struct {
859854 /// Implements the `@bitReverse` builtin. Uses the `un_node` union field.
860855 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
891857 /// Implements the `@bitOffsetOf` builtin.
892858 /// Uses the `pl_node` union field with payload `Bin`.
893859 bit_offset_of,
......@@ -982,6 +948,17 @@ pub const Inst = struct {
982948 @"await",
983949 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
985962 /// The ZIR instruction tag is one of the `Extended` ones.
986963 /// Uses the `extended` union field.
987964 extended,
......@@ -996,6 +973,7 @@ pub const Inst = struct {
996973 .param_anytype_comptime,
997974 .add,
998975 .addwrap,
976 .add_sat,
999977 .alloc,
1000978 .alloc_mut,
1001979 .alloc_comptime,
......@@ -1026,10 +1004,6 @@ pub const Inst = struct {
10261004 .breakpoint,
10271005 .fence,
10281006 .call,
1029 .call_chkused,
1030 .call_compile_time,
1031 .call_nosuspend,
1032 .call_async,
10331007 .cmp_lt,
10341008 .cmp_lte,
10351009 .cmp_eq,
......@@ -1037,9 +1011,6 @@ pub const Inst = struct {
10371011 .cmp_gt,
10381012 .cmp_neq,
10391013 .coerce_result_ptr,
1040 .opaque_decl,
1041 .opaque_decl_anon,
1042 .opaque_decl_func,
10431014 .error_set_decl,
10441015 .error_set_decl_anon,
10451016 .error_set_decl_func,
......@@ -1055,10 +1026,13 @@ pub const Inst = struct {
10551026 .ensure_result_used,
10561027 .ensure_result_non_error,
10571028 .@"export",
1029 .export_value,
10581030 .field_ptr,
10591031 .field_val,
1032 .field_call_bind,
10601033 .field_ptr_named,
10611034 .field_val_named,
1035 .field_call_bind_named,
10621036 .func,
10631037 .func_inferred,
10641038 .has_decl,
......@@ -1074,9 +1048,10 @@ pub const Inst = struct {
10741048 .mod_rem,
10751049 .mul,
10761050 .mulwrap,
1077 .param_type,
1051 .mul_sat,
10781052 .ref,
10791053 .shl,
1054 .shl_sat,
10801055 .shr,
10811056 .store,
10821057 .store_node,
......@@ -1085,6 +1060,7 @@ pub const Inst = struct {
10851060 .str,
10861061 .sub,
10871062 .subwrap,
1063 .sub_sat,
10881064 .negate,
10891065 .negate_wrap,
10901066 .typeof,
......@@ -1231,6 +1207,8 @@ pub const Inst = struct {
12311207 .await_nosuspend,
12321208 .ret_err_value_code,
12331209 .extended,
1210 .closure_get,
1211 .closure_capture,
12341212 => false,
12351213
12361214 .@"break",
......@@ -1256,6 +1234,14 @@ pub const Inst = struct {
12561234 break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{
12571235 .add = .pl_node,
12581236 .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
12591245 .param = .pl_tok,
12601246 .param_comptime = .pl_tok,
12611247 .param_anytype = .str_tok,
......@@ -1285,10 +1271,6 @@ pub const Inst = struct {
12851271 .break_inline = .@"break",
12861272 .breakpoint = .node,
12871273 .call = .pl_node,
1288 .call_chkused = .pl_node,
1289 .call_compile_time = .pl_node,
1290 .call_nosuspend = .pl_node,
1291 .call_async = .pl_node,
12921274 .cmp_lt = .pl_node,
12931275 .cmp_lte = .pl_node,
12941276 .cmp_eq = .pl_node,
......@@ -1298,9 +1280,6 @@ pub const Inst = struct {
12981280 .coerce_result_ptr = .bin,
12991281 .condbr = .pl_node,
13001282 .condbr_inline = .pl_node,
1301 .opaque_decl = .pl_node,
1302 .opaque_decl_anon = .pl_node,
1303 .opaque_decl_func = .pl_node,
13041283 .error_set_decl = .pl_node,
13051284 .error_set_decl_anon = .pl_node,
13061285 .error_set_decl_func = .pl_node,
......@@ -1318,10 +1297,13 @@ pub const Inst = struct {
13181297 .error_union_type = .pl_node,
13191298 .error_value = .str_tok,
13201299 .@"export" = .pl_node,
1300 .export_value = .pl_node,
13211301 .field_ptr = .pl_node,
13221302 .field_val = .pl_node,
13231303 .field_ptr_named = .pl_node,
13241304 .field_val_named = .pl_node,
1305 .field_call_bind = .pl_node,
1306 .field_call_bind_named = .pl_node,
13251307 .func = .pl_node,
13261308 .func_inferred = .pl_node,
13271309 .import = .str_tok,
......@@ -1339,9 +1321,6 @@ pub const Inst = struct {
13391321 .repeat_inline = .node,
13401322 .merge_error_sets = .pl_node,
13411323 .mod_rem = .pl_node,
1342 .mul = .pl_node,
1343 .mulwrap = .pl_node,
1344 .param_type = .param_type,
13451324 .ref = .un_tok,
13461325 .ret_node = .un_node,
13471326 .ret_load = .un_node,
......@@ -1358,8 +1337,6 @@ pub const Inst = struct {
13581337 .store_to_block_ptr = .bin,
13591338 .store_to_inferred_ptr = .bin,
13601339 .str = .str,
1361 .sub = .pl_node,
1362 .subwrap = .pl_node,
13631340 .negate = .un_node,
13641341 .negate_wrap = .un_node,
13651342 .typeof = .un_node,
......@@ -1416,7 +1393,7 @@ pub const Inst = struct {
14161393 .type_info = .un_node,
14171394 .size_of = .un_node,
14181395 .bit_size_of = .un_node,
1419 .fence = .node,
1396 .fence = .un_node,
14201397
14211398 .ptr_to_int = .un_node,
14221399 .error_to_int = .un_node,
......@@ -1480,6 +1457,7 @@ pub const Inst = struct {
14801457
14811458 .shl = .pl_node,
14821459 .shl_exact = .pl_node,
1460 .shl_sat = .pl_node,
14831461 .shr = .pl_node,
14841462 .shr_exact = .pl_node,
14851463
......@@ -1517,6 +1495,9 @@ pub const Inst = struct {
15171495 .@"await" = .un_node,
15181496 .await_nosuspend = .un_node,
15191497
1498 .closure_capture = .un_tok,
1499 .closure_get = .inst_node,
1500
15201501 .extended = .extended,
15211502 });
15221503 };
......@@ -1549,6 +1530,10 @@ pub const Inst = struct {
15491530 /// `operand` is payload index to `UnionDecl`.
15501531 /// `small` is `UnionDecl.Small`.
15511532 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,
15521537 /// Obtains a pointer to the return value.
15531538 /// `operand` is `src_node: i32`.
15541539 ret_ptr,
......@@ -1629,22 +1614,6 @@ pub const Inst = struct {
16291614 wasm_memory_size,
16301615 /// `operand` is payload index to `BinNode`.
16311616 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
16491618 pub const InstData = struct {
16501619 opcode: Extended,
......@@ -1717,6 +1686,7 @@ pub const Inst = struct {
17171686 atomic_order_type,
17181687 atomic_rmw_op_type,
17191688 calling_convention_type,
1689 address_space_type,
17201690 float_mode_type,
17211691 reduce_op_type,
17221692 call_options_type,
......@@ -1973,6 +1943,10 @@ pub const Inst = struct {
19731943 .ty = Type.initTag(.type),
19741944 .val = Value.initTag(.calling_convention_type),
19751945 },
1946 .address_space_type = .{
1947 .ty = Type.initTag(.type),
1948 .val = Value.initTag(.address_space_type),
1949 },
19761950 .float_mode_type = .{
19771951 .ty = Type.initTag(.type),
19781952 .val = Value.initTag(.float_mode_type),
......@@ -2174,8 +2148,9 @@ pub const Inst = struct {
21742148 is_volatile: bool,
21752149 has_sentinel: bool,
21762150 has_align: bool,
2151 has_addrspace: bool,
21772152 has_bit_range: bool,
2178 _: u2 = undefined,
2153 _: u1 = undefined,
21792154 },
21802155 size: std.builtin.TypeInfo.Pointer.Size,
21812156 /// Index into extra. See `PtrType`.
......@@ -2197,10 +2172,6 @@ pub const Inst = struct {
21972172 /// Points to a `Block`.
21982173 payload_index: u32,
21992174 },
2200 param_type: struct {
2201 callee: Ref,
2202 param_index: u32,
2203 },
22042175 @"unreachable": struct {
22052176 /// Offset from Decl AST node index.
22062177 /// `Tag` determines which kind of AST node this points to.
......@@ -2227,6 +2198,18 @@ pub const Inst = struct {
22272198 line: u32,
22282199 column: u32,
22292200 },
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
22312214 // Make sure we don't accidentally add a field to make this union
22322215 // bigger than expected. Note that in Debug builds, Zig is allowed
......@@ -2259,11 +2242,11 @@ pub const Inst = struct {
22592242 ptr_type,
22602243 int_type,
22612244 bool_br,
2262 param_type,
22632245 @"unreachable",
22642246 @"break",
22652247 switch_capture,
22662248 dbg_stmt,
2249 inst_node,
22672250 };
22682251 };
22692252
......@@ -2386,8 +2369,27 @@ pub const Inst = struct {
23862369 /// Stored inside extra, with trailing arguments according to `args_len`.
23872370 /// Each argument is a `Ref`.
23882371 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,
23892375 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 };
23912393 };
23922394
23932395 pub const BuiltinCall = struct {
......@@ -2405,12 +2407,13 @@ pub const Inst = struct {
24052407 else_body_len: u32,
24062408 };
24072409
2408 /// Stored in extra. Depending on the flags in Data, there will be up to 4
2410 /// Stored in extra. Depending on the flags in Data, there will be up to 5
24092411 /// trailing Ref fields:
24102412 /// 0. sentinel: Ref // if `has_sentinel` flag is set
24112413 /// 1. align: Ref // if `has_align` flag is set
2412 /// 2. bit_start: Ref // if `has_bit_range` flag is set
2413 /// 3. bit_end: Ref // if `has_bit_range` flag is set
2414 /// 2. address_space: Ref // if `has_addrspace` flag is set
2415 /// 3. bit_start: Ref // if `has_bit_range` flag is set
2416 /// 4. bit_end: Ref // if `has_bit_range` flag is set
24142417 pub const PtrType = struct {
24152418 elem_type: Ref,
24162419 };
......@@ -2528,7 +2531,7 @@ pub const Inst = struct {
25282531 /// 0b000X: whether corresponding decl is pub
25292532 /// 0b00X0: whether corresponding decl is exported
25302533 /// 0b0X00: whether corresponding decl has an align expression
2531 /// 0bX000: whether corresponding decl has a linksection expression
2534 /// 0bX000: whether corresponding decl has a linksection or an address space expression
25322535 /// 5. decl: { // for every decls_len
25332536 /// src_hash: [4]u32, // hash of source bytes
25342537 /// line: u32, // line number of decl, relative to parent
......@@ -2540,7 +2543,10 @@ pub const Inst = struct {
25402543 /// this is a test decl, and the name starts at `name+1`.
25412544 /// value: Index,
25422545 /// align: Ref, // if corresponding bit is set
2543 /// link_section: Ref, // if corresponding bit is set
2546 /// link_section_or_address_space: { // if corresponding bit is set.
2547 /// link_section: Ref,
2548 /// address_space: Ref,
2549 /// }
25442550 /// }
25452551 /// 6. inst: Index // for every body_len
25462552 /// 7. flags: u32 // for every 8 fields
......@@ -2592,7 +2598,7 @@ pub const Inst = struct {
25922598 /// 0b000X: whether corresponding decl is pub
25932599 /// 0b00X0: whether corresponding decl is exported
25942600 /// 0b0X00: whether corresponding decl has an align expression
2595 /// 0bX000: whether corresponding decl has a linksection expression
2601 /// 0bX000: whether corresponding decl has a linksection or an address space expression
25962602 /// 6. decl: { // for every decls_len
25972603 /// src_hash: [4]u32, // hash of source bytes
25982604 /// line: u32, // line number of decl, relative to parent
......@@ -2604,7 +2610,10 @@ pub const Inst = struct {
26042610 /// this is a test decl, and the name starts at `name+1`.
26052611 /// value: Index,
26062612 /// align: Ref, // if corresponding bit is set
2607 /// link_section: Ref, // if corresponding bit is set
2613 /// link_section_or_address_space: { // if corresponding bit is set.
2614 /// link_section: Ref,
2615 /// address_space: Ref,
2616 /// }
26082617 /// }
26092618 /// 7. inst: Index // for every body_len
26102619 /// 8. has_bits: u32 // for every 32 fields
......@@ -2637,7 +2646,7 @@ pub const Inst = struct {
26372646 /// 0b000X: whether corresponding decl is pub
26382647 /// 0b00X0: whether corresponding decl is exported
26392648 /// 0b0X00: whether corresponding decl has an align expression
2640 /// 0bX000: whether corresponding decl has a linksection expression
2649 /// 0bX000: whether corresponding decl has a linksection or an address space expression
26412650 /// 6. decl: { // for every decls_len
26422651 /// src_hash: [4]u32, // hash of source bytes
26432652 /// line: u32, // line number of decl, relative to parent
......@@ -2649,7 +2658,10 @@ pub const Inst = struct {
26492658 /// this is a test decl, and the name starts at `name+1`.
26502659 /// value: Index,
26512660 /// align: Ref, // if corresponding bit is set
2652 /// link_section: Ref, // if corresponding bit is set
2661 /// link_section_or_address_space: { // if corresponding bit is set.
2662 /// link_section: Ref,
2663 /// address_space: Ref,
2664 /// }
26532665 /// }
26542666 /// 7. inst: Index // for every body_len
26552667 /// 8. has_bits: u32 // for every 8 fields
......@@ -2673,21 +2685,27 @@ pub const Inst = struct {
26732685 has_decls_len: bool,
26742686 name_strategy: NameStrategy,
26752687 layout: std.builtin.TypeInfo.ContainerLayout,
2676 /// false: union(tag_type)
2677 /// true: union(enum(tag_type))
2688 /// has_tag_type | auto_enum_tag | result
2689 /// -------------------------------------
2690 /// false | false | union { }
2691 /// false | true | union(enum) { }
2692 /// true | true | union(enum(T)) { }
2693 /// true | false | union(T) { }
26782694 auto_enum_tag: bool,
26792695 _: u6 = undefined,
26802696 };
26812697 };
26822698
26832699 /// Trailing:
2684 /// 0. decl_bits: u32 // for every 8 decls
2700 /// 0. src_node: i32, // if has_src_node
2701 /// 1. decls_len: u32, // if has_decls_len
2702 /// 2. decl_bits: u32 // for every 8 decls
26852703 /// - sets of 4 bits:
26862704 /// 0b000X: whether corresponding decl is pub
26872705 /// 0b00X0: whether corresponding decl is exported
26882706 /// 0b0X00: whether corresponding decl has an align expression
2689 /// 0bX000: whether corresponding decl has a linksection expression
2690 /// 1. decl: { // for every decls_len
2707 /// 0bX000: whether corresponding decl has a linksection or an address space expression
2708 /// 3. decl: { // for every decls_len
26912709 /// src_hash: [4]u32, // hash of source bytes
26922710 /// line: u32, // line number of decl, relative to parent
26932711 /// name: u32, // null terminated string index
......@@ -2698,10 +2716,18 @@ pub const Inst = struct {
26982716 /// this is a test decl, and the name starts at `name+1`.
26992717 /// value: Index,
27002718 /// align: Ref, // if corresponding bit is set
2701 /// link_section: Ref, // if corresponding bit is set
2719 /// link_section_or_address_space: { // if corresponding bit is set.
2720 /// link_section: Ref,
2721 /// address_space: Ref,
2722 /// }
27022723 /// }
27032724 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 };
27052731 };
27062732
27072733 /// Trailing: field_name: u32 // for every field: null terminated string index
......@@ -2767,12 +2793,6 @@ pub const Inst = struct {
27672793 ptr: Ref,
27682794 };
27692795
2770 pub const SaturatingArithmetic = struct {
2771 node: i32,
2772 lhs: Ref,
2773 rhs: Ref,
2774 };
2775
27762796 pub const Cmpxchg = struct {
27772797 ptr: Ref,
27782798 expected_value: Ref,
......@@ -2858,6 +2878,14 @@ pub const Inst = struct {
28582878 /// 1. align_inst: Ref, // if small 0b00X0 is set
28592879 pub const AllocExtended = struct {
28602880 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 };
28612889 };
28622890
28632891 pub const Export = struct {
......@@ -2869,6 +2897,12 @@ pub const Inst = struct {
28692897 options: Ref,
28702898 };
28712899
2900 pub const ExportValue = struct {
2901 /// The comptime value to export.
2902 operand: Ref,
2903 options: Ref,
2904 };
2905
28722906 /// Trailing: `CompileErrors.Item` for each `items_len`.
28732907 pub const CompileErrors = struct {
28742908 items_len: u32,
......@@ -2904,1794 +2938,6 @@ pub const Inst = struct {
29042938
29052939pub 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
46952941pub const DeclIterator = struct {
46962942 extra_index: usize,
46972943 bit_bag_index: usize,
......@@ -4735,15 +2981,6 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator {
47352981 const tags = zir.instructions.items(.tag);
47362982 const datas = zir.instructions.items(.data);
47372983 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
47472984 // Functions are allowed and yield no iterations.
47482985 // There is one case matching this in the extended instruction set below.
47492986 .func,
......@@ -4798,6 +3035,18 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator {
47983035
47993036 return declIteratorInner(zir, extra_index, decls_len);
48003037 },
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 },
48013050 else => unreachable,
48023051 }
48033052 },
......@@ -4835,13 +3084,6 @@ fn findDeclsInner(
48353084 const datas = zir.instructions.items(.data);
48363085
48373086 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
48453087 // Functions instructions are interesting and have a body.
48463088 .func,
48473089 .func_inferred,
......@@ -4869,9 +3111,12 @@ fn findDeclsInner(
48693111 return zir.findDeclsBody(list, body);
48703112 },
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.
48723116 .struct_decl,
48733117 .union_decl,
48743118 .enum_decl,
3119 .opaque_decl,
48753120 => return list.append(inst),
48763121
48773122 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"),
24342434flagpd1("emit-header-module"),
24352435flagpd1("emit-html"),
24362436flagpd1("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},
24382445flagpd1("emit-llvm-bc"),
24392446flagpd1("emit-llvm-only"),
24402447flagpd1("emit-llvm-uselists"),
src/codegen.zig+281-68
......@@ -21,7 +21,7 @@ const log = std.log.scoped(.codegen);
2121const build_options = @import("build_options");
2222const 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
2626pub const FnResult = union(enum) {
2727 /// The `code` parameter passed to `generateSymbol` has the value appended.
......@@ -48,6 +48,28 @@ pub const DebugInfoOutput = union(enum) {
4848 dbg_info: *std.ArrayList(u8),
4949 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable,
5050 },
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 },
5173 none,
5274};
5375
......@@ -141,7 +163,7 @@ pub fn generateSymbol(
141163 // TODO populate .debug_info for the array
142164 if (typed_value.val.castTag(.bytes)) |payload| {
143165 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);
145167 code.appendSliceAssumeCapacity(payload.data);
146168 switch (try generateSymbol(bin_file, src_loc, .{
147169 .ty = typed_value.ty.elemType(),
......@@ -448,7 +470,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
448470 /// A branch in the ARM instruction set
449471 arm_branch: struct {
450472 pos: usize,
451 cond: @import("codegen/arm.zig").Condition,
473 cond: @import("arch/arm/bits.zig").Condition,
452474 },
453475 };
454476
......@@ -568,7 +590,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
568590 fn gen(self: *Self) !void {
569591 switch (arch) {
570592 .x86_64 => {
571 try self.code.ensureCapacity(self.code.items.len + 11);
593 try self.code.ensureUnusedCapacity(11);
572594
573595 const cc = self.fn_type.fnCallingConvention();
574596 if (cc != .Naked) {
......@@ -607,7 +629,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
607629 // Important to be after the possible self.code.items.len -= 5 above.
608630 try self.dbgSetEpilogueBegin();
609631
610 try self.code.ensureCapacity(self.code.items.len + 9);
632 try self.code.ensureUnusedCapacity(9);
611633 // add rsp, x
612634 if (aligned_stack_end > math.maxInt(i8)) {
613635 // example: 48 81 c4 ff ff ff 7f add rsp,0x7fffffff
......@@ -802,14 +824,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
802824
803825 switch (air_tags[inst]) {
804826 // zig fmt: off
805 .add, .ptr_add => try self.airAdd(inst),
806 .addwrap => try self.airAddWrap(inst),
807 .sub, .ptr_sub => try self.airSub(inst),
808 .subwrap => try self.airSubWrap(inst),
809 .mul => try self.airMul(inst),
810 .mulwrap => try self.airMulWrap(inst),
811 .div => try self.airDiv(inst),
812 .rem => try self.airRem(inst),
827 .add, .ptr_add => try self.airAdd(inst),
828 .addwrap => try self.airAddWrap(inst),
829 .add_sat => try self.airAddSat(inst),
830 .sub, .ptr_sub => try self.airSub(inst),
831 .subwrap => try self.airSubWrap(inst),
832 .sub_sat => try self.airSubSat(inst),
833 .mul => try self.airMul(inst),
834 .mulwrap => try self.airMulWrap(inst),
835 .mul_sat => try self.airMulSat(inst),
836 .div => try self.airDiv(inst),
837 .rem => try self.airRem(inst),
838 .mod => try self.airMod(inst),
839 .shl, .shl_exact => try self.airShl(inst),
840 .shl_sat => try self.airShlSat(inst),
813841
814842 .cmp_lt => try self.airCmp(inst, .lt),
815843 .cmp_lte => try self.airCmp(inst, .lte),
......@@ -824,7 +852,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
824852 .bit_or => try self.airBitOr(inst),
825853 .xor => try self.airXor(inst),
826854 .shr => try self.airShr(inst),
827 .shl => try self.airShl(inst),
828855
829856 .alloc => try self.airAlloc(inst),
830857 .arg => try self.airArg(inst),
......@@ -833,10 +860,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
833860 .block => try self.airBlock(inst),
834861 .br => try self.airBr(inst),
835862 .breakpoint => try self.airBreakpoint(),
863 .fence => try self.airFence(),
836864 .call => try self.airCall(inst),
837865 .cond_br => try self.airCondBr(inst),
838866 .dbg_stmt => try self.airDbgStmt(inst),
839 .floatcast => try self.airFloatCast(inst),
867 .fptrunc => try self.airFptrunc(inst),
868 .fpext => try self.airFpext(inst),
840869 .intcast => try self.airIntCast(inst),
841870 .trunc => try self.airTrunc(inst),
842871 .bool_to_int => try self.airBoolToInt(inst),
......@@ -857,8 +886,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
857886 .struct_field_ptr=> try self.airStructFieldPtr(inst),
858887 .struct_field_val=> try self.airStructFieldVal(inst),
859888 .array_to_slice => try self.airArrayToSlice(inst),
889 .int_to_float => try self.airIntToFloat(inst),
890 .float_to_int => try self.airFloatToInt(inst),
860891 .cmpxchg_strong => try self.airCmpxchg(inst),
861892 .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
863907 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
864908 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
......@@ -905,6 +949,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
905949 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
906950 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
907951 },
952 .plan9 => {},
908953 .none => {},
909954 }
910955 }
......@@ -915,15 +960,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
915960 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
916961 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
917962 },
963 .plan9 => {},
918964 .none => {},
919965 }
920966 }
921967
922968 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;
923971 switch (self.debug_output) {
924972 .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;
927973 // TODO Look into using the DWARF special opcodes to compress this data.
928974 // It lets you emit single-byte opcodes that add different numbers to
929975 // both the PC and the line number at the same time.
......@@ -935,12 +981,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
935981 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
936982 }
937983 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;
9381014 },
9391015 .none => {},
9401016 }
941 self.prev_di_line = line;
942 self.prev_di_column = column;
943 self.prev_di_pc = self.code.items.len;
9441017 }
9451018
9461019 /// 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 {
10241097 }
10251098 try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));
10261099 },
1100 .plan9 => {},
10271101 .none => {},
10281102 }
10291103 }
......@@ -1110,10 +1184,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11101184 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
11111185 }
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 {
11141196 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
11151197 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}),
11171199 };
11181200 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11191201 }
......@@ -1226,6 +1308,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12261308 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12271309 }
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
12291319 fn airSub(self: *Self, inst: Air.Inst.Index) !void {
12301320 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
12311321 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 {
12441334 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12451335 }
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
12471345 fn airMul(self: *Self, inst: Air.Inst.Index) !void {
12481346 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
12491347 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 {
12621360 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12631361 }
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
12651371 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
12661372 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
12671373 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 {
12781384 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12791385 }
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
12811395 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
12821396 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
12831397 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 {
13161430 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13171431 }
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
13191441 fn airShr(self: *Self, inst: Air.Inst.Index) !void {
13201442 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
13211443 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 {
14701592 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
14711593 }
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
14731627 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
14741628 if (!self.liveness.operandDies(inst, op_index))
14751629 return false;
......@@ -1848,15 +2002,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18482002 },
18492003 .shl => {
18502004 assert(!swap_lhs_and_rhs);
1851 const shift_amout = switch (operand) {
2005 const shift_amount = switch (operand) {
18522006 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
18532007 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
18542008 };
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());
18562010 },
18572011 .shr => {
18582012 assert(!swap_lhs_and_rhs);
1859 const shift_amout = switch (operand) {
2013 const shift_amount = switch (operand) {
18602014 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
18612015 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
18622016 };
......@@ -1865,7 +2019,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18652019 .signed => Instruction.asr,
18662020 .unsigned => Instruction.lsr,
18672021 };
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());
18692023 },
18702024 else => unreachable, // not a binary instruction
18712025 }
......@@ -1952,7 +2106,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19522106 //
19532107 // TODO: make this algorithm less bad
19542108
1955 try self.code.ensureCapacity(self.code.items.len + 8);
2109 try self.code.ensureUnusedCapacity(8);
19562110
19572111 const lhs = try self.resolveInst(op_lhs);
19582112 const rhs = try self.resolveInst(op_rhs);
......@@ -2439,16 +2593,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24392593 .register => |reg| {
24402594 switch (self.debug_output) {
24412595 .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);
24432597 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
24442598 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
24452599 1, // ULEB128 dwarf expression length
24462600 reg.dwarfLocOp(),
24472601 });
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);
24492603 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
24502604 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
24512605 },
2606 .plan9 => {},
24522607 .none => {},
24532608 }
24542609 },
......@@ -2476,13 +2631,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24762631 try dbg_out.dbg_info.append(DW.OP.breg11);
24772632 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);
24802635 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
24812636 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
24822637 },
24832638 else => {},
24842639 }
24852640 },
2641 .plan9 => {},
24862642 .none => {},
24872643 }
24882644 },
......@@ -2549,6 +2705,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25492705 return self.finishAirBookkeeping();
25502706 }
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
25522713 fn airCall(self: *Self, inst: Air.Inst.Index) !void {
25532714 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
25542715 const fn_ty = self.air.typeOf(pl_op.operand);
......@@ -2613,7 +2774,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26132774 unreachable;
26142775
26152776 // 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);
26172778 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
26182779 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
26192780 } else if (func_value.castTag(.extern_fn)) |_| {
......@@ -2826,7 +2987,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28262987 .memory = func.owner_decl.link.macho.local_sym_index,
28272988 });
28282989 // callq *%rax
2829 try self.code.ensureCapacity(self.code.items.len + 2);
2990 try self.code.ensureUnusedCapacity(2);
28302991 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
28312992 },
28322993 .aarch64 => {
......@@ -2840,12 +3001,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28403001 }
28413002 } else if (func_value.castTag(.extern_fn)) |func_payload| {
28423003 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));
28443005 const offset = blk: {
28453006 switch (arch) {
28463007 .x86_64 => {
28473008 // callq
2848 try self.code.ensureCapacity(self.code.items.len + 5);
3009 try self.code.ensureUnusedCapacity(5);
28493010 self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 });
28503011 break :blk @intCast(u32, self.code.items.len) - 4;
28513012 },
......@@ -2861,8 +3022,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28613022 // Add relocation to the decl.
28623023 try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{
28633024 .offset = offset,
2864 .where = .undef,
2865 .where_index = where_index,
3025 .where = switch (resolv.where) {
3026 .local => .local,
3027 .undef => .undef,
3028 },
3029 .where_index = resolv.where_index,
28663030 .payload = .{ .branch = .{
28673031 .arch = arch,
28683032 } },
......@@ -2911,12 +3075,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29113075 }
29123076 if (self.air.value(callee)) |func_value| {
29133077 if (func_value.castTag(.function)) |func_payload| {
3078 try p9.seeDecl(func_payload.data.owner_decl);
29143079 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
29153080 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
29163081 const got_addr = p9.bases.data;
29173082 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
29183083 // 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);
29203085 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
29213086 const fn_got_addr = got_addr + got_index * ptr_bytes;
29223087 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 {
29583123 }
29593124 if (self.air.value(callee)) |func_value| {
29603125 if (func_value.castTag(.function)) |func_payload| {
3126 try p9.seeDecl(func_payload.data.owner_decl);
29613127 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
29623128 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
29633129 const got_addr = p9.bases.data;
......@@ -3059,7 +3225,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30593225 const rhs = try self.resolveInst(bin_op.rhs);
30603226 const result: MCValue = switch (arch) {
30613227 .x86_64 => result: {
3062 try self.code.ensureCapacity(self.code.items.len + 8);
3228 try self.code.ensureUnusedCapacity(8);
30633229
30643230 // There are 2 operands, destination and source.
30653231 // Either one, but not both, can be a memory operand.
......@@ -3143,7 +3309,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31433309
31443310 const reloc: Reloc = switch (arch) {
31453311 .i386, .x86_64 => reloc: {
3146 try self.code.ensureCapacity(self.code.items.len + 6);
3312 try self.code.ensureUnusedCapacity(6);
31473313
31483314 const opcode: u8 = switch (cond) {
31493315 .compare_flags_signed => |cmp_op| blk: {
......@@ -3503,7 +3669,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
35033669 fn jump(self: *Self, index: usize) !void {
35043670 switch (arch) {
35053671 .i386, .x86_64 => {
3506 try self.code.ensureCapacity(self.code.items.len + 5);
3672 try self.code.ensureUnusedCapacity(5);
35073673 if (math.cast(i8, @intCast(i32, index) - (@intCast(i32, self.code.items.len + 2)))) |delta| {
35083674 self.code.appendAssumeCapacity(0xeb); // jmp rel8
35093675 self.code.appendAssumeCapacity(@bitCast(u8, delta));
......@@ -3535,7 +3701,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
35353701 try self.blocks.putNoClobber(self.gpa, inst, .{
35363702 // A block is a setup to be able to jump to the end.
35373703 .relocs = .{},
3538 // It also acts as a receptical for break operands.
3704 // It also acts as a receptacle for break operands.
35393705 // Here we use `MCValue.none` to represent a null value so that the first
35403706 // break instruction will choose a MCValue for the block result and overwrite
35413707 // 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 {
35893755 return self.fail("TODO: enable larger branch offset", .{});
35903756 }
35913757 },
3592 else => unreachable, // attempting to perfrom an ARM relocation on a non-ARM target arch
3758 else => unreachable, // attempting to perform an ARM relocation on a non-ARM target arch
35933759 }
35943760 },
35953761 }
......@@ -3641,7 +3807,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
36413807 const block_data = self.blocks.getPtr(block).?;
36423808
36433809 // 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
36463812 switch (arch) {
36473813 .i386, .x86_64 => {
......@@ -4025,7 +4191,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
40254191 if (adj_off > 128) {
40264192 return self.fail("TODO implement set stack variable with large stack offset", .{});
40274193 }
4028 try self.code.ensureCapacity(self.code.items.len + 8);
4194 try self.code.ensureUnusedCapacity(8);
40294195 switch (abi_size) {
40304196 1 => {
40314197 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 {
40514217
40524218 // 64 bit write to memory would take two mov's anyways so we
40534219 // 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);
40554221 var buf: [8]u8 = undefined;
40564222 mem.writeIntLittle(u64, &buf, x_big);
40574223
......@@ -4753,6 +4919,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
47534919 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
47544920 }
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
47564942 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
47574943 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
47584944 const extra = self.air.extraData(Air.Block, ty_pl.payload);
......@@ -4764,6 +4950,32 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
47644950 return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
47654951 }
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
47674979 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
47684980 // First section of indexes correspond to a set number of constant values.
47694981 const ref_int = @enumToInt(inst);
......@@ -4841,7 +5053,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
48415053 switch (typed_value.ty.zigTypeTag()) {
48425054 .Pointer => switch (typed_value.ty.ptrSize()) {
48435055 .Slice => {
4844 var buf: Type.Payload.ElemType = undefined;
5056 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
48455057 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
48465058 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
48475059 const slice_len = typed_value.val.sliceLen();
......@@ -4869,6 +5081,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
48695081 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
48705082 return MCValue{ .memory = got_addr };
48715083 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
5084 try p9.seeDecl(decl);
48725085 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
48735086 return MCValue{ .memory = got_addr };
48745087 } else {
......@@ -5216,11 +5429,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
52165429 }
52175430
52185431 const Register = switch (arch) {
5219 .i386 => @import("codegen/x86.zig").Register,
5220 .x86_64 => @import("codegen/x86_64.zig").Register,
5221 .riscv64 => @import("codegen/riscv64.zig").Register,
5222 .arm, .armeb => @import("codegen/arm.zig").Register,
5223 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Register,
5432 .i386 => @import("arch/x86/bits.zig").Register,
5433 .x86_64 => @import("arch/x86_64/bits.zig").Register,
5434 .riscv64 => @import("arch/riscv64/bits.zig").Register,
5435 .arm, .armeb => @import("arch/arm/bits.zig").Register,
5436 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Register,
52245437 else => enum {
52255438 dummy,
52265439
......@@ -5232,39 +5445,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
52325445 };
52335446
52345447 const Instruction = switch (arch) {
5235 .riscv64 => @import("codegen/riscv64.zig").Instruction,
5236 .arm, .armeb => @import("codegen/arm.zig").Instruction,
5237 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").Instruction,
5448 .riscv64 => @import("arch/riscv64/bits.zig").Instruction,
5449 .arm, .armeb => @import("arch/arm/bits.zig").Instruction,
5450 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Instruction,
52385451 else => void,
52395452 };
52405453
52415454 const Condition = switch (arch) {
5242 .arm, .armeb => @import("codegen/arm.zig").Condition,
5455 .arm, .armeb => @import("arch/arm/bits.zig").Condition,
52435456 else => void,
52445457 };
52455458
52465459 const callee_preserved_regs = switch (arch) {
5247 .i386 => @import("codegen/x86.zig").callee_preserved_regs,
5248 .x86_64 => @import("codegen/x86_64.zig").callee_preserved_regs,
5249 .riscv64 => @import("codegen/riscv64.zig").callee_preserved_regs,
5250 .arm, .armeb => @import("codegen/arm.zig").callee_preserved_regs,
5251 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").callee_preserved_regs,
5460 .i386 => @import("arch/x86/bits.zig").callee_preserved_regs,
5461 .x86_64 => @import("arch/x86_64/bits.zig").callee_preserved_regs,
5462 .riscv64 => @import("arch/riscv64/bits.zig").callee_preserved_regs,
5463 .arm, .armeb => @import("arch/arm/bits.zig").callee_preserved_regs,
5464 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").callee_preserved_regs,
52525465 else => [_]Register{},
52535466 };
52545467
52555468 const c_abi_int_param_regs = switch (arch) {
5256 .i386 => @import("codegen/x86.zig").c_abi_int_param_regs,
5257 .x86_64 => @import("codegen/x86_64.zig").c_abi_int_param_regs,
5258 .arm, .armeb => @import("codegen/arm.zig").c_abi_int_param_regs,
5259 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_param_regs,
5469 .i386 => @import("arch/x86/bits.zig").c_abi_int_param_regs,
5470 .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_param_regs,
5471 .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_param_regs,
5472 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_param_regs,
52605473 else => [_]Register{},
52615474 };
52625475
52635476 const c_abi_int_return_regs = switch (arch) {
5264 .i386 => @import("codegen/x86.zig").c_abi_int_return_regs,
5265 .x86_64 => @import("codegen/x86_64.zig").c_abi_int_return_regs,
5266 .arm, .armeb => @import("codegen/arm.zig").c_abi_int_return_regs,
5267 .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig").c_abi_int_return_regs,
5477 .i386 => @import("arch/x86/bits.zig").c_abi_int_return_regs,
5478 .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_return_regs,
5479 .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_return_regs,
5480 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_return_regs,
52685481 else => [_]Register{},
52695482 };
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) {
9191 return .{ .data = ident };
9292}
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`.
9595/// It is not available when generating .h file.
96pub const Object = struct {
97 dg: DeclGen,
96pub const Function = struct {
9897 air: Air,
9998 liveness: Liveness,
100 gpa: *mem.Allocator,
101 code: std.ArrayList(u8),
10299 value_map: CValueMap,
103100 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
104101 next_arg_index: usize = 0,
105102 next_local_index: usize = 0,
106103 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 {
110 if (o.air.value(inst)) |_| {
107 fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue {
108 if (f.air.value(inst)) |_| {
111109 return CValue{ .constant = inst };
112110 }
113111 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.
115113 }
116114
117 fn allocLocalValue(o: *Object) CValue {
118 const result = o.next_local_index;
119 o.next_local_index += 1;
115 fn allocLocalValue(f: *Function) CValue {
116 const result = f.next_local_index;
117 f.next_local_index += 1;
120118 return .{ .local = result };
121119 }
122120
123 fn allocLocal(o: *Object, ty: Type, mutability: Mutability) !CValue {
124 const local_value = o.allocLocalValue();
125 try o.renderTypeAndName(o.writer(), ty, local_value, mutability);
121 fn allocLocal(f: *Function, ty: Type, mutability: Mutability) !CValue {
122 const local_value = f.allocLocalValue();
123 try f.object.renderTypeAndName(f.object.writer(), ty, local_value, mutability);
126124 return local_value;
127125 }
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
129154 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
130155 return o.indent_writer.writer();
131156 }
132157
133 fn writeCValue(o: *Object, w: anytype, c_value: CValue) !void {
158 fn writeCValue(w: anytype, c_value: CValue) !void {
134159 switch (c_value) {
135160 .none => unreachable,
136161 .local => |i| return w.print("t{d}", .{i}),
137162 .local_ref => |i| return w.print("&t{d}", .{i}),
138 .constant => |inst| {
139 const ty = o.air.typeOf(inst);
140 const val = o.air.value(inst).?;
141 return o.dg.renderValue(w, ty, val);
142 },
163 .constant => unreachable,
143164 .arg => |i| return w.print("a{d}", .{i}),
144165 .decl => |decl| return w.writeAll(mem.span(decl.name)),
145166 .decl_ref => |decl| return w.print("&{s}", .{decl.name}),
......@@ -153,7 +174,7 @@ pub const Object = struct {
153174 name: CValue,
154175 mutability: Mutability,
155176 ) error{ OutOfMemory, AnalysisFail }!void {
156 var suffix = std.ArrayList(u8).init(o.gpa);
177 var suffix = std.ArrayList(u8).init(o.dg.gpa);
157178 defer suffix.deinit();
158179
159180 var render_ty = ty;
......@@ -177,7 +198,7 @@ pub const Object = struct {
177198 .Const => try w.writeAll("const "),
178199 .Mut => {},
179200 }
180 try o.writeCValue(w, name);
201 try writeCValue(w, name);
181202 try w.writeAll(")(");
182203 const param_len = render_ty.fnParamLen();
183204 const is_var_args = render_ty.fnIsVarArgs();
......@@ -205,7 +226,7 @@ pub const Object = struct {
205226 .Mut => "",
206227 };
207228 try w.print(" {s}", .{const_prefix});
208 try o.writeCValue(w, name);
229 try writeCValue(w, name);
209230 }
210231 try w.writeAll(suffix.items);
211232 }
......@@ -213,11 +234,14 @@ pub const Object = struct {
213234
214235/// This data is available both when outputting .c code and when outputting an .h file.
215236pub const DeclGen = struct {
237 gpa: *std.mem.Allocator,
216238 module: *Module,
217239 decl: *Decl,
218240 fwd_decl: std.ArrayList(u8),
219241 error_msg: ?*Module.ErrorMsg,
242 /// The key of this map is Type which has references to typedefs_arena.
220243 typedefs: TypedefMap,
244 typedefs_arena: *std.mem.Allocator,
221245
222246 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
223247 @setCold(true);
......@@ -251,7 +275,7 @@ pub const DeclGen = struct {
251275 try writer.writeByte('(');
252276 try dg.renderType(writer, t);
253277 try writer.writeAll("){");
254 var buf: Type.Payload.ElemType = undefined;
278 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
255279 try dg.renderValue(writer, t.slicePtrFieldType(&buf), val);
256280 try writer.writeAll(", ");
257281 try writer.print("{d}", .{val.sliceLen()});
......@@ -545,7 +569,10 @@ pub const DeclGen = struct {
545569
546570 try dg.typedefs.ensureUnusedCapacity(1);
547571 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 );
549576 } else {
550577 try dg.renderType(w, t.elemType());
551578 try w.writeAll(" *");
......@@ -586,7 +613,10 @@ pub const DeclGen = struct {
586613
587614 try dg.typedefs.ensureUnusedCapacity(1);
588615 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 );
590620 },
591621 .ErrorSet => {
592622 comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2);
......@@ -626,7 +656,10 @@ pub const DeclGen = struct {
626656
627657 try dg.typedefs.ensureUnusedCapacity(1);
628658 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 );
630663 },
631664 .Struct => {
632665 if (dg.typedefs.get(t)) |some| {
......@@ -659,7 +692,10 @@ pub const DeclGen = struct {
659692
660693 try dg.typedefs.ensureUnusedCapacity(1);
661694 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 );
663699 },
664700 .Enum => {
665701 // For enums, we simply use the integer tag type.
......@@ -724,6 +760,29 @@ pub const DeclGen = struct {
724760 }
725761};
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
727786pub fn genDecl(o: *Object) !void {
728787 const tracy = trace(@src());
729788 defer tracy.end();
......@@ -732,28 +791,6 @@ pub fn genDecl(o: *Object) !void {
732791 .ty = o.dg.decl.ty,
733792 .val = o.dg.decl.val,
734793 };
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 }
757794 if (tv.val.tag() == .extern_fn) {
758795 const writer = o.writer();
759796 try writer.writeAll("ZIG_EXTERN_C ");
......@@ -821,240 +858,263 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void {
821858 }
822859}
823860
824fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
825 const writer = o.writer();
861fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
862 const writer = f.object.writer();
826863 if (body.len == 0) {
827864 try writer.writeAll("{}");
828865 return;
829866 }
830867
831868 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
836873 for (body) |inst| {
837874 const result_value = switch (air_tags[inst]) {
838875 // zig fmt: off
839876 .constant => unreachable, // excluded from function bodies
840877 .const_ty => unreachable, // excluded from function bodies
841 .arg => airArg(o),
878 .arg => airArg(f),
842879
843 .breakpoint => try airBreakpoint(o),
844 .unreach => try airUnreach(o),
880 .breakpoint => try airBreakpoint(f),
881 .unreach => try airUnreach(f),
882 .fence => try airFence(f, inst),
845883
846884 // TODO use a different strategy for add that communicates to the optimizer
847885 // that wrapping is UB.
848 .add, .ptr_add => try airBinOp( o, inst, " + "),
849 .addwrap => try airWrapOp(o, inst, " + ", "addw_"),
886 .add, .ptr_add => try airBinOp (f, inst, " + "),
850887 // TODO use a different strategy for sub that communicates to the optimizer
851888 // that wrapping is UB.
852 .sub, .ptr_sub => try airBinOp( o, inst, " - "),
853 .subwrap => try airWrapOp(o, inst, " - ", "subw_"),
889 .sub, .ptr_sub => try airBinOp (f, inst, " - "),
854890 // TODO use a different strategy for mul that communicates to the optimizer
855891 // that wrapping is UB.
856 .mul => try airBinOp( o, inst, " * "),
857 .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"),
892 .mul => try airBinOp (f, inst, " * "),
858893 // TODO use a different strategy for div that communicates to the optimizer
859894 // that wrapping is UB.
860 .div => try airBinOp( o, inst, " / "),
861 .rem => try airBinOp( o, inst, " % "),
862
863 .cmp_eq => try airBinOp(o, inst, " == "),
864 .cmp_gt => try airBinOp(o, inst, " > "),
865 .cmp_gte => try airBinOp(o, inst, " >= "),
866 .cmp_lt => try airBinOp(o, inst, " < "),
867 .cmp_lte => try airBinOp(o, inst, " <= "),
868 .cmp_neq => try airBinOp(o, inst, " != "),
895 .div => try airBinOp( f, inst, " / "),
896 .rem => try airBinOp( f, inst, " % "),
897 .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division
898
899 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
900 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
901 .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
902
903 .add_sat => try airSatOp(f, inst, "adds_"),
904 .sub_sat => try airSatOp(f, inst, "subs_"),
905 .mul_sat => try airSatOp(f, inst, "muls_"),
906 .shl_sat => try airSatOp(f, inst, "shls_"),
907
908 .cmp_eq => try airBinOp(f, inst, " == "),
909 .cmp_gt => try airBinOp(f, inst, " > "),
910 .cmp_gte => try airBinOp(f, inst, " >= "),
911 .cmp_lt => try airBinOp(f, inst, " < "),
912 .cmp_lte => try airBinOp(f, inst, " <= "),
913 .cmp_neq => try airBinOp(f, inst, " != "),
869914
870915 // bool_and and bool_or are non-short-circuit operations
871 .bool_and => try airBinOp(o, inst, " & "),
872 .bool_or => try airBinOp(o, inst, " | "),
873 .bit_and => try airBinOp(o, inst, " & "),
874 .bit_or => try airBinOp(o, inst, " | "),
875 .xor => try airBinOp(o, inst, " ^ "),
876
877 .shr => try airBinOp(o, inst, " >> "),
878 .shl => try airBinOp(o, inst, " << "),
879
880 .not => try airNot( o, inst),
881
882 .optional_payload => try airOptionalPayload(o, inst),
883 .optional_payload_ptr => try airOptionalPayload(o, inst),
884
885 .is_err => try airIsErr(o, inst, "", ".", "!="),
886 .is_non_err => try airIsErr(o, inst, "", ".", "=="),
887 .is_err_ptr => try airIsErr(o, inst, "*", "->", "!="),
888 .is_non_err_ptr => try airIsErr(o, inst, "*", "->", "=="),
889
890 .is_null => try airIsNull(o, inst, "==", ""),
891 .is_non_null => try airIsNull(o, inst, "!=", ""),
892 .is_null_ptr => try airIsNull(o, inst, "==", "[0]"),
893 .is_non_null_ptr => try airIsNull(o, inst, "!=", "[0]"),
894
895 .alloc => try airAlloc(o, inst),
896 .assembly => try airAsm(o, inst),
897 .block => try airBlock(o, inst),
898 .bitcast => try airBitcast(o, inst),
899 .call => try airCall(o, inst),
900 .dbg_stmt => try airDbgStmt(o, inst),
901 .intcast => try airIntCast(o, inst),
902 .trunc => try airTrunc(o, inst),
903 .bool_to_int => try airBoolToInt(o, inst),
904 .load => try airLoad(o, inst),
905 .ret => try airRet(o, inst),
906 .store => try airStore(o, inst),
907 .loop => try airLoop(o, inst),
908 .cond_br => try airCondBr(o, inst),
909 .br => try airBr(o, inst),
910 .switch_br => try airSwitchBr(o, inst),
911 .wrap_optional => try airWrapOptional(o, inst),
912 .struct_field_ptr => try airStructFieldPtr(o, inst),
913 .array_to_slice => try airArrayToSlice(o, inst),
914 .cmpxchg_weak => try airCmpxchg(o, inst, "weak"),
915 .cmpxchg_strong => try airCmpxchg(o, inst, "strong"),
916
917 .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0),
918 .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1),
919 .struct_field_ptr_index_2 => try airStructFieldPtrIndex(o, inst, 2),
920 .struct_field_ptr_index_3 => try airStructFieldPtrIndex(o, inst, 3),
921
922 .struct_field_val => try airStructFieldVal(o, inst),
923 .slice_ptr => try airSliceField(o, inst, ".ptr;\n"),
924 .slice_len => try airSliceField(o, inst, ".len;\n"),
925
926 .ptr_elem_val => try airPtrElemVal(o, inst, "["),
927 .ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["),
928 .ptr_elem_ptr => try airPtrElemPtr(o, inst),
929 .slice_elem_val => try airSliceElemVal(o, inst, "["),
930 .ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["),
931
932 .unwrap_errunion_payload => try airUnwrapErrUnionPay(o, inst),
933 .unwrap_errunion_err => try airUnwrapErrUnionErr(o, inst),
934 .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(o, inst),
935 .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(o, inst),
936 .wrap_errunion_payload => try airWrapErrUnionPay(o, inst),
937 .wrap_errunion_err => try airWrapErrUnionErr(o, inst),
938
939 .ptrtoint => return o.dg.fail("TODO: C backend: implement codegen for ptrtoint", .{}),
940 .floatcast => return o.dg.fail("TODO: C backend: implement codegen for floatcast", .{}),
916 .bool_and => try airBinOp(f, inst, " & "),
917 .bool_or => try airBinOp(f, inst, " | "),
918 .bit_and => try airBinOp(f, inst, " & "),
919 .bit_or => try airBinOp(f, inst, " | "),
920 .xor => try airBinOp(f, inst, " ^ "),
921 .shr => try airBinOp(f, inst, " >> "),
922 .shl, .shl_exact => try airBinOp(f, inst, " << "),
923 .not => try airNot (f, inst),
924
925 .optional_payload => try airOptionalPayload(f, inst),
926 .optional_payload_ptr => try airOptionalPayload(f, inst),
927
928 .is_err => try airIsErr(f, inst, "", ".", "!="),
929 .is_non_err => try airIsErr(f, inst, "", ".", "=="),
930 .is_err_ptr => try airIsErr(f, inst, "*", "->", "!="),
931 .is_non_err_ptr => try airIsErr(f, inst, "*", "->", "=="),
932
933 .is_null => try airIsNull(f, inst, "==", ""),
934 .is_non_null => try airIsNull(f, inst, "!=", ""),
935 .is_null_ptr => try airIsNull(f, inst, "==", "[0]"),
936 .is_non_null_ptr => try airIsNull(f, inst, "!=", "[0]"),
937
938 .alloc => try airAlloc(f, inst),
939 .assembly => try airAsm(f, inst),
940 .block => try airBlock(f, inst),
941 .bitcast => try airBitcast(f, inst),
942 .call => try airCall(f, inst),
943 .dbg_stmt => try airDbgStmt(f, inst),
944 .intcast => try airIntCast(f, inst),
945 .trunc => try airTrunc(f, inst),
946 .bool_to_int => try airBoolToInt(f, inst),
947 .load => try airLoad(f, inst),
948 .ret => try airRet(f, inst),
949 .store => try airStore(f, inst),
950 .loop => try airLoop(f, inst),
951 .cond_br => try airCondBr(f, inst),
952 .br => try airBr(f, inst),
953 .switch_br => try airSwitchBr(f, inst),
954 .wrap_optional => try airWrapOptional(f, inst),
955 .struct_field_ptr => try airStructFieldPtr(f, inst),
956 .array_to_slice => try airArrayToSlice(f, inst),
957 .cmpxchg_weak => try airCmpxchg(f, inst, "weak"),
958 .cmpxchg_strong => try airCmpxchg(f, inst, "strong"),
959 .atomic_rmw => try airAtomicRmw(f, inst),
960 .atomic_load => try airAtomicLoad(f, inst),
961 .memset => try airMemset(f, inst),
962 .memcpy => try airMemcpy(f, inst),
963 .set_union_tag => try airSetUnionTag(f, inst),
964 .get_union_tag => try airGetUnionTag(f, inst),
965 .clz => try airBuiltinCall(f, inst, "clz"),
966 .ctz => try airBuiltinCall(f, inst, "ctz"),
967
968 .int_to_float,
969 .float_to_int,
970 .fptrunc,
971 .fpext,
972 .ptrtoint,
973 => try airSimpleCast(f, inst),
974
975 .atomic_store_unordered => try airAtomicStore(f, inst, toMemoryOrder(.Unordered)),
976 .atomic_store_monotonic => try airAtomicStore(f, inst, toMemoryOrder(.Monotonic)),
977 .atomic_store_release => try airAtomicStore(f, inst, toMemoryOrder(.Release)),
978 .atomic_store_seq_cst => try airAtomicStore(f, inst, toMemoryOrder(.SeqCst)),
979
980 .struct_field_ptr_index_0 => try airStructFieldPtrIndex(f, inst, 0),
981 .struct_field_ptr_index_1 => try airStructFieldPtrIndex(f, inst, 1),
982 .struct_field_ptr_index_2 => try airStructFieldPtrIndex(f, inst, 2),
983 .struct_field_ptr_index_3 => try airStructFieldPtrIndex(f, inst, 3),
984
985 .struct_field_val => try airStructFieldVal(f, inst),
986 .slice_ptr => try airSliceField(f, inst, ".ptr;\n"),
987 .slice_len => try airSliceField(f, inst, ".len;\n"),
988
989 .ptr_elem_val => try airPtrElemVal(f, inst, "["),
990 .ptr_ptr_elem_val => try airPtrElemVal(f, inst, "[0]["),
991 .ptr_elem_ptr => try airPtrElemPtr(f, inst),
992 .slice_elem_val => try airSliceElemVal(f, inst, "["),
993 .ptr_slice_elem_val => try airSliceElemVal(f, inst, "[0]["),
994
995 .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst),
996 .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst),
997 .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst),
998 .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(f, inst),
999 .wrap_errunion_payload => try airWrapErrUnionPay(f, inst),
1000 .wrap_errunion_err => try airWrapErrUnionErr(f, inst),
9411001 // zig fmt: on
9421002 };
9431003 switch (result_value) {
9441004 .none => {},
945 else => try o.value_map.putNoClobber(inst, result_value),
1005 else => try f.value_map.putNoClobber(inst, result_value),
9461006 }
9471007 }
9481008
949 o.indent_writer.popIndent();
1009 f.object.indent_writer.popIndent();
9501010 try writer.writeAll("}");
9511011}
9521012
953fn airSliceField(o: *Object, inst: Air.Inst.Index, suffix: []const u8) !CValue {
954 if (o.liveness.isUnused(inst))
1013fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue {
1014 if (f.liveness.isUnused(inst))
9551015 return CValue.none;
9561016
957 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
958 const operand = try o.resolveInst(ty_op.operand);
959 const writer = o.writer();
960 const local = try o.allocLocal(Type.initTag(.usize), .Const);
1017 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1018 const operand = try f.resolveInst(ty_op.operand);
1019 const writer = f.object.writer();
1020 const local = try f.allocLocal(Type.initTag(.usize), .Const);
9611021 try writer.writeAll(" = ");
962 try o.writeCValue(writer, operand);
1022 try f.writeCValue(writer, operand);
9631023 try writer.writeAll(suffix);
9641024 return local;
9651025}
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 {
9681028 const is_volatile = false; // TODO
969 if (!is_volatile and o.liveness.isUnused(inst))
1029 if (!is_volatile and f.liveness.isUnused(inst))
9701030 return CValue.none;
9711031
9721032 _ = prefix;
973 return o.dg.fail("TODO: C backend: airPtrElemVal", .{});
1033 return f.fail("TODO: C backend: airPtrElemVal", .{});
9741034}
9751035
976fn airPtrElemPtr(o: *Object, inst: Air.Inst.Index) !CValue {
977 if (o.liveness.isUnused(inst))
1036fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
1037 if (f.liveness.isUnused(inst))
9781038 return CValue.none;
9791039
980 return o.dg.fail("TODO: C backend: airPtrElemPtr", .{});
1040 return f.fail("TODO: C backend: airPtrElemPtr", .{});
9811041}
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 {
9841044 const is_volatile = false; // TODO
985 if (!is_volatile and o.liveness.isUnused(inst))
1045 if (!is_volatile and f.liveness.isUnused(inst))
9861046 return CValue.none;
9871047
988 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
989 const slice = try o.resolveInst(bin_op.lhs);
990 const index = try o.resolveInst(bin_op.rhs);
991 const writer = o.writer();
992 const local = try o.allocLocal(o.air.typeOfIndex(inst), .Const);
1048 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1049 const slice = try f.resolveInst(bin_op.lhs);
1050 const index = try f.resolveInst(bin_op.rhs);
1051 const writer = f.object.writer();
1052 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
9931053 try writer.writeAll(" = ");
994 try o.writeCValue(writer, slice);
1054 try f.writeCValue(writer, slice);
9951055 try writer.writeAll(prefix);
996 try o.writeCValue(writer, index);
1056 try f.writeCValue(writer, index);
9971057 try writer.writeAll("];\n");
9981058 return local;
9991059}
10001060
1001fn airAlloc(o: *Object, inst: Air.Inst.Index) !CValue {
1002 const writer = o.writer();
1003 const inst_ty = o.air.typeOfIndex(inst);
1061fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
1062 const writer = f.object.writer();
1063 const inst_ty = f.air.typeOfIndex(inst);
10041064
10051065 // First line: the variable used as data storage.
10061066 const elem_type = inst_ty.elemType();
10071067 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);
10091069 try writer.writeAll(";\n");
10101070
10111071 return CValue{ .local_ref = local.local };
10121072}
10131073
1014fn airArg(o: *Object) CValue {
1015 const i = o.next_arg_index;
1016 o.next_arg_index += 1;
1074fn airArg(f: *Function) CValue {
1075 const i = f.next_arg_index;
1076 f.next_arg_index += 1;
10171077 return .{ .arg = i };
10181078}
10191079
1020fn airLoad(o: *Object, inst: Air.Inst.Index) !CValue {
1021 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1022 const is_volatile = o.air.typeOf(ty_op.operand).isVolatilePtr();
1023 if (!is_volatile and o.liveness.isUnused(inst))
1080fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
1081 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1082 const is_volatile = f.air.typeOf(ty_op.operand).isVolatilePtr();
1083 if (!is_volatile and f.liveness.isUnused(inst))
10241084 return CValue.none;
1025 const inst_ty = o.air.typeOfIndex(inst);
1026 const operand = try o.resolveInst(ty_op.operand);
1027 const writer = o.writer();
1028 const local = try o.allocLocal(inst_ty, .Const);
1085 const inst_ty = f.air.typeOfIndex(inst);
1086 const operand = try f.resolveInst(ty_op.operand);
1087 const writer = f.object.writer();
1088 const local = try f.allocLocal(inst_ty, .Const);
10291089 switch (operand) {
10301090 .local_ref => |i| {
10311091 const wrapped: CValue = .{ .local = i };
10321092 try writer.writeAll(" = ");
1033 try o.writeCValue(writer, wrapped);
1093 try f.writeCValue(writer, wrapped);
10341094 try writer.writeAll(";\n");
10351095 },
10361096 .decl_ref => |decl| {
10371097 const wrapped: CValue = .{ .decl = decl };
10381098 try writer.writeAll(" = ");
1039 try o.writeCValue(writer, wrapped);
1099 try f.writeCValue(writer, wrapped);
10401100 try writer.writeAll(";\n");
10411101 },
10421102 else => {
10431103 try writer.writeAll(" = *");
1044 try o.writeCValue(writer, operand);
1104 try f.writeCValue(writer, operand);
10451105 try writer.writeAll(";\n");
10461106 },
10471107 }
10481108 return local;
10491109}
10501110
1051fn airRet(o: *Object, inst: Air.Inst.Index) !CValue {
1052 const un_op = o.air.instructions.items(.data)[inst].un_op;
1053 const writer = o.writer();
1054 if (o.air.typeOf(un_op).hasCodeGenBits()) {
1055 const operand = try o.resolveInst(un_op);
1111fn airRet(f: *Function, inst: Air.Inst.Index) !CValue {
1112 const un_op = f.air.instructions.items(.data)[inst].un_op;
1113 const writer = f.object.writer();
1114 if (f.air.typeOf(un_op).hasCodeGenBits()) {
1115 const operand = try f.resolveInst(un_op);
10561116 try writer.writeAll("return ");
1057 try o.writeCValue(writer, operand);
1117 try f.writeCValue(writer, operand);
10581118 try writer.writeAll(";\n");
10591119 } else {
10601120 try writer.writeAll("return;\n");
......@@ -1062,75 +1122,75 @@ fn airRet(o: *Object, inst: Air.Inst.Index) !CValue {
10621122 return CValue.none;
10631123}
10641124
1065fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
1066 if (o.liveness.isUnused(inst))
1125fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
1126 if (f.liveness.isUnused(inst))
10671127 return CValue.none;
10681128
1069 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1070 const operand = try o.resolveInst(ty_op.operand);
1129 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1130 const operand = try f.resolveInst(ty_op.operand);
10711131
1072 const writer = o.writer();
1073 const inst_ty = o.air.typeOfIndex(inst);
1074 const local = try o.allocLocal(inst_ty, .Const);
1132 const writer = f.object.writer();
1133 const inst_ty = f.air.typeOfIndex(inst);
1134 const local = try f.allocLocal(inst_ty, .Const);
10751135 try writer.writeAll(" = (");
1076 try o.dg.renderType(writer, inst_ty);
1136 try f.renderType(writer, inst_ty);
10771137 try writer.writeAll(")");
1078 try o.writeCValue(writer, operand);
1138 try f.writeCValue(writer, operand);
10791139 try writer.writeAll(";\n");
10801140 return local;
10811141}
10821142
1083fn airTrunc(o: *Object, inst: Air.Inst.Index) !CValue {
1084 if (o.liveness.isUnused(inst))
1143fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
1144 if (f.liveness.isUnused(inst))
10851145 return CValue.none;
10861146
1087 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1088 const operand = try o.resolveInst(ty_op.operand);
1147 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1148 const operand = try f.resolveInst(ty_op.operand);
10891149 _ = operand;
1090 return o.dg.fail("TODO: C backend: airTrunc", .{});
1150 return f.fail("TODO: C backend: airTrunc", .{});
10911151}
10921152
1093fn airBoolToInt(o: *Object, inst: Air.Inst.Index) !CValue {
1094 if (o.liveness.isUnused(inst))
1153fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
1154 if (f.liveness.isUnused(inst))
10951155 return CValue.none;
1096 const un_op = o.air.instructions.items(.data)[inst].un_op;
1097 const writer = o.writer();
1098 const inst_ty = o.air.typeOfIndex(inst);
1099 const operand = try o.resolveInst(un_op);
1100 const local = try o.allocLocal(inst_ty, .Const);
1156 const un_op = f.air.instructions.items(.data)[inst].un_op;
1157 const writer = f.object.writer();
1158 const inst_ty = f.air.typeOfIndex(inst);
1159 const operand = try f.resolveInst(un_op);
1160 const local = try f.allocLocal(inst_ty, .Const);
11011161 try writer.writeAll(" = ");
1102 try o.writeCValue(writer, operand);
1162 try f.writeCValue(writer, operand);
11031163 try writer.writeAll(";\n");
11041164 return local;
11051165}
11061166
1107fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {
1167fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
11081168 // *a = b;
1109 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
1110 const dest_ptr = try o.resolveInst(bin_op.lhs);
1111 const src_val = try o.resolveInst(bin_op.rhs);
1169 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1170 const dest_ptr = try f.resolveInst(bin_op.lhs);
1171 const src_val = try f.resolveInst(bin_op.rhs);
11121172
1113 const writer = o.writer();
1173 const writer = f.object.writer();
11141174 switch (dest_ptr) {
11151175 .local_ref => |i| {
11161176 const dest: CValue = .{ .local = i };
1117 try o.writeCValue(writer, dest);
1177 try f.writeCValue(writer, dest);
11181178 try writer.writeAll(" = ");
1119 try o.writeCValue(writer, src_val);
1179 try f.writeCValue(writer, src_val);
11201180 try writer.writeAll(";\n");
11211181 },
11221182 .decl_ref => |decl| {
11231183 const dest: CValue = .{ .decl = decl };
1124 try o.writeCValue(writer, dest);
1184 try f.writeCValue(writer, dest);
11251185 try writer.writeAll(" = ");
1126 try o.writeCValue(writer, src_val);
1186 try f.writeCValue(writer, src_val);
11271187 try writer.writeAll(";\n");
11281188 },
11291189 else => {
11301190 try writer.writeAll("*");
1131 try o.writeCValue(writer, dest_ptr);
1191 try f.writeCValue(writer, dest_ptr);
11321192 try writer.writeAll(" = ");
1133 try o.writeCValue(writer, src_val);
1193 try f.writeCValue(writer, src_val);
11341194 try writer.writeAll(";\n");
11351195 },
11361196 }
......@@ -1138,17 +1198,17 @@ fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {
11381198}
11391199
11401200fn airWrapOp(
1141 o: *Object,
1201 f: *Function,
11421202 inst: Air.Inst.Index,
11431203 str_op: [*:0]const u8,
11441204 fn_op: [*:0]const u8,
11451205) !CValue {
1146 if (o.liveness.isUnused(inst))
1206 if (f.liveness.isUnused(inst))
11471207 return CValue.none;
11481208
1149 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
1150 const inst_ty = o.air.typeOfIndex(inst);
1151 const int_info = inst_ty.intInfo(o.dg.module.getTarget());
1209 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1210 const inst_ty = f.air.typeOfIndex(inst);
1211 const int_info = inst_ty.intInfo(f.object.dg.module.getTarget());
11521212 const bits = int_info.bits;
11531213
11541214 // if it's an unsigned int with non-arbitrary bit size then we can just add
......@@ -1158,12 +1218,12 @@ fn airWrapOp(
11581218 else => false,
11591219 };
11601220 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);
11621222 }
11631223 }
11641224
11651225 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", .{});
11671227 }
11681228
11691229 var min_buf: [80]u8 = undefined;
......@@ -1210,11 +1270,11 @@ fn airWrapOp(
12101270 },
12111271 };
12121272
1213 const lhs = try o.resolveInst(bin_op.lhs);
1214 const rhs = try o.resolveInst(bin_op.rhs);
1215 const w = o.writer();
1273 const lhs = try f.resolveInst(bin_op.lhs);
1274 const rhs = try f.resolveInst(bin_op.rhs);
1275 const w = f.object.writer();
12161276
1217 const ret = try o.allocLocal(inst_ty, .Mut);
1277 const ret = try f.allocLocal(inst_ty, .Mut);
12181278 try w.print(" = zig_{s}", .{fn_op});
12191279
12201280 switch (inst_ty.tag()) {
......@@ -1240,71 +1300,179 @@ fn airWrapOp(
12401300 }
12411301
12421302 try w.writeByte('(');
1243 try o.writeCValue(w, lhs);
1303 try f.writeCValue(w, lhs);
12441304 try w.writeAll(", ");
1245 try o.writeCValue(w, rhs);
1305 try f.writeCValue(w, rhs);
12461306
12471307 if (int_info.signedness == .signed) {
12481308 try w.print(", {s}", .{min});
12491309 }
12501310
12511311 try w.print(", {s});", .{max});
1252 try o.indent_writer.insertNewline();
1312 try f.object.indent_writer.insertNewline();
12531313
12541314 return ret;
12551315}
12561316
1257fn airNot(o: *Object, inst: Air.Inst.Index) !CValue {
1258 if (o.liveness.isUnused(inst))
1317fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {
1318 if (f.liveness.isUnused(inst))
12591319 return CValue.none;
12601320
1261 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1262 const op = try o.resolveInst(ty_op.operand);
1321 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1322 const inst_ty = f.air.typeOfIndex(inst);
1323 const int_info = inst_ty.intInfo(f.object.dg.module.getTarget());
1324 const bits = int_info.bits;
1325
1326 switch (bits) {
1327 8, 16, 32, 64, 128 => {},
1328 else => return f.object.dg.fail("TODO: C backend: airSatOp for non power of 2 integers", .{}),
1329 }
12631330
1264 const writer = o.writer();
1265 const inst_ty = o.air.typeOfIndex(inst);
1266 const local = try o.allocLocal(inst_ty, .Const);
1331 // if it's an unsigned int with non-arbitrary bit size then we can just add
1332 if (bits > 64) {
1333 return f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{});
1334 }
1335
1336 var min_buf: [80]u8 = undefined;
1337 const min = switch (int_info.signedness) {
1338 .unsigned => "0",
1339 else => switch (inst_ty.tag()) {
1340 .c_short => "SHRT_MIN",
1341 .c_int => "INT_MIN",
1342 .c_long => "LONG_MIN",
1343 .c_longlong => "LLONG_MIN",
1344 .isize => "INTPTR_MIN",
1345 else => blk: {
1346 // compute the type minimum based on the bitcount (bits)
1347 const val = -1 * std.math.pow(i65, 2, @intCast(i65, bits - 1));
1348 break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) {
1349 error.NoSpaceLeft => unreachable,
1350 else => |e| return e,
1351 };
1352 },
1353 },
1354 };
1355
1356 var max_buf: [80]u8 = undefined;
1357 const max = switch (inst_ty.tag()) {
1358 .c_short => "SHRT_MAX",
1359 .c_ushort => "USHRT_MAX",
1360 .c_int => "INT_MAX",
1361 .c_uint => "UINT_MAX",
1362 .c_long => "LONG_MAX",
1363 .c_ulong => "ULONG_MAX",
1364 .c_longlong => "LLONG_MAX",
1365 .c_ulonglong => "ULLONG_MAX",
1366 .isize => "INTPTR_MAX",
1367 .usize => "UINTPTR_MAX",
1368 else => blk: {
1369 const pow_bits = switch (int_info.signedness) {
1370 .signed => bits - 1,
1371 .unsigned => bits,
1372 };
1373 const val = std.math.pow(u65, 2, pow_bits) - 1;
1374 break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) {
1375 error.NoSpaceLeft => unreachable,
1376 else => |e| return e,
1377 };
1378 },
1379 };
1380
1381 const lhs = try f.resolveInst(bin_op.lhs);
1382 const rhs = try f.resolveInst(bin_op.rhs);
1383 const w = f.object.writer();
1384
1385 const ret = try f.allocLocal(inst_ty, .Mut);
1386 try w.print(" = zig_{s}", .{fn_op});
1387
1388 switch (inst_ty.tag()) {
1389 .isize => try w.writeAll("isize"),
1390 .c_short => try w.writeAll("short"),
1391 .c_int => try w.writeAll("int"),
1392 .c_long => try w.writeAll("long"),
1393 .c_longlong => try w.writeAll("longlong"),
1394 else => {
1395 const prefix_byte: u8 = switch (int_info.signedness) {
1396 .signed => 'i',
1397 .unsigned => 'u',
1398 };
1399 for ([_]u8{ 8, 16, 32, 64 }) |nbits| {
1400 if (bits <= nbits) {
1401 try w.print("{c}{d}", .{ prefix_byte, nbits });
1402 break;
1403 }
1404 } else {
1405 unreachable;
1406 }
1407 },
1408 }
1409
1410 try w.writeByte('(');
1411 try f.writeCValue(w, lhs);
1412 try w.writeAll(", ");
1413 try f.writeCValue(w, rhs);
1414
1415 if (int_info.signedness == .signed) {
1416 try w.print(", {s}", .{min});
1417 }
1418
1419 try w.print(", {s});", .{max});
1420 try f.object.indent_writer.insertNewline();
1421
1422 return ret;
1423}
1424
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
12681436 try writer.writeAll(" = ");
12691437 if (inst_ty.zigTypeTag() == .Bool)
12701438 try writer.writeAll("!")
12711439 else
12721440 try writer.writeAll("~");
1273 try o.writeCValue(writer, op);
1441 try f.writeCValue(writer, op);
12741442 try writer.writeAll(";\n");
12751443
12761444 return local;
12771445}
12781446
1279fn airBinOp(o: *Object, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1280 if (o.liveness.isUnused(inst))
1447fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1448 if (f.liveness.isUnused(inst))
12811449 return CValue.none;
12821450
1283 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
1284 const lhs = try o.resolveInst(bin_op.lhs);
1285 const rhs = try o.resolveInst(bin_op.rhs);
1451 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1452 const lhs = try f.resolveInst(bin_op.lhs);
1453 const rhs = try f.resolveInst(bin_op.rhs);
12861454
1287 const writer = o.writer();
1288 const inst_ty = o.air.typeOfIndex(inst);
1289 const local = try o.allocLocal(inst_ty, .Const);
1455 const writer = f.object.writer();
1456 const inst_ty = f.air.typeOfIndex(inst);
1457 const local = try f.allocLocal(inst_ty, .Const);
12901458
12911459 try writer.writeAll(" = ");
1292 try o.writeCValue(writer, lhs);
1460 try f.writeCValue(writer, lhs);
12931461 try writer.print("{s}", .{operator});
1294 try o.writeCValue(writer, rhs);
1462 try f.writeCValue(writer, rhs);
12951463 try writer.writeAll(";\n");
12961464
12971465 return local;
12981466}
12991467
1300fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
1301 const pl_op = o.air.instructions.items(.data)[inst].pl_op;
1302 const extra = o.air.extraData(Air.Call, pl_op.payload);
1303 const args = @bitCast([]const Air.Inst.Ref, o.air.extra[extra.end..][0..extra.data.args_len]);
1304 const fn_ty = o.air.typeOf(pl_op.operand);
1468fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {
1469 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
1470 const extra = f.air.extraData(Air.Call, pl_op.payload);
1471 const args = @bitCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]);
1472 const fn_ty = f.air.typeOf(pl_op.operand);
13051473 const ret_ty = fn_ty.fnReturnType();
1306 const unused_result = o.liveness.isUnused(inst);
1307 const writer = o.writer();
1474 const unused_result = f.liveness.isUnused(inst);
1475 const writer = f.object.writer();
13081476
13091477 var result_local: CValue = .none;
13101478 if (unused_result) {
......@@ -1312,11 +1480,11 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
13121480 try writer.print("(void)", .{});
13131481 }
13141482 } else {
1315 result_local = try o.allocLocal(ret_ty, .Const);
1483 result_local = try f.allocLocal(ret_ty, .Const);
13161484 try writer.writeAll(" = ");
13171485 }
13181486
1319 if (o.air.value(pl_op.operand)) |func_val| {
1487 if (f.air.value(pl_op.operand)) |func_val| {
13201488 const fn_decl = if (func_val.castTag(.extern_fn)) |extern_fn|
13211489 extern_fn.data
13221490 else if (func_val.castTag(.function)) |func_payload|
......@@ -1326,8 +1494,8 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
13261494
13271495 try writer.writeAll(mem.spanZ(fn_decl.name));
13281496 } else {
1329 const callee = try o.resolveInst(pl_op.operand);
1330 try o.writeCValue(writer, callee);
1497 const callee = try f.resolveInst(pl_op.operand);
1498 try f.writeCValue(writer, callee);
13311499 }
13321500
13331501 try writer.writeAll("(");
......@@ -1335,189 +1503,200 @@ fn airCall(o: *Object, inst: Air.Inst.Index) !CValue {
13351503 if (i != 0) {
13361504 try writer.writeAll(", ");
13371505 }
1338 if (o.air.value(arg)) |val| {
1339 try o.dg.renderValue(writer, o.air.typeOf(arg), val);
1506 if (f.air.value(arg)) |val| {
1507 try f.object.dg.renderValue(writer, f.air.typeOf(arg), val);
13401508 } else {
1341 const val = try o.resolveInst(arg);
1342 try o.writeCValue(writer, val);
1509 const val = try f.resolveInst(arg);
1510 try f.writeCValue(writer, val);
13431511 }
13441512 }
13451513 try writer.writeAll(");\n");
13461514 return result_local;
13471515}
13481516
1349fn airDbgStmt(o: *Object, inst: Air.Inst.Index) !CValue {
1350 const dbg_stmt = o.air.instructions.items(.data)[inst].dbg_stmt;
1351 const writer = o.writer();
1517fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
1518 const dbg_stmt = f.air.instructions.items(.data)[inst].dbg_stmt;
1519 const writer = f.object.writer();
13521520 try writer.print("#line {d}\n", .{dbg_stmt.line + 1});
13531521 return CValue.none;
13541522}
13551523
1356fn airBlock(o: *Object, inst: Air.Inst.Index) !CValue {
1357 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1358 const extra = o.air.extraData(Air.Block, ty_pl.payload);
1359 const body = o.air.extra[extra.end..][0..extra.data.body_len];
1524fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
1525 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1526 const extra = f.air.extraData(Air.Block, ty_pl.payload);
1527 const body = f.air.extra[extra.end..][0..extra.data.body_len];
13601528
1361 const block_id: usize = o.next_block_index;
1362 o.next_block_index += 1;
1363 const writer = o.writer();
1529 const block_id: usize = f.next_block_index;
1530 f.next_block_index += 1;
1531 const writer = f.object.writer();
13641532
1365 const inst_ty = o.air.typeOfIndex(inst);
1366 const result = if (inst_ty.tag() != .void and !o.liveness.isUnused(inst)) blk: {
1533 const inst_ty = f.air.typeOfIndex(inst);
1534 const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) blk: {
13671535 // allocate a location for the result
1368 const local = try o.allocLocal(inst_ty, .Mut);
1536 const local = try f.allocLocal(inst_ty, .Mut);
13691537 try writer.writeAll(";\n");
13701538 break :blk local;
13711539 } else CValue{ .none = {} };
13721540
1373 try o.blocks.putNoClobber(o.gpa, inst, .{
1541 try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{
13741542 .block_id = block_id,
13751543 .result = result,
13761544 });
13771545
1378 try genBody(o, body);
1379 try o.indent_writer.insertNewline();
1546 try genBody(f, body);
1547 try f.object.indent_writer.insertNewline();
13801548 // label must be followed by an expression, add an empty one.
13811549 try writer.print("zig_block_{d}:;\n", .{block_id});
13821550 return result;
13831551}
13841552
1385fn airBr(o: *Object, inst: Air.Inst.Index) !CValue {
1386 const branch = o.air.instructions.items(.data)[inst].br;
1387 const block = o.blocks.get(branch.block_inst).?;
1553fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
1554 const branch = f.air.instructions.items(.data)[inst].br;
1555 const block = f.blocks.get(branch.block_inst).?;
13881556 const result = block.result;
1389 const writer = o.writer();
1557 const writer = f.object.writer();
13901558
13911559 // If result is .none then the value of the block is unused.
13921560 if (result != .none) {
1393 const operand = try o.resolveInst(branch.operand);
1394 try o.writeCValue(writer, result);
1561 const operand = try f.resolveInst(branch.operand);
1562 try f.writeCValue(writer, result);
13951563 try writer.writeAll(" = ");
1396 try o.writeCValue(writer, operand);
1564 try f.writeCValue(writer, operand);
13971565 try writer.writeAll(";\n");
13981566 }
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});
14011569 return CValue.none;
14021570}
14031571
1404fn airBitcast(o: *Object, inst: Air.Inst.Index) !CValue {
1405 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1406 const operand = try o.resolveInst(ty_op.operand);
1572fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
1573 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1574 const operand = try f.resolveInst(ty_op.operand);
14071575
1408 const writer = o.writer();
1409 const inst_ty = o.air.typeOfIndex(inst);
1576 const writer = f.object.writer();
1577 const inst_ty = f.air.typeOfIndex(inst);
14101578 if (inst_ty.zigTypeTag() == .Pointer and
1411 o.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer)
1579 f.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer)
14121580 {
1413 const local = try o.allocLocal(inst_ty, .Const);
1581 const local = try f.allocLocal(inst_ty, .Const);
14141582 try writer.writeAll(" = (");
1415 try o.dg.renderType(writer, inst_ty);
1583 try f.renderType(writer, inst_ty);
14161584
14171585 try writer.writeAll(")");
1418 try o.writeCValue(writer, operand);
1586 try f.writeCValue(writer, operand);
14191587 try writer.writeAll(";\n");
14201588 return local;
14211589 }
14221590
1423 const local = try o.allocLocal(inst_ty, .Mut);
1591 const local = try f.allocLocal(inst_ty, .Mut);
14241592 try writer.writeAll(";\n");
14251593
14261594 try writer.writeAll("memcpy(&");
1427 try o.writeCValue(writer, local);
1595 try f.writeCValue(writer, local);
14281596 try writer.writeAll(", &");
1429 try o.writeCValue(writer, operand);
1597 try f.writeCValue(writer, operand);
14301598 try writer.writeAll(", sizeof ");
1431 try o.writeCValue(writer, local);
1599 try f.writeCValue(writer, local);
14321600 try writer.writeAll(");\n");
14331601
14341602 return local;
14351603}
14361604
1437fn airBreakpoint(o: *Object) !CValue {
1438 try o.writer().writeAll("zig_breakpoint();\n");
1605fn airBreakpoint(f: *Function) !CValue {
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
14391618 return CValue.none;
14401619}
14411620
1442fn airUnreach(o: *Object) !CValue {
1443 try o.writer().writeAll("zig_unreachable();\n");
1621fn airUnreach(f: *Function) !CValue {
1622 try f.object.writer().writeAll("zig_unreachable();\n");
14441623 return CValue.none;
14451624}
14461625
1447fn airLoop(o: *Object, inst: Air.Inst.Index) !CValue {
1448 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1449 const loop = o.air.extraData(Air.Block, ty_pl.payload);
1450 const body = o.air.extra[loop.end..][0..loop.data.body_len];
1451 try o.writer().writeAll("while (true) ");
1452 try genBody(o, body);
1453 try o.indent_writer.insertNewline();
1626fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue {
1627 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1628 const loop = f.air.extraData(Air.Block, ty_pl.payload);
1629 const body = f.air.extra[loop.end..][0..loop.data.body_len];
1630 try f.object.writer().writeAll("while (true) ");
1631 try genBody(f, body);
1632 try f.object.indent_writer.insertNewline();
14541633 return CValue.none;
14551634}
14561635
1457fn airCondBr(o: *Object, inst: Air.Inst.Index) !CValue {
1458 const pl_op = o.air.instructions.items(.data)[inst].pl_op;
1459 const cond = try o.resolveInst(pl_op.operand);
1460 const extra = o.air.extraData(Air.CondBr, pl_op.payload);
1461 const then_body = o.air.extra[extra.end..][0..extra.data.then_body_len];
1462 const else_body = o.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1463 const writer = o.writer();
1636fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue {
1637 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
1638 const cond = try f.resolveInst(pl_op.operand);
1639 const extra = f.air.extraData(Air.CondBr, pl_op.payload);
1640 const then_body = f.air.extra[extra.end..][0..extra.data.then_body_len];
1641 const else_body = f.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1642 const writer = f.object.writer();
14641643
14651644 try writer.writeAll("if (");
1466 try o.writeCValue(writer, cond);
1645 try f.writeCValue(writer, cond);
14671646 try writer.writeAll(") ");
1468 try genBody(o, then_body);
1647 try genBody(f, then_body);
14691648 try writer.writeAll(" else ");
1470 try genBody(o, else_body);
1471 try o.indent_writer.insertNewline();
1649 try genBody(f, else_body);
1650 try f.object.indent_writer.insertNewline();
14721651
14731652 return CValue.none;
14741653}
14751654
1476fn airSwitchBr(o: *Object, inst: Air.Inst.Index) !CValue {
1477 const pl_op = o.air.instructions.items(.data)[inst].pl_op;
1478 const condition = try o.resolveInst(pl_op.operand);
1479 const condition_ty = o.air.typeOf(pl_op.operand);
1480 const switch_br = o.air.extraData(Air.SwitchBr, pl_op.payload);
1481 const writer = o.writer();
1655fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
1656 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
1657 const condition = try f.resolveInst(pl_op.operand);
1658 const condition_ty = f.air.typeOf(pl_op.operand);
1659 const switch_br = f.air.extraData(Air.SwitchBr, pl_op.payload);
1660 const writer = f.object.writer();
14821661
14831662 try writer.writeAll("switch (");
1484 try o.writeCValue(writer, condition);
1663 try f.writeCValue(writer, condition);
14851664 try writer.writeAll(") {");
1486 o.indent_writer.pushIndent();
1665 f.object.indent_writer.pushIndent();
14871666
14881667 var extra_index: usize = switch_br.end;
14891668 var case_i: u32 = 0;
14901669 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
1491 const case = o.air.extraData(Air.SwitchBr.Case, extra_index);
1492 const items = @bitCast([]const Air.Inst.Ref, o.air.extra[case.end..][0..case.data.items_len]);
1493 const case_body = o.air.extra[case.end + items.len ..][0..case.data.body_len];
1670 const case = f.air.extraData(Air.SwitchBr.Case, extra_index);
1671 const items = @bitCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]);
1672 const case_body = f.air.extra[case.end + items.len ..][0..case.data.body_len];
14941673 extra_index = case.end + case.data.items_len + case_body.len;
14951674
14961675 for (items) |item| {
1497 try o.indent_writer.insertNewline();
1676 try f.object.indent_writer.insertNewline();
14981677 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).?);
15001679 try writer.writeAll(": ");
15011680 }
15021681 // 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);
15041683 }
15051684
1506 const else_body = o.air.extra[extra_index..][0..switch_br.data.else_body_len];
1507 try o.indent_writer.insertNewline();
1685 const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len];
1686 try f.object.indent_writer.insertNewline();
15081687 try writer.writeAll("default: ");
1509 try genBody(o, else_body);
1510 try o.indent_writer.insertNewline();
1688 try genBody(f, else_body);
1689 try f.object.indent_writer.insertNewline();
15111690
1512 o.indent_writer.popIndent();
1691 f.object.indent_writer.popIndent();
15131692 try writer.writeAll("}\n");
15141693 return CValue.none;
15151694}
15161695
1517fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
1518 const air_datas = o.air.instructions.items(.data);
1519 const air_extra = o.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
1520 const zir = o.dg.decl.namespace.file_scope.zir;
1696fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
1697 const air_datas = f.air.instructions.items(.data);
1698 const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
1699 const zir = f.object.dg.decl.namespace.file_scope.zir;
15211700 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
15221701 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
15231702 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
......@@ -1526,14 +1705,14 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
15261705 const clobbers_len = @truncate(u5, extended.small >> 10);
15271706 _ = clobbers_len; // TODO honor these
15281707 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
1529 const outputs = @bitCast([]const Air.Inst.Ref, o.air.extra[air_extra.end..][0..outputs_len]);
1530 const args = @bitCast([]const Air.Inst.Ref, o.air.extra[air_extra.end + outputs.len ..][0..args_len]);
1708 const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]);
1709 const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]);
15311710
15321711 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", .{});
15341713 }
15351714
1536 if (o.liveness.isUnused(inst) and !is_volatile)
1715 if (f.liveness.isUnused(inst) and !is_volatile)
15371716 return CValue.none;
15381717
15391718 var extra_i: usize = zir_extra.end;
......@@ -1548,28 +1727,28 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
15481727 };
15491728 const args_extra_begin = extra_i;
15501729
1551 const writer = o.writer();
1730 const writer = f.object.writer();
15521731 for (args) |arg| {
15531732 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
15541733 extra_i = input.end;
15551734 const constraint = zir.nullTerminatedString(input.data.constraint);
15561735 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
15571736 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);
15591738 try writer.writeAll("register ");
1560 try o.dg.renderType(writer, o.air.typeOf(arg));
1739 try f.renderType(writer, f.air.typeOf(arg));
15611740
15621741 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);
15641743 try writer.writeAll(";\n");
15651744 } else {
1566 return o.dg.fail("TODO non-explicit inline asm regs", .{});
1745 return f.fail("TODO non-explicit inline asm regs", .{});
15671746 }
15681747 }
15691748 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";
15701749 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });
15711750 if (output_constraint) |_| {
1572 return o.dg.fail("TODO: CBE inline asm output", .{});
1751 return f.fail("TODO: CBE inline asm output", .{});
15731752 }
15741753 if (args.len > 0) {
15751754 if (output_constraint == null) {
......@@ -1595,30 +1774,30 @@ fn airAsm(o: *Object, inst: Air.Inst.Index) !CValue {
15951774 }
15961775 try writer.writeAll(");\n");
15971776
1598 if (o.liveness.isUnused(inst))
1777 if (f.liveness.isUnused(inst))
15991778 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", .{});
16021781}
16031782
16041783fn airIsNull(
1605 o: *Object,
1784 f: *Function,
16061785 inst: Air.Inst.Index,
16071786 operator: [*:0]const u8,
16081787 deref_suffix: [*:0]const u8,
16091788) !CValue {
1610 if (o.liveness.isUnused(inst))
1789 if (f.liveness.isUnused(inst))
16111790 return CValue.none;
16121791
1613 const un_op = o.air.instructions.items(.data)[inst].un_op;
1614 const writer = o.writer();
1615 const operand = try o.resolveInst(un_op);
1792 const un_op = f.air.instructions.items(.data)[inst].un_op;
1793 const writer = f.object.writer();
1794 const operand = try f.resolveInst(un_op);
16161795
1617 const local = try o.allocLocal(Type.initTag(.bool), .Const);
1796 const local = try f.allocLocal(Type.initTag(.bool), .Const);
16181797 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()) {
16221801 // operand is a regular pointer, test `operand !=/== NULL`
16231802 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
16241803 } else {
......@@ -1627,14 +1806,14 @@ fn airIsNull(
16271806 return local;
16281807}
16291808
1630fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue {
1631 if (o.liveness.isUnused(inst))
1809fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
1810 if (f.liveness.isUnused(inst))
16321811 return CValue.none;
16331812
1634 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1635 const writer = o.writer();
1636 const operand = try o.resolveInst(ty_op.operand);
1637 const operand_ty = o.air.typeOf(ty_op.operand);
1813 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1814 const writer = f.object.writer();
1815 const operand = try f.resolveInst(ty_op.operand);
1816 const operand_ty = f.air.typeOf(ty_op.operand);
16381817
16391818 const opt_ty = if (operand_ty.zigTypeTag() == .Pointer)
16401819 operand_ty.elemType()
......@@ -1647,98 +1826,98 @@ fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue {
16471826 return operand;
16481827 }
16491828
1650 const inst_ty = o.air.typeOfIndex(inst);
1829 const inst_ty = f.air.typeOfIndex(inst);
16511830 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
16521831 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);
16551834 try writer.print(" = {s}(", .{maybe_addrof});
1656 try o.writeCValue(writer, operand);
1835 try f.writeCValue(writer, operand);
16571836
16581837 try writer.print("){s}payload;\n", .{maybe_deref});
16591838 return local;
16601839}
16611840
1662fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue {
1663 if (o.liveness.isUnused(inst))
1841fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {
1842 if (f.liveness.isUnused(inst))
16641843 // TODO this @as is needed because of a stage1 bug
16651844 return @as(CValue, CValue.none);
16661845
1667 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1668 const extra = o.air.extraData(Air.StructField, ty_pl.payload).data;
1669 const struct_ptr = try o.resolveInst(extra.struct_operand);
1670 const struct_ptr_ty = o.air.typeOf(extra.struct_operand);
1671 return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, extra.field_index);
1846 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1847 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
1848 const struct_ptr = try f.resolveInst(extra.struct_operand);
1849 const struct_ptr_ty = f.air.typeOf(extra.struct_operand);
1850 return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, extra.field_index);
16721851}
16731852
1674fn airStructFieldPtrIndex(o: *Object, inst: Air.Inst.Index, index: u8) !CValue {
1675 if (o.liveness.isUnused(inst))
1853fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue {
1854 if (f.liveness.isUnused(inst))
16761855 // TODO this @as is needed because of a stage1 bug
16771856 return @as(CValue, CValue.none);
16781857
1679 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1680 const struct_ptr = try o.resolveInst(ty_op.operand);
1681 const struct_ptr_ty = o.air.typeOf(ty_op.operand);
1682 return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, index);
1858 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1859 const struct_ptr = try f.resolveInst(ty_op.operand);
1860 const struct_ptr_ty = f.air.typeOf(ty_op.operand);
1861 return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, index);
16831862}
16841863
1685fn structFieldPtr(o: *Object, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue {
1686 const writer = o.writer();
1864fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue {
1865 const writer = f.object.writer();
16871866 const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;
16881867 const field_name = struct_obj.fields.keys()[index];
16891868
1690 const inst_ty = o.air.typeOfIndex(inst);
1691 const local = try o.allocLocal(inst_ty, .Const);
1869 const inst_ty = f.air.typeOfIndex(inst);
1870 const local = try f.allocLocal(inst_ty, .Const);
16921871 switch (struct_ptr) {
16931872 .local_ref => |i| {
16941873 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });
16951874 },
16961875 else => {
16971876 try writer.writeAll(" = &");
1698 try o.writeCValue(writer, struct_ptr);
1877 try f.writeCValue(writer, struct_ptr);
16991878 try writer.print("->{};\n", .{fmtIdent(field_name)});
17001879 },
17011880 }
17021881 return local;
17031882}
17041883
1705fn airStructFieldVal(o: *Object, inst: Air.Inst.Index) !CValue {
1706 if (o.liveness.isUnused(inst))
1884fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
1885 if (f.liveness.isUnused(inst))
17071886 return CValue.none;
17081887
1709 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1710 const extra = o.air.extraData(Air.StructField, ty_pl.payload).data;
1711 const writer = o.writer();
1712 const struct_byval = try o.resolveInst(extra.struct_operand);
1713 const struct_ty = o.air.typeOf(extra.struct_operand);
1888 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1889 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
1890 const writer = f.object.writer();
1891 const struct_byval = try f.resolveInst(extra.struct_operand);
1892 const struct_ty = f.air.typeOf(extra.struct_operand);
17141893 const struct_obj = struct_ty.castTag(.@"struct").?.data;
17151894 const field_name = struct_obj.fields.keys()[extra.field_index];
17161895
1717 const inst_ty = o.air.typeOfIndex(inst);
1718 const local = try o.allocLocal(inst_ty, .Const);
1896 const inst_ty = f.air.typeOfIndex(inst);
1897 const local = try f.allocLocal(inst_ty, .Const);
17191898 try writer.writeAll(" = ");
1720 try o.writeCValue(writer, struct_byval);
1899 try f.writeCValue(writer, struct_byval);
17211900 try writer.print(".{};\n", .{fmtIdent(field_name)});
17221901 return local;
17231902}
17241903
17251904// *(E!T) -> E NOT *E
1726fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
1727 if (o.liveness.isUnused(inst))
1905fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
1906 if (f.liveness.isUnused(inst))
17281907 return CValue.none;
17291908
1730 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1731 const inst_ty = o.air.typeOfIndex(inst);
1732 const writer = o.writer();
1733 const operand = try o.resolveInst(ty_op.operand);
1734 const operand_ty = o.air.typeOf(ty_op.operand);
1909 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1910 const inst_ty = f.air.typeOfIndex(inst);
1911 const writer = f.object.writer();
1912 const operand = try f.resolveInst(ty_op.operand);
1913 const operand_ty = f.air.typeOf(ty_op.operand);
17351914
17361915 const payload_ty = operand_ty.errorUnionPayload();
17371916 if (!payload_ty.hasCodeGenBits()) {
17381917 if (operand_ty.zigTypeTag() == .Pointer) {
1739 const local = try o.allocLocal(inst_ty, .Const);
1918 const local = try f.allocLocal(inst_ty, .Const);
17401919 try writer.writeAll(" = *");
1741 try o.writeCValue(writer, operand);
1920 try f.writeCValue(writer, operand);
17421921 try writer.writeAll(";\n");
17431922 return local;
17441923 } else {
......@@ -1748,154 +1927,189 @@ fn airUnwrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
17481927
17491928 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);
17521931 try writer.writeAll(" = (");
1753 try o.writeCValue(writer, operand);
1932 try f.writeCValue(writer, operand);
17541933
17551934 try writer.print("){s}error;\n", .{maybe_deref});
17561935 return local;
17571936}
17581937
1759fn airUnwrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {
1760 if (o.liveness.isUnused(inst))
1938fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
1939 if (f.liveness.isUnused(inst))
17611940 return CValue.none;
17621941
1763 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1764 const writer = o.writer();
1765 const operand = try o.resolveInst(ty_op.operand);
1766 const operand_ty = o.air.typeOf(ty_op.operand);
1942 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1943 const writer = f.object.writer();
1944 const operand = try f.resolveInst(ty_op.operand);
1945 const operand_ty = f.air.typeOf(ty_op.operand);
17671946
17681947 const payload_ty = operand_ty.errorUnionPayload();
17691948 if (!payload_ty.hasCodeGenBits()) {
17701949 return CValue.none;
17711950 }
17721951
1773 const inst_ty = o.air.typeOfIndex(inst);
1952 const inst_ty = f.air.typeOfIndex(inst);
17741953 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
17751954 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);
17781957 try writer.print(" = {s}(", .{maybe_addrof});
1779 try o.writeCValue(writer, operand);
1958 try f.writeCValue(writer, operand);
17801959
17811960 try writer.print("){s}payload;\n", .{maybe_deref});
17821961 return local;
17831962}
17841963
1785fn airWrapOptional(o: *Object, inst: Air.Inst.Index) !CValue {
1786 if (o.liveness.isUnused(inst))
1964fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
1965 if (f.liveness.isUnused(inst))
17871966 return CValue.none;
17881967
1789 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1790 const writer = o.writer();
1791 const operand = try o.resolveInst(ty_op.operand);
1968 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1969 const writer = f.object.writer();
1970 const operand = try f.resolveInst(ty_op.operand);
17921971
1793 const inst_ty = o.air.typeOfIndex(inst);
1972 const inst_ty = f.air.typeOfIndex(inst);
17941973 if (inst_ty.isPtrLikeOptional()) {
17951974 // the operand is just a regular pointer, no need to do anything special.
17961975 return operand;
17971976 }
17981977
17991978 // .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);
18011980 try writer.writeAll(" = { .is_null = false, .payload =");
1802 try o.writeCValue(writer, operand);
1981 try f.writeCValue(writer, operand);
18031982 try writer.writeAll("};\n");
18041983 return local;
18051984}
1806fn airWrapErrUnionErr(o: *Object, inst: Air.Inst.Index) !CValue {
1807 if (o.liveness.isUnused(inst))
1985fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
1986 if (f.liveness.isUnused(inst))
18081987 return CValue.none;
18091988
1810 const writer = o.writer();
1811 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1812 const operand = try o.resolveInst(ty_op.operand);
1989 const writer = f.object.writer();
1990 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1991 const operand = try f.resolveInst(ty_op.operand);
18131992
1814 const inst_ty = o.air.typeOfIndex(inst);
1815 const local = try o.allocLocal(inst_ty, .Const);
1993 const inst_ty = f.air.typeOfIndex(inst);
1994 const local = try f.allocLocal(inst_ty, .Const);
18161995 try writer.writeAll(" = { .error = ");
1817 try o.writeCValue(writer, operand);
1996 try f.writeCValue(writer, operand);
18181997 try writer.writeAll(" };\n");
18191998 return local;
18201999}
18212000
1822fn airWrapErrUnionPay(o: *Object, inst: Air.Inst.Index) !CValue {
1823 if (o.liveness.isUnused(inst))
2001fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
2002 if (f.liveness.isUnused(inst))
18242003 return CValue.none;
18252004
1826 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1827 const writer = o.writer();
1828 const operand = try o.resolveInst(ty_op.operand);
2005 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
2006 const writer = f.object.writer();
2007 const operand = try f.resolveInst(ty_op.operand);
18292008
1830 const inst_ty = o.air.typeOfIndex(inst);
1831 const local = try o.allocLocal(inst_ty, .Const);
2009 const inst_ty = f.air.typeOfIndex(inst);
2010 const local = try f.allocLocal(inst_ty, .Const);
18322011 try writer.writeAll(" = { .error = 0, .payload = ");
1833 try o.writeCValue(writer, operand);
2012 try f.writeCValue(writer, operand);
18342013 try writer.writeAll(" };\n");
18352014 return local;
18362015}
18372016
18382017fn airIsErr(
1839 o: *Object,
2018 f: *Function,
18402019 inst: Air.Inst.Index,
18412020 deref_prefix: [*:0]const u8,
18422021 deref_suffix: [*:0]const u8,
18432022 op_str: [*:0]const u8,
18442023) !CValue {
1845 if (o.liveness.isUnused(inst))
2024 if (f.liveness.isUnused(inst))
18462025 return CValue.none;
18472026
1848 const un_op = o.air.instructions.items(.data)[inst].un_op;
1849 const writer = o.writer();
1850 const operand = try o.resolveInst(un_op);
1851 const operand_ty = o.air.typeOf(un_op);
1852 const local = try o.allocLocal(Type.initTag(.bool), .Const);
2027 const un_op = f.air.instructions.items(.data)[inst].un_op;
2028 const writer = f.object.writer();
2029 const operand = try f.resolveInst(un_op);
2030 const operand_ty = f.air.typeOf(un_op);
2031 const local = try f.allocLocal(Type.initTag(.bool), .Const);
18532032 const payload_ty = operand_ty.errorUnionPayload();
18542033 if (!payload_ty.hasCodeGenBits()) {
18552034 try writer.print(" = {s}", .{deref_prefix});
1856 try o.writeCValue(writer, operand);
2035 try f.writeCValue(writer, operand);
18572036 try writer.print(" {s} 0;\n", .{op_str});
18582037 } else {
18592038 try writer.writeAll(" = ");
1860 try o.writeCValue(writer, operand);
2039 try f.writeCValue(writer, operand);
18612040 try writer.print("{s}error {s} 0;\n", .{ deref_suffix, op_str });
18622041 }
18632042 return local;
18642043}
18652044
1866fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue {
1867 if (o.liveness.isUnused(inst))
2045fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
2046 if (f.liveness.isUnused(inst))
18682047 return CValue.none;
18692048
1870 const inst_ty = o.air.typeOfIndex(inst);
1871 const local = try o.allocLocal(inst_ty, .Const);
1872 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1873 const writer = o.writer();
1874 const operand = try o.resolveInst(ty_op.operand);
1875 const array_len = o.air.typeOf(ty_op.operand).elemType().arrayLen();
2049 const inst_ty = f.air.typeOfIndex(inst);
2050 const local = try f.allocLocal(inst_ty, .Const);
2051 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
2052 const writer = f.object.writer();
2053 const operand = try f.resolveInst(ty_op.operand);
2054 const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();
18762055
18772056 try writer.writeAll(" = { .ptr = ");
1878 try o.writeCValue(writer, operand);
2057 try f.writeCValue(writer, operand);
18792058 try writer.print(", .len = {d} }};\n", .{array_len});
18802059 return local;
18812060}
18822061
1883fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
1884 const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
1885 const extra = o.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
1886 const inst_ty = o.air.typeOfIndex(inst);
1887 const ptr = try o.resolveInst(extra.ptr);
1888 const expected_value = try o.resolveInst(extra.expected_value);
1889 const new_value = try o.resolveInst(extra.new_value);
1890 const local = try o.allocLocal(inst_ty, .Const);
1891 const writer = o.writer();
2062/// Emits a local variable with the result type and initializes it
2063/// with the operand.
2064fn airSimpleCast(f: *Function, inst: Air.Inst.Index) !CValue {
2065 if (f.liveness.isUnused(inst))
2066 return CValue.none;
2067
2068 const inst_ty = f.air.typeOfIndex(inst);
2069 const local = try f.allocLocal(inst_ty, .Const);
2070 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
2071 const writer = f.object.writer();
2072 const operand = try f.resolveInst(ty_op.operand);
2073
2074 try writer.writeAll(" = ");
2075 try f.writeCValue(writer, operand);
2076 try writer.writeAll(";\n");
2077 return local;
2078}
2079
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
18932107 try writer.print(" = zig_cmpxchg_{s}(", .{flavor});
1894 try o.writeCValue(writer, ptr);
2108 try f.writeCValue(writer, ptr);
18952109 try writer.writeAll(", ");
1896 try o.writeCValue(writer, expected_value);
2110 try f.writeCValue(writer, expected_value);
18972111 try writer.writeAll(", ");
1898 try o.writeCValue(writer, new_value);
2112 try f.writeCValue(writer, new_value);
18992113 try writer.writeAll(", ");
19002114 try writeMemoryOrder(writer, extra.successOrder());
19012115 try writer.writeAll(", ");
......@@ -1905,8 +2119,134 @@ fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
19052119 return local;
19062120}
19072121
1908fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void {
1909 const str = switch (order) {
2122fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
2123 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
2124 const extra = f.air.extraData(Air.AtomicRmw, pl_op.payload).data;
2125 const inst_ty = f.air.typeOfIndex(inst);
2126 const ptr = try f.resolveInst(pl_op.operand);
2127 const operand = try f.resolveInst(extra.operand);
2128 const local = try f.allocLocal(inst_ty, .Const);
2129 const writer = f.object.writer();
2130
2131 try writer.print(" = zig_atomicrmw_{s}(", .{toAtomicRmwSuffix(extra.op())});
2132 try f.writeCValue(writer, ptr);
2133 try writer.writeAll(", ");
2134 try f.writeCValue(writer, operand);
2135 try writer.writeAll(", ");
2136 try writeMemoryOrder(writer, extra.ordering());
2137 try writer.writeAll(");\n");
2138
2139 return local;
2140}
2141
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) {
19102250 .Unordered => "memory_order_relaxed",
19112251 .Monotonic => "memory_order_consume",
19122252 .Acquire => "memory_order_acquire",
......@@ -1914,7 +2254,24 @@ fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void {
19142254 .AcqRel => "memory_order_acq_rel",
19152255 .SeqCst => "memory_order_seq_cst",
19162256 };
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 };
19182275}
19192276
19202277fn IndentWriter(comptime UnderlyingWriter: type) type {
src/codegen/llvm.zig+1032-210
......@@ -158,15 +158,42 @@ pub const Object = struct {
158158 llvm_module: *const llvm.Module,
159159 context: *const llvm.Context,
160160 target_machine: *const llvm.TargetMachine,
161
162 pub fn create(gpa: *Allocator, options: link.Options) !*Object {
161 /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function,
162 /// but that has some downsides:
163 /// * we have to compute the fully qualified name every time we want to do the lookup
164 /// * for externally linked functions, the name is not fully qualified, but when
165 /// a Decl goes from exported to not exported and vice-versa, we would use the wrong
166 /// version of the name and incorrectly get function not found in the llvm module.
167 /// * it works for functions not all globals.
168 /// Therefore, this table keeps track of the mapping.
169 decl_map: std.AutoHashMapUnmanaged(*const Module.Decl, *const llvm.Value),
170 /// Maps Zig types to LLVM types. The table memory itself is backed by the GPA of
171 /// the compiler, but the Type/Value memory here is backed by `type_map_arena`.
172 /// TODO we need to remove entries from this map in response to incremental compilation
173 /// but I think the frontend won't tell us about types that get deleted because
174 /// hasCodeGenBits() is false for types.
175 type_map: TypeMap,
176 /// The backing memory for `type_map`. Periodically garbage collected after flush().
177 /// The code for doing the periodical GC is not yet implemented.
178 type_map_arena: std.heap.ArenaAllocator,
179 /// Where to put the output object file, relative to bin_file.options.emit directory.
180 sub_path: []const u8,
181
182 pub const TypeMap = std.HashMapUnmanaged(
183 Type,
184 *const llvm.Type,
185 Type.HashContext64,
186 std.hash_map.default_max_load_percentage,
187 );
188
189 pub fn create(gpa: *Allocator, sub_path: []const u8, options: link.Options) !*Object {
163190 const obj = try gpa.create(Object);
164191 errdefer gpa.destroy(obj);
165 obj.* = try Object.init(gpa, options);
192 obj.* = try Object.init(gpa, sub_path, options);
166193 return obj;
167194 }
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 {
170197 const context = llvm.Context.create();
171198 errdefer context.dispose();
172199
......@@ -240,22 +267,34 @@ pub const Object = struct {
240267 );
241268 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
243275 return Object{
244276 .llvm_module = llvm_module,
245277 .context = context,
246278 .target_machine = target_machine,
279 .decl_map = .{},
280 .type_map = .{},
281 .type_map_arena = std.heap.ArenaAllocator.init(gpa),
282 .sub_path = sub_path,
247283 };
248284 }
249285
250 pub fn deinit(self: *Object) void {
286 pub fn deinit(self: *Object, gpa: *Allocator) void {
251287 self.target_machine.dispose();
252288 self.llvm_module.dispose();
253289 self.context.dispose();
290 self.decl_map.deinit(gpa);
291 self.type_map.deinit(gpa);
292 self.type_map_arena.deinit();
254293 self.* = undefined;
255294 }
256295
257296 pub fn destroy(self: *Object, gpa: *Allocator) void {
258 self.deinit();
297 self.deinit(gpa);
259298 gpa.destroy(self);
260299 }
261300
......@@ -293,23 +332,18 @@ pub const Object = struct {
293332 const mod = comp.bin_file.options.module.?;
294333 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: {
297 const obj_basename = try std.zig.binNameAlloc(arena, .{
298 .root_name = comp.bin_file.options.root_name,
299 .target = comp.bin_file.options.target,
300 .output_mode = .Obj,
301 });
302 if (cache_dir.joinZ(arena, &[_][]const u8{obj_basename})) |p| {
303 break :blk p.ptr;
304 } else |err| {
305 return err;
306 }
307 } else null;
335 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit|
336 try emit.directory.joinZ(arena, &[_][]const u8{self.sub_path})
337 else
338 null;
308339
309340 const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);
310341 const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);
311342 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
313347 var error_message: [*:0]const u8 = undefined;
314348 if (self.target_machine.emitToFile(
315349 self.llvm_module,
......@@ -358,18 +392,31 @@ pub const Object = struct {
358392
359393 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
361409 // This gets the LLVM values from the function and stores them in `dg.args`.
362 const fn_param_len = decl.ty.fnParamLen();
363 var args = try dg.gpa.alloc(*const llvm.Value, fn_param_len);
410 const fn_info = decl.ty.fnInfo();
411 var args = try dg.gpa.alloc(*const llvm.Value, fn_info.param_types.len);
364412
365413 for (args) |*arg, i| {
366414 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));
367415 }
368416
369 // We remove all the basic blocks of a function to support incremental
370 // compilation!
371 // TODO: remove all basic blocks if functions can have more than one
372 if (llvm_func.getFirstBasicBlock()) |bb| {
417 // Remove all the basic blocks of a function in order to start over, generating
418 // LLVM IR from an empty function body.
419 while (llvm_func.getFirstBasicBlock()) |bb| {
373420 bb.deleteBasicBlock();
374421 }
375422
......@@ -440,30 +487,62 @@ pub const Object = struct {
440487 ) !void {
441488 // If the module does not already have the function, we ignore this function call
442489 // 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;
444491 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);
446495 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);
447499 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 }
448534 } else {
535 const fqn = try decl.getFullyQualifiedName(module.gpa);
536 defer module.gpa.free(fqn);
537 llvm_fn.setValueName2(fqn.ptr, fqn.len);
449538 llvm_fn.setLinkage(.Internal);
450539 llvm_fn.setUnnamedAddr(.True);
451540 }
452 // TODO LLVM C API does not support deleting aliases. We need to
453 // patch it to support this or figure out how to wrap the C++ API ourselves.
454 // Until then we iterate over existing aliases and make them point
455 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
456 for (exports) |exp| {
457 const exp_name_z = try module.gpa.dupeZ(u8, exp.options.name);
458 defer module.gpa.free(exp_name_z);
541 }
459542
460 if (self.llvm_module.getNamedGlobalAlias(exp_name_z.ptr, exp_name_z.len)) |alias| {
461 alias.setAliasee(llvm_fn);
462 } else {
463 const alias = self.llvm_module.addAlias(llvm_fn.typeOf(), llvm_fn, exp_name_z);
464 _ = alias;
465 }
466 }
543 pub fn freeDecl(self: *Object, decl: *Module.Decl) void {
544 const llvm_value = self.decl_map.get(decl) orelse return;
545 llvm_value.deleteGlobal();
467546 }
468547};
469548
......@@ -472,9 +551,8 @@ pub const DeclGen = struct {
472551 object: *Object,
473552 module: *Module,
474553 decl: *Module.Decl,
475 err_msg: ?*Module.ErrorMsg,
476
477554 gpa: *Allocator,
555 err_msg: ?*Module.ErrorMsg,
478556
479557 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
480558 @setCold(true);
......@@ -515,33 +593,47 @@ pub const DeclGen = struct {
515593 }
516594 }
517595
518 /// If the llvm function does not exist, create it
596 /// If the llvm function does not exist, create it.
597 /// Note that this can be called before the function's semantic analysis has
598 /// completed, so if any attributes rely on that, they must be done in updateFunc, not here.
519599 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
522603 assert(decl.has_tv);
523604 const zig_fn_type = decl.ty;
524 const return_type = zig_fn_type.fnReturnType();
525 const fn_param_len = zig_fn_type.fnParamLen();
605 const fn_info = zig_fn_type.fnInfo();
606 const return_type = fn_info.return_type;
526607
527 const fn_param_types = try self.gpa.alloc(Type, fn_param_len);
528 defer self.gpa.free(fn_param_types);
529 zig_fn_type.fnParamTypes(fn_param_types);
608 const llvm_param_buffer = try self.gpa.alloc(*const llvm.Type, fn_info.param_types.len);
609 defer self.gpa.free(llvm_param_buffer);
530610
531 const llvm_param = try self.gpa.alloc(*const llvm.Type, fn_param_len);
532 defer self.gpa.free(llvm_param);
533
534 for (fn_param_types) |fn_param, i| {
535 llvm_param[i] = try self.llvmType(fn_param);
611 var llvm_params_len: c_uint = 0;
612 for (fn_info.param_types) |param_ty| {
613 if (param_ty.hasCodeGenBits()) {
614 llvm_param_buffer[llvm_params_len] = try self.llvmType(param_ty);
615 llvm_params_len += 1;
616 }
536617 }
537618
619 const llvm_ret_ty = if (!return_type.hasCodeGenBits())
620 self.context.voidType()
621 else
622 try self.llvmType(return_type);
623
538624 const fn_type = llvm.functionType(
539 try self.llvmType(return_type),
540 llvm_param.ptr,
541 @intCast(c_uint, fn_param_len),
625 llvm_ret_ty,
626 llvm_param_buffer.ptr,
627 llvm_params_len,
542628 .False,
543629 );
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
546638 const is_extern = decl.val.tag() == .extern_fn;
547639 if (!is_extern) {
......@@ -549,8 +641,85 @@ pub const DeclGen = struct {
549641 llvm_fn.setUnnamedAddr(.True);
550642 }
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
554723 if (return_type.isNoReturn()) {
555724 self.addFnAttr(llvm_fn, "noreturn");
556725 }
......@@ -563,18 +732,41 @@ pub const DeclGen = struct {
563732 if (llvm_module.getNamedGlobal(decl.name)) |val| return val;
564733 // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.
565734 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 };
567753 }
568754
569755 fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
756 const gpa = self.gpa;
570757 log.debug("llvmType for {}", .{t});
571758 switch (t.zigTypeTag()) {
572 .Void => return self.context.voidType(),
573 .NoReturn => return self.context.voidType(),
759 .Void, .NoReturn => return self.context.voidType(),
574760 .Int => {
575761 const info = t.intInfo(self.module.getTarget());
576762 return self.context.intType(info.bits);
577763 },
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 },
578770 .Float => switch (t.floatBits(self.module.getTarget())) {
579771 16 => return self.context.halfType(),
580772 32 => return self.context.floatType(),
......@@ -586,7 +778,7 @@ pub const DeclGen = struct {
586778 .Bool => return self.context.intType(1),
587779 .Pointer => {
588780 if (t.isSlice()) {
589 var buf: Type.Payload.ElemType = undefined;
781 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
590782 const ptr_type = t.slicePtrFieldType(&buf);
591783
592784 const fields: [2]*const llvm.Type = .{
......@@ -596,7 +788,8 @@ pub const DeclGen = struct {
596788 return self.context.structType(&fields, fields.len, .False);
597789 } else {
598790 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);
600793 }
601794 },
602795 .Array => {
......@@ -605,18 +798,18 @@ pub const DeclGen = struct {
605798 return elem_type.arrayType(@intCast(c_uint, total_len));
606799 },
607800 .Optional => {
608 if (!t.isPtrLikeOptional()) {
609 var buf: Type.Payload.ElemType = undefined;
610 const child_type = t.optionalChild(&buf);
801 var buf: Type.Payload.ElemType = undefined;
802 const child_type = t.optionalChild(&buf);
803 const payload_llvm_ty = try self.llvmType(child_type);
611804
612 const optional_types: [2]*const llvm.Type = .{
613 try self.llvmType(child_type),
614 self.context.intType(1),
615 };
616 return self.context.structType(&optional_types, 2, .False);
617 } else {
618 return self.todo("implement optional pointers as actual pointers", .{});
805 if (t.isPtrLikeOptional()) {
806 return payload_llvm_ty;
619807 }
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);
620813 },
621814 .ErrorUnion => {
622815 const error_type = t.errorUnionSet();
......@@ -634,24 +827,67 @@ pub const DeclGen = struct {
634827 return self.context.intType(16);
635828 },
636829 .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
637837 const struct_obj = t.castTag(.@"struct").?.data;
638838 assert(struct_obj.haveFieldTypes());
639 const llvm_fields = try self.gpa.alloc(*const llvm.Type, struct_obj.fields.count());
640 defer self.gpa.free(llvm_fields);
641 for (struct_obj.fields.values()) |field, i| {
642 llvm_fields[i] = try self.llvmType(field.ty);
839
840 const name = try struct_obj.getFullyQualifiedName(gpa);
841 defer gpa.free(name);
842
843 const llvm_struct_ty = self.context.structCreateNamed(name);
844 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
845
846 var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{};
847 try llvm_field_types.ensureTotalCapacity(gpa, struct_obj.fields.count());
848 defer llvm_field_types.deinit(gpa);
849
850 for (struct_obj.fields.values()) |field| {
851 if (!field.ty.hasCodeGenBits()) continue;
852 llvm_field_types.appendAssumeCapacity(try self.llvmType(field.ty));
643853 }
644 return self.context.structType(
645 llvm_fields.ptr,
646 @intCast(c_uint, llvm_fields.len),
647 .False,
854
855 llvm_struct_ty.structSetBody(
856 llvm_field_types.items.ptr,
857 @intCast(c_uint, llvm_field_types.items.len),
858 llvm.Bool.fromBool(struct_obj.layout == .Packed),
648859 );
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);
649885 },
650886 .Fn => {
651887 const ret_ty = try self.llvmType(t.fnReturnType());
652888 const params_len = t.fnParamLen();
653 const llvm_params = try self.gpa.alloc(*const llvm.Type, params_len);
654 defer self.gpa.free(llvm_params);
889 const llvm_params = try gpa.alloc(*const llvm.Type, params_len);
890 defer gpa.free(llvm_params);
655891 for (llvm_params) |*llvm_param, i| {
656892 llvm_param.* = try self.llvmType(t.fnParamType(i));
657893 }
......@@ -662,7 +898,9 @@ pub const DeclGen = struct {
662898 @intCast(c_uint, llvm_params.len),
663899 llvm.Bool.fromBool(is_var_args),
664900 );
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);
666904 },
667905 .ComptimeInt => unreachable,
668906 .ComptimeFloat => unreachable,
......@@ -673,8 +911,6 @@ pub const DeclGen = struct {
673911
674912 .BoundFn => @panic("TODO remove BoundFn from the language"),
675913
676 .Enum,
677 .Union,
678914 .Opaque,
679915 .Frame,
680916 .AnyFrame,
......@@ -701,13 +937,24 @@ pub const DeclGen = struct {
701937 const llvm_type = try self.llvmType(tv.ty);
702938 if (bigint.eqZero()) return llvm_type.constNull();
703939
704 if (bigint.limbs.len != 1) {
705 return self.todo("implement bigger bigint", .{});
706 }
707 const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
940 const unsigned_val = if (bigint.limbs.len == 1)
941 llvm_type.constInt(bigint.limbs[0], .False)
942 else
943 llvm_type.constIntOfArbitraryPrecision(@intCast(c_uint, bigint.limbs.len), bigint.limbs.ptr);
708944 if (!bigint.positive) {
709 return llvm.constNeg(llvm_int);
945 return llvm.constNeg(unsigned_val);
710946 }
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);
711958 return llvm_int;
712959 },
713960 .Float => {
......@@ -720,7 +967,7 @@ pub const DeclGen = struct {
720967 .Pointer => switch (tv.val.tag()) {
721968 .decl_ref => {
722969 if (tv.ty.isSlice()) {
723 var buf: Type.Payload.ElemType = undefined;
970 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
724971 const ptr_ty = tv.ty.slicePtrFieldType(&buf);
725972 var slice_len: Value.Payload.U64 = .{
726973 .base = .{ .tag = .int_u64 },
......@@ -750,12 +997,13 @@ pub const DeclGen = struct {
750997 decl.alive = true;
751998 const val = try self.resolveGlobalDecl(decl);
752999 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);
7541002 return val.constBitCast(llvm_type);
7551003 },
7561004 .slice => {
7571005 const slice = tv.val.castTag(.slice).?.data;
758 var buf: Type.Payload.ElemType = undefined;
1006 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
7591007 const fields: [2]*const llvm.Value = .{
7601008 try self.genTypedValue(.{
7611009 .ty = tv.ty.slicePtrFieldType(&buf),
......@@ -873,42 +1121,80 @@ pub const DeclGen = struct {
8731121 return self.context.constStruct(&fields, fields.len, .False);
8741122 },
8751123 .Struct => {
876 const fields_len = tv.ty.structFieldCount();
1124 const llvm_struct_ty = try self.llvmType(tv.ty);
8771125 const field_vals = tv.val.castTag(.@"struct").?.data;
8781126 const gpa = self.gpa;
879 const llvm_fields = try gpa.alloc(*const llvm.Value, fields_len);
880 defer gpa.free(llvm_fields);
881 for (llvm_fields) |*llvm_field, i| {
882 llvm_field.* = try self.genTypedValue(.{
883 .ty = tv.ty.structFieldType(i),
884 .val = field_vals[i],
885 });
1127
1128 var llvm_fields: std.ArrayListUnmanaged(*const llvm.Value) = .{};
1129 try llvm_fields.ensureTotalCapacity(gpa, field_vals.len);
1130 defer llvm_fields.deinit(gpa);
1131
1132 for (field_vals) |field_val, i| {
1133 const field_ty = tv.ty.structFieldType(i);
1134 if (!field_ty.hasCodeGenBits()) continue;
1135
1136 llvm_fields.appendAssumeCapacity(try self.genTypedValue(.{
1137 .ty = field_ty,
1138 .val = field_val,
1139 }));
8861140 }
887 return self.context.constStruct(
888 llvm_fields.ptr,
889 @intCast(c_uint, llvm_fields.len),
890 .False,
1141 return llvm_struct_ty.constNamedStruct(
1142 llvm_fields.items.ptr,
1143 @intCast(c_uint, llvm_fields.items.len),
8911144 );
8921145 },
8931146 .ComptimeInt => unreachable,
8941147 .ComptimeFloat => unreachable,
8951148 .Type => unreachable,
8961149 .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}),
8981162 }
8991163 }
9001164
901 // Helper functions
902 fn addAttr(self: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
1165 fn addAttr(dg: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
1166 return dg.addAttrInt(val, index, name, 0);
1167 }
1168
1169 fn removeAttr(val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
9031170 const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);
9041171 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);
9061185 val.addAttributeAtIndex(index, llvm_attr);
9071186 }
9081187
909 fn addFnAttr(self: *DeclGen, val: *const llvm.Value, attr_name: []const u8) void {
910 // TODO: improve this API, `addAttr(-1, attr_name)`
911 self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name);
1188 fn addFnAttr(dg: *DeclGen, val: *const llvm.Value, name: []const u8) void {
1189 dg.addAttr(val, std.math.maxInt(llvm.AttributeIndex), name);
1190 }
1191
1192 fn removeFnAttr(fn_val: *const llvm.Value, name: []const u8) void {
1193 removeAttr(fn_val, std.math.maxInt(llvm.AttributeIndex), name);
1194 }
1195
1196 fn addFnAttrInt(dg: *DeclGen, fn_val: *const llvm.Value, name: []const u8, int: u64) void {
1197 return dg.addAttrInt(fn_val, std.math.maxInt(llvm.AttributeIndex), name, int);
9121198 }
9131199
9141200 /// If the operand type of an atomic operation is not byte sized we need to
......@@ -920,7 +1206,7 @@ pub const DeclGen = struct {
9201206 var buffer: Type.Payload.Bits = undefined;
9211207 const int_ty = switch (ty.zigTypeTag()) {
9221208 .Int => ty,
923 .Enum => ty.enumTagType(&buffer),
1209 .Enum => ty.intTagType(&buffer),
9241210 .Float => {
9251211 if (!is_rmw_xchg) return null;
9261212 return dg.context.intType(@intCast(c_uint, ty.abiSize(target) * 8));
......@@ -950,7 +1236,7 @@ pub const FuncGen = struct {
9501236 /// in other instructions. This table is cleared before every function is generated.
9511237 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 paramaters
1239 /// These fields are used to refer to the LLVM value of the function parameters
9541240 /// in an Arg instruction.
9551241 args: []*const llvm.Value,
9561242 arg_index: usize,
......@@ -1003,22 +1289,27 @@ pub const FuncGen = struct {
10031289 for (body) |inst| {
10041290 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
10051291 // zig fmt: off
1006 .add => try self.airAdd(inst, false),
1007 .addwrap => try self.airAdd(inst, true),
1008 .sub => try self.airSub(inst, false),
1009 .subwrap => try self.airSub(inst, true),
1010 .mul => try self.airMul(inst, false),
1011 .mulwrap => try self.airMul(inst, true),
1012 .div => try self.airDiv(inst),
1013 .rem => try self.airRem(inst),
1014 .ptr_add => try self.airPtrAdd(inst),
1015 .ptr_sub => try self.airPtrSub(inst),
1292 .add => try self.airAdd(inst),
1293 .addwrap => try self.airAddWrap(inst),
1294 .add_sat => try self.airAddSat(inst),
1295 .sub => try self.airSub(inst),
1296 .subwrap => try self.airSubWrap(inst),
1297 .sub_sat => try self.airSubSat(inst),
1298 .mul => try self.airMul(inst),
1299 .mulwrap => try self.airMulWrap(inst),
1300 .mul_sat => try self.airMulSat(inst),
1301 .div => try self.airDiv(inst),
1302 .rem => try self.airRem(inst),
1303 .mod => try self.airMod(inst),
1304 .ptr_add => try self.airPtrAdd(inst),
1305 .ptr_sub => try self.airPtrSub(inst),
1306 .shl => try self.airShl(inst),
1307 .shl_sat => try self.airShlSat(inst),
1308 .shl_exact => try self.airShlExact(inst),
10161309
10171310 .bit_and, .bool_and => try self.airAnd(inst),
10181311 .bit_or, .bool_or => try self.airOr(inst),
10191312 .xor => try self.airXor(inst),
1020
1021 .shl => try self.airShl(inst),
10221313 .shr => try self.airShr(inst),
10231314
10241315 .cmp_eq => try self.airCmp(inst, .eq),
......@@ -1049,7 +1340,8 @@ pub const FuncGen = struct {
10491340 .cond_br => try self.airCondBr(inst),
10501341 .intcast => try self.airIntCast(inst),
10511342 .trunc => try self.airTrunc(inst),
1052 .floatcast => try self.airFloatCast(inst),
1343 .fptrunc => try self.airFptrunc(inst),
1344 .fpext => try self.airFpext(inst),
10531345 .ptrtoint => try self.airPtrToInt(inst),
10541346 .load => try self.airLoad(inst),
10551347 .loop => try self.airLoop(inst),
......@@ -1060,8 +1352,24 @@ pub const FuncGen = struct {
10601352 .slice_ptr => try self.airSliceField(inst, 0),
10611353 .slice_len => try self.airSliceField(inst, 1),
10621354 .array_to_slice => try self.airArrayToSlice(inst),
1355 .float_to_int => try self.airFloatToInt(inst),
1356 .int_to_float => try self.airIntToFloat(inst),
10631357 .cmpxchg_weak => try self.airCmpxchg(inst, true),
10641358 .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
10661374 .struct_field_ptr => try self.airStructFieldPtr(inst),
10671375 .struct_field_val => try self.airStructFieldVal(inst),
......@@ -1152,21 +1460,15 @@ pub const FuncGen = struct {
11521460 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
11531461 const lhs = try self.resolveInst(bin_op.lhs);
11541462 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()) {
1158 .Int, .Bool, .Pointer, .ErrorSet => {
1159 const is_signed = inst_ty.isSignedInt();
1160 const operation = switch (op) {
1161 .eq => .EQ,
1162 .neq => .NE,
1163 .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT),
1164 .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE),
1165 .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT),
1166 .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE),
1167 };
1168 return self.builder.buildICmp(operation, lhs, rhs, "");
1465 const int_ty = switch (operand_ty.zigTypeTag()) {
1466 .Enum => blk: {
1467 var buffer: Type.Payload.Bits = undefined;
1468 const int_ty = operand_ty.intTagType(&buffer);
1469 break :blk int_ty;
11691470 },
1471 .Int, .Bool, .Pointer, .ErrorSet => operand_ty,
11701472 .Float => {
11711473 const operation: llvm.RealPredicate = switch (op) {
11721474 .eq => .OEQ,
......@@ -1179,7 +1481,17 @@ pub const FuncGen = struct {
11791481 return self.builder.buildFCmp(operation, lhs, rhs, "");
11801482 },
11811483 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, "");
11831495 }
11841496
11851497 fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -1285,19 +1597,57 @@ pub const FuncGen = struct {
12851597 return null;
12861598
12871599 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1288 const operand = try self.resolveInst(ty_op.operand);
1289 const array_len = self.air.typeOf(ty_op.operand).elemType().arrayLen();
1290 const usize_llvm_ty = try self.dg.llvmType(Type.initTag(.usize));
1291 const len = usize_llvm_ty.constInt(array_len, .False);
1600 const operand_ty = self.air.typeOf(ty_op.operand);
1601 const array_ty = operand_ty.childType();
1602 const llvm_usize = try self.dg.llvmType(Type.usize);
1603 const len = llvm_usize.constInt(array_ty.arrayLen(), .False);
12921604 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);
12931609 const indices: [2]*const llvm.Value = .{
1294 usize_llvm_ty.constNull(), usize_llvm_ty.constNull(),
1610 llvm_usize.constNull(), llvm_usize.constNull(),
12951611 };
12961612 const ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
12971613 const partial = self.builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr, 0, "");
12981614 return self.builder.buildInsertValue(partial, len, 1, "");
12991615 }
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
13011651 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {
13021652 if (self.liveness.isUnused(inst))
13031653 return null;
......@@ -1398,27 +1748,31 @@ pub const FuncGen = struct {
13981748 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
13991749 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
14001750 const struct_ptr = try self.resolveInst(struct_field.struct_operand);
1401 const field_index = @intCast(c_uint, struct_field.field_index);
1402 return self.builder.buildStructGEP(struct_ptr, field_index, "");
1751 const struct_ptr_ty = self.air.typeOf(struct_field.struct_operand);
1752 return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, struct_field.field_index);
14031753 }
14041754
1405 fn airStructFieldPtrIndex(self: *FuncGen, inst: Air.Inst.Index, field_index: c_uint) !?*const llvm.Value {
1406 if (self.liveness.isUnused(inst))
1407 return null;
1755 fn airStructFieldPtrIndex(
1756 self: *FuncGen,
1757 inst: Air.Inst.Index,
1758 field_index: u32,
1759 ) !?*const llvm.Value {
1760 if (self.liveness.isUnused(inst)) return null;
14081761
14091762 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
14101763 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);
14121766 }
14131767
14141768 fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1415 if (self.liveness.isUnused(inst))
1416 return null;
1769 if (self.liveness.isUnused(inst)) return null;
14171770
14181771 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
14191772 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
1773 const struct_ty = self.air.typeOf(struct_field.struct_operand);
14201774 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);
14221776 return self.builder.buildExtractValue(struct_byval, field_index, "");
14231777 }
14241778
......@@ -1569,6 +1923,13 @@ pub const FuncGen = struct {
15691923 const operand = try self.resolveInst(un_op);
15701924
15711925 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
15721933 const index_type = self.context.intType(32);
15731934
15741935 var indices: [2]*const llvm.Value = .{
......@@ -1577,9 +1938,15 @@ pub const FuncGen = struct {
15771938 };
15781939
15791940 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""), "");
1580 } else {
1581 return self.builder.buildExtractValue(operand, 1, "");
15821941 }
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, "");
15831950 }
15841951
15851952 fn airIsNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value {
......@@ -1632,17 +1999,24 @@ pub const FuncGen = struct {
16321999 const operand = try self.resolveInst(ty_op.operand);
16332000
16342001 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);
16372008 var indices: [2]*const llvm.Value = .{
1638 index_type.constNull(),
1639 index_type.constNull(),
2009 index_type.constNull(), index_type.constNull(),
16402010 };
1641
16422011 return self.builder.buildInBoundsGEP(operand, &indices, 2, "");
1643 } else {
1644 return self.builder.buildExtractValue(operand, 0, "");
16452012 }
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, "");
16462020 }
16472021
16482022 fn airErrUnionPayload(
......@@ -1716,51 +2090,115 @@ pub const FuncGen = struct {
17162090 return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});
17172091 }
17182092
1719 fn airAdd(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {
1720 if (self.liveness.isUnused(inst))
1721 return null;
2093 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2094 if (self.liveness.isUnused(inst)) return null;
17222095
17232096 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
17242097 const lhs = try self.resolveInst(bin_op.lhs);
17252098 const rhs = try self.resolveInst(bin_op.rhs);
17262099 const inst_ty = self.air.typeOfIndex(inst);
17272100
1728 if (inst_ty.isFloat()) return self.builder.buildFAdd(lhs, rhs, "");
1729 if (wrap) return self.builder.buildAdd(lhs, rhs, "");
2101 if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, "");
17302102 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
17312103 return self.builder.buildNUWAdd(lhs, rhs, "");
17322104 }
17332105
1734 fn airSub(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {
1735 if (self.liveness.isUnused(inst))
1736 return null;
2106 fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2107 if (self.liveness.isUnused(inst)) return null;
2108
2109 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2110 const lhs = try self.resolveInst(bin_op.lhs);
2111 const rhs = try self.resolveInst(bin_op.rhs);
2112
2113 return self.builder.buildAdd(lhs, rhs, "");
2114 }
2115
2116 fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2117 if (self.liveness.isUnused(inst)) return null;
2118
2119 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2120 const lhs = try self.resolveInst(bin_op.lhs);
2121 const rhs = try self.resolveInst(bin_op.rhs);
2122 const inst_ty = self.air.typeOfIndex(inst);
2123
2124 if (inst_ty.isAnyFloat()) return self.todo("saturating float add", .{});
2125 if (inst_ty.isSignedInt()) return self.builder.buildSAddSat(lhs, rhs, "");
2126
2127 return self.builder.buildUAddSat(lhs, rhs, "");
2128 }
2129
2130 fn airSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2131 if (self.liveness.isUnused(inst)) return null;
17372132
17382133 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
17392134 const lhs = try self.resolveInst(bin_op.lhs);
17402135 const rhs = try self.resolveInst(bin_op.rhs);
17412136 const inst_ty = self.air.typeOfIndex(inst);
17422137
1743 if (inst_ty.isFloat()) return self.builder.buildFSub(lhs, rhs, "");
1744 if (wrap) return self.builder.buildSub(lhs, rhs, "");
2138 if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, "");
17452139 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
17462140 return self.builder.buildNUWSub(lhs, rhs, "");
17472141 }
17482142
1749 fn airMul(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {
1750 if (self.liveness.isUnused(inst))
1751 return null;
2143 fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2144 if (self.liveness.isUnused(inst)) return null;
2145
2146 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2147 const lhs = try self.resolveInst(bin_op.lhs);
2148 const rhs = try self.resolveInst(bin_op.rhs);
2149
2150 return self.builder.buildSub(lhs, rhs, "");
2151 }
2152
2153 fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2154 if (self.liveness.isUnused(inst)) return null;
17522155
17532156 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
17542157 const lhs = try self.resolveInst(bin_op.lhs);
17552158 const rhs = try self.resolveInst(bin_op.rhs);
17562159 const inst_ty = self.air.typeOfIndex(inst);
17572160
1758 if (inst_ty.isFloat()) return self.builder.buildFMul(lhs, rhs, "");
1759 if (wrap) return self.builder.buildMul(lhs, rhs, "");
2161 if (inst_ty.isAnyFloat()) return self.todo("saturating float sub", .{});
2162 if (inst_ty.isSignedInt()) return self.builder.buildSSubSat(lhs, rhs, "");
2163 return self.builder.buildUSubSat(lhs, rhs, "");
2164 }
2165
2166 fn airMul(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2167 if (self.liveness.isUnused(inst)) return null;
2168
2169 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2170 const lhs = try self.resolveInst(bin_op.lhs);
2171 const rhs = try self.resolveInst(bin_op.rhs);
2172 const inst_ty = self.air.typeOfIndex(inst);
2173
2174 if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, "");
17602175 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
17612176 return self.builder.buildNUWMul(lhs, rhs, "");
17622177 }
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
17642202 fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
17652203 if (self.liveness.isUnused(inst))
17662204 return null;
......@@ -1770,7 +2208,7 @@ pub const FuncGen = struct {
17702208 const rhs = try self.resolveInst(bin_op.rhs);
17712209 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, "");
17742212 if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
17752213 return self.builder.buildUDiv(lhs, rhs, "");
17762214 }
......@@ -1783,11 +2221,39 @@ pub const FuncGen = struct {
17832221 const rhs = try self.resolveInst(bin_op.rhs);
17842222 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, "");
17872225 if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");
17882226 return self.builder.buildURem(lhs, rhs, "");
17892227 }
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
17912257 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
17922258 if (self.liveness.isUnused(inst))
17932259 return null;
......@@ -1838,9 +2304,25 @@ pub const FuncGen = struct {
18382304 return self.builder.buildXor(lhs, rhs, "");
18392305 }
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
18412323 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1842 if (self.liveness.isUnused(inst))
1843 return null;
2324 if (self.liveness.isUnused(inst)) return null;
2325
18442326 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
18452327 const lhs = try self.resolveInst(bin_op.lhs);
18462328 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -1853,6 +2335,22 @@ pub const FuncGen = struct {
18532335 return self.builder.buildShl(lhs, casted_rhs, "");
18542336 }
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
18562354 fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
18572355 if (self.liveness.isUnused(inst))
18582356 return null;
......@@ -1877,14 +2375,22 @@ pub const FuncGen = struct {
18772375 if (self.liveness.isUnused(inst))
18782376 return null;
18792377
2378 const target = self.dg.module.getTarget();
18802379 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);
18812383 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();
1885 // TODO: Should we use intcast here or just a simple bitcast?
1886 // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes
1887 return self.builder.buildIntCast2(operand, try self.dg.llvmType(inst_ty), llvm.Bool.fromBool(signed), "");
2387 if (operand_info.bits < dest_info.bits) {
2388 switch (operand_info.signedness) {
2389 .signed => return self.builder.buildSExt(operand, dest_llvm_ty, ""),
2390 .unsigned => return self.builder.buildZExt(operand, dest_llvm_ty, ""),
2391 }
2392 }
2393 return self.builder.buildTrunc(operand, dest_llvm_ty, "");
18882394 }
18892395
18902396 fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -1897,12 +2403,26 @@ pub const FuncGen = struct {
18972403 return self.builder.buildTrunc(operand, dest_llvm_ty, "");
18982404 }
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 {
19012407 if (self.liveness.isUnused(inst))
19022408 return null;
19032409
1904 // TODO split floatcast AIR into float_widen and float_shorten
1905 return self.todo("implement 'airFloatCast'", .{});
2410 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2411 const operand = try self.resolveInst(ty_op.operand);
2412 const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));
2413
2414 return self.builder.buildFPTrunc(operand, dest_llvm_ty, "");
2415 }
2416
2417 fn airFpext(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2418 if (self.liveness.isUnused(inst))
2419 return null;
2420
2421 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2422 const operand = try self.resolveInst(ty_op.operand);
2423 const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));
2424
2425 return self.builder.buildFPExt(operand, dest_llvm_ty, "");
19062426 }
19072427
19082428 fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -1947,16 +2467,17 @@ pub const FuncGen = struct {
19472467 }
19482468
19492469 fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1950 if (self.liveness.isUnused(inst))
1951 return null;
2470 if (self.liveness.isUnused(inst)) return null;
19522471 // buildAlloca expects the pointee type, not the pointer type, so assert that
19532472 // a Payload.PointerSimple is passed to the alloc instruction.
1954 const inst_ty = self.air.typeOfIndex(inst);
1955 const pointee_type = inst_ty.castPointer().?.data;
1956
1957 // TODO: figure out a way to get the name of the var decl.
1958 // TODO: set alignment and volatile
1959 return self.buildAlloca(try self.dg.llvmType(pointee_type));
2473 const ptr_ty = self.air.typeOfIndex(inst);
2474 const pointee_type = ptr_ty.elemType();
2475 if (!pointee_type.hasCodeGenBits()) return null;
2476 const pointee_llvm_ty = try self.dg.llvmType(pointee_type);
2477 const target = self.dg.module.getTarget();
2478 const alloca_inst = self.buildAlloca(pointee_llvm_ty);
2479 alloca_inst.setAlignment(ptr_ty.ptrAlignment(target));
2480 return alloca_inst;
19602481 }
19612482
19622483 /// Use this instead of builder.buildAlloca, because this function makes sure to
......@@ -1986,20 +2507,19 @@ pub const FuncGen = struct {
19862507 fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
19872508 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
19882509 const dest_ptr = try self.resolveInst(bin_op.lhs);
2510 const ptr_ty = self.air.typeOf(bin_op.lhs);
19892511 const src_operand = try self.resolveInst(bin_op.rhs);
1990 // TODO set volatile on this store properly
1991 _ = self.builder.buildStore(src_operand, dest_ptr);
2512 _ = self.store(dest_ptr, ptr_ty, src_operand);
19922513 return null;
19932514 }
19942515
19952516 fn airLoad(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
19962517 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1997 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();
1998 if (!is_volatile and self.liveness.isUnused(inst))
2518 const ptr_ty = self.air.typeOf(ty_op.operand);
2519 if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst))
19992520 return null;
20002521 const ptr = try self.resolveInst(ty_op.operand);
2001 // TODO set volatile on this load properly
2002 return self.builder.buildLoad(ptr, "");
2522 return self.load(ptr, ptr_ty);
20032523 }
20042524
20052525 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -2009,6 +2529,14 @@ pub const FuncGen = struct {
20092529 return null;
20102530 }
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
20122540 fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !?*const llvm.Value {
20132541 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
20142542 const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
......@@ -2039,8 +2567,6 @@ pub const FuncGen = struct {
20392567 result.setWeak(llvm.Bool.fromBool(is_weak));
20402568
20412569 const optional_ty = self.air.typeOfIndex(inst);
2042 var buffer: Type.Payload.ElemType = undefined;
2043 const child_ty = optional_ty.optionalChild(&buffer);
20442570
20452571 var payload = self.builder.buildExtractValue(result, 0, "");
20462572 if (opt_abi_ty != null) {
......@@ -2049,8 +2575,7 @@ pub const FuncGen = struct {
20492575 const success_bit = self.builder.buildExtractValue(result, 1, "");
20502576
20512577 if (optional_ty.isPtrLikeOptional()) {
2052 const child_llvm_ty = try self.dg.llvmType(child_ty);
2053 return self.builder.buildSelect(success_bit, child_llvm_ty.constNull(), payload, "");
2578 return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, "");
20542579 }
20552580
20562581 const optional_llvm_ty = try self.dg.llvmType(optional_ty);
......@@ -2059,6 +2584,252 @@ pub const FuncGen = struct {
20592584 return self.builder.buildInsertValue(partial, non_null_bit, 1, "");
20602585 }
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
20622833 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
20632834 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
20642835 assert(id != 0);
......@@ -2067,6 +2838,27 @@ pub const FuncGen = struct {
20672838 // `getIntrinsicDeclaration`
20682839 return self.llvmModule().getIntrinsicDeclaration(id, null, 0);
20692840 }
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 }
20702862};
20712863
20722864fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void {
......@@ -2261,3 +3053,33 @@ fn toLlvmAtomicOrdering(atomic_order: std.builtin.AtomicOrder) llvm.AtomicOrderi
22613053 .SeqCst => .SequentiallyConsistent,
22623054 };
22633055}
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 {
8585 pub const addAttributeAtIndex = LLVMAddAttributeAtIndex;
8686 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
8891 pub const getFirstBasicBlock = LLVMGetFirstBasicBlock;
8992 extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock;
9093
......@@ -136,6 +139,30 @@ pub const Value = opaque {
136139
137140 pub const setWeak = LLVMSetWeak;
138141 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;
139166};
140167
141168pub const Type = opaque {
......@@ -148,12 +175,22 @@ pub const Type = opaque {
148175 pub const constInt = LLVMConstInt;
149176 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
151181 pub const constReal = LLVMConstReal;
152182 extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value;
153183
154184 pub const constArray = LLVMConstArray;
155185 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
157194 pub const getUndef = LLVMGetUndef;
158195 extern fn LLVMGetUndef(Ty: *const Type) *const Value;
159196
......@@ -170,6 +207,9 @@ pub const Type = opaque {
170207 ElementCount: c_uint,
171208 Packed: Bool,
172209 ) void;
210
211 pub const getTypeKind = LLVMGetTypeKind;
212 extern fn LLVMGetTypeKind(Ty: *const Type) TypeKind;
173213};
174214
175215pub const Module = opaque {
......@@ -182,9 +222,15 @@ pub const Module = opaque {
182222 pub const verify = LLVMVerifyModule;
183223 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
185228 pub const addFunction = LLVMAddFunction;
186229 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
188234 pub const getNamedFunction = LLVMGetNamedFunction;
189235 extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value;
190236
......@@ -197,6 +243,9 @@ pub const Module = opaque {
197243 pub const addGlobal = LLVMAddGlobal;
198244 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
200249 pub const getNamedGlobal = LLVMGetNamedGlobal;
201250 extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value;
202251
......@@ -268,7 +317,7 @@ extern fn LLVMGetInlineAsm(
268317pub const functionType = LLVMFunctionType;
269318extern fn LLVMFunctionType(
270319 ReturnType: *const Type,
271 ParamTypes: [*]*const Type,
320 ParamTypes: [*]const *const Type,
272321 ParamCount: c_uint,
273322 IsVarArg: Bool,
274323) *const Type;
......@@ -314,7 +363,7 @@ pub const Builder = opaque {
314363 extern fn LLVMBuildCall(
315364 *const Builder,
316365 Fn: *const Value,
317 Args: [*]*const Value,
366 Args: [*]const *const Value,
318367 NumArgs: c_uint,
319368 Name: [*:0]const u8,
320369 ) *const Value;
......@@ -365,6 +414,12 @@ pub const Builder = opaque {
365414 pub const buildNUWAdd = LLVMBuildNUWAdd;
366415 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
368423 pub const buildFSub = LLVMBuildFSub;
369424 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 {
377432 pub const buildNUWSub = LLVMBuildNUWSub;
378433 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
380441 pub const buildFMul = LLVMBuildFMul;
381442 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 {
389450 pub const buildNUWMul = LLVMBuildNUWMul;
390451 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
392459 pub const buildUDiv = LLVMBuildUDiv;
393460 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 {
419486 pub const buildShl = LLVMBuildShl;
420487 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
422501 pub const buildOr = LLVMBuildOr;
423502 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 {
481560 Name: [*:0]const u8,
482561 ) *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
484571 pub const buildStructGEP = LLVMBuildStructGEP;
485572 extern fn LLVMBuildStructGEP(
486573 B: *const Builder,
......@@ -525,6 +612,93 @@ pub const Builder = opaque {
525612 Else: *const Value,
526613 Name: [*:0]const u8,
527614 ) *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;
528702};
529703
530704pub const IntPredicate = enum(c_uint) {
......@@ -598,6 +772,14 @@ pub const TargetMachine = opaque {
598772 llvm_ir_filename: ?[*:0]const u8,
599773 bitcode_filename: ?[*:0]const u8,
600774 ) 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;
601783};
602784
603785pub const CodeModel = enum(c_int) {
......@@ -915,3 +1097,151 @@ pub const AtomicOrdering = enum(c_uint) {
9151097 AcquireRelease = 6,
9161098 SequentiallyConsistent = 7,
9171099};
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 {
260260 };
261261 }
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,
264264 /// a message is returned by this function. Callee owns the memory. If this function
265265 /// returns such a reportable error, it is valid to be called again for a different decl.
266266 pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg {
......@@ -565,7 +565,7 @@ pub const DeclGen = struct {
565565 }
566566 },
567567 // 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", .{}),
569569 .Vector => {
570570 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations
571571 // which work on them), so simply use those.
......@@ -629,7 +629,7 @@ pub const DeclGen = struct {
629629 const params = decl.ty.fnParamLen();
630630 var i: usize = 0;
631631
632 try self.args.ensureCapacity(params);
632 try self.args.ensureTotalCapacity(params);
633633 while (i < params) : (i += 1) {
634634 const param_type_id = self.spv.types.get(decl.ty.fnParamType(i)).?;
635635 const arg_result_id = self.spv.allocResultId();
src/codegen/wasm.zig+3-3
......@@ -1005,7 +1005,7 @@ pub const Context = struct {
10051005 const rhs = self.resolveInst(bin_op.rhs);
10061006
10071007 // 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.
10091009 const offset = blk: {
10101010 if (lhs == .code_offset) break :blk lhs.code_offset;
10111011 if (rhs == .code_offset) break :blk rhs.code_offset;
......@@ -1031,7 +1031,7 @@ pub const Context = struct {
10311031 const rhs = self.resolveInst(bin_op.rhs);
10321032
10331033 // 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.
10351035 const offset = blk: {
10361036 if (lhs == .code_offset) break :blk lhs.code_offset;
10371037 if (rhs == .code_offset) break :blk rhs.code_offset;
......@@ -1395,7 +1395,7 @@ pub const Context = struct {
13951395 }
13961396
13971397 // We map every block to its block index.
1398 // We then determine how far we have to jump to it by substracting it from current block depth
1398 // We then determine how far we have to jump to it by subtracting it from current block depth
13991399 const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;
14001400 const writer = self.code.writer();
14011401 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 {
221221 batch.add(&async self.findNativeIncludeDirPosix(args));
222222 switch (Target.current.os.tag) {
223223 .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"),
224225 .linux => batch.add(&async self.findNativeCrtDirPosix(args)),
225226 else => {},
226227 }
src/libcxx.zig+2-2
......@@ -110,7 +110,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
110110 const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" });
111111 const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" });
112112 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
115115 for (libcxx_files) |cxx_src| {
116116 var cflags = std.ArrayList([]const u8).init(arena);
......@@ -250,7 +250,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
250250 const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" });
251251 const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" });
252252 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
255255 for (libcxxabi_files) |cxxabi_src| {
256256 var cflags = std.ArrayList([]const u8).init(arena);
src/libtsan.zig+6-6
......@@ -34,7 +34,7 @@ pub fn buildTsan(comp: *Compilation) !void {
3434 };
3535
3636 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
3939 const tsan_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"tsan"});
4040 for (tsan_sources) |tsan_src| {
......@@ -58,7 +58,7 @@ pub fn buildTsan(comp: *Compilation) !void {
5858 &darwin_tsan_sources
5959 else
6060 &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);
6262 for (platform_tsan_sources) |tsan_src| {
6363 var cflags = std.ArrayList([]const u8).init(arena);
6464
......@@ -96,7 +96,7 @@ pub fn buildTsan(comp: *Compilation) !void {
9696 });
9797 }
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);
100100 const sanitizer_common_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
101101 "tsan", "sanitizer_common",
102102 });
......@@ -123,7 +123,7 @@ pub fn buildTsan(comp: *Compilation) !void {
123123 &sanitizer_libcdep_sources
124124 else
125125 &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);
127127 for (to_c_or_not_to_c_sources) |c_src| {
128128 var cflags = std.ArrayList([]const u8).init(arena);
129129
......@@ -143,7 +143,7 @@ pub fn buildTsan(comp: *Compilation) !void {
143143 });
144144 }
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);
147147 for (sanitizer_symbolizer_sources) |c_src| {
148148 var cflags = std.ArrayList([]const u8).init(arena);
149149
......@@ -168,7 +168,7 @@ pub fn buildTsan(comp: *Compilation) !void {
168168 &[_][]const u8{"interception"},
169169 );
170170
171 try c_source_files.ensureCapacity(c_source_files.items.len + interception_sources.len);
171 try c_source_files.ensureUnusedCapacity(interception_sources.len);
172172 for (interception_sources) |c_src| {
173173 var cflags = std.ArrayList([]const u8).init(arena);
174174
src/link.zig+10-5
......@@ -149,7 +149,7 @@ pub const File = struct {
149149 coff: Coff.TextBlock,
150150 macho: MachO.TextBlock,
151151 plan9: Plan9.DeclBlock,
152 c: C.DeclBlock,
152 c: void,
153153 wasm: Wasm.DeclBlock,
154154 spirv: void,
155155 };
......@@ -159,7 +159,7 @@ pub const File = struct {
159159 coff: Coff.SrcFn,
160160 macho: MachO.SrcFn,
161161 plan9: void,
162 c: C.FnBlock,
162 c: void,
163163 wasm: Wasm.FnData,
164164 spirv: SpirV.FnData,
165165 };
......@@ -245,6 +245,9 @@ pub const File = struct {
245245 };
246246
247247 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.
248251 file.intermediary_basename = sub_path;
249252 }
250253
......@@ -372,16 +375,18 @@ pub const File = struct {
372375
373376 /// Must be called before any call to updateDecl or updateDeclExports for
374377 /// 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.
375381 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
376382 log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name });
377383 switch (base.tag) {
378384 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
379385 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
380386 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl),
381 .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl),
382387 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl),
383388 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl),
384 .spirv => {},
389 .c, .spirv => {},
385390 }
386391 }
387392
......@@ -635,7 +640,7 @@ pub const File = struct {
635640 var object_files = std.ArrayList([*:0]const u8).init(base.allocator);
636641 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);
639644 for (base.options.objects) |obj_path| {
640645 object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj_path));
641646 }
src/link/C.zig+166-98
......@@ -21,30 +21,34 @@ base: link.File,
2121/// This linker backend does not try to incrementally link output C source code.
2222/// Instead, it tracks all declarations in this table, and iterates over it
2323/// 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
2629/// Per-declaration data. For functions this is the body, and
2730/// the forward declaration is stored in the FnBlock.
28pub const DeclBlock = struct {
29 code: std.ArrayListUnmanaged(u8),
30
31 pub const empty: DeclBlock = .{
32 .code = .{},
33 };
34};
35
36/// Per-function data.
37pub const FnBlock = struct {
38 fwd_decl: std.ArrayListUnmanaged(u8),
39 typedefs: codegen.TypedefMap.Unmanaged,
40
41 pub const empty: FnBlock = .{
42 .fwd_decl = .{},
43 .typedefs = .{},
44 };
31const DeclBlock = struct {
32 code: std.ArrayListUnmanaged(u8) = .{},
33 fwd_decl: std.ArrayListUnmanaged(u8) = .{},
34 /// Each Decl stores a mapping of Zig Types to corresponding C types, for every
35 /// Zig Type used by the Decl. In flush(), we iterate over each Decl
36 /// and emit the typedef code for all types, making sure to not emit the same thing twice.
37 /// Any arena memory the Type points to lives in the `arena` field of `C`.
38 typedefs: codegen.TypedefMap.Unmanaged = .{},
39
40 fn deinit(db: *DeclBlock, gpa: *Allocator) void {
41 db.code.deinit(gpa);
42 db.fwd_decl.deinit(gpa);
43 for (db.typedefs.values()) |typedef| {
44 gpa.free(typedef.rendered);
45 }
46 db.typedefs.deinit(gpa);
47 db.* = undefined;
48 }
4549};
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 {
4852 assert(options.object_format == .c);
4953
5054 if (options.use_llvm) return error.LLVMHasNoCBackend;
......@@ -57,15 +61,16 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
5761 });
5862 errdefer file.close();
5963
60 var c_file = try allocator.create(C);
61 errdefer allocator.destroy(c_file);
64 var c_file = try gpa.create(C);
65 errdefer gpa.destroy(c_file);
6266
6367 c_file.* = C{
68 .arena = std.heap.ArenaAllocator.init(gpa),
6469 .base = .{
6570 .tag = .c,
6671 .options = options,
6772 .file = file,
68 .allocator = allocator,
73 .allocator = gpa,
6974 },
7075 };
7176
......@@ -73,38 +78,105 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
7378}
7479
7580pub fn deinit(self: *C) void {
76 for (self.decl_table.keys()) |key| {
77 deinitDecl(self.base.allocator, key);
81 const gpa = self.base.allocator;
82
83 for (self.decl_table.values()) |*db| {
84 db.deinit(gpa);
7885 }
79 self.decl_table.deinit(self.base.allocator);
80}
86 self.decl_table.deinit(gpa);
8187
82pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {
83 _ = self;
84 _ = decl;
88 self.arena.deinit();
8589}
8690
8791pub fn freeDecl(self: *C, decl: *Module.Decl) void {
88 _ = self.decl_table.swapRemove(decl);
89 deinitDecl(self.base.allocator, decl);
92 const gpa = self.base.allocator;
93 if (self.decl_table.fetchSwapRemove(decl)) |*kv| {
94 kv.value.deinit(gpa);
95 }
9096}
9197
92fn deinitDecl(gpa: *Allocator, decl: *Module.Decl) void {
93 decl.link.c.code.deinit(gpa);
94 decl.fn_link.c.fwd_decl.deinit(gpa);
95 for (decl.fn_link.c.typedefs.values()) |value| {
96 gpa.free(value.rendered);
98pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
99 const tracy = trace(@src());
100 defer tracy.end();
101
102 const decl = func.owner_decl;
103 const gop = try self.decl_table.getOrPut(self.base.allocator, decl);
104 if (!gop.found_existing) {
105 gop.value_ptr.* = .{};
106 }
107 const fwd_decl = &gop.value_ptr.fwd_decl;
108 const typedefs = &gop.value_ptr.typedefs;
109 const code = &gop.value_ptr.code;
110 fwd_decl.shrinkRetainingCapacity(0);
111 {
112 for (typedefs.values()) |value| {
113 module.gpa.free(value.rendered);
114 }
115 }
116 typedefs.clearRetainingCapacity();
117 code.shrinkRetainingCapacity(0);
118
119 var function: codegen.Function = .{
120 .value_map = codegen.CValueMap.init(module.gpa),
121 .air = air,
122 .liveness = liveness,
123 .func = func,
124 .object = .{
125 .dg = .{
126 .gpa = module.gpa,
127 .module = module,
128 .error_msg = null,
129 .decl = decl,
130 .fwd_decl = fwd_decl.toManaged(module.gpa),
131 .typedefs = typedefs.promote(module.gpa),
132 .typedefs_arena = &self.arena.allocator,
133 },
134 .code = code.toManaged(module.gpa),
135 .indent_writer = undefined, // set later so we can get a pointer to object.code
136 },
137 };
138
139 function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };
140 defer {
141 function.value_map.deinit();
142 function.blocks.deinit(module.gpa);
143 function.object.code.deinit();
144 function.object.dg.fwd_decl.deinit();
145 for (function.object.dg.typedefs.values()) |value| {
146 module.gpa.free(value.rendered);
147 }
148 function.object.dg.typedefs.deinit();
97149 }
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);
99167}
100168
101pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air, liveness: Liveness) !void {
102 // Keep track of all decls so we can iterate over them on flush().
103 _ = try self.decl_table.getOrPut(self.base.allocator, decl);
169pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
170 const tracy = trace(@src());
171 defer tracy.end();
104172
105 const fwd_decl = &decl.fn_link.c.fwd_decl;
106 const typedefs = &decl.fn_link.c.typedefs;
107 const code = &decl.link.c.code;
173 const gop = try self.decl_table.getOrPut(self.base.allocator, decl);
174 if (!gop.found_existing) {
175 gop.value_ptr.* = .{};
176 }
177 const fwd_decl = &gop.value_ptr.fwd_decl;
178 const typedefs = &gop.value_ptr.typedefs;
179 const code = &gop.value_ptr.code;
108180 fwd_decl.shrinkRetainingCapacity(0);
109181 {
110182 for (typedefs.values()) |value| {
......@@ -116,23 +188,19 @@ pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air,
116188
117189 var object: codegen.Object = .{
118190 .dg = .{
191 .gpa = module.gpa,
119192 .module = module,
120193 .error_msg = null,
121194 .decl = decl,
122195 .fwd_decl = fwd_decl.toManaged(module.gpa),
123196 .typedefs = typedefs.promote(module.gpa),
197 .typedefs_arena = &self.arena.allocator,
124198 },
125 .gpa = module.gpa,
126199 .code = code.toManaged(module.gpa),
127 .value_map = codegen.CValueMap.init(module.gpa),
128200 .indent_writer = undefined, // set later so we can get a pointer to object.code
129 .air = air,
130 .liveness = liveness,
131201 };
132202 object.indent_writer = .{ .underlying_writer = object.code.writer() };
133203 defer {
134 object.value_map.deinit();
135 object.blocks.deinit(module.gpa);
136204 object.code.deinit();
137205 object.dg.fwd_decl.deinit();
138206 for (object.dg.typedefs.values()) |value| {
......@@ -159,24 +227,12 @@ pub fn finishUpdateDecl(self: *C, module: *Module, decl: *Module.Decl, air: Air,
159227 code.shrinkAndFree(module.gpa, code.items.len);
160228}
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
176230pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {
177231 // The C backend does not have the ability to fix line numbers without re-generating
178232 // the entire Decl.
179 return self.updateDecl(module, decl);
233 _ = self;
234 _ = module;
235 _ = decl;
180236}
181237
182238pub fn flush(self: *C, comp: *Compilation) !void {
......@@ -197,7 +253,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
197253 defer all_buffers.deinit();
198254
199255 // 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
202258 var file_size: u64 = zig_h.len;
203259 all_buffers.appendAssumeCapacity(.{
......@@ -223,32 +279,42 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
223279 var typedefs = std.HashMap(Type, void, Type.HashContext64, std.hash_map.default_max_load_percentage).init(comp.gpa);
224280 defer typedefs.deinit();
225281
226 // Typedefs, forward decls and non-functions first.
282 // Typedefs, forward decls, and non-functions first.
227283 // TODO: performance investigation: would keeping a list of Decls that we should
228284 // generate, rather than querying here, be faster?
229 for (self.decl_table.keys()) |decl| {
230 if (!decl.has_tv) continue;
231 const buf = buf: {
232 if (decl.val.castTag(.function)) |_| {
233 try typedefs.ensureUnusedCapacity(@intCast(u32, decl.fn_link.c.typedefs.count()));
234 var it = decl.fn_link.c.typedefs.iterator();
235 while (it.next()) |new| {
236 const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*);
237 if (!gop.found_existing) {
238 try err_typedef_writer.writeAll(new.value_ptr.rendered);
239 }
285 const decl_keys = self.decl_table.keys();
286 const decl_values = self.decl_table.values();
287 for (decl_keys) |decl, i| {
288 if (!decl.has_tv) continue; // TODO do we really need this branch?
289
290 const decl_block = &decl_values[i];
291
292 if (decl_block.fwd_decl.items.len != 0) {
293 try typedefs.ensureUnusedCapacity(@intCast(u32, decl_block.typedefs.count()));
294 var it = decl_block.typedefs.iterator();
295 while (it.next()) |new| {
296 const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*);
297 if (!gop.found_existing) {
298 try err_typedef_writer.writeAll(new.value_ptr.rendered);
240299 }
241 fn_count += 1;
242 break :buf decl.fn_link.c.fwd_decl.items;
243 } else {
244 break :buf decl.link.c.code.items;
245300 }
246 };
247 all_buffers.appendAssumeCapacity(.{
248 .iov_base = buf.ptr,
249 .iov_len = buf.len,
250 });
251 file_size += buf.len;
301 const buf = decl_block.fwd_decl.items;
302 all_buffers.appendAssumeCapacity(.{
303 .iov_base = buf.ptr,
304 .iov_len = buf.len,
305 });
306 file_size += buf.len;
307 }
308 if (decl.getFunction() != null) {
309 fn_count += 1;
310 } else if (decl_block.code.items.len != 0) {
311 const buf = decl_block.code.items;
312 all_buffers.appendAssumeCapacity(.{
313 .iov_base = buf.ptr,
314 .iov_len = buf.len,
315 });
316 file_size += buf.len;
317 }
252318 }
253319
254320 err_typedef_item.* = .{
......@@ -258,16 +324,18 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
258324 file_size += err_typedef_buf.items.len;
259325
260326 // Now the function bodies.
261 try all_buffers.ensureCapacity(all_buffers.items.len + fn_count);
262 for (self.decl_table.keys()) |decl| {
263 if (!decl.has_tv) continue;
264 if (decl.val.castTag(.function)) |_| {
265 const buf = decl.link.c.code.items;
266 all_buffers.appendAssumeCapacity(.{
267 .iov_base = buf.ptr,
268 .iov_len = buf.len,
269 });
270 file_size += buf.len;
327 try all_buffers.ensureUnusedCapacity(fn_count);
328 for (decl_keys) |decl, i| {
329 if (decl.getFunction() != null) {
330 const decl_block = &decl_values[i];
331 const buf = decl_block.code.items;
332 if (buf.len != 0) {
333 all_buffers.appendAssumeCapacity(.{
334 .iov_base = buf.ptr,
335 .iov_len = buf.len,
336 });
337 file_size += buf.len;
338 }
271339 }
272340 }
273341
......@@ -286,7 +354,7 @@ pub fn flushEmitH(module: *Module) !void {
286354 var all_buffers = std.ArrayList(std.os.iovec_const).init(module.gpa);
287355 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
291359 var file_size: u64 = zig_h.len;
292360 all_buffers.appendAssumeCapacity(.{
src/link/C/zig.h+148-5
......@@ -62,14 +62,62 @@
6262
6363#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
6464#include <stdatomic.h>
65#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail)
66#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail)
65#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail)
66#define zig_cmpxchg_weak (obj, expected, desired, succ, fail) atomic_compare_exchange_weak_explicit (obj, &(expected), desired, succ, fail)
67#define zig_atomicrmw_xchg(obj, arg, order) atomic_exchange_explicit (obj, arg, order)
68#define zig_atomicrmw_add (obj, arg, order) atomic_fetch_add_explicit (obj, arg, order)
69#define zig_atomicrmw_sub (obj, arg, order) atomic_fetch_sub_explicit (obj, arg, order)
70#define zig_atomicrmw_or (obj, arg, order) atomic_fetch_or_explicit (obj, arg, order)
71#define zig_atomicrmw_xor (obj, arg, order) atomic_fetch_xor_explicit (obj, arg, order)
72#define zig_atomicrmw_and (obj, arg, order) atomic_fetch_and_explicit (obj, arg, order)
73#define zig_atomicrmw_nand(obj, arg, order) atomic_fetch_nand_explicit(obj, arg, order)
74#define zig_atomicrmw_min (obj, arg, order) atomic_fetch_min_explicit (obj, arg, order)
75#define zig_atomicrmw_max (obj, arg, order) atomic_fetch_max_explicit (obj, arg, order)
76#define zig_atomic_store (obj, arg, order) atomic_store_explicit (obj, arg, order)
77#define zig_atomic_load (obj, order) atomic_load_explicit (obj, order)
78#define zig_fence(order) atomic_thread_fence(order)
6779#elif __GNUC__
68#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired)
69#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired)
80#define memory_order_relaxed __ATOMIC_RELAXED
81#define memory_order_consume __ATOMIC_CONSUME
82#define memory_order_acquire __ATOMIC_ACQUIRE
83#define memory_order_release __ATOMIC_RELEASE
84#define memory_order_acq_rel __ATOMIC_ACQ_REL
85#define memory_order_seq_cst __ATOMIC_SEQ_CST
86#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, false, succ, fail)
87#define zig_cmpxchg_weak (obj, expected, desired, succ, fail) __atomic_compare_exchange_n(obj, &(expected), desired, true , succ, fail)
88#define zig_atomicrmw_xchg(obj, arg, order) __atomic_exchange_n(obj, arg, order)
89#define zig_atomicrmw_add (obj, arg, order) __atomic_fetch_add (obj, arg, order)
90#define zig_atomicrmw_sub (obj, arg, order) __atomic_fetch_sub (obj, arg, order)
91#define zig_atomicrmw_or (obj, arg, order) __atomic_fetch_or (obj, arg, order)
92#define zig_atomicrmw_xor (obj, arg, order) __atomic_fetch_xor (obj, arg, order)
93#define zig_atomicrmw_and (obj, arg, order) __atomic_fetch_and (obj, arg, order)
94#define zig_atomicrmw_nand(obj, arg, order) __atomic_fetch_nand(obj, arg, order)
95#define zig_atomicrmw_min (obj, arg, order) __atomic_fetch_min (obj, arg, order)
96#define zig_atomicrmw_max (obj, arg, order) __atomic_fetch_max (obj, arg, order)
97#define zig_atomic_store (obj, arg, order) __atomic_store (obj, arg, order)
98#define zig_atomic_load (obj, order) __atomic_load (obj, order)
99#define zig_fence(order) __atomic_thread_fence(order)
70100#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
71107#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()
73121#endif
74122
75123#include <stdint.h>
......@@ -78,6 +126,7 @@
78126#define int128_t __int128
79127#define uint128_t unsigned __int128
80128ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);
129ZIG_EXTERN_C void *memset (void *, int, size_t);
81130
82131static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
83132 uint8_t thresh = max - rhs;
......@@ -307,3 +356,97 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon
307356 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));
308357}
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,
5050section_table_offset: u32 = 0,
5151/// Section data file pointer.
5252section_data_offset: u32 = 0,
53/// Optiona header file pointer.
53/// Optional header file pointer.
5454optional_header_offset: u32 = 0,
5555
5656/// 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
132132 const self = try createEmpty(allocator, options);
133133 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);
136136 return self;
137137 }
138138
......@@ -418,7 +418,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff {
418418pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
419419 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
423423 if (self.offset_table_free_list.popOrNull()) |i| {
424424 decl.link.coff.offset_table_index = i;
......@@ -602,7 +602,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
602602 const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
603603 const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
604604 // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section
605 // and the virutal size of the `.got` section
605 // and the virtual size of the `.got` section
606606
607607 if (new_virtual_size != current_virtual_size) {
608608 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
770770}
771771
772772pub 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
775777 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
776778 self.freeTextBlock(&decl.link.coff);
......@@ -793,7 +795,7 @@ pub fn updateDeclExports(
793795 for (exports) |exp| {
794796 if (exp.options.section) |section_name| {
795797 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);
797799 module.failed_exports.putAssumeCapacityNoClobber(
798800 exp,
799801 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
......@@ -804,7 +806,7 @@ pub fn updateDeclExports(
804806 if (mem.eql(u8, exp.options.name, "_start")) {
805807 self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base;
806808 } else {
807 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);
809 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
808810 module.failed_exports.putAssumeCapacityNoClobber(
809811 exp,
810812 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 {
882884 // If there is no Zig code to compile, then we should skip flushing the output file because it
883885 // will not be part of the linker line anyway.
884886 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
885 // Both stage1 and stage2 LLVM backend put the object file in the cache directory.
886 if (self.base.options.use_llvm) {
887 // Stage2 has to call flushModule since that outputs the LLVM object file.
888 if (!build_options.is_stage1 or !self.base.options.use_stage1) try self.flushModule(comp);
889
887 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
888 if (use_stage1) {
890889 const obj_basename = try std.zig.binNameAlloc(arena, .{
891890 .root_name = self.base.options.root_name,
892891 .target = self.base.options.target,
......@@ -981,7 +980,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
981980 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
982981
983982 if (self.base.options.output_mode == .Obj) {
984 // LLD's COFF driver does not support the equvialent of `-r` so we do a simple file copy
983 // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
985984 // here. TODO: think carefully about how we can avoid this redundant operation when doing
986985 // build-obj. See also the corresponding TODO in linkAsArchive.
987986 const the_object_path = blk: {
......@@ -1267,22 +1266,23 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
12671266 try argv.append(comp.libunwind_static_lib.?.full_object_path);
12681267 }
12691268
1270 // TODO: remove when stage2 can build compiler_rt.zig, c.zig and ssp.zig
1271 // compiler-rt, libc and libssp
1272 if (is_exe_or_dyn_lib and
1273 !self.base.options.skip_linker_dependencies and
1274 build_options.is_stage1 and self.base.options.use_stage1)
1275 {
1269 if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
12761270 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 }
12781274 }
12791275 // MinGW doesn't provide libssp symbols
12801276 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 }
12821280 }
12831281 // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
12841282 // 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 }
12861286 }
12871287
12881288 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
235235 const self = try createEmpty(allocator, options);
236236 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);
239239 return self;
240240 }
241241
......@@ -411,7 +411,7 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
411411
412412/// TODO Improve this to use a table.
413413fn 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);
415415 const result = self.shstrtab.items.len;
416416 self.shstrtab.appendSliceAssumeCapacity(bytes);
417417 self.shstrtab.appendAssumeCapacity(0);
......@@ -420,7 +420,7 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 {
420420
421421/// TODO Improve this to use a table.
422422fn 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);
424424 const result = self.debug_strtab.items.len;
425425 self.debug_strtab.appendSliceAssumeCapacity(bytes);
426426 self.debug_strtab.appendAssumeCapacity(0);
......@@ -856,7 +856,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
856856
857857 // We have a function to compute the upper bound size, because it's needed
858858 // 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
861861 // initial length - length of the .debug_info contribution for this compilation unit,
862862 // not including the initial length itself.
......@@ -925,7 +925,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
925925
926926 // Enough for all the data without resizing. When support for more compilation units
927927 // is added, the size of this section will become more variable.
928 try di_buf.ensureCapacity(100);
928 try di_buf.ensureTotalCapacity(100);
929929
930930 // initial length - length of the .debug_aranges contribution for this compilation unit,
931931 // not including the initial length itself.
......@@ -1004,7 +1004,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
10041004 // The size of this header is variable, depending on the number of directories,
10051005 // files, and padding. We have a function to compute the upper bound size, however,
10061006 // 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
10091009 // initial length - length of the .debug_line contribution for this compilation unit,
10101010 // not including the initial length itself.
......@@ -1254,11 +1254,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12541254 // If there is no Zig code to compile, then we should skip flushing the output file because it
12551255 // will not be part of the linker line anyway.
12561256 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
1257 // Both stage1 and stage2 LLVM backend put the object file in the cache directory.
1258 if (self.base.options.use_llvm) {
1259 // Stage2 has to call flushModule since that outputs the LLVM object file.
1260 if (!build_options.is_stage1 or !self.base.options.use_stage1) try self.flushModule(comp);
1261
1257 // stage1 puts the object file in the cache directory.
1258 if (self.base.options.use_stage1) {
12621259 const obj_basename = try std.zig.binNameAlloc(arena, .{
12631260 .root_name = self.base.options.root_name,
12641261 .target = self.base.options.target,
......@@ -1285,20 +1282,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
12851282 const gc_sections = self.base.options.gc_sections orelse !is_obj;
12861283 const stack_size = self.base.options.stack_size_override orelse 16777216;
12871284 const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
1288 const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt) blk: {
1289 // TODO: remove when stage2 can build compiler_rt.zig
1290 if (!build_options.is_stage1 or !self.base.options.use_stage1) break :blk null;
1291
1292 // In the case of build-obj we include the compiler-rt symbols directly alongside
1293 // the symbols of the root source file, in the same compilation unit.
1294 if (is_obj) break :blk null;
1295
1296 if (is_exe_or_dyn_lib) {
1297 break :blk comp.compiler_rt_static_lib.?.full_object_path;
1298 } else {
1299 break :blk comp.compiler_rt_obj.?.full_object_path;
1300 }
1301 } else null;
1285 const compiler_rt_path: ?[]const u8 = blk: {
1286 if (comp.compiler_rt_static_lib) |x| break :blk x.full_object_path;
1287 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
1288 break :blk null;
1289 };
13021290
13031291 // Here we want to determine whether we can save time by not invoking LLD when the
13041292 // 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 {
16211609 }
16221610
16231611 // libc
1624 // TODO: enable when stage2 can build c.zig
16251612 if (is_exe_or_dyn_lib and
16261613 !self.base.options.skip_linker_dependencies and
1627 !self.base.options.link_libc and
1628 build_options.is_stage1 and
1629 self.base.options.use_stage1)
1614 !self.base.options.link_libc)
16301615 {
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 }
16321619 }
16331620
16341621 // compiler-rt
......@@ -1639,7 +1626,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
16391626 // Shared libraries.
16401627 if (is_exe_or_dyn_lib) {
16411628 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);
16431630 for (system_libs) |link_lib| {
16441631 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
16451632 // (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 {
21132100
21142101 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);
2117 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
2103 try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1);
2104 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
21182105
21192106 if (self.local_symbol_free_list.popOrNull()) |i| {
21202107 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
......@@ -2147,7 +2134,9 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
21472134}
21482135
21492136pub 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
21522141 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
21532142 self.freeTextBlock(&decl.link.elf);
......@@ -2316,7 +2305,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
23162305 defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs);
23172306
23182307 // 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
23212310 const decl = func.owner_decl;
23222311 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
23512340
23522341 // .debug_info subprogram
23532342 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
23562345 const fn_ret_type = decl.ty.fnReturnType();
23572346 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
25932582 },
25942583 .Int => {
25952584 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);
25972586 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
25982587 // DW.AT.encoding, DW.FORM.data1
25992588 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {
......@@ -2607,7 +2596,7 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
26072596 },
26082597 .Optional => {
26092598 if (ty.isPtrLikeOptional()) {
2610 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
2599 try dbg_info_buffer.ensureUnusedCapacity(12);
26112600 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
26122601 // DW.AT.encoding, DW.FORM.data1
26132602 dbg_info_buffer.appendAssumeCapacity(DW.ATE.address);
......@@ -2747,14 +2736,14 @@ pub fn updateDeclExports(
27472736 const tracy = trace(@src());
27482737 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);
27512740 if (decl.link.elf.local_sym_index == 0) return;
27522741 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
27532742
27542743 for (exports) |exp| {
27552744 if (exp.options.section) |section_name| {
27562745 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);
27582747 module.failed_exports.putAssumeCapacityNoClobber(
27592748 exp,
27602749 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
......@@ -2772,7 +2761,7 @@ pub fn updateDeclExports(
27722761 },
27732762 .Weak => elf.STB_WEAK,
27742763 .LinkOnce => {
2775 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);
2764 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
27762765 module.failed_exports.putAssumeCapacityNoClobber(
27772766 exp,
27782767 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}),
......@@ -3448,6 +3437,15 @@ const CsuObjects = struct {
34483437 .static_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
34493438 // zig fmt: on
34503439 },
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 },
34513449 else => {},
34523450 }
34533451 }
src/link/MachO.zig+270-118
......@@ -12,7 +12,7 @@ const math = std.math;
1212const mem = std.mem;
1313const meta = std.meta;
1414
15const aarch64 = @import("../codegen/aarch64.zig");
15const aarch64 = @import("../arch/aarch64/bits.zig");
1616const bind = @import("MachO/bind.zig");
1717const codegen = @import("../codegen.zig");
1818const commands = @import("MachO/commands.zig");
......@@ -200,7 +200,7 @@ atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},
200200
201201/// List of atoms that are owned directly by the linker.
202202/// 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
204204/// at present owned by Module.Decl.
205205/// TODO consolidate this.
206206managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
......@@ -213,7 +213,7 @@ decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{},
213213
214214/// Currently active Module.Decl.
215215/// 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 }
217217/// somewhere else in the codegen.
218218active_decl: ?*Module.Decl = null,
219219
......@@ -231,7 +231,7 @@ const SymbolWithLoc = struct {
231231 },
232232 where_index: u32,
233233 local_sym_index: u32 = 0,
234 file: u16 = 0,
234 file: ?u16 = null, // null means Zig module
235235};
236236
237237pub const GotIndirectionKey = struct {
......@@ -290,7 +290,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
290290 const self = try createEmpty(allocator, options);
291291 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);
294294 return self;
295295 }
296296
......@@ -512,7 +512,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
512512 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
513513
514514 if (self.base.options.output_mode == .Obj) {
515 // LLD's MachO driver does not support the equvialent of `-r` so we do a simple file copy
515 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
516516 // here. TODO: think carefully about how we can avoid this redundant operation when doing
517517 // build-obj. See also the corresponding TODO in linkAsArchive.
518518 const the_object_path = blk: {
......@@ -543,9 +543,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
543543 .mode = link.determineMode(self.base.options),
544544 });
545545 try self.populateMissingMetadata();
546
547 // TODO mimicking insertion of null symbol from incremental linker.
548 // This will need to moved.
549546 try self.locals.append(self.base.allocator, .{
550547 .n_strx = 0,
551548 .n_type = macho.N_UNDF,
......@@ -557,13 +554,56 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
557554 }
558555
559556 if (needs_full_relink) {
557 for (self.objects.items) |*object| {
558 object.free(self.base.allocator, self);
559 object.deinit(self.base.allocator);
560 }
560561 self.objects.clearRetainingCapacity();
562
563 for (self.archives.items) |*archive| {
564 archive.deinit(self.base.allocator);
565 }
561566 self.archives.clearRetainingCapacity();
567
568 for (self.dylibs.items) |*dylib| {
569 dylib.deinit(self.base.allocator);
570 }
562571 self.dylibs.clearRetainingCapacity();
563572 self.dylibs_map.clearRetainingCapacity();
564573 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
568608 // Positional arguments to the linker such as object files and static archives.
569609 var positionals = std.ArrayList([]const u8).init(arena);
......@@ -802,13 +842,35 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
802842 try self.createDsoHandleAtom();
803843 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
805865 for (self.unresolved.keys()) |index| {
806866 const sym = self.undefs.items[index];
807867 const sym_name = self.getString(sym.n_strx);
808868 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;
809869
810870 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 }
812874 }
813875 if (self.unresolved.count() > 0) {
814876 return error.UndefinedSymbolReference;
......@@ -1807,7 +1869,7 @@ fn writeAtoms(self: *MachO) !void {
18071869pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom {
18081870 const local_sym_index = @intCast(u32, self.locals.items.len);
18091871 try self.locals.append(self.base.allocator, .{
1810 .n_strx = try self.makeString("l_zld_got_entry"),
1872 .n_strx = 0,
18111873 .n_type = macho.N_SECT,
18121874 .n_sect = 0,
18131875 .n_desc = 0,
......@@ -1845,7 +1907,7 @@ fn createDyldPrivateAtom(self: *MachO) !void {
18451907 const local_sym_index = @intCast(u32, self.locals.items.len);
18461908 const sym = try self.locals.addOne(self.base.allocator);
18471909 sym.* = .{
1848 .n_strx = try self.makeString("l_zld_dyld_private"),
1910 .n_strx = 0,
18491911 .n_type = macho.N_SECT,
18501912 .n_sect = 0,
18511913 .n_desc = 0,
......@@ -1879,7 +1941,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
18791941 const local_sym_index = @intCast(u32, self.locals.items.len);
18801942 const sym = try self.locals.addOne(self.base.allocator);
18811943 sym.* = .{
1882 .n_strx = try self.makeString("l_zld_stub_preamble"),
1944 .n_strx = 0,
18831945 .n_type = macho.N_SECT,
18841946 .n_sect = 0,
18851947 .n_desc = 0,
......@@ -2021,7 +2083,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
20212083 };
20222084 const local_sym_index = @intCast(u32, self.locals.items.len);
20232085 try self.locals.append(self.base.allocator, .{
2024 .n_strx = try self.makeString("l_zld_stub_in_stub_helper"),
2086 .n_strx = 0,
20252087 .n_type = macho.N_SECT,
20262088 .n_sect = 0,
20272089 .n_desc = 0,
......@@ -2076,7 +2138,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
20762138pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym_index: u32) !*Atom {
20772139 const local_sym_index = @intCast(u32, self.locals.items.len);
20782140 try self.locals.append(self.base.allocator, .{
2079 .n_strx = try self.makeString("l_zld_lazy_ptr"),
2141 .n_strx = 0,
20802142 .n_type = macho.N_SECT,
20812143 .n_sect = 0,
20822144 .n_desc = 0,
......@@ -2117,7 +2179,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
21172179 };
21182180 const local_sym_index = @intCast(u32, self.locals.items.len);
21192181 try self.locals.append(self.base.allocator, .{
2120 .n_strx = try self.makeString("l_zld_stub"),
2182 .n_strx = 0,
21212183 .n_type = macho.N_SECT,
21222184 .n_sect = 0,
21232185 .n_desc = 0,
......@@ -2183,7 +2245,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
21832245fn createTentativeDefAtoms(self: *MachO) !void {
21842246 if (self.tentatives.count() == 0) return;
21852247 // Convert any tentative definition into a regular symbol and allocate
2186 // text blocks for each tentative defintion.
2248 // text blocks for each tentative definition.
21872249 while (self.tentatives.popOrNull()) |entry| {
21882250 const match = MatchingSection{
21892251 .seg = self.data_segment_cmd_index.?,
......@@ -2349,7 +2411,9 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
23492411 !(symbolIsWeakDef(global.*) or symbolIsPext(global.*)))
23502412 {
23512413 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 }
23532417 log.err(" next definition in '{s}'", .{object.name});
23542418 return error.MultipleSymbolDefinitions;
23552419 } else if (symbolIsWeakDef(sym) or symbolIsPext(sym)) continue; // Current symbol is weak, so skip it.
......@@ -2632,10 +2696,10 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
26322696 const tracy = trace(@src());
26332697 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);
26362700 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);
26392703 defer first_atoms.deinit();
26402704
26412705 var section_metadata = std.AutoHashMap(MatchingSection, struct {
......@@ -2644,13 +2708,12 @@ fn parseObjectsIntoAtoms(self: *MachO) !void {
26442708 }).init(self.base.allocator);
26452709 defer section_metadata.deinit();
26462710
2647 for (self.objects.items) |*object, object_id| {
2711 for (self.objects.items) |*object| {
26482712 if (object.analyzed) continue;
26492713
2650 var atoms_in_objects = try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_id), self);
2651 defer atoms_in_objects.deinit();
2714 try object.parseIntoAtoms(self.base.allocator, self);
26522715
2653 var it = atoms_in_objects.iterator();
2716 var it = object.end_atoms.iterator();
26542717 while (it.next()) |entry| {
26552718 const match = entry.key_ptr.*;
26562719 const last_atom = entry.value_ptr.*;
......@@ -3292,8 +3355,6 @@ pub fn updateDeclExports(
32923355 decl: *Module.Decl,
32933356 exports: []const *Module.Export,
32943357) !void {
3295 // TODO If we are exporting with global linkage, check for already defined globals and flag
3296 // symbol duplicate/collision!
32973358 if (build_options.skip_non_native and builtin.object_format != .macho) {
32983359 @panic("Attempted to compile for object format that was disabled by build configuration");
32993360 }
......@@ -3303,7 +3364,7 @@ pub fn updateDeclExports(
33033364 const tracy = trace(@src());
33043365 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);
33073368 if (decl.link.macho.local_sym_index == 0) return;
33083369 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];
33093370
......@@ -3313,15 +3374,76 @@ pub fn updateDeclExports(
33133374
33143375 if (exp.options.section) |section_name| {
33153376 if (!mem.eql(u8, section_name, "__text")) {
3316 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);
3317 module.failed_exports.putAssumeCapacityNoClobber(
3377 try module.failed_exports.putNoClobber(
3378 module.gpa,
33183379 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 ),
33203386 );
33213387 continue;
33223388 }
33233389 }
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
33253447 var n_type: u8 = macho.N_SECT | macho.N_EXT;
33263448 var n_desc: u16 = 0;
33273449
......@@ -3339,14 +3461,7 @@ pub fn updateDeclExports(
33393461 // Symbol's n_type is like for a symbol with strong linkage.
33403462 n_desc |= macho.N_WEAK_DEF;
33413463 },
3342 .LinkOnce => {
3343 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.count() + 1);
3344 module.failed_exports.putAssumeCapacityNoClobber(
3345 exp,
3346 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}),
3347 );
3348 continue;
3349 },
3464 else => unreachable,
33503465 }
33513466
33523467 const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {
......@@ -3356,8 +3471,6 @@ pub fn updateDeclExports(
33563471 };
33573472 break :blk i;
33583473 };
3359
3360 const n_strx = try self.makeString(exp_name);
33613474 const sym = &self.globals.items[global_sym_index];
33623475 sym.* = .{
33633476 .n_strx = try self.makeString(exp_name),
......@@ -3368,12 +3481,11 @@ pub fn updateDeclExports(
33683481 };
33693482 exp.link.macho.sym_index = global_sym_index;
33703483
3371 const resolv = try self.symbol_resolver.getOrPut(self.base.allocator, n_strx);
3372 resolv.value_ptr.* = .{
3484 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
33733485 .where = .global,
33743486 .where_index = global_sym_index,
33753487 .local_sym_index = decl.link.macho.local_sym_index,
3376 };
3488 });
33773489 }
33783490}
33793491
......@@ -3381,11 +3493,17 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
33813493 const sym_index = exp.sym_index orelse return;
33823494 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
33833495 const global = &self.globals.items[sym_index];
3384 global.n_type = 0;
3496 log.debug("deleting export '{s}': {}", .{ self.getString(global.n_strx), global });
33853497 assert(self.symbol_resolver.remove(global.n_strx));
3498 global.n_type = 0;
3499 global.n_strx = 0;
3500 global.n_value = 0;
33863501}
33873502
33883503pub 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 }
33893507 log.debug("freeDecl {*}", .{decl});
33903508 _ = self.decls.swapRemove(decl);
33913509 // 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
39704088 return mem.alignForwardGeneric(u64, final_off, alignment);
39714089}
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
39734155fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
39744156 const tracy = trace(@src());
39754157 defer tracy.end();
......@@ -3983,7 +4165,14 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void {
39834165 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
39844166
39854167 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
39874176 // Need to move all sections below in file and address spaces.
39884177 const offset_amt = offset: {
39894178 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 {
39994188
40004189 if (last_sect_off + offset_amt > seg_off) {
40014190 // Need to grow segment first.
4002 log.debug(" (need to grow segment first)", .{});
40034191 const spill_size = (last_sect_off + offset_amt) - seg_off;
4004 const seg_offset_amt = mem.alignForwardGeneric(u64, spill_size, self.page_size);
4005 seg.inner.filesize += seg_offset_amt;
4006 seg.inner.vmsize += seg_offset_amt;
4007
4008 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4009 seg.inner.segname,
4010 seg.inner.fileoff,
4011 seg.inner.fileoff + seg.inner.filesize,
4012 seg.inner.vmaddr,
4013 seg.inner.vmaddr + seg.inner.vmsize,
4014 });
4015
4016 // TODO We should probably nop the expanded by distance, or put 0s.
4017
4018 // TODO copyRangeAll doesn't automatically extend the file on macOS.
4019 const ledit_seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
4020 const new_filesize = seg_offset_amt + ledit_seg.inner.fileoff + ledit_seg.inner.filesize;
4021 try self.base.file.?.pwriteAll(&[_]u8{0}, new_filesize - 1);
4022
4023 var next: usize = match.seg + 1;
4024 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
4025 const next_seg = &self.load_commands.items[next].Segment;
4026 _ = try self.base.file.?.copyRangeAll(
4027 next_seg.inner.fileoff,
4028 self.base.file.?,
4029 next_seg.inner.fileoff + seg_offset_amt,
4030 next_seg.inner.filesize,
4031 );
4032 next_seg.inner.fileoff += seg_offset_amt;
4033 next_seg.inner.vmaddr += seg_offset_amt;
4034
4035 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4036 next_seg.inner.segname,
4037 next_seg.inner.fileoff,
4038 next_seg.inner.fileoff + next_seg.inner.filesize,
4039 next_seg.inner.vmaddr,
4040 next_seg.inner.vmaddr + next_seg.inner.vmsize,
4041 });
4042
4043 for (next_seg.sections.items) |*moved_sect, moved_sect_id| {
4044 moved_sect.offset += @intCast(u32, seg_offset_amt);
4045 moved_sect.addr += seg_offset_amt;
4046
4047 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4048 commands.segmentName(moved_sect.*),
4049 commands.sectionName(moved_sect.*),
4050 moved_sect.offset,
4051 moved_sect.offset + moved_sect.size,
4052 moved_sect.addr,
4053 moved_sect.addr + moved_sect.size,
4054 });
4055
4056 try self.allocateLocalSymbols(.{
4057 .seg = @intCast(u16, next),
4058 .sect = @intCast(u16, moved_sect_id),
4059 }, @intCast(i64, seg_offset_amt));
4060 }
4061 }
4192 try self.growSegment(match.seg, seg.inner.filesize + spill_size);
40624193 }
40634194
4064 if (match.sect + 1 >= seg.sections.items.len) break :blk;
4065
40664195 // We have enough space to expand within the segment, so move all sections by
40674196 // the required amount and update their header offsets.
40684197 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
42284357 return vaddr;
42294358}
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 {
42324369 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
42334370 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{
4236 .bytes = &self.strtab,
4237 })) |n_strx| {
4238 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
4239 return resolv.where_index;
4373 if (self.symbol_resolver.get(n_strx)) |resolv| {
4374 return switch (resolv.where) {
4375 .global => AddExternFnRes{
4376 .where = .local,
4377 .where_index = resolv.local_sym_index,
4378 },
4379 .undef => AddExternFnRes{
4380 .where = .undef,
4381 .where_index = resolv.where_index,
4382 },
4383 };
42404384 }
42414385
42424386 log.debug("adding new extern function '{s}'", .{sym_name});
42434387 const sym_index = @intCast(u32, self.undefs.items.len);
4244 const n_strx = try self.makeString(sym_name);
42454388 try self.undefs.append(self.base.allocator, .{
42464389 .n_strx = n_strx,
42474390 .n_type = macho.N_UNDF,
......@@ -4255,7 +4398,10 @@ pub fn addExternFn(self: *MachO, name: []const u8) !u32 {
42554398 });
42564399 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 };
42594405}
42604406
42614407const NextSegmentAddressAndOffset = struct {
......@@ -4386,6 +4532,7 @@ fn writeDyldInfoData(self: *MachO) !void {
43864532 const base_address = text_segment.inner.vmaddr;
43874533
43884534 for (self.globals.items) |sym| {
4535 if (sym.n_type == 0) continue;
43894536 const sym_name = self.getString(sym.n_strx);
43904537 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 {
44624609
44634610 // Because we insert lazy binding opcodes in reverse order (from last to the first atom),
44644611 // 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!
44664613 var atom = last_atom;
44674614 while (atom.prev) |prev| {
44684615 atom = prev;
......@@ -4608,7 +4755,12 @@ fn writeSymbolTable(self: *MachO) !void {
46084755
46094756 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
46104757 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
46134765 if (self.has_stabs) {
46144766 for (self.objects.items) |object| {
......@@ -4638,7 +4790,7 @@ fn writeSymbolTable(self: *MachO) !void {
46384790 .n_value = object.mtime orelse 0,
46394791 });
46404792
4641 for (object.atoms.items) |atom| {
4793 for (object.contained_atoms.items) |atom| {
46424794 if (atom.stab) |stab| {
46434795 const nlists = try stab.asNlists(atom.local_sym_index, self);
46444796 defer self.base.allocator.free(nlists);
src/link/MachO/Atom.zig+61-54
......@@ -2,7 +2,7 @@ const Atom = @This();
22
33const std = @import("std");
44const build_options = @import("build_options");
5const aarch64 = @import("../../codegen/aarch64.zig");
5const aarch64 = @import("../../arch/aarch64/bits.zig");
66const assert = std.debug.assert;
77const commands = @import("commands.zig");
88const log = std.log.scoped(.text_block);
......@@ -41,7 +41,7 @@ code: std.ArrayListUnmanaged(u8) = .{},
4141size: u64,
4242
4343/// 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.
4545alignment: u32,
4646
4747/// List of relocations belonging to this atom.
......@@ -645,7 +645,6 @@ const RelocContext = struct {
645645 allocator: *Allocator,
646646 object: *Object,
647647 macho_file: *MachO,
648 parsed_atoms: *Object.ParsedAtoms,
649648};
650649
651650fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Relocation {
......@@ -664,15 +663,8 @@ fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Reloc
664663 const sect = seg.sections.items[sect_id];
665664 const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable;
666665 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
674666 try context.macho_file.locals.append(context.allocator, .{
675 .n_strx = try context.macho_file.makeString(sym_name),
667 .n_strx = 0,
676668 .n_type = macho.N_SECT,
677669 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),
678670 .n_desc = 0,
......@@ -877,12 +869,16 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
877869 .sect = context.macho_file.got_section_index.?,
878870 };
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| {
881877 last.*.next = atom;
882878 atom.prev = last.*;
883879 last.* = atom;
884880 } else {
885 try context.parsed_atoms.putNoClobber(match, atom);
881 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
886882 }
887883 } else if (parsed_rel.payload == .unsigned) {
888884 switch (parsed_rel.where) {
......@@ -939,52 +935,63 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
939935 if (parsed_rel.where != .undef) break :blk;
940936 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);
949938 // TODO clean this up!
950 if (context.parsed_atoms.getPtr(.{
951 .seg = context.macho_file.text_segment_cmd_index.?,
952 .sect = context.macho_file.stub_helper_section_index.?,
953 })) |last| {
954 last.*.next = stub_helper_atom;
955 stub_helper_atom.prev = last.*;
956 last.* = stub_helper_atom;
957 } else {
958 try context.parsed_atoms.putNoClobber(.{
939 const stub_helper_atom = atom: {
940 const atom = try context.macho_file.createStubHelperAtom();
941 const match = MachO.MatchingSection{
959942 .seg = context.macho_file.text_segment_cmd_index.?,
960943 .sect = context.macho_file.stub_helper_section_index.?,
961 }, stub_helper_atom);
962 }
963 if (context.parsed_atoms.getPtr(.{
964 .seg = context.macho_file.text_segment_cmd_index.?,
965 .sect = context.macho_file.stubs_section_index.?,
966 })) |last| {
967 last.*.next = stub_atom;
968 stub_atom.prev = last.*;
969 last.* = stub_atom;
970 } else {
971 try context.parsed_atoms.putNoClobber(.{
972 .seg = context.macho_file.text_segment_cmd_index.?,
973 .sect = context.macho_file.stubs_section_index.?,
974 }, stub_atom);
975 }
976 if (context.parsed_atoms.getPtr(.{
977 .seg = context.macho_file.data_segment_cmd_index.?,
978 .sect = context.macho_file.la_symbol_ptr_section_index.?,
979 })) |last| {
980 last.*.next = laptr_atom;
981 laptr_atom.prev = last.*;
982 last.* = laptr_atom;
983 } else {
984 try context.parsed_atoms.putNoClobber(.{
944 };
945 if (!context.object.start_atoms.contains(match)) {
946 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
947 }
948 if (context.object.end_atoms.getPtr(match)) |last| {
949 last.*.next = atom;
950 atom.prev = last.*;
951 last.* = atom;
952 } else {
953 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
954 }
955 break :atom atom;
956 };
957 const laptr_atom = atom: {
958 const atom = try context.macho_file.createLazyPointerAtom(
959 stub_helper_atom.local_sym_index,
960 parsed_rel.where_index,
961 );
962 const match = MachO.MatchingSection{
985963 .seg = context.macho_file.data_segment_cmd_index.?,
986964 .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);
988995 }
989996 }
990997 }
src/link/MachO/CodeSignature.zig+1-1
......@@ -102,7 +102,7 @@ pub fn calcAdhocSignature(
102102 var buffer = try allocator.alloc(u8, page_size);
103103 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
107107 // 1. Save the identifier and update offsets
108108 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
353353
354354 // We have a function to compute the upper bound size, because it's needed
355355 // 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
358358 // initial length - length of the .debug_info contribution for this compilation unit,
359359 // not including the initial length itself.
......@@ -408,7 +408,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
408408
409409 // Enough for all the data without resizing. When support for more compilation units
410410 // is added, the size of this section will become more variable.
411 try di_buf.ensureCapacity(100);
411 try di_buf.ensureTotalCapacity(100);
412412
413413 // initial length - length of the .debug_aranges contribution for this compilation unit,
414414 // not including the initial length itself.
......@@ -479,7 +479,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
479479 // The size of this header is variable, depending on the number of directories,
480480 // files, and padding. We have a function to compute the upper bound size, however,
481481 // 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
484484 // initial length - length of the .debug_line contribution for this compilation unit,
485485 // not including the initial length itself.
......@@ -607,7 +607,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: Segm
607607 };
608608 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);
611611 for (base_cmd.sections.items) |base_sect, i| {
612612 var sect = macho.section_64{
613613 .sectname = undefined,
......@@ -855,7 +855,7 @@ pub fn initDeclDebugBuffers(
855855 switch (decl.ty.zigTypeTag()) {
856856 .Fn => {
857857 // 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
860860 const func = decl.val.castTag(.function).?.data;
861861 const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
......@@ -889,7 +889,7 @@ pub fn initDeclDebugBuffers(
889889
890890 // .debug_info subprogram
891891 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
894894 const fn_ret_type = decl.ty.fnReturnType();
895895 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
......@@ -1124,7 +1124,7 @@ fn addDbgInfoType(
11241124 },
11251125 .Int => {
11261126 const info = ty.intInfo(target);
1127 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
1127 try dbg_info_buffer.ensureUnusedCapacity(12);
11281128 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
11291129 // DW.AT.encoding, DW.FORM.data1
11301130 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {
......@@ -1261,7 +1261,7 @@ fn getDebugLineProgramEnd(self: DebugSymbols) u32 {
12611261
12621262/// TODO Improve this to use a table.
12631263fn 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);
12651265 const result = self.debug_string_table.items.len;
12661266 self.debug_string_table.appendSliceAssumeCapacity(bytes);
12671267 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 {
180180fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void {
181181 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
185185 var i: u16 = 0;
186186 while (i < self.header.?.ncmds) : (i += 1) {
src/link/MachO/Object.zig+154-396
......@@ -31,14 +31,12 @@ header: ?macho.mach_header_64 = null,
3131load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
3232
3333segment_cmd_index: ?u16 = null,
34text_section_index: ?u16 = null,
3435symtab_cmd_index: ?u16 = null,
3536dysymtab_cmd_index: ?u16 = null,
3637build_version_cmd_index: ?u16 = null,
3738data_in_code_cmd_index: ?u16 = null,
3839
39text_section_index: ?u16 = null,
40mod_init_func_section_index: ?u16 = null,
41
4240// __DWARF segment sections
4341dwarf_debug_info_index: ?u16 = null,
4442dwarf_debug_abbrev_index: ?u16 = null,
......@@ -56,7 +54,9 @@ tu_name: ?[]const u8 = null,
5654tu_comp_dir: ?[]const u8 = null,
5755mtime: ?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) = .{},
6060sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
6161
6262// TODO symbol mapping and its inverse can probably be simple arrays
......@@ -138,12 +138,15 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {
138138 self.data_in_code_entries.deinit(allocator);
139139 self.symtab.deinit(allocator);
140140 self.strtab.deinit(allocator);
141 self.atoms.deinit(allocator);
142141 self.sections_as_symbols.deinit(allocator);
143142 self.symbol_mapping.deinit(allocator);
144143 self.reverse_symbol_mapping.deinit(allocator);
145144 allocator.free(self.name);
146145
146 self.contained_atoms.deinit(allocator);
147 self.start_atoms.deinit(allocator);
148 self.end_atoms.deinit(allocator);
149
147150 if (self.debug_info) |*db| {
148151 db.deinit(allocator);
149152 }
......@@ -157,6 +160,67 @@ pub fn deinit(self: *Object, allocator: *Allocator) void {
157160 }
158161}
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
160224pub fn parse(self: *Object, allocator: *Allocator, target: std.Target) !void {
161225 const reader = self.file.reader();
162226 if (self.file_offset) |offset| {
......@@ -197,7 +261,7 @@ pub fn readLoadCommands(self: *Object, allocator: *Allocator, reader: anytype) !
197261 const header = self.header orelse unreachable; // Unreachable here signifies a fatal unexplored condition.
198262 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
202266 var i: u16 = 0;
203267 while (i < header.ncmds) : (i += 1) {
......@@ -226,10 +290,6 @@ pub fn readLoadCommands(self: *Object, allocator: *Allocator, reader: anytype) !
226290 if (mem.eql(u8, sectname, "__text")) {
227291 self.text_section_index = index;
228292 }
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 }
233293 }
234294
235295 sect.offset += offset;
......@@ -315,166 +375,10 @@ fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64)
315375 return dices[start..end];
316376}
317377
318const Context = struct {
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 {
378pub fn parseIntoAtoms(self: *Object, allocator: *Allocator, macho_file: *MachO) !void {
474379 const tracy = trace(@src());
475380 defer tracy.end();
476381
477 var parsed_atoms = ParsedAtoms.init(allocator);
478382 const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
479383
480384 log.debug("analysing {s}", .{self.name});
......@@ -540,16 +444,6 @@ pub fn parseIntoAtoms(
540444 // Symbols within this section only.
541445 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
553447 macho_file.has_dices = macho_file.has_dices or blk: {
554448 if (self.text_section_index) |index| {
555449 if (index != id) break :blk false;
......@@ -560,237 +454,101 @@ pub fn parseIntoAtoms(
560454 };
561455 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
562456
563 next: {
564 if (is_splittable) atoms: {
565 if (filtered_nlists.len == 0) break :atoms;
566
567 // If the first nlist does not match the start of the section,
568 // then we need to encapsulate the memory range [section start, first symbol)
569 // as a temporary symbol and insert the matching Atom.
570 const first_nlist = filtered_nlists[0].nlist;
571 if (first_nlist.n_value > sect.addr) {
572 const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{
573 self.name,
574 segmentName(sect),
575 sectionName(sect),
576 });
577 defer allocator.free(sym_name);
578
579 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
580 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
581 try macho_file.locals.append(allocator, .{
582 .n_strx = try macho_file.makeString(sym_name),
583 .n_type = macho.N_SECT,
584 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
585 .n_desc = 0,
586 .n_value = 0,
587 });
588 try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index);
589 break :blk atom_local_sym_index;
590 };
591 const atom_code = code[0 .. first_nlist.n_value - sect.addr];
592 const atom_size = atom_code.len;
593 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, atom_size, sect.@"align");
594
595 const is_zerofill = blk: {
596 const section_type = commands.sectionType(sect);
597 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
598 };
599 if (!is_zerofill) {
600 mem.copy(u8, atom.code.items, atom_code);
601 }
602
603 try atom.parseRelocs(relocs, .{
604 .base_addr = sect.addr,
605 .base_offset = 0,
606 .allocator = allocator,
607 .object = self,
608 .macho_file = macho_file,
609 .parsed_atoms = &parsed_atoms,
610 });
611
612 if (macho_file.has_dices) {
613 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + atom_size);
614 try atom.dices.ensureTotalCapacity(allocator, dices.len);
615
616 for (dices) |dice| {
617 atom.dices.appendAssumeCapacity(.{
618 .offset = dice.offset - try math.cast(u32, sect.addr),
619 .length = dice.length,
620 .kind = dice.kind,
621 });
622 }
623 }
624
625 if (parsed_atoms.getPtr(match)) |last| {
626 last.*.next = atom;
627 atom.prev = last.*;
628 last.* = atom;
629 } else {
630 try parsed_atoms.putNoClobber(match, atom);
631 }
632 try self.atoms.append(allocator, atom);
633 }
634
635 var parser = AtomParser{
636 .section = sect,
637 .code = code,
638 .relocs = relocs,
639 .nlists = filtered_nlists,
640 };
641
642 while (try parser.next(.{
643 .allocator = allocator,
644 .object = self,
645 .macho_file = macho_file,
646 .match = match,
647 .parsed_atoms = &parsed_atoms,
648 })) |atom| {
649 const sym = macho_file.locals.items[atom.local_sym_index];
650 const is_ext = blk: {
651 const orig_sym_id = self.reverse_symbol_mapping.get(atom.local_sym_index) orelse unreachable;
652 break :blk MachO.symbolIsExt(self.symtab.items[orig_sym_id]);
653 };
654 if (is_ext) {
655 if (macho_file.symbol_resolver.get(sym.n_strx)) |resolv| {
656 assert(resolv.where == .global);
657 if (resolv.file != object_id) {
658 log.debug("deduping definition of {s} in {s}", .{
659 macho_file.getString(sym.n_strx),
660 self.name,
661 });
662 log.debug(" already defined in {s}", .{
663 macho_file.objects.items[resolv.file].name,
664 });
665 continue;
666 }
667 }
668 }
457 // Since there is no symbol to refer to this atom, we create
458 // a temp one, unless we already did that when working out the relocations
459 // of other atoms.
460 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
461 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
462 try macho_file.locals.append(allocator, .{
463 .n_strx = 0,
464 .n_type = macho.N_SECT,
465 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
466 .n_desc = 0,
467 .n_value = 0,
468 });
469 try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index);
470 break :blk atom_local_sym_index;
471 };
472 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, sect.size, sect.@"align");
669473
670 if (sym.n_value == sect.addr) {
671 if (self.sections_as_symbols.get(sect_id)) |alias| {
672 // In x86_64 relocs, it can so happen that the compiler refers to the same
673 // atom by both the actual assigned symbol and the start of the section. In this
674 // case, we need to link the two together so add an alias.
675 try atom.aliases.append(allocator, alias);
676 }
677 }
474 const is_zerofill = blk: {
475 const section_type = commands.sectionType(sect);
476 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
477 };
478 if (!is_zerofill) {
479 mem.copy(u8, atom.code.items, code);
480 }
678481
679 if (parsed_atoms.getPtr(match)) |last| {
680 last.*.next = atom;
681 atom.prev = last.*;
682 last.* = atom;
683 } else {
684 try parsed_atoms.putNoClobber(match, atom);
685 }
686 try self.atoms.append(allocator, atom);
687 }
482 try atom.parseRelocs(relocs, .{
483 .base_addr = sect.addr,
484 .base_offset = 0,
485 .allocator = allocator,
486 .object = self,
487 .macho_file = macho_file,
488 });
688489
689 break :next;
690 }
490 if (macho_file.has_dices) {
491 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);
492 try atom.dices.ensureTotalCapacity(allocator, dices.len);
691493
692 // Since there is no symbol to refer to this atom, we create
693 // a temp one, unless we already did that when working out the relocations
694 // of other atoms.
695 const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{
696 self.name,
697 segmentName(sect),
698 sectionName(sect),
699 });
700 defer allocator.free(sym_name);
701
702 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
703 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
704 try macho_file.locals.append(allocator, .{
705 .n_strx = try macho_file.makeString(sym_name),
706 .n_type = macho.N_SECT,
707 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
708 .n_desc = 0,
709 .n_value = 0,
494 for (dices) |dice| {
495 atom.dices.appendAssumeCapacity(.{
496 .offset = dice.offset - try math.cast(u32, sect.addr),
497 .length = dice.length,
498 .kind = dice.kind,
710499 });
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 }
744500 }
501 }
745502
746 // Since this is atom gets a helper local temporary symbol that didn't exist
747 // in the object file which encompasses the entire section, we need traverse
748 // the filtered symbols and note which symbol is contained within so that
749 // we can properly allocate addresses down the line.
750 // While we're at it, we need to update segment,section mapping of each symbol too.
751 try atom.contained.ensureTotalCapacity(allocator, filtered_nlists.len);
752
753 for (filtered_nlists) |nlist_with_index| {
754 const nlist = nlist_with_index.nlist;
755 const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
756 const local = &macho_file.locals.items[local_sym_index];
757 local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);
758
759 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {
760 // TODO there has to be a better to handle this.
761 for (di.inner.func_list.items) |func| {
762 if (func.pc_range) |range| {
763 if (nlist.n_value >= range.start and nlist.n_value < range.end) {
764 break :blk Atom.Stab{
765 .function = range.end - range.start,
766 };
767 }
503 // Since this is atom gets a helper local temporary symbol that didn't exist
504 // in the object file which encompasses the entire section, we need traverse
505 // the filtered symbols and note which symbol is contained within so that
506 // we can properly allocate addresses down the line.
507 // While we're at it, we need to update segment,section mapping of each symbol too.
508 try atom.contained.ensureTotalCapacity(allocator, filtered_nlists.len);
509
510 for (filtered_nlists) |nlist_with_index| {
511 const nlist = nlist_with_index.nlist;
512 const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
513 const local = &macho_file.locals.items[local_sym_index];
514 local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);
515
516 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {
517 // TODO there has to be a better to handle this.
518 for (di.inner.func_list.items) |func| {
519 if (func.pc_range) |range| {
520 if (nlist.n_value >= range.start and nlist.n_value < range.end) {
521 break :blk Atom.Stab{
522 .function = range.end - range.start,
523 };
768524 }
769525 }
770 // TODO
771 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
772 break :blk .static;
773 } else null;
774
775 atom.contained.appendAssumeCapacity(.{
776 .local_sym_index = local_sym_index,
777 .offset = nlist.n_value - sect.addr,
778 .stab = stab,
779 });
780 }
526 }
527 // TODO
528 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
529 break :blk .static;
530 } else null;
531
532 atom.contained.appendAssumeCapacity(.{
533 .local_sym_index = local_sym_index,
534 .offset = nlist.n_value - sect.addr,
535 .stab = stab,
536 });
537 }
781538
782 if (parsed_atoms.getPtr(match)) |last| {
783 last.*.next = atom;
784 atom.prev = last.*;
785 last.* = atom;
786 } else {
787 try parsed_atoms.putNoClobber(match, atom);
788 }
789 try self.atoms.append(allocator, atom);
539 if (!self.start_atoms.contains(match)) {
540 try self.start_atoms.putNoClobber(allocator, match, atom);
790541 }
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 }
794552}
795553
796554fn 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 {
326326 if (!self.trie_dirty) return;
327327
328328 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
331331 var fifo = std.fifo.LinearFifo(*Node, .Dynamic).init(allocator);
332332 defer fifo.deinit();
......@@ -506,7 +506,7 @@ test "write Trie to a byte stream" {
506506 });
507507
508508 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
511511 const exp_buffer = [_]u8{
512512 0x0, 0x1, // node root
src/link/MachO/commands.zig+1-1
......@@ -223,7 +223,7 @@ pub const SegmentCommand = struct {
223223 var segment = SegmentCommand{
224224 .inner = inner,
225225 };
226 try segment.sections.ensureCapacity(alloc, inner.nsects);
226 try segment.sections.ensureTotalCapacity(alloc, inner.nsects);
227227
228228 var i: usize = 0;
229229 while (i < inner.nsects) : (i += 1) {
src/link/Plan9.zig+332-64
......@@ -20,6 +20,14 @@ const Allocator = std.mem.Allocator;
2020const log = std.log.scoped(.link);
2121const 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
2331base: link.File,
2432sixtyfour_bit: bool,
2533error_flags: File.ErrorFlags = File.ErrorFlags{},
......@@ -27,16 +35,45 @@ bases: Bases,
2735
2836/// A symbol's value is just casted down when compiling
2937/// 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.
3041syms: 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) = .{},
3363data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
3464
3565hdr: aout.ExecHdr = undefined,
3666
67magic: u32,
68
3769entry_val: ?u64 = null,
3870
3971got_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
4178const Bases = struct {
4279 text: u64,
......@@ -103,8 +140,12 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
103140 33...64 => true,
104141 else => return error.UnsupportedP9Architecture,
105142 };
143
144 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
145
106146 const self = try gpa.create(Plan9);
107147 self.* = .{
148 .path_arena = arena_allocator,
108149 .base = .{
109150 .tag = .plan9,
110151 .options = options,
......@@ -113,21 +154,102 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Plan9 {
113154 },
114155 .sixtyfour_bit = sixtyfour_bit,
115156 .bases = undefined,
157 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),
116158 };
159 // a / will always be in a file path
160 try self.file_segments.put(self.base.allocator, "/", 1);
117161 return self;
118162}
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
120219pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
121220 if (build_options.skip_non_native and builtin.object_format != .plan9) {
122221 @panic("Attempted to compile for object format that was disabled by build configuration");
123222 }
124223
125224 const decl = func.owner_decl;
225
226 try self.seeDecl(decl);
126227 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
127228
128229 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
129230 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 );
131253 const code = switch (res) {
132254 .appended => code_buffer.toOwnedSlice(),
133255 .fail => |em| {
......@@ -136,7 +258,13 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
136258 return;
137259 },
138260 };
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);
140268 return self.updateFinish(decl);
141269}
142270
......@@ -151,6 +279,8 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
151279 }
152280 }
153281
282 try self.seeDecl(decl);
283
154284 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
155285
156286 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
......@@ -192,8 +322,12 @@ fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {
192322 if (decl.link.plan9.sym_index) |s| {
193323 self.syms.items[s] = sym;
194324 } else {
195 try self.syms.append(self.base.allocator, sym);
196 decl.link.plan9.sym_index = self.syms.items.len - 1;
325 if (self.syms_index_free_list.popOrNull()) |i| {
326 decl.link.plan9.sym_index = i;
327 } else {
328 try self.syms.append(self.base.allocator, sym);
329 decl.link.plan9.sym_index = self.syms.items.len - 1;
330 }
197331 }
198332}
199333
......@@ -209,6 +343,30 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void {
209343 return self.flushModule(comp);
210344}
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
212370pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
213371 if (build_options.skip_non_native and builtin.object_format != .plan9) {
214372 @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 {
224382
225383 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; see
228 // the comment in `freeDecl`.
229 assert(self.got_len >= self.fn_decl_table.count() + self.data_decl_table.count());
385 assert(self.got_len == self.declCount() + self.got_index_free_list.items.len);
230386 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
231387 var got_table = try self.base.allocator.alloc(u8, got_size);
232388 defer self.base.allocator.free(got_table);
233389
234 // + 2 for header, got, symbols
235 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.fn_decl_table.count() + self.data_decl_table.count() + 3);
390 // + 4 for header, got, symbols, linecountinfo
391 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.declCount() + 4);
236392 defer self.base.allocator.free(iovecs);
237393
238394 const file = self.base.file.?;
......@@ -245,30 +401,52 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
245401 iovecs[0] = .{ .iov_base = hdr_slice.ptr, .iov_len = hdr_slice.len };
246402 var iovecs_i: usize = 1;
247403 var text_i: u64 = 0;
404
405 var linecountinfo = std.ArrayList(u8).init(self.base.allocator);
406 defer linecountinfo.deinit();
248407 // text
249408 {
250 var it = self.fn_decl_table.iterator();
251 while (it.next()) |entry| {
252 const decl = entry.key_ptr.*;
253 const code = entry.value_ptr.*;
254 log.debug("write text decl {*} ({s})", .{ decl, decl.name });
255 foff += code.len;
256 iovecs[iovecs_i] = .{ .iov_base = code.ptr, .iov_len = code.len };
257 iovecs_i += 1;
258 const off = self.getAddr(text_i, .t);
259 text_i += code.len;
260 decl.link.plan9.offset = off;
261 if (!self.sixtyfour_bit) {
262 mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
263 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
264 } else {
265 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
266 }
267 self.syms.items[decl.link.plan9.sym_index.?].value = off;
268 if (mod.decl_exports.get(decl)) |exports| {
269 try self.addDeclExports(mod, decl, exports);
409 var linecount: u32 = 0;
410 var it_file = self.fn_decl_table.iterator();
411 while (it_file.next()) |fentry| {
412 var it = fentry.value_ptr.functions.iterator();
413 while (it.next()) |entry| {
414 const decl = entry.key_ptr.*;
415 const out = entry.value_ptr.*;
416 log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line, out.end_line });
417 {
418 // connect the previous decl to the next
419 const delta_line = @intCast(i32, out.start_line) - @intCast(i32, linecount);
420
421 try changeLine(&linecountinfo, delta_line);
422 // TODO change the pc too (maybe?)
423
424 // write out the actual info that was generated in codegen now
425 try linecountinfo.appendSlice(out.lineinfo);
426 linecount = out.end_line;
427 }
428 foff += out.code.len;
429 iovecs[iovecs_i] = .{ .iov_base = out.code.ptr, .iov_len = out.code.len };
430 iovecs_i += 1;
431 const off = self.getAddr(text_i, .t);
432 text_i += out.code.len;
433 decl.link.plan9.offset = off;
434 if (!self.sixtyfour_bit) {
435 mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
436 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
437 } else {
438 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
439 }
440 self.syms.items[decl.link.plan9.sym_index.?].value = off;
441 if (mod.decl_exports.get(decl)) |exports| {
442 try self.addDeclExports(mod, decl, exports);
443 }
270444 }
271445 }
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 }
272450 // etext symbol
273451 self.syms.items[2].value = self.getAddr(text_i, .t);
274452 }
......@@ -306,20 +484,23 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void {
306484 // edata
307485 self.syms.items[1].value = self.getAddr(0x0, .b);
308486 var sym_buf = std.ArrayList(u8).init(self.base.allocator);
309 defer sym_buf.deinit();
310487 try self.writeSyms(&sym_buf);
311 assert(2 + self.fn_decl_table.count() + self.data_decl_table.count() == iovecs_i); // we didn't write all the decls
312 iovecs[iovecs_i] = .{ .iov_base = sym_buf.items.ptr, .iov_len = sym_buf.items.len };
488 const syms = sym_buf.toOwnedSlice();
489 defer self.base.allocator.free(syms);
490 assert(2 + self.declCount() == iovecs_i); // we didn't write all the decls
491 iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len };
492 iovecs_i += 1;
493 iovecs[iovecs_i] = .{ .iov_base = linecountinfo.items.ptr, .iov_len = linecountinfo.items.len };
313494 iovecs_i += 1;
314495 // generate the header
315496 self.hdr = .{
316 .magic = try aout.magicFromArch(self.base.options.target.cpu.arch),
497 .magic = self.magic,
317498 .text = @intCast(u32, text_i),
318499 .data = @intCast(u32, data_i),
319 .syms = @intCast(u32, sym_buf.items.len),
500 .syms = @intCast(u32, syms.len),
320501 .bss = 0,
321 .pcsz = 0,
322502 .spsz = 0,
503 .pcsz = @intCast(u32, linecountinfo.items.len),
323504 .entry = @intCast(u32, self.entry_val.?),
324505 };
325506 std.mem.copy(u8, hdr_slice, self.hdr.toU8s()[0..hdr_size]);
......@@ -360,18 +541,43 @@ fn addDeclExports(
360541}
361542
362543pub 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.
365544 // TODO audit the lifetimes of decls table entries. It's possible to get
366545 // allocateDeclIndexes and then freeDecl without any updateDecl in between.
367546 // However that is planned to change, see the TODO comment in Module.zig
368547 // in the deleteUnusedDecl function.
369 const is_fn = (decl.ty.zigTypeTag() == .Fn);
548 const is_fn = (decl.val.tag() == .function);
370549 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 }
372559 } else {
373560 _ = self.data_decl_table.swapRemove(decl);
374561 }
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 }
375581}
376582
377583pub fn updateDeclExports(
......@@ -380,6 +586,7 @@ pub fn updateDeclExports(
380586 decl: *Module.Decl,
381587 exports: []const *Module.Export,
382588) !void {
589 try self.seeDecl(decl);
383590 // we do all the things in flush
384591 _ = self;
385592 _ = module;
......@@ -387,17 +594,29 @@ pub fn updateDeclExports(
387594 _ = exports;
388595}
389596pub fn deinit(self: *Plan9) void {
390 var itf = self.fn_decl_table.iterator();
391 while (itf.next()) |entry| {
392 self.base.allocator.free(entry.value_ptr.*);
597 const gpa = self.base.allocator;
598 var itf_files = self.fn_decl_table.iterator();
599 while (itf_files.next()) |ent| {
600 // get the submap
601 var submap = ent.value_ptr.functions;
602 defer submap.deinit(gpa);
603 var itf = submap.iterator();
604 while (itf.next()) |entry| {
605 gpa.free(entry.value_ptr.code);
606 gpa.free(entry.value_ptr.lineinfo);
607 }
393608 }
394 self.fn_decl_table.deinit(self.base.allocator);
609 self.fn_decl_table.deinit(gpa);
395610 var itd = self.data_decl_table.iterator();
396611 while (itd.next()) |entry| {
397 self.base.allocator.free(entry.value_ptr.*);
612 gpa.free(entry.value_ptr.*);
398613 }
399 self.data_decl_table.deinit(self.base.allocator);
400 self.syms.deinit(self.base.allocator);
614 self.data_decl_table.deinit(gpa);
615 self.syms.deinit(gpa);
616 self.got_index_free_list.deinit(gpa);
617 self.syms_index_free_list.deinit(gpa);
618 self.file_segments.deinit(gpa);
619 self.path_arena.deinit();
401620}
402621
403622pub const Export = ?usize;
......@@ -407,7 +626,6 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
407626 return error.LLVMBackendDoesNotSupportPlan9;
408627 assert(options.object_format == .plan9);
409628 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
410 .truncate = false,
411629 .read = true,
412630 .mode = link.determineMode(options),
413631 });
......@@ -441,27 +659,77 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
441659 return self;
442660}
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}
444675pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
445676 const writer = buf.writer();
446 for (self.syms.items) |sym| {
447 log.debug("sym.name: {s}", .{sym.name});
448 log.debug("sym.value: {x}", .{sym.value});
449 if (mem.eql(u8, sym.name, "_start"))
450 self.entry_val = sym.value;
451 if (!self.sixtyfour_bit) {
452 try writer.writeIntBig(u32, @intCast(u32, sym.value));
453 } else {
454 try writer.writeIntBig(u64, sym.value);
677 // write the f symbols
678 {
679 var it = self.file_segments.iterator();
680 while (it.next()) |entry| {
681 try self.writeSym(writer, .{
682 .type = .f,
683 .value = entry.value_ptr.*,
684 .name = entry.key_ptr.*,
685 });
686 }
687 }
688 // write the data symbols
689 {
690 var it = self.data_decl_table.iterator();
691 while (it.next()) |entry| {
692 const decl = entry.key_ptr.*;
693 const sym = self.syms.items[decl.link.plan9.sym_index.?];
694 try self.writeSym(writer, sym);
695 if (self.base.options.module.?.decl_exports.get(decl)) |exports| {
696 for (exports) |e| {
697 try self.writeSym(writer, self.syms.items[e.link.plan9.?]);
698 }
699 }
700 }
701 }
702 // text symbols are the hardest:
703 // the file of a text symbol is the .z symbol before it
704 // so we have to write everything in the right order
705 {
706 var it_file = self.fn_decl_table.iterator();
707 while (it_file.next()) |fentry| {
708 var symidx_and_submap = fentry.value_ptr;
709 // write the z symbol
710 try self.writeSym(writer, self.syms.items[symidx_and_submap.sym_index]);
711
712 // write all the decls come from the file of the z symbol
713 var submap_it = symidx_and_submap.functions.iterator();
714 while (submap_it.next()) |entry| {
715 const decl = entry.key_ptr.*;
716 const sym = self.syms.items[decl.link.plan9.sym_index.?];
717 try self.writeSym(writer, sym);
718 if (self.base.options.module.?.decl_exports.get(decl)) |exports| {
719 for (exports) |e| {
720 const s = self.syms.items[e.link.plan9.?];
721 if (mem.eql(u8, s.name, "_start"))
722 self.entry_val = s.value;
723 try self.writeSym(writer, s);
724 }
725 }
726 }
455727 }
456 try writer.writeByte(@enumToInt(sym.type));
457 try writer.writeAll(sym.name);
458 try writer.writeByte(0);
459728 }
460729}
461730
731/// this will be removed, moved to updateFinish
462732pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
463 if (decl.link.plan9.got_index == null) {
464 self.got_len += 1;
465 decl.link.plan9.got_index = self.got_len - 1;
466 }
733 _ = self;
734 _ = decl;
467735}
src/link/Plan9/aout.zig+18-1
......@@ -16,7 +16,7 @@ pub const ExecHdr = extern struct {
1616 comptime {
1717 assert(@sizeOf(@This()) == 32);
1818 }
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.
2020 pub fn toU8s(self: *@This()) [40]u8 {
2121 var buf: [40]u8 = undefined;
2222 var i: u8 = 0;
......@@ -34,6 +34,12 @@ pub const Sym = struct {
3434 type: Type,
3535 name: []const u8,
3636
37 pub const undefined_symbol: Sym = .{
38 .value = undefined,
39 .type = .bad,
40 .name = "undefined_symbol",
41 };
42
3743 /// The type field is one of the following characters with the
3844 /// high bit set:
3945 /// T text segment symbol
......@@ -65,6 +71,8 @@ pub const Sym = struct {
6571 z = 0x80 | 'z',
6672 Z = 0x80 | 'Z',
6773 m = 0x80 | 'm',
74 /// represents an undefined symbol, to be removed in flush
75 bad = 0,
6876
6977 pub fn toGlobal(self: Type) Type {
7078 return switch (self) {
......@@ -112,3 +120,12 @@ pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 {
112120 else => error.ArchNotSupportedByPlan9,
113121 };
114122}
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 @@
1212//! - OpName and OpMemberName instructions.
1313//! - OpModuleProcessed instructions.
1414//! 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.
1616//! All function declarations without a body (extern functions presumably).
1717//! All regular functions.
1818
......@@ -93,7 +93,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
9393 if (options.use_llvm) return error.LLVM_BackendIsTODO_ForSpirV; // TODO: LLVM Doesn't support SpirV at all.
9494 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 truncating
96 // TODO: read the file and keep valid parts instead of truncating
9797 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
9898 errdefer file.close();
9999
src/link/Wasm.zig+10-6
......@@ -35,7 +35,7 @@ llvm_object: ?*LlvmObject = null,
3535/// TODO: can/should we access some data structure in Module directly?
3636funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
3737/// List of all extern function Decls to be written to the `import` section of the
38/// wasm binary. The positin in the list defines the function index
38/// wasm binary. The position in the list defines the function index
3939ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{},
4040/// When importing objects from the host environment, a name must be supplied.
4141/// 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
121121 const self = try createEmpty(allocator, options);
122122 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);
125125 return self;
126126 }
127127
......@@ -172,8 +172,8 @@ pub fn deinit(self: *Wasm) void {
172172pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
173173 if (decl.link.wasm.init) return;
174174
175 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
176 try self.symbols.ensureCapacity(self.base.allocator, self.symbols.items.len + 1);
175 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
176 try self.symbols.ensureUnusedCapacity(self.base.allocator, 1);
177177
178178 const block = &decl.link.wasm;
179179 block.init = true;
......@@ -339,6 +339,10 @@ pub fn updateDeclExports(
339339}
340340
341341pub 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
342346 if (self.getFuncidx(decl)) |func_idx| {
343347 switch (decl.val.tag()) {
344348 .function => _ = self.funcs.swapRemove(func_idx),
......@@ -710,7 +714,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
710714 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
711715
712716 if (self.base.options.output_mode == .Obj) {
713 // LLD's WASM driver does not support the equvialent of `-r` so we do a simple file copy
717 // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy
714718 // here. TODO: think carefully about how we can avoid this redundant operation when doing
715719 // build-obj. See also the corresponding TODO in linkAsArchive.
716720 const the_object_path = blk: {
......@@ -752,7 +756,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
752756
753757 if (self.base.options.output_mode == .Exe) {
754758 // 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.
756760 try argv.append("-z");
757761 const stack_size = self.base.options.stack_size_override orelse 1048576;
758762 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");
2020const Cache = @import("Cache.zig");
2121const target_util = @import("target.zig");
2222const 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
2428pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
2529 std.log.emerg(format, args);
......@@ -134,6 +138,8 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{
134138}){};
135139
136140pub fn main() anyerror!void {
141 crash_report.initialize();
142
137143 var gpa_need_deinit = false;
138144 const gpa = gpa: {
139145 if (!std.builtin.link_libc) {
......@@ -1151,6 +1157,7 @@ fn buildOutputType(
11511157 var is_shared_lib = false;
11521158 var linker_args = std.ArrayList([]const u8).init(arena);
11531159 var it = ClangArgIterator.init(arena, all_args);
1160 var emit_llvm = false;
11541161 while (it.has_next) {
11551162 it.next() catch |err| {
11561163 fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
......@@ -1161,6 +1168,7 @@ fn buildOutputType(
11611168 .c => c_out_mode = .object, // -c
11621169 .asm_only => c_out_mode = .assembly, // -S
11631170 .preprocess_only => c_out_mode = .preprocessor, // -E
1171 .emit_llvm => emit_llvm = true,
11641172 .other => {
11651173 try clang_argv.appendSlice(it.other_args);
11661174 },
......@@ -1518,22 +1526,42 @@ fn buildOutputType(
15181526 output_mode = if (is_shared_lib) .Lib else .Exe;
15191527 emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
15201528 enable_cache = true;
1529 if (emit_llvm) {
1530 fatal("-emit-llvm cannot be used when linking", .{});
1531 }
15211532 },
15221533 .object => {
15231534 output_mode = .Obj;
1524 if (out_path) |p| {
1525 emit_bin = .{ .yes = p };
1535 if (emit_llvm) {
1536 emit_bin = .no;
1537 if (out_path) |p| {
1538 emit_llvm_bc = .{ .yes = p };
1539 } else {
1540 emit_llvm_bc = .yes_default_path;
1541 }
15261542 } 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 }
15281548 }
15291549 },
15301550 .assembly => {
15311551 output_mode = .Obj;
15321552 emit_bin = .no;
1533 if (out_path) |p| {
1534 emit_asm = .{ .yes = p };
1553 if (emit_llvm) {
1554 if (out_path) |p| {
1555 emit_llvm_ir = .{ .yes = p };
1556 } else {
1557 emit_llvm_ir = .yes_default_path;
1558 }
15351559 } 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 }
15371565 }
15381566 },
15391567 .preprocessor => {
......@@ -1682,22 +1710,22 @@ fn buildOutputType(
16821710 } else true;
16831711 if (!should_get_sdk_path) break :outer false;
16841712 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);
16861714 clang_argv.appendAssumeCapacity("-isysroot");
16871715 clang_argv.appendAssumeCapacity(sdk_path);
16881716 break :outer true;
16891717 } else break :outer false;
16901718 } 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);
16931721 const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem";
16941722 for (paths.include_dirs.items) |include_dir| {
16951723 clang_argv.appendAssumeCapacity(isystem_flag);
16961724 clang_argv.appendAssumeCapacity(include_dir);
16971725 }
16981726
1699 try clang_argv.ensureCapacity(clang_argv.items.len + paths.framework_dirs.items.len * 2);
1700 try framework_dirs.ensureCapacity(framework_dirs.items.len + paths.framework_dirs.items.len);
1727 try clang_argv.ensureUnusedCapacity(paths.framework_dirs.items.len * 2);
1728 try framework_dirs.ensureUnusedCapacity(paths.framework_dirs.items.len);
17011729 const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework";
17021730 for (paths.framework_dirs.items) |framework_dir| {
17031731 clang_argv.appendAssumeCapacity(iframework_flag);
......@@ -2761,7 +2789,7 @@ pub fn cmdInit(
27612789 fatal("unable to read template file 'build.zig': {s}", .{@errorName(err)});
27622790 };
27632791 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);
27652793 for (build_zig_contents) |c| {
27662794 if (c == '$') {
27672795 try modified_build_zig_contents.appendSlice(cwd_basename);
......@@ -2778,6 +2806,12 @@ pub fn cmdInit(
27782806 error.FileNotFound => {},
27792807 else => fatal("unable to test existence of build.zig: {s}\n", .{@errorName(err)}),
27802808 }
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 }
27812815 var src_dir = try fs.cwd().makeOpenPath("src", .{});
27822816 defer src_dir.close();
27832817
......@@ -3442,7 +3476,7 @@ fn fmtPathFile(
34423476
34433477 // As a heuristic, we make enough capacity for the same as the input source.
34443478 fmt.out_buffer.shrinkRetainingCapacity(0);
3445 try fmt.out_buffer.ensureCapacity(source_code.len);
3479 try fmt.out_buffer.ensureTotalCapacity(source_code.len);
34463480
34473481 try tree.renderToArrayList(&fmt.out_buffer);
34483482 if (mem.eql(u8, fmt.out_buffer.items, source_code))
......@@ -3663,6 +3697,7 @@ pub const ClangArgIterator = struct {
36633697 no_red_zone,
36643698 strip,
36653699 exec_model,
3700 emit_llvm,
36663701 };
36673702
36683703 const Args = struct {
......@@ -4109,7 +4144,7 @@ pub fn cmdAstCheck(
41094144 // zig fmt: on
41104145 }
41114146
4112 return Zir.renderAsTextToFile(gpa, &file, io.getStdOut());
4147 return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
41134148}
41144149
41154150/// 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 {
312312 if (try man.hit()) {
313313 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);
316316 comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{
317317 .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
318318 "o", &digest, final_lib_basename,
......@@ -857,6 +857,7 @@ const mingwex_generic_src = [_][]const u8{
857857 "stdio" ++ path.sep_str ++ "fopen64.c",
858858 "stdio" ++ path.sep_str ++ "fseeko32.c",
859859 "stdio" ++ path.sep_str ++ "fseeko64.c",
860 "stdio" ++ path.sep_str ++ "fseeki64.c",
860861 "stdio" ++ path.sep_str ++ "fsetpos64.c",
861862 "stdio" ++ path.sep_str ++ "ftello.c",
862863 "stdio" ++ path.sep_str ++ "ftello64.c",
src/musl.zig+2-2
......@@ -112,7 +112,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
112112 var source_table = std.StringArrayHashMap(Ext).init(comp.gpa);
113113 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
117117 for (src_files) |src_file| {
118118 try addSrcFile(arena, &source_table, src_file);
......@@ -231,7 +231,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
231231
232232 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
236236 const basename = try comp.gpa.dupe(u8, "libc.so");
237237 errdefer comp.gpa.free(basename);
src/print_air.zig+81-1
......@@ -104,12 +104,16 @@ const Writer = struct {
104104
105105 .add,
106106 .addwrap,
107 .add_sat,
107108 .sub,
108109 .subwrap,
110 .sub_sat,
109111 .mul,
110112 .mulwrap,
113 .mul_sat,
111114 .div,
112115 .rem,
116 .mod,
113117 .ptr_add,
114118 .ptr_sub,
115119 .bit_and,
......@@ -129,7 +133,10 @@ const Writer = struct {
129133 .ptr_elem_val,
130134 .ptr_ptr_elem_val,
131135 .shl,
136 .shl_exact,
137 .shl_sat,
132138 .shr,
139 .set_union_tag,
133140 => try w.writeBinOp(s, inst),
134141
135142 .is_null,
......@@ -156,7 +163,8 @@ const Writer = struct {
156163 .not,
157164 .bitcast,
158165 .load,
159 .floatcast,
166 .fptrunc,
167 .fpext,
160168 .intcast,
161169 .trunc,
162170 .optional_payload,
......@@ -175,6 +183,11 @@ const Writer = struct {
175183 .struct_field_ptr_index_2,
176184 .struct_field_ptr_index_3,
177185 .array_to_slice,
186 .int_to_float,
187 .float_to_int,
188 .get_union_tag,
189 .clz,
190 .ctz,
178191 => try w.writeTyOp(s, inst),
179192
180193 .block,
......@@ -192,6 +205,15 @@ const Writer = struct {
192205 .cond_br => try w.writeCondBr(s, inst),
193206 .switch_br => try w.writeSwitchBr(s, inst),
194207 .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),
195217 }
196218 }
197219
......@@ -276,6 +298,64 @@ const Writer = struct {
276298 });
277299 }
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
279359 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
280360 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
281361 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 {
8686 CallingConventionSysV
8787};
8888
89// Stage 1 supports only the generic address space
90enum AddressSpace {
91 AddressSpaceGeneric,
92 AddressSpaceGS,
93 AddressSpaceFS,
94 AddressSpaceSS,
95};
96
8997// This one corresponds to the builtin.zig enum.
9098enum BuiltinPtrSize {
9199 BuiltinPtrSizeOne,
......@@ -804,14 +812,18 @@ enum BinOpType {
804812 BinOpTypeInvalid,
805813 BinOpTypeAssign,
806814 BinOpTypeAssignTimes,
815 BinOpTypeAssignTimesSat,
807816 BinOpTypeAssignTimesWrap,
808817 BinOpTypeAssignDiv,
809818 BinOpTypeAssignMod,
810819 BinOpTypeAssignPlus,
820 BinOpTypeAssignPlusSat,
811821 BinOpTypeAssignPlusWrap,
812822 BinOpTypeAssignMinus,
823 BinOpTypeAssignMinusSat,
813824 BinOpTypeAssignMinusWrap,
814825 BinOpTypeAssignBitShiftLeft,
826 BinOpTypeAssignBitShiftLeftSat,
815827 BinOpTypeAssignBitShiftRight,
816828 BinOpTypeAssignBitAnd,
817829 BinOpTypeAssignBitXor,
......@@ -828,12 +840,16 @@ enum BinOpType {
828840 BinOpTypeBinXor,
829841 BinOpTypeBinAnd,
830842 BinOpTypeBitShiftLeft,
843 BinOpTypeBitShiftLeftSat,
831844 BinOpTypeBitShiftRight,
832845 BinOpTypeAdd,
846 BinOpTypeAddSat,
833847 BinOpTypeAddWrap,
834848 BinOpTypeSub,
849 BinOpTypeSubSat,
835850 BinOpTypeSubWrap,
836851 BinOpTypeMult,
852 BinOpTypeMultSat,
837853 BinOpTypeMultWrap,
838854 BinOpTypeDiv,
839855 BinOpTypeMod,
......@@ -1802,10 +1818,6 @@ enum BuiltinFnId {
18021818 BuiltinFnIdReduce,
18031819 BuiltinFnIdMaximum,
18041820 BuiltinFnIdMinimum,
1805 BuiltinFnIdSatAdd,
1806 BuiltinFnIdSatSub,
1807 BuiltinFnIdSatMul,
1808 BuiltinFnIdSatShl,
18091821};
18101822
18111823struct BuiltinFnEntry {
......@@ -2950,10 +2962,10 @@ enum IrBinOp {
29502962 IrBinOpArrayMult,
29512963 IrBinOpMaximum,
29522964 IrBinOpMinimum,
2953 IrBinOpSatAdd,
2954 IrBinOpSatSub,
2955 IrBinOpSatMul,
2956 IrBinOpSatShl,
2965 IrBinOpAddSat,
2966 IrBinOpSubSat,
2967 IrBinOpMultSat,
2968 IrBinOpShlSat,
29572969};
29582970
29592971struct Stage1ZirInstBinOp {
src/stage1/analyze.cpp+11-1
......@@ -1019,6 +1019,16 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
10191019 zig_unreachable();
10201020}
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
10221032ZigType *get_stack_trace_type(CodeGen *g) {
10231033 if (g->stack_trace_type == nullptr) {
10241034 g->stack_trace_type = get_builtin_type(g, "StackTrace");
......@@ -6794,7 +6804,7 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
67946804 // Since this frame is async, an await might represent a suspend point, and
67956805 // therefore need to spill. It also needs to mark expr scopes as having to spill.
67966806 // 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.
67986808 for (size_t i = 0; i < fn->await_list.length; i += 1) {
67996809 Stage1AirInstAwait *await = fn->await_list.at(i);
68006810 if (await->is_nosuspend) {
src/stage1/analyze.hpp+2
......@@ -242,6 +242,8 @@ Error get_primitive_type(CodeGen *g, Buf *name, ZigType **result);
242242bool calling_convention_allows_zig_types(CallingConvention cc);
243243const char *calling_convention_name(CallingConvention cc);
244244
245const char *address_space_name(AddressSpace as);
246
245247Error ATTRIBUTE_MUST_USE file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents);
246248
247249void 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
36723672 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMult), lval, result_loc);
36733673 case BinOpTypeAssignTimesWrap:
36743674 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);
36753677 case BinOpTypeAssignDiv:
36763678 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
36773679 case BinOpTypeAssignMod:
......@@ -3680,12 +3682,18 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
36803682 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAdd), lval, result_loc);
36813683 case BinOpTypeAssignPlusWrap:
36823684 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);
36833687 case BinOpTypeAssignMinus:
36843688 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSub), lval, result_loc);
36853689 case BinOpTypeAssignMinusWrap:
36863690 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);
36873693 case BinOpTypeAssignBitShiftLeft:
36883694 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);
36893697 case BinOpTypeAssignBitShiftRight:
36903698 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
36913699 case BinOpTypeAssignBitAnd:
......@@ -3718,20 +3726,28 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
37183726 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBinAnd), lval, result_loc);
37193727 case BinOpTypeBitShiftLeft:
37203728 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);
37213731 case BinOpTypeBitShiftRight:
37223732 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
37233733 case BinOpTypeAdd:
37243734 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAdd), lval, result_loc);
37253735 case BinOpTypeAddWrap:
37263736 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);
37273739 case BinOpTypeSub:
37283740 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSub), lval, result_loc);
37293741 case BinOpTypeSubWrap:
37303742 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);
37313745 case BinOpTypeMult:
37323746 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMult), lval, result_loc);
37333747 case BinOpTypeMultWrap:
37343748 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);
37353751 case BinOpTypeDiv:
37363752 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
37373753 case BinOpTypeMod:
......@@ -4704,66 +4720,6 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
47044720 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);
47054721 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
47064722 }
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 }
47674723 case BuiltinFnIdMemcpy:
47684724 {
47694725 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) {
487487 if (mangled_symbol_buf) buf_destroy(mangled_symbol_buf);
488488 }
489489 } else {
490 if (llvm_fn == nullptr) {
491 llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type);
492 }
490 llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type);
493491
494492 for (size_t i = 1; i < fn->export_list.length; i += 1) {
495493 GlobalExport *fn_export = &fn->export_list.items[i];
......@@ -3335,7 +3333,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33353333 } else {
33363334 zig_unreachable();
33373335 }
3338 case IrBinOpSatAdd:
3336 case IrBinOpAddSat:
33393337 if (scalar_type->id == ZigTypeIdInt) {
33403338 if (scalar_type->data.integral.is_signed) {
33413339 return ZigLLVMBuildSAddSat(g->builder, op1_value, op2_value, "");
......@@ -3345,7 +3343,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33453343 } else {
33463344 zig_unreachable();
33473345 }
3348 case IrBinOpSatSub:
3346 case IrBinOpSubSat:
33493347 if (scalar_type->id == ZigTypeIdInt) {
33503348 if (scalar_type->data.integral.is_signed) {
33513349 return ZigLLVMBuildSSubSat(g->builder, op1_value, op2_value, "");
......@@ -3355,7 +3353,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33553353 } else {
33563354 zig_unreachable();
33573355 }
3358 case IrBinOpSatMul:
3356 case IrBinOpMultSat:
33593357 if (scalar_type->id == ZigTypeIdInt) {
33603358 if (scalar_type->data.integral.is_signed) {
33613359 return ZigLLVMBuildSMulFixSat(g->builder, op1_value, op2_value, "");
......@@ -3365,7 +3363,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33653363 } else {
33663364 zig_unreachable();
33673365 }
3368 case IrBinOpSatShl:
3366 case IrBinOpShlSat:
33693367 if (scalar_type->id == ZigTypeIdInt) {
33703368 if (scalar_type->data.integral.is_signed) {
33713369 return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, "");
......@@ -9134,10 +9132,6 @@ static void define_builtin_fns(CodeGen *g) {
91349132 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
91359133 create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
91369134 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);
91419135}
91429136
91439137static const char *bool_to_str(bool b) {
......@@ -9323,6 +9317,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
93239317 buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
93249318 buf_appendf(contents, "pub const abi = std.Target.Abi.%s;\n", cur_abi);
93259319 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);
93269321 buf_appendf(contents, "pub const os = std.Target.Os.Tag.defaultVersionRange(.%s);\n", cur_os);
93279322 buf_appendf(contents,
93289323 "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
63746374
63756375 if (target->value->type->data.enumeration.non_exhaustive) {
63766376 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",
63786378 buf_ptr(&wanted_type->name)));
63796379 return ira->codegen->invalid_inst_gen;
63806380 }
......@@ -9820,28 +9820,28 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s
98209820 float_min(out_val, op1_val, op2_val);
98219821 }
98229822 break;
9823 case IrBinOpSatAdd:
9823 case IrBinOpAddSat:
98249824 if (is_int) {
98259825 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);
98269826 } else {
98279827 zig_unreachable();
98289828 }
98299829 break;
9830 case IrBinOpSatSub:
9830 case IrBinOpSubSat:
98319831 if (is_int) {
98329832 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);
98339833 } else {
98349834 zig_unreachable();
98359835 }
98369836 break;
9837 case IrBinOpSatMul:
9837 case IrBinOpMultSat:
98389838 if (is_int) {
98399839 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);
98409840 } else {
98419841 zig_unreachable();
98429842 }
98439843 break;
9844 case IrBinOpSatShl:
9844 case IrBinOpShlSat:
98459845 if (is_int) {
98469846 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);
98479847 } else {
......@@ -10069,10 +10069,10 @@ static bool ok_float_op(IrBinOp op) {
1006910069 case IrBinOpBitShiftRightExact:
1007010070 case IrBinOpAddWrap:
1007110071 case IrBinOpSubWrap:
10072 case IrBinOpSatAdd:
10073 case IrBinOpSatSub:
10074 case IrBinOpSatMul:
10075 case IrBinOpSatShl:
10072 case IrBinOpAddSat:
10073 case IrBinOpSubSat:
10074 case IrBinOpMultSat:
10075 case IrBinOpShlSat:
1007610076 case IrBinOpMultWrap:
1007710077 case IrBinOpArrayCat:
1007810078 case IrBinOpArrayMult:
......@@ -11046,10 +11046,10 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns
1104611046 case IrBinOpRemMod:
1104711047 case IrBinOpMaximum:
1104811048 case IrBinOpMinimum:
11049 case IrBinOpSatAdd:
11050 case IrBinOpSatSub:
11051 case IrBinOpSatMul:
11052 case IrBinOpSatShl:
11049 case IrBinOpAddSat:
11050 case IrBinOpSubSat:
11051 case IrBinOpMultSat:
11052 case IrBinOpShlSat:
1105311053 return ir_analyze_bin_op_math(ira, bin_op_instruction);
1105411054 case IrBinOpArrayCat:
1105511055 return ir_analyze_array_cat(ira, bin_op_instruction);
......@@ -15189,7 +15189,7 @@ static Stage1AirInst *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
1518915189 return ir_analyze_inferred_field_ptr(ira, field_name, scope, source_node, container_ptr, bare_type);
1519015190 }
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.
1519315193 // We do this if the container pointer is undefined and we are in a TypeOf call.
1519415194 bool return_undef = container_ptr->value->special == ConstValSpecialUndef && \
1519515195 get_scope_typeof(scope) != nullptr;
......@@ -15248,7 +15248,7 @@ static Stage1AirInst *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
1524815248 if (type_is_invalid(union_val->type))
1524915249 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:
1525215252 // a undefined union means also the tag is undefined, accessing
1525315253 // its payload slot is UB.
1525415254 const UndefAllowed allow_undef = initializing ? UndefOk : UndefBad;
......@@ -16124,7 +16124,7 @@ static Stage1AirInst *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira,
1612416124
1612516125static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCtz *instruction) {
1612616126 Error err;
16127
16127
1612816128 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
1612916129 if (type_is_invalid(int_type))
1613016130 return ira->codegen->invalid_inst_gen;
......@@ -16166,7 +16166,7 @@ static Stage1AirInst *ir_analyze_instruction_ctz(IrAnalyze *ira, Stage1ZirInstCt
1616616166 return ira->codegen->invalid_inst_gen;
1616716167 if (val->special == ConstValSpecialUndef)
1616816168 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);
16169
16169
1617016170 if (is_vector) {
1617116171 ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);
1617216172 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
1620016200
1620116201static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstClz *instruction) {
1620216202 Error err;
16203
16203
1620416204 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
1620516205 if (type_is_invalid(int_type))
1620616206 return ira->codegen->invalid_inst_gen;
......@@ -16242,7 +16242,7 @@ static Stage1AirInst *ir_analyze_instruction_clz(IrAnalyze *ira, Stage1ZirInstCl
1624216242 return ira->codegen->invalid_inst_gen;
1624316243 if (val->special == ConstValSpecialUndef)
1624416244 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);
16245
16245
1624616246 if (is_vector) {
1624716247 ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);
1624816248 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
1627616276
1627716277static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1ZirInstPopCount *instruction) {
1627816278 Error err;
16279
16279
1628016280 ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
1628116281 if (type_is_invalid(int_type))
1628216282 return ira->codegen->invalid_inst_gen;
......@@ -16318,7 +16318,7 @@ static Stage1AirInst *ir_analyze_instruction_pop_count(IrAnalyze *ira, Stage1Zir
1631816318 return ira->codegen->invalid_inst_gen;
1631916319 if (val->special == ConstValSpecialUndef)
1632016320 return ir_const_undef(ira, instruction->base.scope, instruction->base.source_node, ira->codegen->builtin_types.entry_num_lit_int);
16321
16321
1632216322 if (is_vector) {
1632316323 ZigType *smallest_vec_type = get_vector_type(ira->codegen, vector_len, smallest_type);
1632416324 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
1790417904 result->special = ConstValSpecialStatic;
1790517905 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);
1790817908 result->data.x_struct.fields = fields;
1790917909
1791017910 // size: Size
......@@ -17939,24 +17939,29 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, Scope *scope, AstNode
1793917939 lazy_align_of->base.id = LazyValueIdAlignOf;
1794017940 lazy_align_of->target_type = ir_const_type(ira, scope, source_node, attrs_type->data.pointer.child_type);
1794117941 }
17942 // child: type
17943 ensure_field_index(result->type, "child", 4);
17942 // address_space: AddressSpace,
17943 ensure_field_index(result->type, "address_space", 4);
1794417944 fields[4]->special = ConstValSpecialStatic;
17945 fields[4]->type = ira->codegen->builtin_types.entry_type;
17946 fields[4]->data.x_type = attrs_type->data.pointer.child_type;
17947 // is_allowzero: bool
17948 ensure_field_index(result->type, "is_allowzero", 5);
17945 fields[4]->type = get_builtin_type(ira->codegen, "AddressSpace");
17946 bigint_init_unsigned(&fields[4]->data.x_enum_tag, AddressSpaceGeneric);
17947 // child: type
17948 ensure_field_index(result->type, "child", 5);
1794917949 fields[5]->special = ConstValSpecialStatic;
17950 fields[5]->type = ira->codegen->builtin_types.entry_bool;
17951 fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;
17952 // sentinel: anytype
17953 ensure_field_index(result->type, "sentinel", 6);
17950 fields[5]->type = ira->codegen->builtin_types.entry_type;
17951 fields[5]->data.x_type = attrs_type->data.pointer.child_type;
17952 // is_allowzero: bool
17953 ensure_field_index(result->type, "is_allowzero", 6);
1795417954 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;
1795517960 if (attrs_type->data.pointer.sentinel != nullptr) {
17956 fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
17957 set_optional_payload(fields[6], attrs_type->data.pointer.sentinel);
17961 fields[7]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
17962 set_optional_payload(fields[7], attrs_type->data.pointer.sentinel);
1795817963 } else {
17959 fields[6]->type = ira->codegen->builtin_types.entry_null;
17964 fields[7]->type = ira->codegen->builtin_types.entry_null;
1796017965 }
1796117966
1796217967 return result;
......@@ -18465,7 +18470,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour
1846518470 result->special = ConstValSpecialStatic;
1846618471 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);
1846918474 result->data.x_struct.fields = fields;
1847018475
1847118476 // calling_convention: TypeInfo.CallingConvention
......@@ -18826,11 +18831,11 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
1882618831 assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type));
1882718832 BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag);
1882818833 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);
1883018835 if (type_is_invalid(elem_type))
1883118836 return ira->codegen->invalid_inst_gen->value->type;
1883218837 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,
1883418839 elem_type, &sentinel)))
1883518840 {
1883618841 return ira->codegen->invalid_inst_gen->value->type;
......@@ -18845,6 +18850,19 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
1884518850 if (alignment == nullptr)
1884618851 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
1884818866 bool is_const;
1884918867 if ((err = get_const_field_bool(ira, source_node, payload, "is_const", 1, &is_const)))
1885018868 return ira->codegen->invalid_inst_gen->value->type;
......@@ -18857,13 +18875,12 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
1885718875 }
1885818876
1885918877 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,
1886118879 &is_allowzero)))
1886218880 {
1886318881 return ira->codegen->invalid_inst_gen->value->type;
1886418882 }
1886518883
18866
1886718884 ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen,
1886818885 elem_type,
1886918886 is_const,
src/stage1/ir_print.cpp+6-6
......@@ -558,7 +558,7 @@ const char* ir_inst_gen_type_str(Stage1AirInstId id) {
558558 case Stage1AirInstIdWasmMemoryGrow:
559559 return "GenWasmMemoryGrow";
560560 case Stage1AirInstIdExtern:
561 return "GenExtrern";
561 return "GenExtern";
562562 }
563563 zig_unreachable();
564564}
......@@ -737,13 +737,13 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
737737 return "@maximum";
738738 case IrBinOpMinimum:
739739 return "@minimum";
740 case IrBinOpSatAdd:
740 case IrBinOpAddSat:
741741 return "@addWithSaturation";
742 case IrBinOpSatSub:
742 case IrBinOpSubSat:
743743 return "@subWithSaturation";
744 case IrBinOpSatMul:
744 case IrBinOpMultSat:
745745 return "@mulWithSaturation";
746 case IrBinOpSatShl:
746 case IrBinOpShlSat:
747747 return "@shlWithSaturation";
748748 }
749749 zig_unreachable();
......@@ -829,7 +829,7 @@ static const char *cast_op_str(CastOp op) {
829829 case CastOpIntToFloat: return "IntToFloat";
830830 case CastOpFloatToInt: return "FloatToInt";
831831 case CastOpBoolToInt: return "BoolToInt";
832 case CastOpNumLitToConcrete: return "NumLitToConcrate";
832 case CastOpNumLitToConcrete: return "NumLitToConcrete";
833833 case CastOpErrSet: return "ErrSet";
834834 case CastOpBitCast: return "BitCast";
835835 }
src/stage1/os.hpp+2
......@@ -33,6 +33,8 @@
3333#define ZIG_OS_OPENBSD
3434#elif defined(__HAIKU__)
3535#define ZIG_OS_HAIKU
36#elif defined(__sun)
37#define ZIG_OS_SOLARIS
3638#else
3739#define ZIG_OS_UNKNOWN
3840#endif
src/stage1/parser.cpp+17-1
......@@ -2073,7 +2073,7 @@ static AstNode *ast_parse_field_init(ParseContext *pc) {
20732073 return nullptr;
20742074 }
20752075 if (eat_token_if(pc, TokenIdEq) == 0) {
2076 // Because ".Name" can also be intepreted as an enum literal, we should put back
2076 // Because ".Name" can also be interpreted as an enum literal, we should put back
20772077 // those two tokens again so that the parser can try to parse them as the enum
20782078 // literal later.
20792079 put_back_token(pc);
......@@ -2381,6 +2381,7 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
23812381// / PLUSEQUAL
23822382// / MINUSEQUAL
23832383// / LARROW2EQUAL
2384// / LARROW2PIPEEQUAL
23842385// / RARROW2EQUAL
23852386// / AMPERSANDEQUAL
23862387// / CARETEQUAL
......@@ -2388,6 +2389,9 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
23882389// / ASTERISKPERCENTEQUAL
23892390// / PLUSPERCENTEQUAL
23902391// / MINUSPERCENTEQUAL
2392// / ASTERISKPIPEEQUAL
2393// / PLUSPIPEEQUAL
2394// / MINUSPIPEEQUAL
23912395// / EQUAL
23922396static AstNode *ast_parse_assign_op(ParseContext *pc) {
23932397 // 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) {
23962400 table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd;
23972401 table[TokenIdBitOrEq] = BinOpTypeAssignBitOr;
23982402 table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft;
2403 table[TokenIdBitShiftLeftPipeEq] = BinOpTypeAssignBitShiftLeftSat;
23992404 table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight;
24002405 table[TokenIdBitXorEq] = BinOpTypeAssignBitXor;
24012406 table[TokenIdDivEq] = BinOpTypeAssignDiv;
24022407 table[TokenIdEq] = BinOpTypeAssign;
24032408 table[TokenIdMinusEq] = BinOpTypeAssignMinus;
24042409 table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap;
2410 table[TokenIdMinusPipeEq] = BinOpTypeAssignMinusSat;
24052411 table[TokenIdModEq] = BinOpTypeAssignMod;
24062412 table[TokenIdPlusEq] = BinOpTypeAssignPlus;
24072413 table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap;
2414 table[TokenIdPlusPipeEq] = BinOpTypeAssignPlusSat;
24082415 table[TokenIdTimesEq] = BinOpTypeAssignTimes;
24092416 table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap;
2417 table[TokenIdTimesPipeEq] = BinOpTypeAssignTimesSat;
24102418
24112419 BinOpType op = table[pc->token_ids[pc->current_token]];
24122420 if (op != BinOpTypeInvalid) {
......@@ -2483,10 +2491,12 @@ static AstNode *ast_parse_bitwise_op(ParseContext *pc) {
24832491
24842492// BitShiftOp
24852493// <- LARROW2
2494// / LARROW2PIPE
24862495// / RARROW2
24872496static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
24882497 BinOpType table[TokenIdCount] = {};
24892498 table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft;
2499 table[TokenIdBitShiftLeftPipe] = BinOpTypeBitShiftLeftSat;
24902500 table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight;
24912501
24922502 BinOpType op = table[pc->token_ids[pc->current_token]];
......@@ -2506,6 +2516,8 @@ static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
25062516// / PLUS2
25072517// / PLUSPERCENT
25082518// / MINUSPERCENT
2519// / PLUSPIPE
2520// / MINUSPIPE
25092521static AstNode *ast_parse_addition_op(ParseContext *pc) {
25102522 BinOpType table[TokenIdCount] = {};
25112523 table[TokenIdPlus] = BinOpTypeAdd;
......@@ -2513,6 +2525,8 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
25132525 table[TokenIdPlusPlus] = BinOpTypeArrayCat;
25142526 table[TokenIdPlusPercent] = BinOpTypeAddWrap;
25152527 table[TokenIdMinusPercent] = BinOpTypeSubWrap;
2528 table[TokenIdPlusPipe] = BinOpTypeAddSat;
2529 table[TokenIdMinusPipe] = BinOpTypeSubSat;
25162530
25172531 BinOpType op = table[pc->token_ids[pc->current_token]];
25182532 if (op != BinOpTypeInvalid) {
......@@ -2532,6 +2546,7 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
25322546// / PERCENT
25332547// / ASTERISK2
25342548// / ASTERISKPERCENT
2549// / ASTERISKPIPE
25352550static AstNode *ast_parse_multiply_op(ParseContext *pc) {
25362551 BinOpType table[TokenIdCount] = {};
25372552 table[TokenIdBarBar] = BinOpTypeMergeErrorSets;
......@@ -2540,6 +2555,7 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc) {
25402555 table[TokenIdPercent] = BinOpTypeMod;
25412556 table[TokenIdStarStar] = BinOpTypeArrayMult;
25422557 table[TokenIdTimesPercent] = BinOpTypeMultWrap;
2558 table[TokenIdTimesPipe] = BinOpTypeMultSat;
25432559
25442560 BinOpType op = table[pc->token_ids[pc->current_token]];
25452561 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) {
401401#elif defined(ZIG_OS_HAIKU)
402402 *out_os = OsHaiku;
403403 return ErrorNone;
404#elif defined(ZIG_OS_SOLARIS)
405 *out_os = OsSolaris;
406 return ErrorNone;
404407#else
405408 zig_panic("stage1 is unable to detect native target for this OS");
406409#endif
......@@ -674,6 +677,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
674677 case OsOpenBSD:
675678 case OsWASI:
676679 case OsHaiku:
680 case OsSolaris:
677681 case OsEmscripten:
678682 case OsPlan9:
679683 switch (id) {
......@@ -732,7 +736,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
732736 case OsCloudABI:
733737 case OsKFreeBSD:
734738 case OsLv2:
735 case OsSolaris:
736739 case OsZOS:
737740 case OsMinix:
738741 case OsRTEMS:
src/stage1/tokenizer.cpp+84
......@@ -226,8 +226,10 @@ enum TokenizeState {
226226 TokenizeState_pipe,
227227 TokenizeState_minus,
228228 TokenizeState_minus_percent,
229 TokenizeState_minus_pipe,
229230 TokenizeState_asterisk,
230231 TokenizeState_asterisk_percent,
232 TokenizeState_asterisk_pipe,
231233 TokenizeState_slash,
232234 TokenizeState_line_comment_start,
233235 TokenizeState_line_comment,
......@@ -257,8 +259,10 @@ enum TokenizeState {
257259 TokenizeState_percent,
258260 TokenizeState_plus,
259261 TokenizeState_plus_percent,
262 TokenizeState_plus_pipe,
260263 TokenizeState_angle_bracket_left,
261264 TokenizeState_angle_bracket_angle_bracket_left,
265 TokenizeState_angle_bracket_angle_bracket_left_pipe,
262266 TokenizeState_angle_bracket_right,
263267 TokenizeState_angle_bracket_angle_bracket_right,
264268 TokenizeState_period,
......@@ -548,6 +552,9 @@ void tokenize(const char *source, Tokenization *out) {
548552 case '%':
549553 t.state = TokenizeState_asterisk_percent;
550554 break;
555 case '|':
556 t.state = TokenizeState_asterisk_pipe;
557 break;
551558 default:
552559 t.state = TokenizeState_start;
553560 continue;
......@@ -568,6 +575,21 @@ void tokenize(const char *source, Tokenization *out) {
568575 continue;
569576 }
570577 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;
571593 case TokenizeState_percent:
572594 switch (c) {
573595 case 0:
......@@ -596,6 +618,9 @@ void tokenize(const char *source, Tokenization *out) {
596618 case '%':
597619 t.state = TokenizeState_plus_percent;
598620 break;
621 case '|':
622 t.state = TokenizeState_plus_pipe;
623 break;
599624 default:
600625 t.state = TokenizeState_start;
601626 continue;
......@@ -616,6 +641,21 @@ void tokenize(const char *source, Tokenization *out) {
616641 continue;
617642 }
618643 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;
619659 case TokenizeState_caret:
620660 switch (c) {
621661 case 0:
......@@ -891,6 +931,9 @@ void tokenize(const char *source, Tokenization *out) {
891931 case '%':
892932 t.state = TokenizeState_minus_percent;
893933 break;
934 case '|':
935 t.state = TokenizeState_minus_pipe;
936 break;
894937 default:
895938 t.state = TokenizeState_start;
896939 continue;
......@@ -911,6 +954,21 @@ void tokenize(const char *source, Tokenization *out) {
911954 continue;
912955 }
913956 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;
914972 case TokenizeState_angle_bracket_left:
915973 switch (c) {
916974 case 0:
......@@ -936,12 +994,30 @@ void tokenize(const char *source, Tokenization *out) {
936994 t.out->ids.last() = TokenIdBitShiftLeftEq;
937995 t.state = TokenizeState_start;
938996 break;
997 case '|':
998 t.state = TokenizeState_angle_bracket_angle_bracket_left_pipe;
999 break;
9391000 default:
9401001 t.out->ids.last() = TokenIdBitShiftLeft;
9411002 t.state = TokenizeState_start;
9421003 continue;
9431004 }
9441005 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;
9451021 case TokenizeState_angle_bracket_right:
9461022 switch (c) {
9471023 case 0:
......@@ -1437,6 +1513,8 @@ const char * token_name(TokenId id) {
14371513 case TokenIdBitOrEq: return "|=";
14381514 case TokenIdBitShiftLeft: return "<<";
14391515 case TokenIdBitShiftLeftEq: return "<<=";
1516 case TokenIdBitShiftLeftPipe: return "<<|";
1517 case TokenIdBitShiftLeftPipeEq: return "<<|=";
14401518 case TokenIdBitShiftRight: return ">>";
14411519 case TokenIdBitShiftRightEq: return ">>=";
14421520 case TokenIdBitXorEq: return "^=";
......@@ -1521,12 +1599,16 @@ const char * token_name(TokenId id) {
15211599 case TokenIdMinusEq: return "-=";
15221600 case TokenIdMinusPercent: return "-%";
15231601 case TokenIdMinusPercentEq: return "-%=";
1602 case TokenIdMinusPipe: return "-|";
1603 case TokenIdMinusPipeEq: return "-|=";
15241604 case TokenIdModEq: return "%=";
15251605 case TokenIdPercent: return "%";
15261606 case TokenIdPlus: return "+";
15271607 case TokenIdPlusEq: return "+=";
15281608 case TokenIdPlusPercent: return "+%";
15291609 case TokenIdPlusPercentEq: return "+%=";
1610 case TokenIdPlusPipe: return "+|";
1611 case TokenIdPlusPipeEq: return "+|=";
15301612 case TokenIdPlusPlus: return "++";
15311613 case TokenIdRBrace: return "}";
15321614 case TokenIdRBracket: return "]";
......@@ -1542,6 +1624,8 @@ const char * token_name(TokenId id) {
15421624 case TokenIdTimesEq: return "*=";
15431625 case TokenIdTimesPercent: return "*%";
15441626 case TokenIdTimesPercentEq: return "*%=";
1627 case TokenIdTimesPipe: return "*|";
1628 case TokenIdTimesPipeEq: return "*|=";
15451629 case TokenIdBuiltin: return "Builtin";
15461630 case TokenIdCount:
15471631 zig_unreachable();
src/stage1/tokenizer.hpp+8
......@@ -23,6 +23,8 @@ enum TokenId : uint8_t {
2323 TokenIdBitOrEq,
2424 TokenIdBitShiftLeft,
2525 TokenIdBitShiftLeftEq,
26 TokenIdBitShiftLeftPipe,
27 TokenIdBitShiftLeftPipeEq,
2628 TokenIdBitShiftRight,
2729 TokenIdBitShiftRightEq,
2830 TokenIdBitXorEq,
......@@ -108,12 +110,16 @@ enum TokenId : uint8_t {
108110 TokenIdMinusEq,
109111 TokenIdMinusPercent,
110112 TokenIdMinusPercentEq,
113 TokenIdMinusPipe,
114 TokenIdMinusPipeEq,
111115 TokenIdModEq,
112116 TokenIdPercent,
113117 TokenIdPlus,
114118 TokenIdPlusEq,
115119 TokenIdPlusPercent,
116120 TokenIdPlusPercentEq,
121 TokenIdPlusPipe,
122 TokenIdPlusPipeEq,
117123 TokenIdPlusPlus,
118124 TokenIdRBrace,
119125 TokenIdRBracket,
......@@ -129,6 +135,8 @@ enum TokenId : uint8_t {
129135 TokenIdTimesEq,
130136 TokenIdTimesPercent,
131137 TokenIdTimesPercentEq,
138 TokenIdTimesPipe,
139 TokenIdTimesPipeEq,
132140
133141 TokenIdCount,
134142};
src/target.zig+25
......@@ -438,6 +438,13 @@ pub fn libcFullLinkFlags(target: std.Target) []const []const u8 {
438438 "-lc",
439439 "-lutil",
440440 },
441 .solaris => &[_][]const u8{
442 "-lm",
443 "-lsocket",
444 "-lnsl",
445 // Solaris releases after 10 merged the threading libraries into libc.
446 "-lc",
447 },
441448 .haiku => &[_][]const u8{
442449 "-lm",
443450 "-lroot",
......@@ -550,3 +557,21 @@ pub fn largestAtomicBits(target: std.Target) u32 {
550557 .x86_64 => 128,
551558 };
552559}
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(
48824882 var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
48834883 defer fn_params.deinit();
48844884 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
48874887 var i: usize = 0;
48884888 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 {
728728
729729 // Estimate that each top level node has 10 child nodes.
730730 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);
732732 // Estimate that each each node has 2 tokens.
733733 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);
735735 // Estimate that each each token is 3 bytes long.
736736 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
739739 ctx.nodes.appendAssumeCapacity(.{
740740 .tag = .root,
......@@ -839,7 +839,7 @@ const Context = struct {
839839
840840 fn addExtra(c: *Context, extra: anytype) Allocator.Error!NodeIndex {
841841 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);
843843 const result = @intCast(u32, c.extra_data.items.len);
844844 inline for (fields) |field| {
845845 comptime std.debug.assert(field.field_type == NodeIndex);
......@@ -1462,10 +1462,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
14621462 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
14631463 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
14641464 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),
1465 .shl => return renderBinOpGrouped(c, node, .bit_shift_left, .angle_bracket_angle_bracket_left, "<<"),
1466 .shl_assign => return renderBinOp(c, node, .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal, "<<="),
1467 .shr => return renderBinOpGrouped(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"),
1468 .shr_assign => return renderBinOp(c, node, .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal, ">>="),
1465 .shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"),
1466 .shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="),
1467 .shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"),
1468 .shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="),
14691469 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
14701470 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
14711471 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
......@@ -2614,6 +2614,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex {
26142614 .type_node = type_node,
26152615 .align_node = align_node,
26162616 .section_node = section_node,
2617 .addrspace_node = 0,
26172618 }),
26182619 .rhs = init_node,
26192620 },
......@@ -2705,6 +2706,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
27052706 .lhs = try c.addExtra(std.zig.Ast.Node.FnProtoOne{
27062707 .param = params.items[0],
27072708 .align_expr = align_expr,
2709 .addrspace_expr = 0, // TODO
27082710 .section_expr = section_expr,
27092711 .callconv_expr = callconv_expr,
27102712 }),
......@@ -2720,6 +2722,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
27202722 .params_start = span.start,
27212723 .params_end = span.end,
27222724 .align_expr = align_expr,
2725 .addrspace_expr = 0, // TODO
27232726 .section_expr = section_expr,
27242727 .callconv_expr = callconv_expr,
27252728 }),
......@@ -2797,7 +2800,7 @@ fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.Ar
27972800 _ = try c.addToken(.l_paren, "(");
27982801 var rendered = std.ArrayList(NodeIndex).init(c.gpa);
27992802 errdefer rendered.deinit();
2800 try rendered.ensureCapacity(std.math.max(params.len, 1));
2803 try rendered.ensureTotalCapacity(std.math.max(params.len, 1));
28012804
28022805 for (params) |param, i| {
28032806 if (i != 0) _ = try c.addToken(.comma, ",");
src/type.zig+449-108
......@@ -124,9 +124,11 @@ pub const Type = extern union {
124124 .enum_full,
125125 .enum_nonexhaustive,
126126 .enum_simple,
127 .enum_numbered,
127128 .atomic_order,
128129 .atomic_rmw_op,
129130 .calling_convention,
131 .address_space,
130132 .float_mode,
131133 .reduce_op,
132134 => return .Enum,
......@@ -136,6 +138,7 @@ pub const Type = extern union {
136138 .type_info,
137139 => return .Union,
138140
141 .bound_fn => unreachable,
139142 .var_args_param => unreachable, // can be any type
140143 }
141144 }
......@@ -288,6 +291,7 @@ pub const Type = extern union {
288291 .pointee_type = Type.initTag(.comptime_int),
289292 .sentinel = null,
290293 .@"align" = 0,
294 .@"addrspace" = .generic,
291295 .bit_offset = 0,
292296 .host_size = 0,
293297 .@"allowzero" = false,
......@@ -299,6 +303,7 @@ pub const Type = extern union {
299303 .pointee_type = Type.initTag(.u8),
300304 .sentinel = null,
301305 .@"align" = 0,
306 .@"addrspace" = .generic,
302307 .bit_offset = 0,
303308 .host_size = 0,
304309 .@"allowzero" = false,
......@@ -310,6 +315,7 @@ pub const Type = extern union {
310315 .pointee_type = self.castPointer().?.data,
311316 .sentinel = null,
312317 .@"align" = 0,
318 .@"addrspace" = .generic,
313319 .bit_offset = 0,
314320 .host_size = 0,
315321 .@"allowzero" = false,
......@@ -321,6 +327,7 @@ pub const Type = extern union {
321327 .pointee_type = self.castPointer().?.data,
322328 .sentinel = null,
323329 .@"align" = 0,
330 .@"addrspace" = .generic,
324331 .bit_offset = 0,
325332 .host_size = 0,
326333 .@"allowzero" = false,
......@@ -332,6 +339,7 @@ pub const Type = extern union {
332339 .pointee_type = self.castPointer().?.data,
333340 .sentinel = null,
334341 .@"align" = 0,
342 .@"addrspace" = .generic,
335343 .bit_offset = 0,
336344 .host_size = 0,
337345 .@"allowzero" = false,
......@@ -343,6 +351,7 @@ pub const Type = extern union {
343351 .pointee_type = Type.initTag(.u8),
344352 .sentinel = null,
345353 .@"align" = 0,
354 .@"addrspace" = .generic,
346355 .bit_offset = 0,
347356 .host_size = 0,
348357 .@"allowzero" = false,
......@@ -354,6 +363,7 @@ pub const Type = extern union {
354363 .pointee_type = self.castPointer().?.data,
355364 .sentinel = null,
356365 .@"align" = 0,
366 .@"addrspace" = .generic,
357367 .bit_offset = 0,
358368 .host_size = 0,
359369 .@"allowzero" = false,
......@@ -365,6 +375,7 @@ pub const Type = extern union {
365375 .pointee_type = Type.initTag(.u8),
366376 .sentinel = null,
367377 .@"align" = 0,
378 .@"addrspace" = .generic,
368379 .bit_offset = 0,
369380 .host_size = 0,
370381 .@"allowzero" = false,
......@@ -376,6 +387,7 @@ pub const Type = extern union {
376387 .pointee_type = self.castPointer().?.data,
377388 .sentinel = null,
378389 .@"align" = 0,
390 .@"addrspace" = .generic,
379391 .bit_offset = 0,
380392 .host_size = 0,
381393 .@"allowzero" = false,
......@@ -387,6 +399,7 @@ pub const Type = extern union {
387399 .pointee_type = self.castPointer().?.data,
388400 .sentinel = null,
389401 .@"align" = 0,
402 .@"addrspace" = .generic,
390403 .bit_offset = 0,
391404 .host_size = 0,
392405 .@"allowzero" = false,
......@@ -398,6 +411,7 @@ pub const Type = extern union {
398411 .pointee_type = self.castPointer().?.data,
399412 .sentinel = null,
400413 .@"align" = 0,
414 .@"addrspace" = .generic,
401415 .bit_offset = 0,
402416 .host_size = 0,
403417 .@"allowzero" = false,
......@@ -409,6 +423,7 @@ pub const Type = extern union {
409423 .pointee_type = self.castPointer().?.data,
410424 .sentinel = null,
411425 .@"align" = 0,
426 .@"addrspace" = .generic,
412427 .bit_offset = 0,
413428 .host_size = 0,
414429 .@"allowzero" = false,
......@@ -461,6 +476,8 @@ pub const Type = extern union {
461476 return false;
462477 if (info_a.host_size != info_b.host_size)
463478 return false;
479 if (info_a.@"addrspace" != info_b.@"addrspace")
480 return false;
464481
465482 const sentinel_a = info_a.sentinel;
466483 const sentinel_b = info_b.sentinel;
......@@ -587,8 +604,8 @@ pub const Type = extern union {
587604 }
588605 return false;
589606 },
607 .Float => return a.tag() == b.tag(),
590608 .Opaque,
591 .Float,
592609 .BoundFn,
593610 .Frame,
594611 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
......@@ -746,6 +763,7 @@ pub const Type = extern union {
746763 .atomic_order,
747764 .atomic_rmw_op,
748765 .calling_convention,
766 .address_space,
749767 .float_mode,
750768 .reduce_op,
751769 .call_options,
......@@ -754,6 +772,7 @@ pub const Type = extern union {
754772 .type_info,
755773 .@"anyframe",
756774 .generic_poison,
775 .bound_fn,
757776 => unreachable,
758777
759778 .array_u8,
......@@ -835,6 +854,7 @@ pub const Type = extern union {
835854 .pointee_type = try payload.pointee_type.copy(allocator),
836855 .sentinel = sent,
837856 .@"align" = payload.@"align",
857 .@"addrspace" = payload.@"addrspace",
838858 .bit_offset = payload.bit_offset,
839859 .host_size = payload.host_size,
840860 .@"allowzero" = payload.@"allowzero",
......@@ -857,6 +877,7 @@ pub const Type = extern union {
857877 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
858878 .@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),
859879 .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),
880 .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered),
860881 .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),
861882 .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
862883 }
......@@ -917,6 +938,7 @@ pub const Type = extern union {
917938 .comptime_float,
918939 .noreturn,
919940 .var_args_param,
941 .bound_fn,
920942 => return writer.writeAll(@tagName(t)),
921943
922944 .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"),
......@@ -941,6 +963,10 @@ pub const Type = extern union {
941963 const enum_simple = ty.castTag(.enum_simple).?.data;
942964 return enum_simple.owner_decl.renderFullyQualifiedName(writer);
943965 },
966 .enum_numbered => {
967 const enum_numbered = ty.castTag(.enum_numbered).?.data;
968 return enum_numbered.owner_decl.renderFullyQualifiedName(writer);
969 },
944970 .@"opaque" => {
945971 // TODO use declaration name
946972 return writer.writeAll("opaque {}");
......@@ -958,6 +984,7 @@ pub const Type = extern union {
958984 .atomic_order => return writer.writeAll("std.builtin.AtomicOrder"),
959985 .atomic_rmw_op => return writer.writeAll("std.builtin.AtomicRmwOp"),
960986 .calling_convention => return writer.writeAll("std.builtin.CallingConvention"),
987 .address_space => return writer.writeAll("std.builtin.AddressSpace"),
961988 .float_mode => return writer.writeAll("std.builtin.FloatMode"),
962989 .reduce_op => return writer.writeAll("std.builtin.ReduceOp"),
963990 .call_options => return writer.writeAll("std.builtin.CallOptions"),
......@@ -1111,6 +1138,9 @@ pub const Type = extern union {
11111138 }
11121139 try writer.writeAll(") ");
11131140 }
1141 if (payload.@"addrspace" != .generic) {
1142 try writer.print("addrspace(.{s}) ", .{@tagName(payload.@"addrspace")});
1143 }
11141144 if (!payload.mutable) try writer.writeAll("const ");
11151145 if (payload.@"volatile") try writer.writeAll("volatile ");
11161146 if (payload.@"allowzero") try writer.writeAll("allowzero ");
......@@ -1186,6 +1216,7 @@ pub const Type = extern union {
11861216 .atomic_order,
11871217 .atomic_rmw_op,
11881218 .calling_convention,
1219 .address_space,
11891220 .float_mode,
11901221 .reduce_op,
11911222 .call_options,
......@@ -1220,6 +1251,7 @@ pub const Type = extern union {
12201251 .var_args_param => unreachable,
12211252 .inferred_alloc_mut => unreachable,
12221253 .inferred_alloc_const => unreachable,
1254 .bound_fn => unreachable,
12231255
12241256 .array_u8,
12251257 .array_u8_sentinel_0,
......@@ -1246,6 +1278,7 @@ pub const Type = extern union {
12461278 .@"union",
12471279 .union_tagged,
12481280 .enum_simple,
1281 .enum_numbered,
12491282 .enum_full,
12501283 .enum_nonexhaustive,
12511284 => false, // TODO some of these should be `true` depending on their child types
......@@ -1301,6 +1334,7 @@ pub const Type = extern union {
13011334 .atomic_order => return Value.initTag(.atomic_order_type),
13021335 .atomic_rmw_op => return Value.initTag(.atomic_rmw_op_type),
13031336 .calling_convention => return Value.initTag(.calling_convention_type),
1337 .address_space => return Value.initTag(.address_space_type),
13041338 .float_mode => return Value.initTag(.float_mode_type),
13051339 .reduce_op => return Value.initTag(.reduce_op_type),
13061340 .call_options => return Value.initTag(.call_options_type),
......@@ -1313,6 +1347,8 @@ pub const Type = extern union {
13131347 }
13141348 }
13151349
1350 /// For structs and unions, if the type does not have their fields resolved
1351 /// this will return `false`.
13161352 pub fn hasCodeGenBits(self: Type) bool {
13171353 return switch (self.tag()) {
13181354 .u1,
......@@ -1343,10 +1379,6 @@ pub const Type = extern union {
13431379 .f128,
13441380 .bool,
13451381 .anyerror,
1346 .fn_noreturn_no_args,
1347 .fn_void_no_args,
1348 .fn_naked_noreturn_no_args,
1349 .fn_ccc_void_no_args,
13501382 .single_const_pointer_to_comptime_int,
13511383 .const_slice_u8,
13521384 .array_u8_sentinel_0,
......@@ -1362,6 +1394,7 @@ pub const Type = extern union {
13621394 .atomic_order,
13631395 .atomic_rmw_op,
13641396 .calling_convention,
1397 .address_space,
13651398 .float_mode,
13661399 .reduce_op,
13671400 .call_options,
......@@ -1373,15 +1406,17 @@ pub const Type = extern union {
13731406
13741407 .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
13761415 .@"struct" => {
1377 // TODO introduce lazy value mechanism
13781416 const struct_obj = self.castTag(.@"struct").?.data;
13791417 if (struct_obj.known_has_bits) {
13801418 return true;
13811419 }
1382 assert(struct_obj.status == .have_field_types or
1383 struct_obj.status == .layout_wip or
1384 struct_obj.status == .have_layout);
13851420 for (struct_obj.fields.values()) |value| {
13861421 if (value.ty.hasCodeGenBits())
13871422 return true;
......@@ -1397,7 +1432,7 @@ pub const Type = extern union {
13971432 const enum_simple = self.castTag(.enum_simple).?.data;
13981433 return enum_simple.fields.count() >= 2;
13991434 },
1400 .enum_nonexhaustive => {
1435 .enum_numbered, .enum_nonexhaustive => {
14011436 var buffer: Payload.Bits = undefined;
14021437 const int_tag_ty = self.intTagType(&buffer);
14031438 return int_tag_ty.hasCodeGenBits();
......@@ -1448,6 +1483,7 @@ pub const Type = extern union {
14481483 .empty_struct_literal,
14491484 .@"opaque",
14501485 .type_info,
1486 .bound_fn,
14511487 => false,
14521488
14531489 .inferred_alloc_const => unreachable,
......@@ -1458,7 +1494,9 @@ pub const Type = extern union {
14581494 }
14591495
14601496 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;
14621500 const fast_result = self.tag_if_small_enough == Tag.noreturn;
14631501 assert(fast_result == definitely_correct_result);
14641502 return fast_result;
......@@ -1496,6 +1534,30 @@ pub const Type = extern union {
14961534 }
14971535 }
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
14991561 /// Asserts that hasCodeGenBits() is true.
15001562 pub fn abiAlignment(self: Type, target: Target) u32 {
15011563 return switch (self.tag()) {
......@@ -1508,6 +1570,7 @@ pub const Type = extern union {
15081570 .atomic_order,
15091571 .atomic_rmw_op,
15101572 .calling_convention,
1573 .address_space,
15111574 .float_mode,
15121575 .reduce_op,
15131576 .call_options,
......@@ -1583,7 +1646,11 @@ pub const Type = extern union {
15831646
15841647 .int_signed, .int_unsigned => {
15851648 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;
15871654 },
15881655
15891656 .optional => {
......@@ -1629,7 +1696,7 @@ pub const Type = extern union {
16291696 assert(biggest != 0);
16301697 return biggest;
16311698 },
1632 .enum_full, .enum_nonexhaustive, .enum_simple => {
1699 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
16331700 var buffer: Payload.Bits = undefined;
16341701 const int_tag_ty = self.intTagType(&buffer);
16351702 return int_tag_ty.abiAlignment(target);
......@@ -1676,6 +1743,7 @@ pub const Type = extern union {
16761743 .@"opaque",
16771744 .var_args_param,
16781745 .type_info,
1746 .bound_fn,
16791747 => unreachable,
16801748
16811749 .generic_poison => unreachable,
......@@ -1708,13 +1776,30 @@ pub const Type = extern union {
17081776 .var_args_param => unreachable,
17091777 .generic_poison => unreachable,
17101778 .type_info => unreachable,
1779 .bound_fn => unreachable,
17111780
17121781 .@"struct" => {
17131782 const s = self.castTag(.@"struct").?.data;
17141783 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;
17161801 },
1717 .enum_simple, .enum_full, .enum_nonexhaustive => {
1802 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
17181803 var buffer: Payload.Bits = undefined;
17191804 const int_tag_ty = self.intTagType(&buffer);
17201805 return int_tag_ty.abiSize(target);
......@@ -1730,6 +1815,7 @@ pub const Type = extern union {
17301815 .atomic_order,
17311816 .atomic_rmw_op,
17321817 .calling_convention,
1818 .address_space,
17331819 .float_mode,
17341820 .reduce_op,
17351821 .call_options,
......@@ -1818,6 +1904,7 @@ pub const Type = extern union {
18181904
18191905 .int_signed, .int_unsigned => {
18201906 const bits: u16 = self.cast(Payload.Bits).?.data;
1907 if (bits == 0) return 0;
18211908 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
18221909 },
18231910
......@@ -1875,11 +1962,12 @@ pub const Type = extern union {
18751962 .@"opaque" => unreachable,
18761963 .var_args_param => unreachable,
18771964 .generic_poison => unreachable,
1965 .bound_fn => unreachable,
18781966
18791967 .@"struct" => {
18801968 @panic("TODO bitSize struct");
18811969 },
1882 .enum_simple, .enum_full, .enum_nonexhaustive => {
1970 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
18831971 var buffer: Payload.Bits = undefined;
18841972 const int_tag_ty = self.intTagType(&buffer);
18851973 return int_tag_ty.bitSize(target);
......@@ -2014,6 +2102,7 @@ pub const Type = extern union {
20142102 .atomic_order,
20152103 .atomic_rmw_op,
20162104 .calling_convention,
2105 .address_space,
20172106 .float_mode,
20182107 .reduce_op,
20192108 .call_options,
......@@ -2024,23 +2113,6 @@ pub const Type = extern union {
20242113 };
20252114 }
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
20442116 pub fn isSinglePointer(self: Type) bool {
20452117 return switch (self.tag()) {
20462118 .single_const_pointer,
......@@ -2100,42 +2172,82 @@ pub const Type = extern union {
21002172 };
21012173 }
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 {
21042181 switch (self.tag()) {
21052182 .const_slice_u8 => return Type.initTag(.manyptr_const_u8),
21062183
21072184 .const_slice => {
21082185 const elem_type = self.castTag(.const_slice).?.data;
21092186 buffer.* = .{
2110 .base = .{ .tag = .many_const_pointer },
2111 .data = elem_type,
2187 .elem_type = .{
2188 .base = .{ .tag = .many_const_pointer },
2189 .data = elem_type,
2190 },
21122191 };
2113 return Type.initPayload(&buffer.base);
2192 return Type.initPayload(&buffer.elem_type.base);
21142193 },
21152194 .mut_slice => {
21162195 const elem_type = self.castTag(.mut_slice).?.data;
21172196 buffer.* = .{
2118 .base = .{ .tag = .many_mut_pointer },
2119 .data = elem_type,
2197 .elem_type = .{
2198 .base = .{ .tag = .many_mut_pointer },
2199 .data = elem_type,
2200 },
21202201 };
2121 return Type.initPayload(&buffer.base);
2202 return Type.initPayload(&buffer.elem_type.base);
21222203 },
21232204
21242205 .pointer => {
21252206 const payload = self.castTag(.pointer).?.data;
21262207 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 {
21282217 buffer.* = .{
2129 .base = .{ .tag = .many_mut_pointer },
2130 .data = payload.pointee_type,
2218 .pointer = .{
2219 .data = .{
2220 .pointee_type = payload.pointee_type,
2221 .sentinel = payload.sentinel,
2222 .@"align" = payload.@"align",
2223 .@"addrspace" = payload.@"addrspace",
2224 .bit_offset = payload.bit_offset,
2225 .host_size = payload.host_size,
2226 .@"allowzero" = payload.@"allowzero",
2227 .mutable = payload.mutable,
2228 .@"volatile" = payload.@"volatile",
2229 .size = .Many,
2230 },
2231 },
21312232 };
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);
21322242 } else {
21332243 buffer.* = .{
2134 .base = .{ .tag = .many_const_pointer },
2135 .data = payload.pointee_type,
2244 .elem_type = .{
2245 .base = .{ .tag = .many_const_pointer },
2246 .data = payload.pointee_type,
2247 },
21362248 };
2249 return Type.initPayload(&buffer.elem_type.base);
21372250 }
2138 return Type.initPayload(&buffer.base);
21392251 },
21402252
21412253 else => unreachable,
......@@ -2191,6 +2303,44 @@ pub const Type = extern union {
21912303 };
21922304 }
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
21942344 /// Asserts that the type is an optional
21952345 pub fn isPtrLikeOptional(self: Type) bool {
21962346 switch (self.tag()) {
......@@ -2203,8 +2353,13 @@ pub const Type = extern union {
22032353 const child_type = self.optionalChild(&buf);
22042354 // optionals of zero sized pointers behave like bools
22052355 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 }
22082363 },
22092364 else => unreachable,
22102365 }
......@@ -2252,12 +2407,14 @@ pub const Type = extern union {
22522407 };
22532408 }
22542409
2255 /// Asserts the type is a pointer or array type.
2256 pub fn elemType(self: Type) Type {
2257 return switch (self.tag()) {
2258 .vector => self.castTag(.vector).?.data.elem_type,
2259 .array => self.castTag(.array).?.data.elem_type,
2260 .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,
2410 /// For *[N]T, returns [N]T.
2411 /// For *T, returns T.
2412 /// For [*]T, returns T.
2413 pub fn childType(ty: Type) Type {
2414 return switch (ty.tag()) {
2415 .vector => ty.castTag(.vector).?.data.elem_type,
2416 .array => ty.castTag(.array).?.data.elem_type,
2417 .array_sentinel => ty.castTag(.array_sentinel).?.data.elem_type,
22612418 .single_const_pointer,
22622419 .single_mut_pointer,
22632420 .many_const_pointer,
......@@ -2266,7 +2423,48 @@ pub const Type = extern union {
22662423 .c_mut_pointer,
22672424 .const_slice,
22682425 .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
22712469 .array_u8,
22722470 .array_u8_sentinel_0,
......@@ -2276,12 +2474,29 @@ pub const Type = extern union {
22762474 => Type.initTag(.u8),
22772475
22782476 .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
22812489 else => unreachable,
22822490 };
22832491 }
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
22852500 /// Asserts that the type is an optional.
22862501 /// Resulting `Type` will have inner memory referencing `buf`.
22872502 pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {
......@@ -2320,6 +2535,21 @@ pub const Type = extern union {
23202535 }
23212536 }
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
23232553 /// Asserts that the type is an error union.
23242554 pub fn errorUnionPayload(self: Type) Type {
23252555 return switch (self.tag()) {
......@@ -2476,7 +2706,8 @@ pub const Type = extern union {
24762706 };
24772707 }
24782708
2479 pub fn isFloat(self: Type) bool {
2709 /// Returns `false` for `comptime_float`.
2710 pub fn isRuntimeFloat(self: Type) bool {
24802711 return switch (self.tag()) {
24812712 .f16,
24822713 .f32,
......@@ -2489,13 +2720,29 @@ pub const Type = extern union {
24892720 };
24902721 }
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.
24932740 pub fn floatBits(self: Type, target: Target) u16 {
24942741 return switch (self.tag()) {
24952742 .f16 => 16,
24962743 .f32 => 32,
24972744 .f64 => 64,
2498 .f128 => 128,
2745 .f128, .comptime_float => 128,
24992746 .c_longdouble => CType.longdouble.sizeInBits(target),
25002747
25012748 else => unreachable,
......@@ -2728,6 +2975,7 @@ pub const Type = extern union {
27282975 .atomic_order,
27292976 .atomic_rmw_op,
27302977 .calling_convention,
2978 .address_space,
27312979 .float_mode,
27322980 .reduce_op,
27332981 .call_options,
......@@ -2743,6 +2991,7 @@ pub const Type = extern union {
27432991 .single_const_pointer,
27442992 .single_mut_pointer,
27452993 .pointer,
2994 .bound_fn,
27462995 => return null,
27472996
27482997 .@"struct" => {
......@@ -2772,6 +3021,7 @@ pub const Type = extern union {
27723021 }
27733022 },
27743023 .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
3024 .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty,
27753025 .@"union" => {
27763026 return null; // TODO
27773027 },
......@@ -2832,7 +3082,7 @@ pub const Type = extern union {
28323082 }
28333083
28343084 /// 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 {
28363086 assert(self.zigTypeTag() == .Int);
28373087 const info = self.intInfo(target);
28383088
......@@ -2842,35 +3092,35 @@ pub const Type = extern union {
28423092
28433093 if ((info.bits - 1) <= std.math.maxInt(u6)) {
28443094 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);
28463096 }
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);
28493099 try res.shiftLeft(res, info.bits - 1);
28503100 res.negate();
28513101
28523102 const res_const = res.toConst();
28533103 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);
28553105 } 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);
28573107 }
28583108 }
28593109
28603110 /// 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 {
28623112 assert(self.zigTypeTag() == .Int);
28633113 const info = self.intInfo(target);
28643114
28653115 if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) {
28663116 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);
28683118 } else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) {
28693119 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);
28713121 }
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);
28743124 try res.shiftLeft(res, info.bits - @boolToInt(info.signedness == .signed));
28753125 const one = std.math.big.int.Const{
28763126 .limbs = &[_]std.math.big.Limb{1},
......@@ -2880,37 +3130,27 @@ pub const Type = extern union {
28803130
28813131 const res_const = res.toConst();
28823132 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);
28843134 } 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);
28863136 }
28873137 }
28883138
2889 /// Returns the integer tag type of the enum.
2890 pub fn enumTagType(ty: Type, buffer: *Payload.Bits) Type {
2891 switch (ty.tag()) {
2892 .enum_full, .enum_nonexhaustive => {
2893 const enum_full = ty.cast(Payload.EnumFull).?.data;
2894 return enum_full.tag_ty;
2895 },
3139 /// Asserts the type is an enum or a union.
3140 /// TODO support unions
3141 pub fn intTagType(self: Type, buffer: *Payload.Bits) Type {
3142 switch (self.tag()) {
3143 .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty,
3144 .enum_numbered => return self.castTag(.enum_numbered).?.data.tag_ty,
28963145 .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());
28983148 buffer.* = .{
28993149 .base = .{ .tag = .int_unsigned },
2900 .data = std.math.log2_int_ceil(usize, enum_simple.fields.count()),
3150 .data = bits,
29013151 };
29023152 return Type.initPayload(&buffer.base);
29033153 },
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
29143154 else => unreachable,
29153155 }
29163156 }
......@@ -2928,13 +3168,12 @@ pub const Type = extern union {
29283168 const enum_full = ty.cast(Payload.EnumFull).?.data;
29293169 return enum_full.fields.count();
29303170 },
2931 .enum_simple => {
2932 const enum_simple = ty.castTag(.enum_simple).?.data;
2933 return enum_simple.fields.count();
2934 },
3171 .enum_simple => return ty.castTag(.enum_simple).?.data.fields.count(),
3172 .enum_numbered => return ty.castTag(.enum_numbered).?.data.fields.count(),
29353173 .atomic_order,
29363174 .atomic_rmw_op,
29373175 .calling_convention,
3176 .address_space,
29383177 .float_mode,
29393178 .reduce_op,
29403179 .call_options,
......@@ -2956,9 +3195,14 @@ pub const Type = extern union {
29563195 const enum_simple = ty.castTag(.enum_simple).?.data;
29573196 return enum_simple.fields.keys()[field_index];
29583197 },
3198 .enum_numbered => {
3199 const enum_numbered = ty.castTag(.enum_numbered).?.data;
3200 return enum_numbered.fields.keys()[field_index];
3201 },
29593202 .atomic_order,
29603203 .atomic_rmw_op,
29613204 .calling_convention,
3205 .address_space,
29623206 .float_mode,
29633207 .reduce_op,
29643208 .call_options,
......@@ -2979,9 +3223,14 @@ pub const Type = extern union {
29793223 const enum_simple = ty.castTag(.enum_simple).?.data;
29803224 return enum_simple.fields.getIndex(field_name);
29813225 },
3226 .enum_numbered => {
3227 const enum_numbered = ty.castTag(.enum_numbered).?.data;
3228 return enum_numbered.fields.getIndex(field_name);
3229 },
29823230 .atomic_order,
29833231 .atomic_rmw_op,
29843232 .calling_convention,
3233 .address_space,
29853234 .float_mode,
29863235 .reduce_op,
29873236 .call_options,
......@@ -3021,6 +3270,15 @@ pub const Type = extern union {
30213270 return enum_full.values.getIndexContext(enum_tag, .{ .ty = tag_ty });
30223271 }
30233272 },
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 },
30243282 .enum_simple => {
30253283 const enum_simple = ty.castTag(.enum_simple).?.data;
30263284 const fields_len = enum_simple.fields.count();
......@@ -3035,6 +3293,7 @@ pub const Type = extern union {
30353293 .atomic_order,
30363294 .atomic_rmw_op,
30373295 .calling_convention,
3296 .address_space,
30383297 .float_mode,
30393298 .reduce_op,
30403299 .call_options,
......@@ -3071,6 +3330,7 @@ pub const Type = extern union {
30713330 const enum_full = ty.cast(Payload.EnumFull).?.data;
30723331 return enum_full.srcLoc();
30733332 },
3333 .enum_numbered => return ty.castTag(.enum_numbered).?.data.srcLoc(),
30743334 .enum_simple => {
30753335 const enum_simple = ty.castTag(.enum_simple).?.data;
30763336 return enum_simple.srcLoc();
......@@ -3090,6 +3350,7 @@ pub const Type = extern union {
30903350 .atomic_order,
30913351 .atomic_rmw_op,
30923352 .calling_convention,
3353 .address_space,
30933354 .float_mode,
30943355 .reduce_op,
30953356 .call_options,
......@@ -3107,6 +3368,7 @@ pub const Type = extern union {
31073368 const enum_full = ty.cast(Payload.EnumFull).?.data;
31083369 return enum_full.owner_decl;
31093370 },
3371 .enum_numbered => return ty.castTag(.enum_numbered).?.data.owner_decl,
31103372 .enum_simple => {
31113373 const enum_simple = ty.castTag(.enum_simple).?.data;
31123374 return enum_simple.owner_decl;
......@@ -3127,6 +3389,7 @@ pub const Type = extern union {
31273389 .atomic_order,
31283390 .atomic_rmw_op,
31293391 .calling_convention,
3392 .address_space,
31303393 .float_mode,
31313394 .reduce_op,
31323395 .call_options,
......@@ -3163,6 +3426,15 @@ pub const Type = extern union {
31633426 return enum_full.values.containsContext(int, .{ .ty = tag_ty });
31643427 }
31653428 },
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 },
31663438 .enum_simple => {
31673439 const enum_simple = ty.castTag(.enum_simple).?.data;
31683440 const fields_len = enum_simple.fields.count();
......@@ -3177,6 +3449,7 @@ pub const Type = extern union {
31773449 .atomic_order,
31783450 .atomic_rmw_op,
31793451 .calling_convention,
3452 .address_space,
31803453 .float_mode,
31813454 .reduce_op,
31823455 .call_options,
......@@ -3237,6 +3510,7 @@ pub const Type = extern union {
32373510 atomic_order,
32383511 atomic_rmw_op,
32393512 calling_convention,
3513 address_space,
32403514 float_mode,
32413515 reduce_op,
32423516 call_options,
......@@ -3254,7 +3528,7 @@ pub const Type = extern union {
32543528 anyerror_void_error_union,
32553529 generic_poison,
32563530 /// 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.
32583532 var_args_param,
32593533 /// Same as `empty_struct` except it has an empty namespace.
32603534 empty_struct_literal,
......@@ -3264,6 +3538,7 @@ pub const Type = extern union {
32643538 inferred_alloc_mut,
32653539 /// Same as `inferred_alloc_mut` but the local is `var` not `const`.
32663540 inferred_alloc_const, // See last_no_payload_tag below.
3541 bound_fn,
32673542 // After this, the tag requires a payload.
32683543
32693544 array_u8,
......@@ -3298,10 +3573,11 @@ pub const Type = extern union {
32983573 @"union",
32993574 union_tagged,
33003575 enum_simple,
3576 enum_numbered,
33013577 enum_full,
33023578 enum_nonexhaustive,
33033579
3304 pub const last_no_payload_tag = Tag.inferred_alloc_const;
3580 pub const last_no_payload_tag = Tag.bound_fn;
33053581 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
33063582
33073583 pub fn Type(comptime t: Tag) type {
......@@ -3360,6 +3636,7 @@ pub const Type = extern union {
33603636 .atomic_order,
33613637 .atomic_rmw_op,
33623638 .calling_convention,
3639 .address_space,
33633640 .float_mode,
33643641 .reduce_op,
33653642 .call_options,
......@@ -3367,6 +3644,7 @@ pub const Type = extern union {
33673644 .extern_options,
33683645 .type_info,
33693646 .@"anyframe",
3647 .bound_fn,
33703648 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
33713649
33723650 .array_u8,
......@@ -3405,6 +3683,7 @@ pub const Type = extern union {
34053683 .@"union", .union_tagged => Payload.Union,
34063684 .enum_full, .enum_nonexhaustive => Payload.EnumFull,
34073685 .enum_simple => Payload.EnumSimple,
3686 .enum_numbered => Payload.EnumNumbered,
34083687 .empty_struct => Payload.ContainerScope,
34093688 };
34103689 }
......@@ -3415,12 +3694,12 @@ pub const Type = extern union {
34153694 }
34163695
34173696 pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!file_struct.Type {
3418 const ptr = try ally.create(t.Type());
3419 ptr.* = .{
3697 const p = try ally.create(t.Type());
3698 p.* = .{
34203699 .base = .{ .tag = t },
34213700 .data = data,
34223701 };
3423 return file_struct.Type{ .ptr_otherwise = &ptr.base };
3702 return file_struct.Type{ .ptr_otherwise = &p.base };
34243703 }
34253704
34263705 pub fn Data(comptime t: Tag) type {
......@@ -3510,18 +3789,23 @@ pub const Type = extern union {
35103789 pub const base_tag = Tag.pointer;
35113790
35123791 base: Payload = Payload{ .tag = base_tag },
3513 data: struct {
3792 data: Data,
3793
3794 pub const Data = struct {
35143795 pointee_type: Type,
3515 sentinel: ?Value,
3796 sentinel: ?Value = null,
35163797 /// If zero use pointee_type.AbiAlign()
3517 @"align": u32,
3518 bit_offset: u16,
3519 host_size: u16,
3520 @"allowzero": bool,
3521 mutable: bool,
3522 @"volatile": bool,
3523 size: std.builtin.TypeInfo.Pointer.Size,
3524 },
3798 @"align": u32 = 0,
3799 /// See src/target.zig defaultAddressSpace function for how to obtain
3800 /// an appropriate value for this field.
3801 @"addrspace": std.builtin.AddressSpace,
3802 bit_offset: u16 = 0,
3803 host_size: u16 = 0,
3804 @"allowzero": bool = false,
3805 mutable: bool = true, // TODO change this to const, not mutable
3806 @"volatile": bool = false,
3807 size: std.builtin.TypeInfo.Pointer.Size = .One,
3808 };
35253809 };
35263810
35273811 pub const ErrorUnion = struct {
......@@ -3576,7 +3860,64 @@ pub const Type = extern union {
35763860 base: Payload = .{ .tag = .enum_simple },
35773861 data: *Module.EnumSimple,
35783862 };
3863
3864 pub const EnumNumbered = struct {
3865 base: Payload = .{ .tag = .enum_numbered },
3866 data: *Module.EnumNumbered,
3867 };
35793868 };
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 }
35803921};
35813922
35823923pub const CType = enum {
......@@ -3633,6 +3974,7 @@ pub const CType = enum {
36333974 .wasi,
36343975 .emscripten,
36353976 .plan9,
3977 .solaris,
36363978 => switch (self) {
36373979 .short,
36383980 .ushort,
......@@ -3684,7 +4026,6 @@ pub const CType = enum {
36844026 .fuchsia,
36854027 .kfreebsd,
36864028 .lv2,
3687 .solaris,
36884029 .zos,
36894030 .haiku,
36904031 .minix,
src/value.zig+543-41
......@@ -63,6 +63,7 @@ pub const Value = extern union {
6363 atomic_order_type,
6464 atomic_rmw_op_type,
6565 calling_convention_type,
66 address_space_type,
6667 float_mode_type,
6768 reduce_op_type,
6869 call_options_type,
......@@ -158,6 +159,10 @@ pub const Value = extern union {
158159 /// Used to coordinate alloc_inferred, store_to_inferred_ptr, and resolve_inferred_alloc
159160 /// instructions for comptime code.
160161 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
162167 pub const last_no_payload_tag = Tag.empty_array;
163168 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -226,6 +231,7 @@ pub const Value = extern union {
226231 .atomic_order_type,
227232 .atomic_rmw_op_type,
228233 .calling_convention_type,
234 .address_space_type,
229235 .float_mode_type,
230236 .reduce_op_type,
231237 .call_options_type,
......@@ -277,6 +283,7 @@ pub const Value = extern union {
277283 .inferred_alloc => Payload.InferredAlloc,
278284 .@"struct" => Payload.Struct,
279285 .@"union" => Payload.Union,
286 .bound_fn => Payload.BoundFn,
280287 };
281288 }
282289
......@@ -412,6 +419,7 @@ pub const Value = extern union {
412419 .atomic_order_type,
413420 .atomic_rmw_op_type,
414421 .calling_convention_type,
422 .address_space_type,
415423 .float_mode_type,
416424 .reduce_op_type,
417425 .call_options_type,
......@@ -419,6 +427,7 @@ pub const Value = extern union {
419427 .extern_options_type,
420428 .type_info_type,
421429 .generic_poison,
430 .bound_fn,
422431 => unreachable,
423432
424433 .ty => {
......@@ -625,6 +634,7 @@ pub const Value = extern union {
625634 .atomic_order_type => return out_stream.writeAll("std.builtin.AtomicOrder"),
626635 .atomic_rmw_op_type => return out_stream.writeAll("std.builtin.AtomicRmwOp"),
627636 .calling_convention_type => return out_stream.writeAll("std.builtin.CallingConvention"),
637 .address_space_type => return out_stream.writeAll("std.builtin.AddressSpace"),
628638 .float_mode_type => return out_stream.writeAll("std.builtin.FloatMode"),
629639 .reduce_op_type => return out_stream.writeAll("std.builtin.ReduceOp"),
630640 .call_options_type => return out_stream.writeAll("std.builtin.CallOptions"),
......@@ -712,6 +722,10 @@ pub const Value = extern union {
712722 try out_stream.writeAll("(opt_payload_ptr)");
713723 val = val.castTag(.opt_payload_ptr).?.data;
714724 },
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 },
715729 };
716730 }
717731
......@@ -792,6 +806,7 @@ pub const Value = extern union {
792806 .atomic_order_type => Type.initTag(.atomic_order),
793807 .atomic_rmw_op_type => Type.initTag(.atomic_rmw_op),
794808 .calling_convention_type => Type.initTag(.calling_convention),
809 .address_space_type => Type.initTag(.address_space),
795810 .float_mode_type => Type.initTag(.float_mode),
796811 .reduce_op_type => Type.initTag(.reduce_op),
797812 .call_options_type => Type.initTag(.call_options),
......@@ -932,7 +947,7 @@ pub const Value = extern union {
932947 /// Asserts that the value is a float or an integer.
933948 pub fn toFloat(self: Value, comptime T: type) T {
934949 return switch (self.tag()) {
935 .float_16 => @panic("TODO soft float"),
950 .float_16 => @floatCast(T, self.castTag(.float_16).?.data),
936951 .float_32 => @floatCast(T, self.castTag(.float_32).?.data),
937952 .float_64 => @floatCast(T, self.castTag(.float_64).?.data),
938953 .float_128 => @floatCast(T, self.castTag(.float_128).?.data),
......@@ -947,6 +962,45 @@ pub const Value = extern union {
947962 };
948963 }
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
9501004 /// Asserts the value is an integer and not undefined.
9511005 /// Returns the number of bits the value requires to represent stored in twos complement form.
9521006 pub fn intBitCountTwosComp(self: Value) usize {
......@@ -1036,31 +1090,15 @@ pub const Value = extern union {
10361090 }
10371091 }
10381092
1039 /// Converts an integer or a float to a float.
1040 /// Returns `error.Overflow` if the value does not fit in the new type.
1041 pub fn floatCast(self: Value, allocator: *Allocator, dest_ty: Type) !Value {
1093 /// Converts an integer or a float to a float. May result in a loss of information.
1094 /// Caller can find out by equality checking the result against the operand.
1095 pub fn floatCast(self: Value, arena: *Allocator, dest_ty: Type) !Value {
10421096 switch (dest_ty.tag()) {
1043 .f16 => {
1044 @panic("TODO add __trunctfhf2 to compiler-rt");
1045 //const res = try Value.Tag.float_16.create(allocator, self.toFloat(f16));
1046 //if (!self.eql(res))
1047 // return error.Overflow;
1048 //return res;
1049 },
1050 .f32 => {
1051 const res = try Value.Tag.float_32.create(allocator, self.toFloat(f32));
1052 if (!self.eql(res, dest_ty))
1053 return error.Overflow;
1054 return res;
1055 },
1056 .f64 => {
1057 const res = try Value.Tag.float_64.create(allocator, self.toFloat(f64));
1058 if (!self.eql(res, dest_ty))
1059 return error.Overflow;
1060 return res;
1061 },
1097 .f16 => return Value.Tag.float_16.create(arena, self.toFloat(f16)),
1098 .f32 => return Value.Tag.float_32.create(arena, self.toFloat(f32)),
1099 .f64 => return Value.Tag.float_64.create(arena, self.toFloat(f64)),
10621100 .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));
10641102 },
10651103 else => unreachable,
10661104 }
......@@ -1286,7 +1324,12 @@ pub const Value = extern union {
12861324 }
12871325 },
12881326 .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);
12901333 },
12911334 .Fn => {
12921335 @panic("TODO implement hashing function values");
......@@ -1442,6 +1485,14 @@ pub const Value = extern union {
14421485 }
14431486 }
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
14451496 /// Returns a pointer to the element value at the index.
14461497 pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value {
14471498 if (self.castTag(.elem_ptr)) |elem_ptr| {
......@@ -1524,6 +1575,341 @@ pub const Value = extern union {
15241575 };
15251576 }
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
15271913 pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
15281914 // TODO is this a performance issue? maybe we should try the operation without
15291915 // resorting to BigInt first.
......@@ -1599,6 +1985,82 @@ pub const Value = extern union {
15991985 }
16001986 }
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
16022064 pub fn intMul(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
16032065 // TODO is this a performance issue? maybe we should try the operation without
16042066 // resorting to BigInt first.
......@@ -1634,6 +2096,31 @@ pub const Value = extern union {
16342096 return Tag.int_u64.create(arena, truncated);
16352097 }
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
16372124 pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
16382125 // TODO is this a performance issue? maybe we should try the operation without
16392126 // resorting to BigInt first.
......@@ -1667,10 +2154,9 @@ pub const Value = extern union {
16672154 ) !Value {
16682155 switch (float_type.tag()) {
16692156 .f16 => {
1670 @panic("TODO add __trunctfhf2 to compiler-rt");
1671 //const lhs_val = lhs.toFloat(f16);
1672 //const rhs_val = rhs.toFloat(f16);
1673 //return Value.Tag.float_16.create(arena, lhs_val + rhs_val);
2157 const lhs_val = lhs.toFloat(f16);
2158 const rhs_val = rhs.toFloat(f16);
2159 return Value.Tag.float_16.create(arena, lhs_val + rhs_val);
16742160 },
16752161 .f32 => {
16762162 const lhs_val = lhs.toFloat(f32);
......@@ -1699,10 +2185,9 @@ pub const Value = extern union {
16992185 ) !Value {
17002186 switch (float_type.tag()) {
17012187 .f16 => {
1702 @panic("TODO add __trunctfhf2 to compiler-rt");
1703 //const lhs_val = lhs.toFloat(f16);
1704 //const rhs_val = rhs.toFloat(f16);
1705 //return Value.Tag.float_16.create(arena, lhs_val - rhs_val);
2188 const lhs_val = lhs.toFloat(f16);
2189 const rhs_val = rhs.toFloat(f16);
2190 return Value.Tag.float_16.create(arena, lhs_val - rhs_val);
17062191 },
17072192 .f32 => {
17082193 const lhs_val = lhs.toFloat(f32);
......@@ -1731,10 +2216,9 @@ pub const Value = extern union {
17312216 ) !Value {
17322217 switch (float_type.tag()) {
17332218 .f16 => {
1734 @panic("TODO add __trunctfhf2 to compiler-rt");
1735 //const lhs_val = lhs.toFloat(f16);
1736 //const rhs_val = rhs.toFloat(f16);
1737 //return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
2219 const lhs_val = lhs.toFloat(f16);
2220 const rhs_val = rhs.toFloat(f16);
2221 return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
17382222 },
17392223 .f32 => {
17402224 const lhs_val = lhs.toFloat(f32);
......@@ -1763,10 +2247,9 @@ pub const Value = extern union {
17632247 ) !Value {
17642248 switch (float_type.tag()) {
17652249 .f16 => {
1766 @panic("TODO add __trunctfhf2 to compiler-rt");
1767 //const lhs_val = lhs.toFloat(f16);
1768 //const rhs_val = rhs.toFloat(f16);
1769 //return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
2250 const lhs_val = lhs.toFloat(f16);
2251 const rhs_val = rhs.toFloat(f16);
2252 return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
17702253 },
17712254 .f32 => {
17722255 const lhs_val = lhs.toFloat(f32);
......@@ -1972,6 +2455,16 @@ pub const Value = extern union {
19722455 val: Value,
19732456 },
19742457 };
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 };
19752468 };
19762469
19772470 /// Big enough to fit any non-BigInt value
......@@ -1980,4 +2473,13 @@ pub const Value = extern union {
19802473 /// are possible without using an allocator.
19812474 limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
19822475 };
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,
19832485};
src/windows_com.hpp+1-1
......@@ -352,7 +352,7 @@ extern "C" {
352352 /// <summary>
353353 /// Gets product-specific properties.
354354 /// </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>
356356 /// <returns>Standard HRESULT indicating success or failure, including E_FILENOTFOUND if the instance state does not exist.</returns>
357357 STDMETHOD(GetProperties)(
358358 _Outptr_result_maybenull_ ISetupPropertyStore** ppProperties
src/zig_clang.cpp+4-4
......@@ -1691,10 +1691,10 @@ void ZigClang_detect_enum_ConstantExprKind(clang::Expr::ConstantExprKind x) {
16911691 break;
16921692 }
16931693}
1694static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_Normal == clang::Expr::ConstantExprKind::Normal, "");
1695static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_NonClassTemplateArgument == clang::Expr::ConstantExprKind::NonClassTemplateArgument, "");
1696static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_ClassTemplateArgument == clang::Expr::ConstantExprKind::ClassTemplateArgument, "");
1697static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ContantExprKind_ImmediateInvocation == clang::Expr::ConstantExprKind::ImmediateInvocation, "");
1694static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_Normal == clang::Expr::ConstantExprKind::Normal, "");
1695static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_NonClassTemplateArgument == clang::Expr::ConstantExprKind::NonClassTemplateArgument, "");
1696static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_ClassTemplateArgument == clang::Expr::ConstantExprKind::ClassTemplateArgument, "");
1697static_assert((clang::Expr::ConstantExprKind)ZigClangExpr_ConstantExprKind_ImmediateInvocation == clang::Expr::ConstantExprKind::ImmediateInvocation, "");
16981698
16991699
17001700static_assert(sizeof(ZigClangAPValue) == sizeof(clang::APValue), "");
src/zig_clang.h+4-4
......@@ -1011,10 +1011,10 @@ enum ZigClangPreprocessedEntity_EntityKind {
10111011};
10121012
10131013enum ZigClangExpr_ConstantExprKind {
1014 ZigClangExpr_ContantExprKind_Normal,
1015 ZigClangExpr_ContantExprKind_NonClassTemplateArgument,
1016 ZigClangExpr_ContantExprKind_ClassTemplateArgument,
1017 ZigClangExpr_ContantExprKind_ImmediateInvocation,
1014 ZigClangExpr_ConstantExprKind_Normal,
1015 ZigClangExpr_ConstantExprKind_NonClassTemplateArgument,
1016 ZigClangExpr_ConstantExprKind_ClassTemplateArgument,
1017 ZigClangExpr_ConstantExprKind_ImmediateInvocation,
10181018};
10191019
10201020enum ZigClangUnaryExprOrTypeTrait_Kind {
src/zig_llvm-ar.cpp+1-1
......@@ -232,7 +232,7 @@ static std::string ArchiveName;
232232static std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
233233static std::vector<std::unique_ptr<object::Archive>> Archives;
234234
235// This variable holds the list of member files to proecess, as given
235// This variable holds the list of member files to process, as given
236236// on the command line.
237237static std::vector<StringRef> Members;
238238
src/zig_llvm.cpp+5
......@@ -412,6 +412,11 @@ ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref) {
412412 return wrap(Type::getTokenTy(*unwrap(context_ref)));
413413}
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
415420LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
416421 unsigned NumArgs, ZigLLVM_CallingConv CC, ZigLLVM_CallAttr attr, const char *Name)
417422{
src/zig_llvm.h+3
......@@ -65,6 +65,9 @@ ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, co
6565
6666ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);
6767
68ZIG_EXTERN_C LLVMValueRef ZigLLVMAddFunctionInAddressSpace(LLVMModuleRef M, const char *Name,
69 LLVMTypeRef FunctionTy, unsigned AddressSpace);
70
6871enum ZigLLVM_CallingConv {
6972 ZigLLVM_C = 0,
7073 ZigLLVM_Fast = 8,
test/behavior.zig+43-38
......@@ -2,19 +2,38 @@ const builtin = @import("builtin");
22
33test {
44 // Tests that pass for both.
5 _ = @import("behavior/bool.zig");
5 _ = @import("behavior/array.zig");
6 _ = @import("behavior/atomics.zig");
67 _ = @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");
819 _ = @import("behavior/eval.zig");
9 _ = @import("behavior/pointers.zig");
20 _ = @import("behavior/generics.zig");
1021 _ = @import("behavior/if.zig");
11 _ = @import("behavior/cast.zig");
12 _ = @import("behavior/array.zig");
22 _ = @import("behavior/math.zig");
23 _ = @import("behavior/member_func.zig");
24 _ = @import("behavior/pointers.zig");
25 _ = @import("behavior/sizeof_and_typeof.zig");
26 _ = @import("behavior/struct.zig");
27 _ = @import("behavior/this.zig");
28 _ = @import("behavior/translate_c_macros.zig");
29 _ = @import("behavior/union.zig");
1330 _ = @import("behavior/usingnamespace.zig");
14 _ = @import("behavior/atomics.zig");
31 _ = @import("behavior/widening.zig");
1532
16 if (!builtin.zig_is_stage2) {
17 // Tests that only pass for stage1.
33 if (builtin.zig_is_stage2) {
34 // When all comptime_memory.zig tests pass, #9646 can be closed.
35 // _ = @import("behavior/comptime_memory.zig");
36 } else {
1837 _ = @import("behavior/align.zig");
1938 _ = @import("behavior/alignof.zig");
2039 _ = @import("behavior/array_stage1.zig");
......@@ -22,16 +41,25 @@ test {
2241 _ = @import("behavior/asm.zig");
2342 _ = @import("behavior/async_fn.zig");
2443 }
25 _ = @import("behavior/atomics_stage1.zig");
2644 _ = @import("behavior/await_struct.zig");
2745 _ = @import("behavior/bit_shifting.zig");
2846 _ = @import("behavior/bitcast.zig");
2947 _ = @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");
3059 _ = @import("behavior/bugs/1025.zig");
3160 _ = @import("behavior/bugs/1076.zig");
3261 _ = @import("behavior/bugs/1111.zig");
3362 _ = @import("behavior/bugs/1120.zig");
34 _ = @import("behavior/bugs/1277.zig");
3563 _ = @import("behavior/bugs/1310.zig");
3664 _ = @import("behavior/bugs/1322.zig");
3765 _ = @import("behavior/bugs/1381.zig");
......@@ -41,14 +69,11 @@ test {
4169 _ = @import("behavior/bugs/1500.zig");
4270 _ = @import("behavior/bugs/1607.zig");
4371 _ = @import("behavior/bugs/1735.zig");
44 _ = @import("behavior/bugs/1741.zig");
4572 _ = @import("behavior/bugs/1851.zig");
4673 _ = @import("behavior/bugs/1914.zig");
4774 _ = @import("behavior/bugs/2006.zig");
4875 _ = @import("behavior/bugs/2114.zig");
49 _ = @import("behavior/bugs/2346.zig");
5076 _ = @import("behavior/bugs/2578.zig");
51 _ = @import("behavior/bugs/2692.zig");
5277 _ = @import("behavior/bugs/2889.zig");
5378 _ = @import("behavior/bugs/3007.zig");
5479 _ = @import("behavior/bugs/3046.zig");
......@@ -60,8 +85,6 @@ test {
6085 _ = @import("behavior/bugs/3779.zig");
6186 _ = @import("behavior/bugs/4328.zig");
6287 _ = @import("behavior/bugs/4560.zig");
63 _ = @import("behavior/bugs/4769_a.zig");
64 _ = @import("behavior/bugs/4769_b.zig");
6588 _ = @import("behavior/bugs/4954.zig");
6689 _ = @import("behavior/bugs/5398.zig");
6790 _ = @import("behavior/bugs/5413.zig");
......@@ -69,30 +92,14 @@ test {
6992 _ = @import("behavior/bugs/5487.zig");
7093 _ = @import("behavior/bugs/6456.zig");
7194 _ = @import("behavior/bugs/6781.zig");
72 _ = @import("behavior/bugs/6850.zig");
7395 _ = @import("behavior/bugs/7027.zig");
7496 _ = @import("behavior/bugs/7047.zig");
7597 _ = @import("behavior/bugs/7003.zig");
7698 _ = @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");
9099 _ = @import("behavior/byteswap.zig");
91100 _ = @import("behavior/byval_arg_var.zig");
92101 _ = @import("behavior/call.zig");
93102 _ = @import("behavior/cast_stage1.zig");
94 // When these tests pass, #9646 can be closed.
95 // _ = @import("behavior/comptime_memory.zig");
96103 _ = @import("behavior/const_slice_child.zig");
97104 _ = @import("behavior/defer.zig");
98105 _ = @import("behavior/enum.zig");
......@@ -113,7 +120,7 @@ test {
113120 _ = @import("behavior/incomplete_struct_param_tld.zig");
114121 _ = @import("behavior/inttoptr.zig");
115122 _ = @import("behavior/ir_block_deps.zig");
116 _ = @import("behavior/math.zig");
123 _ = @import("behavior/math_stage1.zig");
117124 _ = @import("behavior/maximum_minimum.zig");
118125 _ = @import("behavior/merge_error_sets.zig");
119126 _ = @import("behavior/misc.zig");
......@@ -130,16 +137,15 @@ test {
130137 _ = @import("behavior/saturating_arithmetic.zig");
131138 _ = @import("behavior/shuffle.zig");
132139 _ = @import("behavior/select.zig");
133 _ = @import("behavior/sizeof_and_typeof.zig");
140 _ = @import("behavior/sizeof_and_typeof_stage1.zig");
134141 _ = @import("behavior/slice.zig");
135142 _ = @import("behavior/slice_sentinel_comptime.zig");
136 _ = @import("behavior/struct.zig");
143 _ = @import("behavior/struct_stage1.zig");
137144 _ = @import("behavior/struct_contains_null_ptr_itself.zig");
138145 _ = @import("behavior/struct_contains_slice_of_itself.zig");
139146 _ = @import("behavior/switch.zig");
140147 _ = @import("behavior/switch_prong_err_enum.zig");
141148 _ = @import("behavior/switch_prong_implicit_cast.zig");
142 _ = @import("behavior/this.zig");
143149 _ = @import("behavior/truncate.zig");
144150 _ = @import("behavior/try.zig");
145151 _ = @import("behavior/tuple.zig");
......@@ -148,7 +154,7 @@ test {
148154 _ = @import("behavior/typename.zig");
149155 _ = @import("behavior/undefined.zig");
150156 _ = @import("behavior/underscore.zig");
151 _ = @import("behavior/union.zig");
157 _ = @import("behavior/union_stage1.zig");
152158 _ = @import("behavior/usingnamespace_stage1.zig");
153159 _ = @import("behavior/var_args.zig");
154160 _ = @import("behavior/vector.zig");
......@@ -157,8 +163,7 @@ test {
157163 _ = @import("behavior/wasm.zig");
158164 }
159165 _ = @import("behavior/while.zig");
160 _ = @import("behavior/widening.zig");
161166 _ = @import("behavior/src.zig");
162 _ = @import("behavior/translate_c_macros.zig");
167 _ = @import("behavior/translate_c_macros_stage1.zig");
163168 }
164169}
test/behavior/array.zig+6
......@@ -27,3 +27,9 @@ test "arrays" {
2727fn getArrayLen(a: []const u32) usize {
2828 return a.len;
2929}
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 {
140140 return b[0];
141141}
142142
143test "comptime evalutating function that takes array by value" {
143test "comptime evaluating function that takes array by value" {
144144 const arr = [_]u8{ 0, 1 };
145145 _ = comptime testArrayByValAtComptime(arr);
146146 _ = comptime testArrayByValAtComptime(arr);
test/behavior/atomics.zig+195
......@@ -24,3 +24,198 @@ fn testCmpxchg() !void {
2424 try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null);
2525 try expect(x == 42);
2626}
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" {
170170test "array mult operator" {
171171 try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
172172}
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 {
4040 }
4141};
4242
43test "intialization" {
43test "initialization" {
4444 var t = a.init();
4545 try std.testing.expect(t.foo.len == 0);
4646}
test/behavior/eval.zig+28
......@@ -155,3 +155,31 @@ fn MakeType(comptime T: type) type {
155155 field: T,
156156 };
157157}
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{
8888 .y = true,
8989};
9090
91test "statically initalized array literal" {
91test "statically initialized array literal" {
9292 const y: [4]u8 = st_init_arr_lit_x;
9393 try expect(y[3] == 4);
9494}
......@@ -109,21 +109,6 @@ test "const slice" {
109109 }
110110}
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
127112test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" {
128113 var runtime = [1]i32{3};
129114 comptime var i: usize = 0;
......@@ -276,19 +261,6 @@ fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void {
276261 try expect(ptr1 == ptr2);
277262}
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
292264test "float literal at compile time not lossy" {
293265 try expect(16777216.0 + 1.0 == 16777217.0);
294266 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 {
118118 prealloc_items: [STATIC_SIZE]T,
119119 };
120120}
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 {
2626 };
2727}
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
4529test "generic fn with implicit cast" {
4630 try expect(getFirstByte(u8, &[_]u8{13}) == 13);
4731 try expect(getFirstByte(u16, &[_]u16{
test/behavior/math.zig+33-699
......@@ -6,171 +6,6 @@ const maxInt = std.math.maxInt;
66const minInt = std.math.minInt;
77const 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
1749test "assignment operators" {
17510 var i: u32 = 0;
17611 i += 5;
......@@ -219,108 +54,31 @@ fn assertFalse(b: bool) !void {
21954 try expect(!b);
22055}
22156
222test "const number literal" {
223 const one = 1;
224 const eleven = ten + one;
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));
57test "@clz" {
58 try testClz();
59 comptime try testClz();
27060}
27161
272test "unsigned 64-bit division" {
273 try test_u64_div();
274 comptime try test_u64_div();
275}
276fn test_u64_div() !void {
277 const result = divWithResult(1152921504606846976, 34359738365);
278 try expect(result.quotient == 33554432);
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);
62fn testClz() !void {
63 try expect(testOneClz(u8, 0b10001010) == 0);
64 try expect(testOneClz(u8, 0b00001010) == 4);
65 try expect(testOneClz(u8, 0b00011010) == 3);
66 try expect(testOneClz(u8, 0b00000000) == 8);
67 try expect(testOneClz(u128, 0xffffffffffffffff) == 64);
68 try expect(testOneClz(u128, 0x10000000000000000) == 63);
30069}
30170
302fn testBinaryNot(x: u16) !void {
303 try expect(~x == 0b0101010101010101);
71fn testOneClz(comptime T: type, x: T) u32 {
72 return @clz(T, x);
30473}
30574
306test "small int addition" {
307 var x: u2 = 0;
308 try expect(x == 0);
309
310 x += 1;
311 try expect(x == 1);
312
313 x += 1;
314 try expect(x == 2);
315
316 x += 1;
317 try expect(x == 3);
318
319 var result: @TypeOf(x) = 3;
320 try expect(@addWithOverflow(@TypeOf(x), x, 1, &result));
75test "const number literal" {
76 const one = 1;
77 const eleven = ten + one;
32178
322 try expect(result == 0);
79 try expect(eleven == 11);
32380}
81const ten = 10;
32482
32583test "float equality" {
32684 const x: f64 = 0.012;
......@@ -335,15 +93,6 @@ fn testFloatEqualityImpl(x: f64, y: f64) !void {
33593 try expect(y == y2);
33694}
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
34796test "hex float literal parsing" {
34897 comptime try expect(0x1.0 == 1.0);
34998}
......@@ -353,102 +102,10 @@ test "quad hex float literal parsing in range" {
353102 const b = 0x1.dedafcff354b6ae9758763545432p-9;
354103 const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534;
355104 const d = 0x1.edcbff8ad76ab5bf46463233214fp-435;
356 if (false) {
357 a;
358 b;
359 c;
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();
105 _ = a;
106 _ = b;
107 _ = c;
108 _ = d;
452109}
453110
454111test "underscore separator parsing" {
......@@ -483,66 +140,9 @@ test "hex float literal within range" {
483140 const a = 0x1.0p16383;
484141 const b = 0x0.1p16387;
485142 const c = 0x1.0p-16382;
486 if (false) {
487 a;
488 b;
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();
143 _ = a;
144 _ = b;
145 _ = c;
546146}
547147
548148test "comptime_int addition" {
......@@ -600,11 +200,15 @@ test "xor" {
600200}
601201
602202fn test_xor() !void {
603 try expect(0xFF ^ 0x00 == 0xFF);
604 try expect(0xF0 ^ 0x0F == 0xFF);
605 try expect(0xFF ^ 0xF0 == 0x0F);
606 try expect(0xFF ^ 0x0F == 0xF0);
607 try expect(0xFF ^ 0xFF == 0x00);
203 try testOneXor(0xFF, 0x00, 0xFF);
204 try testOneXor(0xF0, 0x0F, 0xFF);
205 try testOneXor(0xFF, 0xF0, 0x0F);
206 try testOneXor(0xFF, 0x0F, 0xF0);
207 try testOneXor(0xFF, 0xFF, 0x00);
208}
209
210fn testOneXor(a: u8, b: u8, c: u8) !void {
211 try expect(a ^ b == c);
608212}
609213
610214test "comptime_int xor" {
......@@ -620,191 +224,6 @@ test "comptime_int xor" {
620224 }
621225}
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
808227test "comptime_int param and return" {
809228 const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);
810229 try expect(a == 137114567242441932203689521744947848950);
......@@ -816,88 +235,3 @@ test "comptime_int param and return" {
816235fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
817236 return a + b;
818237}
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;
55const mem = std.mem;
66const 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
188test "slicing" {
199 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 {
1111 const a = test_data[0];
1212 const b = test_data[1];
1313 const expected = test_data[2];
14 const actual = switch (op) {
15 .add => @addWithSaturation(a, b),
16 .sub => @subWithSaturation(a, b),
17 .mul => @mulWithSaturation(a, b),
18 .shl => @shlWithSaturation(a, b),
19 };
20 try expectEqual(expected, actual);
14 {
15 const actual = switch (op) {
16 .add => a +| b,
17 .sub => a -| b,
18 .mul => a *| b,
19 .shl => a <<| b,
20 };
21 try expectEqual(expected, actual);
22 }
23 {
24 var actual = a;
25 switch (op) {
26 .add => actual +|= b,
27 .sub => actual -|= b,
28 .mul => actual *|= b,
29 .shl => actual <<|= b,
30 }
31 try expectEqual(expected, actual);
32 }
2133}
2234
23test "@addWithSaturation" {
35test "saturating add" {
2436 const S = struct {
2537 fn doTheTest() !void {
2638 // .{a, b, expected a+b}
......@@ -38,22 +50,16 @@ test "@addWithSaturation" {
3850 try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) });
3951
4052 const u8x3 = std.meta.Vector(3, u8);
41 try expectEqual(u8x3{ 255, 255, 255 }, @addWithSaturation(
42 u8x3{ 255, 254, 1 },
43 u8x3{ 1, 2, 255 },
44 ));
53 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 }));
4554 const i8x3 = std.meta.Vector(3, i8);
46 try expectEqual(i8x3{ 127, 127, 127 }, @addWithSaturation(
47 i8x3{ 127, 126, 1 },
48 i8x3{ 1, 2, 127 },
49 ));
55 try expectEqual(i8x3{ 127, 127, 127 }, (i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 }));
5056 }
5157 };
5258 try S.doTheTest();
5359 comptime try S.doTheTest();
5460}
5561
56test "@subWithSaturation" {
62test "saturating subtraction" {
5763 const S = struct {
5864 fn doTheTest() !void {
5965 // .{a, b, expected a-b}
......@@ -69,17 +75,14 @@ test "@subWithSaturation" {
6975 try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 });
7076
7177 const u8x3 = std.meta.Vector(3, u8);
72 try expectEqual(u8x3{ 0, 0, 0 }, @subWithSaturation(
73 u8x3{ 0, 0, 0 },
74 u8x3{ 255, 255, 255 },
75 ));
78 try expectEqual(u8x3{ 0, 0, 0 }, (u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 }));
7679 }
7780 };
7881 try S.doTheTest();
7982 comptime try S.doTheTest();
8083}
8184
82test "@mulWithSaturation" {
85test "saturating multiplication" {
8386 // TODO: once #9660 has been solved, remove this line
8487 if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;
8588
......@@ -100,10 +103,7 @@ test "@mulWithSaturation" {
100103 try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) });
101104
102105 const u8x3 = std.meta.Vector(3, u8);
103 try expectEqual(u8x3{ 255, 255, 255 }, @mulWithSaturation(
104 u8x3{ 2, 2, 2 },
105 u8x3{ 255, 255, 255 },
106 ));
106 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 }));
107107 }
108108 };
109109
......@@ -111,7 +111,7 @@ test "@mulWithSaturation" {
111111 comptime try S.doTheTest();
112112}
113113
114test "@shlWithSaturation" {
114test "saturating shift-left" {
115115 const S = struct {
116116 fn doTheTest() !void {
117117 // .{a, b, expected a<<b}
......@@ -128,10 +128,7 @@ test "@shlWithSaturation" {
128128 try testSaturatingOp(.shl, u8, .{ 255, 1, 255 });
129129
130130 const u8x3 = std.meta.Vector(3, u8);
131 try expectEqual(u8x3{ 255, 255, 255 }, @shlWithSaturation(
132 u8x3{ 255, 255, 255 },
133 u8x3{ 1, 1, 1 },
134 ));
131 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 }));
135132 }
136133 };
137134 try S.doTheTest();
test/behavior/sizeof_and_typeof.zig-214
......@@ -10,118 +10,6 @@ test "@sizeOf and @TypeOf" {
1010const x: u16 = 13;
1111const 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
12513test "@sizeOf on compile-time types" {
12614 try expect(@sizeOf(comptime_int) == 0);
12715 try expect(@sizeOf(comptime_float) == 0);
......@@ -129,34 +17,6 @@ test "@sizeOf on compile-time types" {
12917 try expect(@sizeOf(@TypeOf(type)) == 0);
13018}
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
16020test "@TypeOf() with multiple arguments" {
16121 {
16222 var var_1: u32 = undefined;
......@@ -180,19 +40,6 @@ test "@TypeOf() with multiple arguments" {
18040 }
18141}
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
19643fn fn1(alpha: bool) void {
19744 const n: usize = 7;
19845 _ = if (alpha) n else @sizeOf(usize);
......@@ -201,64 +48,3 @@ fn fn1(alpha: bool) void {
20148test "lazy @sizeOf result is checked for definedness" {
20249 _ = fn1;
20350}
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;
55const expectEqual = std.testing.expectEqual;
66const expectEqualSlices = std.testing.expectEqualSlices;
77const maxInt = std.math.maxInt;
8
89const StructWithNoFields = struct {
910 fn add(a: i32, b: i32) i32 {
1011 return a + b;
1112 }
1213};
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
2515test "call struct static method" {
2616 const result = StructWithNoFields.add(3, 4);
2717 try expect(result == 7);
2818}
2919
30test "return empty struct instance" {
31 _ = returnEmptyStructInstance();
32}
33fn returnEmptyStructInstance() StructWithNoFields {
34 return empty_global_instance;
35}
36
3720const should_be_11 = StructWithNoFields.add(5, 6);
3821
3922test "invoke static method in global scope" {
4023 try expect(should_be_11 == 11);
4124}
4225
43test "void struct fields" {
44 const foo = VoidStructFieldsFoo{
45 .a = void{},
46 .b = 1,
47 .c = void{},
48 };
49 try expect(foo.b == 1);
50 try expect(@sizeOf(VoidStructFieldsFoo) == 4);
26const empty_global_instance = StructWithNoFields{};
27
28test "return empty struct instance" {
29 _ = returnEmptyStructInstance();
30}
31fn returnEmptyStructInstance() StructWithNoFields {
32 return empty_global_instance;
5133}
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};
5840test "structs" {
5941 var foo: StructFoo = undefined;
6042 @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo));
......@@ -64,11 +46,6 @@ test "structs" {
6446 testMutation(&foo);
6547 try expect(foo.c == 100);
6648}
67const StructFoo = struct {
68 a: i32,
69 b: bool,
70 c: f32,
71};
7249fn testFoo(foo: StructFoo) !void {
7350 try expect(foo.b);
7451}
......@@ -76,28 +53,6 @@ fn testMutation(foo: *StructFoo) void {
7653 foo.c = 100;
7754}
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
10156test "struct byval assign" {
10257 var foo1: StructFoo = undefined;
10358 var foo2: StructFoo = undefined;
......@@ -109,34 +64,20 @@ test "struct byval assign" {
10964 try expect(foo2.a == 1234);
11065}
11166
112fn structInitializer() void {
113 const val = Val{ .x = 42 };
114 try expect(val.x == 42);
115}
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 }
67const Node = struct {
68 val: Val,
69 next: *Node,
70};
12571
126 fn callStructField(foo: Foo) i32 {
127 return foo.ptr();
128 }
129 };
72const Val = struct {
73 x: i32,
74};
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);
13279}
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}
14081const MemberFnTestFoo = struct {
14182 x: i32,
14283 fn member(foo: MemberFnTestFoo) i32 {
......@@ -150,804 +91,41 @@ test "call member function directly" {
15091 try expect(result == 1234);
15192}
15293
153test "member functions" {
154 const r = MemberFnRand{ .seed = 1234 };
155 try expect(r.getSeed() == 1234);
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};
94test "struct point to self" {
95 var root: Node = undefined;
96 root.val.x = 1;
37197
372test "aligned array of packed struct" {
373 comptime {
374 try expect(@sizeOf(FooStructAligned) == 2);
375 try expect(@sizeOf(FooArrayOfAligned) == 2 * 2);
376 }
98 var node: Node = undefined;
99 node.next = &root;
100 node.val.x = 2;
377101
378 var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned);
379 const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0];
102 root.next = &node;
380103
381 try expect(ptr.a[0].a == 0xbb);
382 try expect(ptr.a[0].b == 0xbb);
383 try expect(ptr.a[1].a == 0xbb);
384 try expect(ptr.a[1].b == 0xbb);
104 try expect(node.next.next.next.val.x == 1);
385105}
386106
387test "runtime struct initialization of bitfield" {
388 const s1 = Nibbles{
389 .x = x1,
390 .y = x1,
391 };
392 const s2 = Nibbles{
393 .x = @intCast(u4, x2),
394 .y = @intCast(u4, x2),
107test "void struct fields" {
108 const foo = VoidStructFieldsFoo{
109 .a = void{},
110 .b = 1,
111 .c = void{},
395112 };
396
397 try expect(s1.x == x1);
398 try expect(s1.y == x1);
399 try expect(s2.x == @intCast(u4, x2));
400 try expect(s2.y == @intCast(u4, x2));
401}
402
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);
113 try expect(foo.b == 1);
114 try expect(@sizeOf(VoidStructFieldsFoo) == 4);
442115}
443
444const Expr = union(enum) {
445 Literal: u8,
446 Question: *Expr,
116const VoidStructFieldsFoo = struct {
117 a: void,
118 b: i32,
119 c: void,
447120};
448121
449fn alloc(comptime T: type) []T {
450 return &[_]T{};
451}
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);
122test "member functions" {
123 const r = MemberFnRand{ .seed = 1234 };
124 try expect(r.getSeed() == 1234);
529125}
530
531const S0 = struct {
532 bar: S1,
533
534 pub const S1 = struct {
535 value: u8,
536 };
537
538 fn init() @This() {
539 return S0{ .bar = S1{ .value = 123 } };
126const MemberFnRand = struct {
127 seed: u32,
128 pub fn getSeed(r: *const MemberFnRand) u32 {
129 return r.seed;
540130 }
541131};
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 {
6565}
6666
6767test "switch statement" {
68 try nonConstSwitch(SwitchStatmentFoo.C);
68 try nonConstSwitch(SwitchStatementFoo.C);
6969}
70fn nonConstSwitch(foo: SwitchStatmentFoo) !void {
70fn nonConstSwitch(foo: SwitchStatementFoo) !void {
7171 const val = switch (foo) {
72 SwitchStatmentFoo.A => @as(i32, 1),
73 SwitchStatmentFoo.B => 2,
74 SwitchStatmentFoo.C => 3,
75 SwitchStatmentFoo.D => 4,
72 SwitchStatementFoo.A => @as(i32, 1),
73 SwitchStatementFoo.B => 2,
74 SwitchStatementFoo.C => 3,
75 SwitchStatementFoo.D => 4,
7676 };
7777 try expect(val == 3);
7878}
79const SwitchStatmentFoo = enum {
79const SwitchStatementFoo = enum {
8080 A,
8181 B,
8282 C,
test/behavior/this.zig+4-5
......@@ -24,11 +24,10 @@ test "this refer to module call private fn" {
2424}
2525
2626test "this refer to container" {
27 var pt = Point(i32){
28 .x = 12,
29 .y = 34,
30 };
31 pt.addOne();
27 var pt: Point(i32) = undefined;
28 pt.x = 12;
29 pt.y = 34;
30 Point(i32).addOne(&pt);
3231 try expect(pt.x == 13);
3332 try expect(pt.y == 35);
3433}
test/behavior/translate_c_macros.zig-22
......@@ -3,28 +3,6 @@ const expectEqual = @import("std").testing.expectEqual;
33
44const 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
286test "casting to void with a macro" {
297 h.IGNORE_ME_1(42);
308 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" {
137137 .is_volatile = false,
138138 .is_allowzero = false,
139139 .alignment = 8,
140 .address_space = .generic,
140141 .child = *i32,
141142 .sentinel = null,
142143 },
test/behavior/union.zig-783
......@@ -3,42 +3,6 @@ const expect = std.testing.expect;
33const expectEqual = std.testing.expectEqual;
44const 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
426const Foo = union {
437 float: f64,
448 int: i32,
......@@ -51,16 +15,6 @@ test "basic unions" {
5115 try expect(foo.float == 12.34);
5216}
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
6418test "init union with runtime value" {
6519 var foo: Foo = undefined;
6620
......@@ -78,740 +32,3 @@ fn setFloat(foo: *Foo, x: f64) void {
7832fn setInt(foo: *Foo, x: i32) void {
7933 foo.* = Foo{ .int = x };
8034}
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" {
116116 _ = vec;
117117}
118118
119test "vector casts of sizes not divisable by 8" {
119test "vector casts of sizes not divisible by 8" {
120120 const S = struct {
121121 fn doTheTest() !void {
122122 {
test/behavior/widening.zig+14-2
......@@ -19,6 +19,12 @@ test "implicit unsigned integer to signed integer" {
1919}
2020
2121test "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 }
2228 var a: f16 = 12.34;
2329 var b: f32 = a;
2430 var c: f64 = b;
......@@ -29,9 +35,15 @@ test "float widening" {
2935}
3036
3137test "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 }
3244 // TODO https://github.com/ziglang/zig/issues/3282
33 if (@import("builtin").target.cpu.arch == .aarch64) return error.SkipZigTest;
34 if (@import("builtin").target.cpu.arch == .powerpc64le) return error.SkipZigTest;
45 if (@import("builtin").stage2_arch == .aarch64) return error.SkipZigTest;
46 if (@import("builtin").stage2_arch == .powerpc64le) return error.SkipZigTest;
3547
3648 var x: f16 = 12.34;
3749 var y: f128 = x;
test/cases.zig+13-1
......@@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void {
2626 var case = ctx.exe("hello world with updates", linux_x64);
2727
2828 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'",
3030 });
3131
3232 // Incorrect return type
......@@ -1807,4 +1807,16 @@ pub fn addCases(ctx: *TestContext) !void {
18071807 \\}
18081808 , "");
18091809 }
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 }
18101822}
test/compile_errors.zig+26-8
......@@ -683,7 +683,7 @@ pub fn addCases(ctx: *TestContext) !void {
683683 \\ _ = u;
684684 \\}
685685 , &[_][]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",
687687 "tmp.zig:17:16: error: no tag by value 15",
688688 });
689689
......@@ -711,6 +711,7 @@ pub fn addCases(ctx: *TestContext) !void {
711711 \\ .is_const = false,
712712 \\ .is_volatile = false,
713713 \\ .alignment = 1,
714 \\ .address_space = .generic,
714715 \\ .child = u8,
715716 \\ .is_allowzero = false,
716717 \\ .sentinel = 0,
......@@ -720,6 +721,23 @@ pub fn addCases(ctx: *TestContext) !void {
720721 "tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers",
721722 });
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
723741 ctx.testErrStage1("helpful return type error message",
724742 \\export fn foo() u32 {
725743 \\ return error.Ohno;
......@@ -6127,9 +6145,9 @@ pub fn addCases(ctx: *TestContext) !void {
61276145 });
61286146
61296147 ctx.objErrStage1("endless loop in function evaluation",
6130 \\const seventh_fib_number = fibbonaci(7);
6131 \\fn fibbonaci(x: i32) i32 {
6132 \\ return fibbonaci(x - 1) + fibbonaci(x - 2);
6148 \\const seventh_fib_number = fibonacci(7);
6149 \\fn fibonacci(x: i32) i32 {
6150 \\ return fibonacci(x - 1) + fibonacci(x - 2);
61336151 \\}
61346152 \\
61356153 \\export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); }
......@@ -6757,7 +6775,7 @@ pub fn addCases(ctx: *TestContext) !void {
67576775 "tmp.zig:2:5: error: expression value is ignored",
67586776 });
67596777
6760 ctx.objErrStage1("ignored defered statement value",
6778 ctx.objErrStage1("ignored deferred statement value",
67616779 \\export fn foo() void {
67626780 \\ defer {1;}
67636781 \\}
......@@ -6765,7 +6783,7 @@ pub fn addCases(ctx: *TestContext) !void {
67656783 "tmp.zig:2:12: error: expression value is ignored",
67666784 });
67676785
6768 ctx.objErrStage1("ignored defered function call",
6786 ctx.objErrStage1("ignored deferred function call",
67696787 \\export fn foo() void {
67706788 \\ defer bar();
67716789 \\}
......@@ -8841,9 +8859,9 @@ pub fn addCases(ctx: *TestContext) !void {
88418859 "tmp.zig:3:12: note: crosses namespace boundary here",
88428860 });
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",
88458863 \\pub fn main() !void {
8846 \\ _ = @addWithSaturation(@as(f32, 1.0), @as(f32, 1.0));
8864 \\ _ = @as(f32, 1.0) +| @as(f32, 1.0);
88478865 \\}
88488866 , &[_][]const u8{
88498867 "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 {
2424 \\}
2525 , "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",
2828 \\void bar(void);
2929 \\void baz(void);
3030 \\struct foo { int x; };
......@@ -394,7 +394,7 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
394394 \\}
395395 , "");
396396
397 cases.add("ensure array casts outisde +=",
397 cases.add("ensure array casts outside +=",
398398 \\#include <stdlib.h>
399399 \\static int hash_binary(int k)
400400 \\{
test/stage2/darwin.zig+3-3
......@@ -12,9 +12,9 @@ pub fn addCases(ctx: *TestContext) !void {
1212 .os_tag = .macos,
1313 };
1414 {
15 var case = ctx.exe("hello world with updates", target);
15 var case = ctx.exe("darwin hello world with updates", target);
1616 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'",
1818 });
1919
2020 // Incorrect return type
......@@ -118,7 +118,7 @@ pub fn addCases(ctx: *TestContext) !void {
118118 {
119119 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 relocated
121 // This test case also covers an infrequent scenario where the string table *may* be relocated
122122 // into the position preceeding the symbol table which results in a dyld error.
123123 case.addCompareOutput(
124124 \\extern fn exit(usize) noreturn;
test/stage2/llvm.zig+180
......@@ -242,4 +242,184 @@ pub fn addCases(ctx: *TestContext) !void {
242242 \\}
243243 , "");
244244 }
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 }
245425}
test/standalone.zig+4
......@@ -28,6 +28,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
2828 cases.addBuildFile("test/standalone/empty_env/build.zig", .{});
2929 cases.addBuildFile("test/standalone/issue_7030/build.zig", .{});
3030 cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{});
31 cases.addBuildFile("test/standalone/issue_9812/build.zig", .{});
3132 if (std.Target.current.os.tag != .wasi) {
3233 cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{});
3334 }
......@@ -36,6 +37,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
3637 }
3738 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 }
3943 // Try to build and run a PIE executable.
4044 if (std.Target.current.os.tag == .linux) {
4145 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
455455 const cases = b.allocator.create(TranslateCContext) catch unreachable;
456456 cases.* = TranslateCContext{
457457 .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"),
459459 .test_index = 0,
460460 .test_filter = test_filter,
461461 };
test/translate_c.zig+2-2
......@@ -3188,7 +3188,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
31883188 \\}
31893189 });
31903190
3191 cases.add("macro comparisions",
3191 cases.add("macro comparisons",
31923192 \\#define MIN(a, b) ((b) < (a) ? (b) : (a))
31933193 \\#define MAX(a, b) ((b) > (a) ? (b) : (a))
31943194 , &[_][]const u8{
......@@ -3443,7 +3443,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
34433443 });
34443444 }
34453445
3446 cases.add("unnamed fields have predictabile names",
3446 cases.add("unnamed fields have predictable names",
34473447 \\struct a {
34483448 \\ struct {};
34493449 \\};
tools/update_clang_options.zig+4
......@@ -376,6 +376,10 @@ const known_options = [_]KnownOpt{
376376 .name = "mexec-model",
377377 .ident = "exec_model",
378378 },
379 .{
380 .name = "emit-llvm",
381 .ident = "emit_llvm",
382 },
379383};
380384
381385const blacklisted_options = [_][]const u8{};