| author | |
| committer | |
| log | d399f8a489dd2ae62fae9b805778bfdc697a969b |
| tree | 60df6836102786a1e665dfc92d815a27d6f8c352 |
| parent | b5b634e4e8a2a1fe32fba50ccd175257b4213936 |
| parent | f6c934677315665c140151b8dd28a56f948205e2 |
122 files changed, 15962 insertions(+), 3861 deletions(-)
CMakeLists.txt+9-8| ... | @@ -434,13 +434,12 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -434,13 +434,12 @@ set(ZIG_STAGE2_SOURCES |
| 434 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log10.zig" | 434 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log10.zig" |
| 435 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log2.zig" | 435 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log2.zig" |
| 436 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/modti3.zig" | 436 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/modti3.zig" |
| 437 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulXi3.zig" | ||
| 437 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/muldf3.zig" | 438 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/muldf3.zig" |
| 438 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/muldi3.zig" | ||
| 439 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulf3.zig" | 439 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulf3.zig" |
| 440 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulo.zig" | 440 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulo.zig" |
| 441 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulsf3.zig" | 441 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulsf3.zig" |
| 442 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multf3.zig" | 442 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multf3.zig" |
| 443 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multi3.zig" | ||
| 444 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulxf3.zig" | 443 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulxf3.zig" |
| 445 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXi2.zig" | 444 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXi2.zig" |
| 446 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negv.zig" | 445 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negv.zig" |
| ... | @@ -569,6 +568,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -569,6 +568,7 @@ set(ZIG_STAGE2_SOURCES |
| 569 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" | 568 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" |
| 570 | "${CMAKE_SOURCE_DIR}/src/codegen.zig" | 569 | "${CMAKE_SOURCE_DIR}/src/codegen.zig" |
| 571 | "${CMAKE_SOURCE_DIR}/src/codegen/c.zig" | 570 | "${CMAKE_SOURCE_DIR}/src/codegen/c.zig" |
| 571 | "${CMAKE_SOURCE_DIR}/src/codegen/c/type.zig" | ||
| 572 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig" | 572 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig" |
| 573 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" | 573 | "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" |
| 574 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" | 574 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" |
| ... | @@ -612,7 +612,6 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -612,7 +612,6 @@ set(ZIG_STAGE2_SOURCES |
| 612 | "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" | 612 | "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" |
| 613 | "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig" | 613 | "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig" |
| 614 | "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig" | 614 | "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig" |
| 615 | "${CMAKE_SOURCE_DIR}/src/link/tapi/parse/test.zig" | ||
| 616 | "${CMAKE_SOURCE_DIR}/src/link/tapi/yaml.zig" | 615 | "${CMAKE_SOURCE_DIR}/src/link/tapi/yaml.zig" |
| 617 | "${CMAKE_SOURCE_DIR}/src/main.zig" | 616 | "${CMAKE_SOURCE_DIR}/src/main.zig" |
| 618 | "${CMAKE_SOURCE_DIR}/src/mingw.zig" | 617 | "${CMAKE_SOURCE_DIR}/src/mingw.zig" |
| ... | @@ -748,10 +747,11 @@ set(BUILD_ZIG2_ARGS | ... | @@ -748,10 +747,11 @@ set(BUILD_ZIG2_ARGS |
| 748 | build-exe src/main.zig -ofmt=c -lc | 747 | build-exe src/main.zig -ofmt=c -lc |
| 749 | -OReleaseSmall | 748 | -OReleaseSmall |
| 750 | --name zig2 -femit-bin="${ZIG2_C_SOURCE}" | 749 | --name zig2 -femit-bin="${ZIG2_C_SOURCE}" |
| 751 | --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end | 750 | --mod "build_options::${ZIG_CONFIG_ZIG_OUT}" |
| 751 | --deps build_options | ||
| 752 | -target "${HOST_TARGET_TRIPLE}" | 752 | -target "${HOST_TARGET_TRIPLE}" |
| 753 | ) | 753 | ) |
| 754 | 754 | ||
| 755 | add_custom_command( | 755 | add_custom_command( |
| 756 | OUTPUT "${ZIG2_C_SOURCE}" | 756 | OUTPUT "${ZIG2_C_SOURCE}" |
| 757 | COMMAND zig1 ${BUILD_ZIG2_ARGS} | 757 | COMMAND zig1 ${BUILD_ZIG2_ARGS} |
| ... | @@ -765,10 +765,11 @@ set(BUILD_COMPILER_RT_ARGS | ... | @@ -765,10 +765,11 @@ set(BUILD_COMPILER_RT_ARGS |
| 765 | build-obj lib/compiler_rt.zig -ofmt=c | 765 | build-obj lib/compiler_rt.zig -ofmt=c |
| 766 | -OReleaseSmall | 766 | -OReleaseSmall |
| 767 | --name compiler_rt -femit-bin="${ZIG_COMPILER_RT_C_SOURCE}" | 767 | --name compiler_rt -femit-bin="${ZIG_COMPILER_RT_C_SOURCE}" |
| 768 | --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end | 768 | --mod "build_options::${ZIG_CONFIG_ZIG_OUT}" |
| 769 | --deps build_options | ||
| 769 | -target "${HOST_TARGET_TRIPLE}" | 770 | -target "${HOST_TARGET_TRIPLE}" |
| 770 | ) | 771 | ) |
| 771 | 772 | ||
| 772 | add_custom_command( | 773 | add_custom_command( |
| 773 | OUTPUT "${ZIG_COMPILER_RT_C_SOURCE}" | 774 | OUTPUT "${ZIG_COMPILER_RT_C_SOURCE}" |
| 774 | COMMAND zig1 ${BUILD_COMPILER_RT_ARGS} | 775 | COMMAND zig1 ${BUILD_COMPILER_RT_ARGS} |
| ... | @@ -782,7 +783,7 @@ set_target_properties(zig2 PROPERTIES | ... | @@ -782,7 +783,7 @@ set_target_properties(zig2 PROPERTIES |
| 782 | COMPILE_FLAGS ${ZIG2_COMPILE_FLAGS} | 783 | COMPILE_FLAGS ${ZIG2_COMPILE_FLAGS} |
| 783 | LINK_FLAGS ${ZIG2_LINK_FLAGS} | 784 | LINK_FLAGS ${ZIG2_LINK_FLAGS} |
| 784 | ) | 785 | ) |
| 785 | target_include_directories(zig2 PUBLIC "${CMAKE_SOURCE_DIR}/lib") | 786 | target_include_directories(zig2 PUBLIC "${CMAKE_SOURCE_DIR}/stage1") |
| 786 | target_link_libraries(zig2 LINK_PUBLIC zigcpp) | 787 | target_link_libraries(zig2 LINK_PUBLIC zigcpp) |
| 787 | 788 | ||
| 788 | if(MSVC) | 789 | if(MSVC) |
build.zig+7| ... | @@ -118,8 +118,11 @@ pub fn build(b: *std.Build) !void { | ... | @@ -118,8 +118,11 @@ pub fn build(b: *std.Build) !void { |
| 118 | ".gz", | 118 | ".gz", |
| 119 | ".z.0", | 119 | ".z.0", |
| 120 | ".z.9", | 120 | ".z.9", |
| 121 | ".zstd.3", | ||
| 122 | ".zstd.19", | ||
| 121 | "rfc1951.txt", | 123 | "rfc1951.txt", |
| 122 | "rfc1952.txt", | 124 | "rfc1952.txt", |
| 125 | "rfc8478.txt", | ||
| 123 | // exclude files from lib/std/compress/deflate/testdata | 126 | // exclude files from lib/std/compress/deflate/testdata |
| 124 | ".expect", | 127 | ".expect", |
| 125 | ".expect-noinput", | 128 | ".expect-noinput", |
| ... | @@ -513,8 +516,12 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { | ... | @@ -513,8 +516,12 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { |
| 513 | run_opt.addArg("-o"); | 516 | run_opt.addArg("-o"); |
| 514 | run_opt.addFileSourceArg(.{ .path = "stage1/zig1.wasm" }); | 517 | run_opt.addFileSourceArg(.{ .path = "stage1/zig1.wasm" }); |
| 515 | 518 | ||
| 519 | const copy_zig_h = b.addWriteFiles(); | ||
| 520 | copy_zig_h.addCopyFileToSource(.{ .path = "lib/zig.h" }, "stage1/zig.h"); | ||
| 521 | |||
| 516 | const update_zig1_step = b.step("update-zig1", "Update stage1/zig1.wasm"); | 522 | const update_zig1_step = b.step("update-zig1", "Update stage1/zig1.wasm"); |
| 517 | update_zig1_step.dependOn(&run_opt.step); | 523 | update_zig1_step.dependOn(&run_opt.step); |
| 524 | update_zig1_step.dependOn(&copy_zig_h.step); | ||
| 518 | } | 525 | } |
| 519 | 526 | ||
| 520 | fn addCompilerStep( | 527 | fn addCompilerStep( |
ci/x86_64-windows-debug.ps1+2-1| ... | @@ -87,7 +87,8 @@ CheckLastExitCode | ... | @@ -87,7 +87,8 @@ CheckLastExitCode |
| 87 | -OReleaseSmall ` | 87 | -OReleaseSmall ` |
| 88 | --name compiler_rt ` | 88 | --name compiler_rt ` |
| 89 | -femit-bin="compiler_rt-x86_64-windows-msvc.c" ` | 89 | -femit-bin="compiler_rt-x86_64-windows-msvc.c" ` |
| 90 | --pkg-begin build_options config.zig --pkg-end ` | 90 | --mod build_options::config.zig ` |
| 91 | --deps build_options ` | ||
| 91 | -target x86_64-windows-msvc | 92 | -target x86_64-windows-msvc |
| 92 | CheckLastExitCode | 93 | CheckLastExitCode |
| 93 | 94 |
ci/x86_64-windows-release.ps1+2-1| ... | @@ -87,7 +87,8 @@ CheckLastExitCode | ... | @@ -87,7 +87,8 @@ CheckLastExitCode |
| 87 | -OReleaseSmall ` | 87 | -OReleaseSmall ` |
| 88 | --name compiler_rt ` | 88 | --name compiler_rt ` |
| 89 | -femit-bin="compiler_rt-x86_64-windows-msvc.c" ` | 89 | -femit-bin="compiler_rt-x86_64-windows-msvc.c" ` |
| 90 | --pkg-begin build_options config.zig --pkg-end ` | 90 | --mod build_options::config.zig ` |
| 91 | --deps build_options ` | ||
| 91 | -target x86_64-windows-msvc | 92 | -target x86_64-windows-msvc |
| 92 | CheckLastExitCode | 93 | CheckLastExitCode |
| 93 | 94 |
lib/compiler_rt.zig+142-133| ... | @@ -3,64 +3,34 @@ const builtin = @import("builtin"); | ... | @@ -3,64 +3,34 @@ const builtin = @import("builtin"); |
| 3 | pub const panic = @import("compiler_rt/common.zig").panic; | 3 | pub const panic = @import("compiler_rt/common.zig").panic; |
| 4 | 4 | ||
| 5 | comptime { | 5 | comptime { |
| 6 | _ = @import("compiler_rt/addf3.zig"); | 6 | // Integer routines |
| 7 | _ = @import("compiler_rt/addhf3.zig"); | 7 | _ = @import("compiler_rt/count0bits.zig"); |
| 8 | _ = @import("compiler_rt/addsf3.zig"); | 8 | _ = @import("compiler_rt/parity.zig"); |
| 9 | _ = @import("compiler_rt/adddf3.zig"); | 9 | _ = @import("compiler_rt/popcount.zig"); |
| 10 | _ = @import("compiler_rt/addtf3.zig"); | 10 | _ = @import("compiler_rt/bswap.zig"); |
| 11 | _ = @import("compiler_rt/addxf3.zig"); | 11 | _ = @import("compiler_rt/cmp.zig"); |
| 12 | |||
| 13 | _ = @import("compiler_rt/subhf3.zig"); | ||
| 14 | _ = @import("compiler_rt/subsf3.zig"); | ||
| 15 | _ = @import("compiler_rt/subdf3.zig"); | ||
| 16 | _ = @import("compiler_rt/subtf3.zig"); | ||
| 17 | _ = @import("compiler_rt/subxf3.zig"); | ||
| 18 | |||
| 19 | _ = @import("compiler_rt/mulf3.zig"); | ||
| 20 | _ = @import("compiler_rt/mulhf3.zig"); | ||
| 21 | _ = @import("compiler_rt/mulsf3.zig"); | ||
| 22 | _ = @import("compiler_rt/muldf3.zig"); | ||
| 23 | _ = @import("compiler_rt/multf3.zig"); | ||
| 24 | _ = @import("compiler_rt/mulxf3.zig"); | ||
| 25 | |||
| 26 | _ = @import("compiler_rt/powiXf2.zig"); | ||
| 27 | _ = @import("compiler_rt/mulc3.zig"); | ||
| 28 | _ = @import("compiler_rt/mulhc3.zig"); | ||
| 29 | _ = @import("compiler_rt/mulsc3.zig"); | ||
| 30 | _ = @import("compiler_rt/muldc3.zig"); | ||
| 31 | _ = @import("compiler_rt/mulxc3.zig"); | ||
| 32 | _ = @import("compiler_rt/multc3.zig"); | ||
| 33 | 12 | ||
| 34 | _ = @import("compiler_rt/divc3.zig"); | 13 | _ = @import("compiler_rt/shift.zig"); |
| 35 | _ = @import("compiler_rt/divhc3.zig"); | 14 | _ = @import("compiler_rt/negXi2.zig"); |
| 36 | _ = @import("compiler_rt/divsc3.zig"); | 15 | _ = @import("compiler_rt/int.zig"); |
| 37 | _ = @import("compiler_rt/divdc3.zig"); | 16 | _ = @import("compiler_rt/mulXi3.zig"); |
| 38 | _ = @import("compiler_rt/divxc3.zig"); | 17 | _ = @import("compiler_rt/divti3.zig"); |
| 39 | _ = @import("compiler_rt/divtc3.zig"); | 18 | _ = @import("compiler_rt/udivti3.zig"); |
| 19 | _ = @import("compiler_rt/modti3.zig"); | ||
| 20 | _ = @import("compiler_rt/umodti3.zig"); | ||
| 40 | 21 | ||
| 41 | _ = @import("compiler_rt/neghf2.zig"); | 22 | _ = @import("compiler_rt/absv.zig"); |
| 42 | _ = @import("compiler_rt/negsf2.zig"); | 23 | _ = @import("compiler_rt/absvsi2.zig"); |
| 43 | _ = @import("compiler_rt/negdf2.zig"); | 24 | _ = @import("compiler_rt/absvdi2.zig"); |
| 44 | _ = @import("compiler_rt/negtf2.zig"); | 25 | _ = @import("compiler_rt/absvti2.zig"); |
| 45 | _ = @import("compiler_rt/negxf2.zig"); | 26 | _ = @import("compiler_rt/negv.zig"); |
| 46 | 27 | ||
| 47 | _ = @import("compiler_rt/comparef.zig"); | 28 | _ = @import("compiler_rt/addo.zig"); |
| 48 | _ = @import("compiler_rt/cmphf2.zig"); | 29 | _ = @import("compiler_rt/subo.zig"); |
| 49 | _ = @import("compiler_rt/cmpsf2.zig"); | 30 | _ = @import("compiler_rt/mulo.zig"); |
| 50 | _ = @import("compiler_rt/cmpdf2.zig"); | ||
| 51 | _ = @import("compiler_rt/cmptf2.zig"); | ||
| 52 | _ = @import("compiler_rt/cmpxf2.zig"); | ||
| 53 | _ = @import("compiler_rt/gehf2.zig"); | ||
| 54 | _ = @import("compiler_rt/gesf2.zig"); | ||
| 55 | _ = @import("compiler_rt/gedf2.zig"); | ||
| 56 | _ = @import("compiler_rt/gexf2.zig"); | ||
| 57 | _ = @import("compiler_rt/getf2.zig"); | ||
| 58 | _ = @import("compiler_rt/unordhf2.zig"); | ||
| 59 | _ = @import("compiler_rt/unordsf2.zig"); | ||
| 60 | _ = @import("compiler_rt/unorddf2.zig"); | ||
| 61 | _ = @import("compiler_rt/unordxf2.zig"); | ||
| 62 | _ = @import("compiler_rt/unordtf2.zig"); | ||
| 63 | 31 | ||
| 32 | // Float routines | ||
| 33 | // conversion | ||
| 64 | _ = @import("compiler_rt/extendf.zig"); | 34 | _ = @import("compiler_rt/extendf.zig"); |
| 65 | _ = @import("compiler_rt/extendhfsf2.zig"); | 35 | _ = @import("compiler_rt/extendhfsf2.zig"); |
| 66 | _ = @import("compiler_rt/extendhfdf2.zig"); | 36 | _ = @import("compiler_rt/extendhfdf2.zig"); |
| ... | @@ -85,37 +55,37 @@ comptime { | ... | @@ -85,37 +55,37 @@ comptime { |
| 85 | _ = @import("compiler_rt/trunctfdf2.zig"); | 55 | _ = @import("compiler_rt/trunctfdf2.zig"); |
| 86 | _ = @import("compiler_rt/trunctfxf2.zig"); | 56 | _ = @import("compiler_rt/trunctfxf2.zig"); |
| 87 | 57 | ||
| 88 | _ = @import("compiler_rt/divhf3.zig"); | 58 | _ = @import("compiler_rt/float_to_int.zig"); |
| 89 | _ = @import("compiler_rt/divsf3.zig"); | 59 | _ = @import("compiler_rt/fixhfsi.zig"); |
| 90 | _ = @import("compiler_rt/divdf3.zig"); | 60 | _ = @import("compiler_rt/fixhfdi.zig"); |
| 91 | _ = @import("compiler_rt/divxf3.zig"); | 61 | _ = @import("compiler_rt/fixhfti.zig"); |
| 92 | _ = @import("compiler_rt/divtf3.zig"); | 62 | _ = @import("compiler_rt/fixsfsi.zig"); |
| 93 | _ = @import("compiler_rt/sin.zig"); | 63 | _ = @import("compiler_rt/fixsfdi.zig"); |
| 94 | _ = @import("compiler_rt/cos.zig"); | 64 | _ = @import("compiler_rt/fixsfti.zig"); |
| 95 | _ = @import("compiler_rt/sincos.zig"); | 65 | _ = @import("compiler_rt/fixdfsi.zig"); |
| 96 | _ = @import("compiler_rt/ceil.zig"); | 66 | _ = @import("compiler_rt/fixdfdi.zig"); |
| 97 | _ = @import("compiler_rt/exp.zig"); | 67 | _ = @import("compiler_rt/fixdfti.zig"); |
| 98 | _ = @import("compiler_rt/exp2.zig"); | 68 | _ = @import("compiler_rt/fixtfsi.zig"); |
| 99 | _ = @import("compiler_rt/fabs.zig"); | 69 | _ = @import("compiler_rt/fixtfdi.zig"); |
| 100 | _ = @import("compiler_rt/floor.zig"); | 70 | _ = @import("compiler_rt/fixtfti.zig"); |
| 101 | _ = @import("compiler_rt/fma.zig"); | 71 | _ = @import("compiler_rt/fixxfsi.zig"); |
| 102 | _ = @import("compiler_rt/fmax.zig"); | 72 | _ = @import("compiler_rt/fixxfdi.zig"); |
| 103 | _ = @import("compiler_rt/fmin.zig"); | 73 | _ = @import("compiler_rt/fixxfti.zig"); |
| 104 | _ = @import("compiler_rt/fmod.zig"); | 74 | _ = @import("compiler_rt/fixunshfsi.zig"); |
| 105 | _ = @import("compiler_rt/log.zig"); | 75 | _ = @import("compiler_rt/fixunshfdi.zig"); |
| 106 | _ = @import("compiler_rt/log10.zig"); | 76 | _ = @import("compiler_rt/fixunshfti.zig"); |
| 107 | _ = @import("compiler_rt/log2.zig"); | 77 | _ = @import("compiler_rt/fixunssfsi.zig"); |
| 108 | _ = @import("compiler_rt/round.zig"); | 78 | _ = @import("compiler_rt/fixunssfdi.zig"); |
| 109 | _ = @import("compiler_rt/sqrt.zig"); | 79 | _ = @import("compiler_rt/fixunssfti.zig"); |
| 110 | _ = @import("compiler_rt/tan.zig"); | 80 | _ = @import("compiler_rt/fixunsdfsi.zig"); |
| 111 | _ = @import("compiler_rt/trunc.zig"); | 81 | _ = @import("compiler_rt/fixunsdfdi.zig"); |
| 112 | _ = @import("compiler_rt/divti3.zig"); | 82 | _ = @import("compiler_rt/fixunsdfti.zig"); |
| 113 | _ = @import("compiler_rt/modti3.zig"); | 83 | _ = @import("compiler_rt/fixunstfsi.zig"); |
| 114 | _ = @import("compiler_rt/multi3.zig"); | 84 | _ = @import("compiler_rt/fixunstfdi.zig"); |
| 115 | _ = @import("compiler_rt/udivti3.zig"); | 85 | _ = @import("compiler_rt/fixunstfti.zig"); |
| 116 | _ = @import("compiler_rt/udivmodei4.zig"); | 86 | _ = @import("compiler_rt/fixunsxfsi.zig"); |
| 117 | _ = @import("compiler_rt/udivmodti4.zig"); | 87 | _ = @import("compiler_rt/fixunsxfdi.zig"); |
| 118 | _ = @import("compiler_rt/umodti3.zig"); | 88 | _ = @import("compiler_rt/fixunsxfti.zig"); |
| 119 | 89 | ||
| 120 | _ = @import("compiler_rt/int_to_float.zig"); | 90 | _ = @import("compiler_rt/int_to_float.zig"); |
| 121 | _ = @import("compiler_rt/floatsihf.zig"); | 91 | _ = @import("compiler_rt/floatsihf.zig"); |
| ... | @@ -149,60 +119,99 @@ comptime { | ... | @@ -149,60 +119,99 @@ comptime { |
| 149 | _ = @import("compiler_rt/floatuntitf.zig"); | 119 | _ = @import("compiler_rt/floatuntitf.zig"); |
| 150 | _ = @import("compiler_rt/floatuntixf.zig"); | 120 | _ = @import("compiler_rt/floatuntixf.zig"); |
| 151 | 121 | ||
| 152 | _ = @import("compiler_rt/float_to_int.zig"); | 122 | // comparison |
| 153 | _ = @import("compiler_rt/fixhfsi.zig"); | 123 | _ = @import("compiler_rt/comparef.zig"); |
| 154 | _ = @import("compiler_rt/fixhfdi.zig"); | 124 | _ = @import("compiler_rt/cmphf2.zig"); |
| 155 | _ = @import("compiler_rt/fixhfti.zig"); | 125 | _ = @import("compiler_rt/cmpsf2.zig"); |
| 156 | _ = @import("compiler_rt/fixsfsi.zig"); | 126 | _ = @import("compiler_rt/cmpdf2.zig"); |
| 157 | _ = @import("compiler_rt/fixsfdi.zig"); | 127 | _ = @import("compiler_rt/cmptf2.zig"); |
| 158 | _ = @import("compiler_rt/fixsfti.zig"); | 128 | _ = @import("compiler_rt/cmpxf2.zig"); |
| 159 | _ = @import("compiler_rt/fixdfsi.zig"); | 129 | _ = @import("compiler_rt/unordhf2.zig"); |
| 160 | _ = @import("compiler_rt/fixdfdi.zig"); | 130 | _ = @import("compiler_rt/unordsf2.zig"); |
| 161 | _ = @import("compiler_rt/fixdfti.zig"); | 131 | _ = @import("compiler_rt/unorddf2.zig"); |
| 162 | _ = @import("compiler_rt/fixtfsi.zig"); | 132 | _ = @import("compiler_rt/unordxf2.zig"); |
| 163 | _ = @import("compiler_rt/fixtfdi.zig"); | 133 | _ = @import("compiler_rt/unordtf2.zig"); |
| 164 | _ = @import("compiler_rt/fixtfti.zig"); | 134 | _ = @import("compiler_rt/gehf2.zig"); |
| 165 | _ = @import("compiler_rt/fixxfsi.zig"); | 135 | _ = @import("compiler_rt/gesf2.zig"); |
| 166 | _ = @import("compiler_rt/fixxfdi.zig"); | 136 | _ = @import("compiler_rt/gedf2.zig"); |
| 167 | _ = @import("compiler_rt/fixxfti.zig"); | 137 | _ = @import("compiler_rt/gexf2.zig"); |
| 168 | _ = @import("compiler_rt/fixunshfsi.zig"); | 138 | _ = @import("compiler_rt/getf2.zig"); |
| 169 | _ = @import("compiler_rt/fixunshfdi.zig"); | ||
| 170 | _ = @import("compiler_rt/fixunshfti.zig"); | ||
| 171 | _ = @import("compiler_rt/fixunssfsi.zig"); | ||
| 172 | _ = @import("compiler_rt/fixunssfdi.zig"); | ||
| 173 | _ = @import("compiler_rt/fixunssfti.zig"); | ||
| 174 | _ = @import("compiler_rt/fixunsdfsi.zig"); | ||
| 175 | _ = @import("compiler_rt/fixunsdfdi.zig"); | ||
| 176 | _ = @import("compiler_rt/fixunsdfti.zig"); | ||
| 177 | _ = @import("compiler_rt/fixunstfsi.zig"); | ||
| 178 | _ = @import("compiler_rt/fixunstfdi.zig"); | ||
| 179 | _ = @import("compiler_rt/fixunstfti.zig"); | ||
| 180 | _ = @import("compiler_rt/fixunsxfsi.zig"); | ||
| 181 | _ = @import("compiler_rt/fixunsxfdi.zig"); | ||
| 182 | _ = @import("compiler_rt/fixunsxfti.zig"); | ||
| 183 | 139 | ||
| 184 | _ = @import("compiler_rt/count0bits.zig"); | 140 | // arithmetic |
| 185 | _ = @import("compiler_rt/parity.zig"); | 141 | _ = @import("compiler_rt/addf3.zig"); |
| 186 | _ = @import("compiler_rt/popcount.zig"); | 142 | _ = @import("compiler_rt/addhf3.zig"); |
| 187 | _ = @import("compiler_rt/bswap.zig"); | 143 | _ = @import("compiler_rt/addsf3.zig"); |
| 188 | _ = @import("compiler_rt/int.zig"); | 144 | _ = @import("compiler_rt/adddf3.zig"); |
| 189 | _ = @import("compiler_rt/shift.zig"); | 145 | _ = @import("compiler_rt/addtf3.zig"); |
| 146 | _ = @import("compiler_rt/addxf3.zig"); | ||
| 190 | 147 | ||
| 191 | _ = @import("compiler_rt/negXi2.zig"); | 148 | _ = @import("compiler_rt/subhf3.zig"); |
| 149 | _ = @import("compiler_rt/subsf3.zig"); | ||
| 150 | _ = @import("compiler_rt/subdf3.zig"); | ||
| 151 | _ = @import("compiler_rt/subtf3.zig"); | ||
| 152 | _ = @import("compiler_rt/subxf3.zig"); | ||
| 192 | 153 | ||
| 193 | _ = @import("compiler_rt/muldi3.zig"); | 154 | _ = @import("compiler_rt/mulf3.zig"); |
| 155 | _ = @import("compiler_rt/mulhf3.zig"); | ||
| 156 | _ = @import("compiler_rt/mulsf3.zig"); | ||
| 157 | _ = @import("compiler_rt/muldf3.zig"); | ||
| 158 | _ = @import("compiler_rt/multf3.zig"); | ||
| 159 | _ = @import("compiler_rt/mulxf3.zig"); | ||
| 194 | 160 | ||
| 195 | _ = @import("compiler_rt/absv.zig"); | 161 | _ = @import("compiler_rt/divhf3.zig"); |
| 196 | _ = @import("compiler_rt/absvsi2.zig"); | 162 | _ = @import("compiler_rt/divsf3.zig"); |
| 197 | _ = @import("compiler_rt/absvdi2.zig"); | 163 | _ = @import("compiler_rt/divdf3.zig"); |
| 198 | _ = @import("compiler_rt/absvti2.zig"); | 164 | _ = @import("compiler_rt/divxf3.zig"); |
| 165 | _ = @import("compiler_rt/divtf3.zig"); | ||
| 199 | 166 | ||
| 200 | _ = @import("compiler_rt/negv.zig"); | 167 | _ = @import("compiler_rt/neghf2.zig"); |
| 201 | _ = @import("compiler_rt/addo.zig"); | 168 | _ = @import("compiler_rt/negsf2.zig"); |
| 202 | _ = @import("compiler_rt/subo.zig"); | 169 | _ = @import("compiler_rt/negdf2.zig"); |
| 203 | _ = @import("compiler_rt/mulo.zig"); | 170 | _ = @import("compiler_rt/negtf2.zig"); |
| 204 | _ = @import("compiler_rt/cmp.zig"); | 171 | _ = @import("compiler_rt/negxf2.zig"); |
| 172 | |||
| 173 | // other | ||
| 174 | _ = @import("compiler_rt/powiXf2.zig"); | ||
| 175 | _ = @import("compiler_rt/mulc3.zig"); | ||
| 176 | _ = @import("compiler_rt/mulhc3.zig"); | ||
| 177 | _ = @import("compiler_rt/mulsc3.zig"); | ||
| 178 | _ = @import("compiler_rt/muldc3.zig"); | ||
| 179 | _ = @import("compiler_rt/mulxc3.zig"); | ||
| 180 | _ = @import("compiler_rt/multc3.zig"); | ||
| 181 | |||
| 182 | _ = @import("compiler_rt/divc3.zig"); | ||
| 183 | _ = @import("compiler_rt/divhc3.zig"); | ||
| 184 | _ = @import("compiler_rt/divsc3.zig"); | ||
| 185 | _ = @import("compiler_rt/divdc3.zig"); | ||
| 186 | _ = @import("compiler_rt/divxc3.zig"); | ||
| 187 | _ = @import("compiler_rt/divtc3.zig"); | ||
| 188 | |||
| 189 | // Math routines. Alphabetically sorted. | ||
| 190 | _ = @import("compiler_rt/ceil.zig"); | ||
| 191 | _ = @import("compiler_rt/cos.zig"); | ||
| 192 | _ = @import("compiler_rt/exp.zig"); | ||
| 193 | _ = @import("compiler_rt/exp2.zig"); | ||
| 194 | _ = @import("compiler_rt/fabs.zig"); | ||
| 195 | _ = @import("compiler_rt/floor.zig"); | ||
| 196 | _ = @import("compiler_rt/fma.zig"); | ||
| 197 | _ = @import("compiler_rt/fmax.zig"); | ||
| 198 | _ = @import("compiler_rt/fmin.zig"); | ||
| 199 | _ = @import("compiler_rt/fmod.zig"); | ||
| 200 | _ = @import("compiler_rt/log.zig"); | ||
| 201 | _ = @import("compiler_rt/log10.zig"); | ||
| 202 | _ = @import("compiler_rt/log2.zig"); | ||
| 203 | _ = @import("compiler_rt/round.zig"); | ||
| 204 | _ = @import("compiler_rt/sin.zig"); | ||
| 205 | _ = @import("compiler_rt/sincos.zig"); | ||
| 206 | _ = @import("compiler_rt/sqrt.zig"); | ||
| 207 | _ = @import("compiler_rt/tan.zig"); | ||
| 208 | _ = @import("compiler_rt/trunc.zig"); | ||
| 209 | |||
| 210 | // BigInt. Alphabetically sorted. | ||
| 211 | _ = @import("compiler_rt/udivmodei4.zig"); | ||
| 212 | _ = @import("compiler_rt/udivmodti4.zig"); | ||
| 205 | 213 | ||
| 214 | // extra | ||
| 206 | _ = @import("compiler_rt/os_version_check.zig"); | 215 | _ = @import("compiler_rt/os_version_check.zig"); |
| 207 | _ = @import("compiler_rt/emutls.zig"); | 216 | _ = @import("compiler_rt/emutls.zig"); |
| 208 | _ = @import("compiler_rt/arm.zig"); | 217 | _ = @import("compiler_rt/arm.zig"); |
lib/compiler_rt/README.md+1-1| ... | @@ -331,7 +331,7 @@ Integer and Float Operations | ... | @@ -331,7 +331,7 @@ Integer and Float Operations |
| 331 | | ✓ | __negdf2 | f64 | ∅ | f64 | .. | | 331 | | ✓ | __negdf2 | f64 | ∅ | f64 | .. | |
| 332 | | ✓ | __negtf2 | f128 | ∅ | f128 | .. | | 332 | | ✓ | __negtf2 | f128 | ∅ | f128 | .. | |
| 333 | | ✓ | __negxf2 | f80 | ∅ | f80 | .. | | 333 | | ✓ | __negxf2 | f80 | ∅ | f80 | .. | |
| 334 | | | | | | | **Floating point raised to integer power** | | 334 | | | | | | | **Other** | |
| 335 | | ✓ | __powihf2 | f16 | i32 | f16 | `a ^ b` | | 335 | | ✓ | __powihf2 | f16 | i32 | f16 | `a ^ b` | |
| 336 | | ✓ | __powisf2 | f32 | i32 | f32 | .. | | 336 | | ✓ | __powisf2 | f32 | i32 | f32 | .. | |
| 337 | | ✓ | __powidf2 | f64 | i32 | f64 | .. | | 337 | | ✓ | __powidf2 | f64 | i32 | f64 | .. | |
lib/compiler_rt/common.zig+18| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const native_endian = builtin.cpu.arch.endian(); | ||
| 3 | 4 | ||
| 4 | pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; | 5 | pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; |
| 5 | /// Determines the symbol's visibility to other objects. | 6 | /// Determines the symbol's visibility to other objects. |
| ... | @@ -221,3 +222,20 @@ pub inline fn fneg(a: anytype) @TypeOf(a) { | ... | @@ -221,3 +222,20 @@ pub inline fn fneg(a: anytype) @TypeOf(a) { |
| 221 | const negated = @bitCast(U, a) ^ sign_bit_mask; | 222 | const negated = @bitCast(U, a) ^ sign_bit_mask; |
| 222 | return @bitCast(F, negated); | 223 | return @bitCast(F, negated); |
| 223 | } | 224 | } |
| 225 | |||
| 226 | /// Allows to access underlying bits as two equally sized lower and higher | ||
| 227 | /// signed or unsigned integers. | ||
| 228 | pub fn HalveInt(comptime T: type, comptime signed_half: bool) type { | ||
| 229 | return extern union { | ||
| 230 | pub const bits = @divExact(@typeInfo(T).Int.bits, 2); | ||
| 231 | pub const HalfTU = std.meta.Int(.unsigned, bits); | ||
| 232 | pub const HalfTS = std.meta.Int(.signed, bits); | ||
| 233 | pub const HalfT = if (signed_half) HalfTS else HalfTU; | ||
| 234 | |||
| 235 | all: T, | ||
| 236 | s: if (native_endian == .Little) | ||
| 237 | extern struct { low: HalfT, high: HalfT } | ||
| 238 | else | ||
| 239 | extern struct { high: HalfT, low: HalfT }, | ||
| 240 | }; | ||
| 241 | } |
lib/compiler_rt/int.zig-57| ... | @@ -16,7 +16,6 @@ pub const panic = common.panic; | ... | @@ -16,7 +16,6 @@ pub const panic = common.panic; |
| 16 | comptime { | 16 | comptime { |
| 17 | @export(__divmodti4, .{ .name = "__divmodti4", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(__divmodti4, .{ .name = "__divmodti4", .linkage = common.linkage, .visibility = common.visibility }); |
| 18 | @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility }); | 18 | @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility }); |
| 19 | @export(__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 20 | @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = common.linkage, .visibility = common.visibility }); | 19 | @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = common.linkage, .visibility = common.visibility }); |
| 21 | if (common.want_aeabi) { | 20 | if (common.want_aeabi) { |
| 22 | @export(__aeabi_idiv, .{ .name = "__aeabi_idiv", .linkage = common.linkage, .visibility = common.visibility }); | 21 | @export(__aeabi_idiv, .{ .name = "__aeabi_idiv", .linkage = common.linkage, .visibility = common.visibility }); |
| ... | @@ -663,59 +662,3 @@ fn test_one_umodsi3(a: u32, b: u32, expected_r: u32) !void { | ... | @@ -663,59 +662,3 @@ fn test_one_umodsi3(a: u32, b: u32, expected_r: u32) !void { |
| 663 | const r: u32 = __umodsi3(a, b); | 662 | const r: u32 = __umodsi3(a, b); |
| 664 | try testing.expect(r == expected_r); | 663 | try testing.expect(r == expected_r); |
| 665 | } | 664 | } |
| 666 | |||
| 667 | pub fn __mulsi3(a: i32, b: i32) callconv(.C) i32 { | ||
| 668 | var ua = @bitCast(u32, a); | ||
| 669 | var ub = @bitCast(u32, b); | ||
| 670 | var r: u32 = 0; | ||
| 671 | |||
| 672 | while (ua > 0) { | ||
| 673 | if ((ua & 1) != 0) r +%= ub; | ||
| 674 | ua >>= 1; | ||
| 675 | ub <<= 1; | ||
| 676 | } | ||
| 677 | |||
| 678 | return @bitCast(i32, r); | ||
| 679 | } | ||
| 680 | |||
| 681 | fn test_one_mulsi3(a: i32, b: i32, result: i32) !void { | ||
| 682 | try testing.expectEqual(result, __mulsi3(a, b)); | ||
| 683 | } | ||
| 684 | |||
| 685 | test "mulsi3" { | ||
| 686 | try test_one_mulsi3(0, 0, 0); | ||
| 687 | try test_one_mulsi3(0, 1, 0); | ||
| 688 | try test_one_mulsi3(1, 0, 0); | ||
| 689 | try test_one_mulsi3(0, 10, 0); | ||
| 690 | try test_one_mulsi3(10, 0, 0); | ||
| 691 | try test_one_mulsi3(0, maxInt(i32), 0); | ||
| 692 | try test_one_mulsi3(maxInt(i32), 0, 0); | ||
| 693 | try test_one_mulsi3(0, -1, 0); | ||
| 694 | try test_one_mulsi3(-1, 0, 0); | ||
| 695 | try test_one_mulsi3(0, -10, 0); | ||
| 696 | try test_one_mulsi3(-10, 0, 0); | ||
| 697 | try test_one_mulsi3(0, minInt(i32), 0); | ||
| 698 | try test_one_mulsi3(minInt(i32), 0, 0); | ||
| 699 | try test_one_mulsi3(1, 1, 1); | ||
| 700 | try test_one_mulsi3(1, 10, 10); | ||
| 701 | try test_one_mulsi3(10, 1, 10); | ||
| 702 | try test_one_mulsi3(1, maxInt(i32), maxInt(i32)); | ||
| 703 | try test_one_mulsi3(maxInt(i32), 1, maxInt(i32)); | ||
| 704 | try test_one_mulsi3(1, -1, -1); | ||
| 705 | try test_one_mulsi3(1, -10, -10); | ||
| 706 | try test_one_mulsi3(-10, 1, -10); | ||
| 707 | try test_one_mulsi3(1, minInt(i32), minInt(i32)); | ||
| 708 | try test_one_mulsi3(minInt(i32), 1, minInt(i32)); | ||
| 709 | try test_one_mulsi3(46340, 46340, 2147395600); | ||
| 710 | try test_one_mulsi3(-46340, 46340, -2147395600); | ||
| 711 | try test_one_mulsi3(46340, -46340, -2147395600); | ||
| 712 | try test_one_mulsi3(-46340, -46340, 2147395600); | ||
| 713 | try test_one_mulsi3(4194303, 8192, @truncate(i32, 34359730176)); | ||
| 714 | try test_one_mulsi3(-4194303, 8192, @truncate(i32, -34359730176)); | ||
| 715 | try test_one_mulsi3(4194303, -8192, @truncate(i32, -34359730176)); | ||
| 716 | try test_one_mulsi3(-4194303, -8192, @truncate(i32, 34359730176)); | ||
| 717 | try test_one_mulsi3(8192, 4194303, @truncate(i32, 34359730176)); | ||
| 718 | try test_one_mulsi3(-8192, 4194303, @truncate(i32, -34359730176)); | ||
| 719 | try test_one_mulsi3(8192, -4194303, @truncate(i32, -34359730176)); | ||
| 720 | try test_one_mulsi3(-8192, -4194303, @truncate(i32, 34359730176)); | ||
| 721 | } |
lib/compiler_rt/mulXi3.zig created+101| ... | @@ -0,0 +1,101 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | ||
| 3 | const testing = std.testing; | ||
| 4 | const common = @import("common.zig"); | ||
| 5 | const native_endian = builtin.cpu.arch.endian(); | ||
| 6 | |||
| 7 | pub const panic = common.panic; | ||
| 8 | |||
| 9 | comptime { | ||
| 10 | @export(__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 11 | if (common.want_aeabi) { | ||
| 12 | @export(__aeabi_lmul, .{ .name = "__aeabi_lmul", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 13 | } else { | ||
| 14 | @export(__muldi3, .{ .name = "__muldi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 15 | } | ||
| 16 | if (common.want_windows_v2u64_abi) { | ||
| 17 | @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 18 | } else { | ||
| 19 | @export(__multi3, .{ .name = "__multi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | pub fn __mulsi3(a: i32, b: i32) callconv(.C) i32 { | ||
| 24 | var ua = @bitCast(u32, a); | ||
| 25 | var ub = @bitCast(u32, b); | ||
| 26 | var r: u32 = 0; | ||
| 27 | |||
| 28 | while (ua > 0) { | ||
| 29 | if ((ua & 1) != 0) r +%= ub; | ||
| 30 | ua >>= 1; | ||
| 31 | ub <<= 1; | ||
| 32 | } | ||
| 33 | |||
| 34 | return @bitCast(i32, r); | ||
| 35 | } | ||
| 36 | |||
| 37 | pub fn __muldi3(a: i64, b: i64) callconv(.C) i64 { | ||
| 38 | return mulX(i64, a, b); | ||
| 39 | } | ||
| 40 | |||
| 41 | fn __aeabi_lmul(a: i64, b: i64) callconv(.AAPCS) i64 { | ||
| 42 | return mulX(i64, a, b); | ||
| 43 | } | ||
| 44 | |||
| 45 | inline fn mulX(comptime T: type, a: T, b: T) T { | ||
| 46 | const word_t = common.HalveInt(T, false); | ||
| 47 | const x = word_t{ .all = a }; | ||
| 48 | const y = word_t{ .all = b }; | ||
| 49 | var r = switch (T) { | ||
| 50 | i64, i128 => word_t{ .all = muldXi(word_t.HalfT, x.s.low, y.s.low) }, | ||
| 51 | else => unreachable, | ||
| 52 | }; | ||
| 53 | r.s.high +%= x.s.high *% y.s.low +% x.s.low *% y.s.high; | ||
| 54 | return r.all; | ||
| 55 | } | ||
| 56 | |||
| 57 | fn DoubleInt(comptime T: type) type { | ||
| 58 | return switch (T) { | ||
| 59 | u32 => i64, | ||
| 60 | u64 => i128, | ||
| 61 | i32 => i64, | ||
| 62 | i64 => i128, | ||
| 63 | else => unreachable, | ||
| 64 | }; | ||
| 65 | } | ||
| 66 | |||
| 67 | fn muldXi(comptime T: type, a: T, b: T) DoubleInt(T) { | ||
| 68 | const DT = DoubleInt(T); | ||
| 69 | const word_t = common.HalveInt(DT, false); | ||
| 70 | const bits_in_word_2 = @sizeOf(T) * 8 / 2; | ||
| 71 | const lower_mask = (~@as(T, 0)) >> bits_in_word_2; | ||
| 72 | |||
| 73 | var r: word_t = undefined; | ||
| 74 | r.s.low = (a & lower_mask) *% (b & lower_mask); | ||
| 75 | var t: T = r.s.low >> bits_in_word_2; | ||
| 76 | r.s.low &= lower_mask; | ||
| 77 | t += (a >> bits_in_word_2) *% (b & lower_mask); | ||
| 78 | r.s.low +%= (t & lower_mask) << bits_in_word_2; | ||
| 79 | r.s.high = t >> bits_in_word_2; | ||
| 80 | t = r.s.low >> bits_in_word_2; | ||
| 81 | r.s.low &= lower_mask; | ||
| 82 | t +%= (b >> bits_in_word_2) *% (a & lower_mask); | ||
| 83 | r.s.low +%= (t & lower_mask) << bits_in_word_2; | ||
| 84 | r.s.high +%= t >> bits_in_word_2; | ||
| 85 | r.s.high +%= (a >> bits_in_word_2) *% (b >> bits_in_word_2); | ||
| 86 | return r.all; | ||
| 87 | } | ||
| 88 | |||
| 89 | pub fn __multi3(a: i128, b: i128) callconv(.C) i128 { | ||
| 90 | return mulX(i128, a, b); | ||
| 91 | } | ||
| 92 | |||
| 93 | const v2u64 = @Vector(2, u64); | ||
| 94 | |||
| 95 | fn __multi3_windows_x86_64(a: v2u64, b: v2u64) callconv(.C) v2u64 { | ||
| 96 | return @bitCast(v2u64, mulX(i128, @bitCast(i128, a), @bitCast(i128, b))); | ||
| 97 | } | ||
| 98 | |||
| 99 | test { | ||
| 100 | _ = @import("mulXi3_test.zig"); | ||
| 101 | } | ||
lib/compiler_rt/mulXi3_test.zig created+147| ... | @@ -0,0 +1,147 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const testing = std.testing; | ||
| 3 | const mulXi3 = @import("mulXi3.zig"); | ||
| 4 | const maxInt = std.math.maxInt; | ||
| 5 | const minInt = std.math.minInt; | ||
| 6 | |||
| 7 | fn test_one_mulsi3(a: i32, b: i32, result: i32) !void { | ||
| 8 | try testing.expectEqual(result, mulXi3.__mulsi3(a, b)); | ||
| 9 | } | ||
| 10 | |||
| 11 | fn test__muldi3(a: i64, b: i64, expected: i64) !void { | ||
| 12 | const x = mulXi3.__muldi3(a, b); | ||
| 13 | try testing.expect(x == expected); | ||
| 14 | } | ||
| 15 | |||
| 16 | fn test__multi3(a: i128, b: i128, expected: i128) !void { | ||
| 17 | const x = mulXi3.__multi3(a, b); | ||
| 18 | try testing.expect(x == expected); | ||
| 19 | } | ||
| 20 | |||
| 21 | test "mulsi3" { | ||
| 22 | try test_one_mulsi3(0, 0, 0); | ||
| 23 | try test_one_mulsi3(0, 1, 0); | ||
| 24 | try test_one_mulsi3(1, 0, 0); | ||
| 25 | try test_one_mulsi3(0, 10, 0); | ||
| 26 | try test_one_mulsi3(10, 0, 0); | ||
| 27 | try test_one_mulsi3(0, maxInt(i32), 0); | ||
| 28 | try test_one_mulsi3(maxInt(i32), 0, 0); | ||
| 29 | try test_one_mulsi3(0, -1, 0); | ||
| 30 | try test_one_mulsi3(-1, 0, 0); | ||
| 31 | try test_one_mulsi3(0, -10, 0); | ||
| 32 | try test_one_mulsi3(-10, 0, 0); | ||
| 33 | try test_one_mulsi3(0, minInt(i32), 0); | ||
| 34 | try test_one_mulsi3(minInt(i32), 0, 0); | ||
| 35 | try test_one_mulsi3(1, 1, 1); | ||
| 36 | try test_one_mulsi3(1, 10, 10); | ||
| 37 | try test_one_mulsi3(10, 1, 10); | ||
| 38 | try test_one_mulsi3(1, maxInt(i32), maxInt(i32)); | ||
| 39 | try test_one_mulsi3(maxInt(i32), 1, maxInt(i32)); | ||
| 40 | try test_one_mulsi3(1, -1, -1); | ||
| 41 | try test_one_mulsi3(1, -10, -10); | ||
| 42 | try test_one_mulsi3(-10, 1, -10); | ||
| 43 | try test_one_mulsi3(1, minInt(i32), minInt(i32)); | ||
| 44 | try test_one_mulsi3(minInt(i32), 1, minInt(i32)); | ||
| 45 | try test_one_mulsi3(46340, 46340, 2147395600); | ||
| 46 | try test_one_mulsi3(-46340, 46340, -2147395600); | ||
| 47 | try test_one_mulsi3(46340, -46340, -2147395600); | ||
| 48 | try test_one_mulsi3(-46340, -46340, 2147395600); | ||
| 49 | try test_one_mulsi3(4194303, 8192, @truncate(i32, 34359730176)); | ||
| 50 | try test_one_mulsi3(-4194303, 8192, @truncate(i32, -34359730176)); | ||
| 51 | try test_one_mulsi3(4194303, -8192, @truncate(i32, -34359730176)); | ||
| 52 | try test_one_mulsi3(-4194303, -8192, @truncate(i32, 34359730176)); | ||
| 53 | try test_one_mulsi3(8192, 4194303, @truncate(i32, 34359730176)); | ||
| 54 | try test_one_mulsi3(-8192, 4194303, @truncate(i32, -34359730176)); | ||
| 55 | try test_one_mulsi3(8192, -4194303, @truncate(i32, -34359730176)); | ||
| 56 | try test_one_mulsi3(-8192, -4194303, @truncate(i32, 34359730176)); | ||
| 57 | } | ||
| 58 | |||
| 59 | test "muldi3" { | ||
| 60 | try test__muldi3(0, 0, 0); | ||
| 61 | try test__muldi3(0, 1, 0); | ||
| 62 | try test__muldi3(1, 0, 0); | ||
| 63 | try test__muldi3(0, 10, 0); | ||
| 64 | try test__muldi3(10, 0, 0); | ||
| 65 | try test__muldi3(0, 81985529216486895, 0); | ||
| 66 | try test__muldi3(81985529216486895, 0, 0); | ||
| 67 | |||
| 68 | try test__muldi3(0, -1, 0); | ||
| 69 | try test__muldi3(-1, 0, 0); | ||
| 70 | try test__muldi3(0, -10, 0); | ||
| 71 | try test__muldi3(-10, 0, 0); | ||
| 72 | try test__muldi3(0, -81985529216486895, 0); | ||
| 73 | try test__muldi3(-81985529216486895, 0, 0); | ||
| 74 | |||
| 75 | try test__muldi3(1, 1, 1); | ||
| 76 | try test__muldi3(1, 10, 10); | ||
| 77 | try test__muldi3(10, 1, 10); | ||
| 78 | try test__muldi3(1, 81985529216486895, 81985529216486895); | ||
| 79 | try test__muldi3(81985529216486895, 1, 81985529216486895); | ||
| 80 | |||
| 81 | try test__muldi3(1, -1, -1); | ||
| 82 | try test__muldi3(1, -10, -10); | ||
| 83 | try test__muldi3(-10, 1, -10); | ||
| 84 | try test__muldi3(1, -81985529216486895, -81985529216486895); | ||
| 85 | try test__muldi3(-81985529216486895, 1, -81985529216486895); | ||
| 86 | |||
| 87 | try test__muldi3(3037000499, 3037000499, 9223372030926249001); | ||
| 88 | try test__muldi3(-3037000499, 3037000499, -9223372030926249001); | ||
| 89 | try test__muldi3(3037000499, -3037000499, -9223372030926249001); | ||
| 90 | try test__muldi3(-3037000499, -3037000499, 9223372030926249001); | ||
| 91 | |||
| 92 | try test__muldi3(4398046511103, 2097152, 9223372036852678656); | ||
| 93 | try test__muldi3(-4398046511103, 2097152, -9223372036852678656); | ||
| 94 | try test__muldi3(4398046511103, -2097152, -9223372036852678656); | ||
| 95 | try test__muldi3(-4398046511103, -2097152, 9223372036852678656); | ||
| 96 | |||
| 97 | try test__muldi3(2097152, 4398046511103, 9223372036852678656); | ||
| 98 | try test__muldi3(-2097152, 4398046511103, -9223372036852678656); | ||
| 99 | try test__muldi3(2097152, -4398046511103, -9223372036852678656); | ||
| 100 | try test__muldi3(-2097152, -4398046511103, 9223372036852678656); | ||
| 101 | } | ||
| 102 | |||
| 103 | test "multi3" { | ||
| 104 | try test__multi3(0, 0, 0); | ||
| 105 | try test__multi3(0, 1, 0); | ||
| 106 | try test__multi3(1, 0, 0); | ||
| 107 | try test__multi3(0, 10, 0); | ||
| 108 | try test__multi3(10, 0, 0); | ||
| 109 | try test__multi3(0, 81985529216486895, 0); | ||
| 110 | try test__multi3(81985529216486895, 0, 0); | ||
| 111 | |||
| 112 | try test__multi3(0, -1, 0); | ||
| 113 | try test__multi3(-1, 0, 0); | ||
| 114 | try test__multi3(0, -10, 0); | ||
| 115 | try test__multi3(-10, 0, 0); | ||
| 116 | try test__multi3(0, -81985529216486895, 0); | ||
| 117 | try test__multi3(-81985529216486895, 0, 0); | ||
| 118 | |||
| 119 | try test__multi3(1, 1, 1); | ||
| 120 | try test__multi3(1, 10, 10); | ||
| 121 | try test__multi3(10, 1, 10); | ||
| 122 | try test__multi3(1, 81985529216486895, 81985529216486895); | ||
| 123 | try test__multi3(81985529216486895, 1, 81985529216486895); | ||
| 124 | |||
| 125 | try test__multi3(1, -1, -1); | ||
| 126 | try test__multi3(1, -10, -10); | ||
| 127 | try test__multi3(-10, 1, -10); | ||
| 128 | try test__multi3(1, -81985529216486895, -81985529216486895); | ||
| 129 | try test__multi3(-81985529216486895, 1, -81985529216486895); | ||
| 130 | |||
| 131 | try test__multi3(3037000499, 3037000499, 9223372030926249001); | ||
| 132 | try test__multi3(-3037000499, 3037000499, -9223372030926249001); | ||
| 133 | try test__multi3(3037000499, -3037000499, -9223372030926249001); | ||
| 134 | try test__multi3(-3037000499, -3037000499, 9223372030926249001); | ||
| 135 | |||
| 136 | try test__multi3(4398046511103, 2097152, 9223372036852678656); | ||
| 137 | try test__multi3(-4398046511103, 2097152, -9223372036852678656); | ||
| 138 | try test__multi3(4398046511103, -2097152, -9223372036852678656); | ||
| 139 | try test__multi3(-4398046511103, -2097152, 9223372036852678656); | ||
| 140 | |||
| 141 | try test__multi3(2097152, 4398046511103, 9223372036852678656); | ||
| 142 | try test__multi3(-2097152, 4398046511103, -9223372036852678656); | ||
| 143 | try test__multi3(2097152, -4398046511103, -9223372036852678656); | ||
| 144 | try test__multi3(-2097152, -4398046511103, 9223372036852678656); | ||
| 145 | |||
| 146 | try test__multi3(0x00000000000000B504F333F9DE5BE000, 0x000000000000000000B504F333F9DE5B, 0x7FFFFFFFFFFFF328DF915DA296E8A000); | ||
| 147 | } | ||
lib/compiler_rt/muldi3.zig deleted-71| ... | @@ -1,71 +0,0 @@ | ||
| 1 | //! Ported from | ||
| 2 | //! https://github.com/llvm/llvm-project/blob/llvmorg-9.0.0/compiler-rt/lib/builtins/muldi3.c | ||
| 3 | |||
| 4 | const std = @import("std"); | ||
| 5 | const builtin = @import("builtin"); | ||
| 6 | const native_endian = builtin.cpu.arch.endian(); | ||
| 7 | const common = @import("common.zig"); | ||
| 8 | |||
| 9 | pub const panic = common.panic; | ||
| 10 | |||
| 11 | comptime { | ||
| 12 | if (common.want_aeabi) { | ||
| 13 | @export(__aeabi_lmul, .{ .name = "__aeabi_lmul", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 14 | } else { | ||
| 15 | @export(__muldi3, .{ .name = "__muldi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | pub fn __muldi3(a: i64, b: i64) callconv(.C) i64 { | ||
| 20 | return mul(a, b); | ||
| 21 | } | ||
| 22 | |||
| 23 | fn __aeabi_lmul(a: i64, b: i64) callconv(.AAPCS) i64 { | ||
| 24 | return mul(a, b); | ||
| 25 | } | ||
| 26 | |||
| 27 | inline fn mul(a: i64, b: i64) i64 { | ||
| 28 | const x = dwords{ .all = a }; | ||
| 29 | const y = dwords{ .all = b }; | ||
| 30 | var r = dwords{ .all = muldsi3(x.s.low, y.s.low) }; | ||
| 31 | r.s.high +%= x.s.high *% y.s.low +% x.s.low *% y.s.high; | ||
| 32 | return r.all; | ||
| 33 | } | ||
| 34 | |||
| 35 | const dwords = extern union { | ||
| 36 | all: i64, | ||
| 37 | s: switch (native_endian) { | ||
| 38 | .Little => extern struct { | ||
| 39 | low: u32, | ||
| 40 | high: u32, | ||
| 41 | }, | ||
| 42 | .Big => extern struct { | ||
| 43 | high: u32, | ||
| 44 | low: u32, | ||
| 45 | }, | ||
| 46 | }, | ||
| 47 | }; | ||
| 48 | |||
| 49 | fn muldsi3(a: u32, b: u32) i64 { | ||
| 50 | const bits_in_word_2 = @sizeOf(i32) * 8 / 2; | ||
| 51 | const lower_mask = (~@as(u32, 0)) >> bits_in_word_2; | ||
| 52 | |||
| 53 | var r: dwords = undefined; | ||
| 54 | r.s.low = (a & lower_mask) *% (b & lower_mask); | ||
| 55 | var t: u32 = r.s.low >> bits_in_word_2; | ||
| 56 | r.s.low &= lower_mask; | ||
| 57 | t += (a >> bits_in_word_2) *% (b & lower_mask); | ||
| 58 | r.s.low +%= (t & lower_mask) << bits_in_word_2; | ||
| 59 | r.s.high = t >> bits_in_word_2; | ||
| 60 | t = r.s.low >> bits_in_word_2; | ||
| 61 | r.s.low &= lower_mask; | ||
| 62 | t +%= (b >> bits_in_word_2) *% (a & lower_mask); | ||
| 63 | r.s.low +%= (t & lower_mask) << bits_in_word_2; | ||
| 64 | r.s.high +%= t >> bits_in_word_2; | ||
| 65 | r.s.high +%= (a >> bits_in_word_2) *% (b >> bits_in_word_2); | ||
| 66 | return r.all; | ||
| 67 | } | ||
| 68 | |||
| 69 | test { | ||
| 70 | _ = @import("muldi3_test.zig"); | ||
| 71 | } | ||
lib/compiler_rt/muldi3_test.zig deleted-51| ... | @@ -1,51 +0,0 @@ | ||
| 1 | const __muldi3 = @import("muldi3.zig").__muldi3; | ||
| 2 | const testing = @import("std").testing; | ||
| 3 | |||
| 4 | fn test__muldi3(a: i64, b: i64, expected: i64) !void { | ||
| 5 | const x = __muldi3(a, b); | ||
| 6 | try testing.expect(x == expected); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "muldi3" { | ||
| 10 | try test__muldi3(0, 0, 0); | ||
| 11 | try test__muldi3(0, 1, 0); | ||
| 12 | try test__muldi3(1, 0, 0); | ||
| 13 | try test__muldi3(0, 10, 0); | ||
| 14 | try test__muldi3(10, 0, 0); | ||
| 15 | try test__muldi3(0, 81985529216486895, 0); | ||
| 16 | try test__muldi3(81985529216486895, 0, 0); | ||
| 17 | |||
| 18 | try test__muldi3(0, -1, 0); | ||
| 19 | try test__muldi3(-1, 0, 0); | ||
| 20 | try test__muldi3(0, -10, 0); | ||
| 21 | try test__muldi3(-10, 0, 0); | ||
| 22 | try test__muldi3(0, -81985529216486895, 0); | ||
| 23 | try test__muldi3(-81985529216486895, 0, 0); | ||
| 24 | |||
| 25 | try test__muldi3(1, 1, 1); | ||
| 26 | try test__muldi3(1, 10, 10); | ||
| 27 | try test__muldi3(10, 1, 10); | ||
| 28 | try test__muldi3(1, 81985529216486895, 81985529216486895); | ||
| 29 | try test__muldi3(81985529216486895, 1, 81985529216486895); | ||
| 30 | |||
| 31 | try test__muldi3(1, -1, -1); | ||
| 32 | try test__muldi3(1, -10, -10); | ||
| 33 | try test__muldi3(-10, 1, -10); | ||
| 34 | try test__muldi3(1, -81985529216486895, -81985529216486895); | ||
| 35 | try test__muldi3(-81985529216486895, 1, -81985529216486895); | ||
| 36 | |||
| 37 | try test__muldi3(3037000499, 3037000499, 9223372030926249001); | ||
| 38 | try test__muldi3(-3037000499, 3037000499, -9223372030926249001); | ||
| 39 | try test__muldi3(3037000499, -3037000499, -9223372030926249001); | ||
| 40 | try test__muldi3(-3037000499, -3037000499, 9223372030926249001); | ||
| 41 | |||
| 42 | try test__muldi3(4398046511103, 2097152, 9223372036852678656); | ||
| 43 | try test__muldi3(-4398046511103, 2097152, -9223372036852678656); | ||
| 44 | try test__muldi3(4398046511103, -2097152, -9223372036852678656); | ||
| 45 | try test__muldi3(-4398046511103, -2097152, 9223372036852678656); | ||
| 46 | |||
| 47 | try test__muldi3(2097152, 4398046511103, 9223372036852678656); | ||
| 48 | try test__muldi3(-2097152, 4398046511103, -9223372036852678656); | ||
| 49 | try test__muldi3(2097152, -4398046511103, -9223372036852678656); | ||
| 50 | try test__muldi3(-2097152, -4398046511103, 9223372036852678656); | ||
| 51 | } | ||
lib/compiler_rt/multi3.zig deleted-75| ... | @@ -1,75 +0,0 @@ | ||
| 1 | //! Ported from git@github.com:llvm-project/llvm-project-20170507.git | ||
| 2 | //! ae684fad6d34858c014c94da69c15e7774a633c3 | ||
| 3 | //! 2018-08-13 | ||
| 4 | |||
| 5 | const std = @import("std"); | ||
| 6 | const builtin = @import("builtin"); | ||
| 7 | const native_endian = builtin.cpu.arch.endian(); | ||
| 8 | const common = @import("common.zig"); | ||
| 9 | |||
| 10 | pub const panic = common.panic; | ||
| 11 | |||
| 12 | comptime { | ||
| 13 | if (common.want_windows_v2u64_abi) { | ||
| 14 | @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 15 | } else { | ||
| 16 | @export(__multi3, .{ .name = "__multi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | pub fn __multi3(a: i128, b: i128) callconv(.C) i128 { | ||
| 21 | return mul(a, b); | ||
| 22 | } | ||
| 23 | |||
| 24 | const v2u64 = @Vector(2, u64); | ||
| 25 | |||
| 26 | fn __multi3_windows_x86_64(a: v2u64, b: v2u64) callconv(.C) v2u64 { | ||
| 27 | return @bitCast(v2u64, mul(@bitCast(i128, a), @bitCast(i128, b))); | ||
| 28 | } | ||
| 29 | |||
| 30 | inline fn mul(a: i128, b: i128) i128 { | ||
| 31 | const x = twords{ .all = a }; | ||
| 32 | const y = twords{ .all = b }; | ||
| 33 | var r = twords{ .all = mulddi3(x.s.low, y.s.low) }; | ||
| 34 | r.s.high +%= x.s.high *% y.s.low +% x.s.low *% y.s.high; | ||
| 35 | return r.all; | ||
| 36 | } | ||
| 37 | |||
| 38 | fn mulddi3(a: u64, b: u64) i128 { | ||
| 39 | const bits_in_dword_2 = (@sizeOf(i64) * 8) / 2; | ||
| 40 | const lower_mask = ~@as(u64, 0) >> bits_in_dword_2; | ||
| 41 | var r: twords = undefined; | ||
| 42 | r.s.low = (a & lower_mask) *% (b & lower_mask); | ||
| 43 | var t: u64 = r.s.low >> bits_in_dword_2; | ||
| 44 | r.s.low &= lower_mask; | ||
| 45 | t +%= (a >> bits_in_dword_2) *% (b & lower_mask); | ||
| 46 | r.s.low +%= (t & lower_mask) << bits_in_dword_2; | ||
| 47 | r.s.high = t >> bits_in_dword_2; | ||
| 48 | t = r.s.low >> bits_in_dword_2; | ||
| 49 | r.s.low &= lower_mask; | ||
| 50 | t +%= (b >> bits_in_dword_2) *% (a & lower_mask); | ||
| 51 | r.s.low +%= (t & lower_mask) << bits_in_dword_2; | ||
| 52 | r.s.high +%= t >> bits_in_dword_2; | ||
| 53 | r.s.high +%= (a >> bits_in_dword_2) *% (b >> bits_in_dword_2); | ||
| 54 | return r.all; | ||
| 55 | } | ||
| 56 | |||
| 57 | const twords = extern union { | ||
| 58 | all: i128, | ||
| 59 | s: S, | ||
| 60 | |||
| 61 | const S = if (native_endian == .Little) | ||
| 62 | extern struct { | ||
| 63 | low: u64, | ||
| 64 | high: u64, | ||
| 65 | } | ||
| 66 | else | ||
| 67 | extern struct { | ||
| 68 | high: u64, | ||
| 69 | low: u64, | ||
| 70 | }; | ||
| 71 | }; | ||
| 72 | |||
| 73 | test { | ||
| 74 | _ = @import("multi3_test.zig"); | ||
| 75 | } | ||
lib/compiler_rt/multi3_test.zig deleted-53| ... | @@ -1,53 +0,0 @@ | ||
| 1 | const __multi3 = @import("multi3.zig").__multi3; | ||
| 2 | const testing = @import("std").testing; | ||
| 3 | |||
| 4 | fn test__multi3(a: i128, b: i128, expected: i128) !void { | ||
| 5 | const x = __multi3(a, b); | ||
| 6 | try testing.expect(x == expected); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "multi3" { | ||
| 10 | try test__multi3(0, 0, 0); | ||
| 11 | try test__multi3(0, 1, 0); | ||
| 12 | try test__multi3(1, 0, 0); | ||
| 13 | try test__multi3(0, 10, 0); | ||
| 14 | try test__multi3(10, 0, 0); | ||
| 15 | try test__multi3(0, 81985529216486895, 0); | ||
| 16 | try test__multi3(81985529216486895, 0, 0); | ||
| 17 | |||
| 18 | try test__multi3(0, -1, 0); | ||
| 19 | try test__multi3(-1, 0, 0); | ||
| 20 | try test__multi3(0, -10, 0); | ||
| 21 | try test__multi3(-10, 0, 0); | ||
| 22 | try test__multi3(0, -81985529216486895, 0); | ||
| 23 | try test__multi3(-81985529216486895, 0, 0); | ||
| 24 | |||
| 25 | try test__multi3(1, 1, 1); | ||
| 26 | try test__multi3(1, 10, 10); | ||
| 27 | try test__multi3(10, 1, 10); | ||
| 28 | try test__multi3(1, 81985529216486895, 81985529216486895); | ||
| 29 | try test__multi3(81985529216486895, 1, 81985529216486895); | ||
| 30 | |||
| 31 | try test__multi3(1, -1, -1); | ||
| 32 | try test__multi3(1, -10, -10); | ||
| 33 | try test__multi3(-10, 1, -10); | ||
| 34 | try test__multi3(1, -81985529216486895, -81985529216486895); | ||
| 35 | try test__multi3(-81985529216486895, 1, -81985529216486895); | ||
| 36 | |||
| 37 | try test__multi3(3037000499, 3037000499, 9223372030926249001); | ||
| 38 | try test__multi3(-3037000499, 3037000499, -9223372030926249001); | ||
| 39 | try test__multi3(3037000499, -3037000499, -9223372030926249001); | ||
| 40 | try test__multi3(-3037000499, -3037000499, 9223372030926249001); | ||
| 41 | |||
| 42 | try test__multi3(4398046511103, 2097152, 9223372036852678656); | ||
| 43 | try test__multi3(-4398046511103, 2097152, -9223372036852678656); | ||
| 44 | try test__multi3(4398046511103, -2097152, -9223372036852678656); | ||
| 45 | try test__multi3(-4398046511103, -2097152, 9223372036852678656); | ||
| 46 | |||
| 47 | try test__multi3(2097152, 4398046511103, 9223372036852678656); | ||
| 48 | try test__multi3(-2097152, 4398046511103, -9223372036852678656); | ||
| 49 | try test__multi3(2097152, -4398046511103, -9223372036852678656); | ||
| 50 | try test__multi3(-2097152, -4398046511103, 9223372036852678656); | ||
| 51 | |||
| 52 | try test__multi3(0x00000000000000B504F333F9DE5BE000, 0x000000000000000000B504F333F9DE5B, 0x7FFFFFFFFFFFF328DF915DA296E8A000); | ||
| 53 | } | ||
lib/compiler_rt/shift.zig+24-40| ... | @@ -1,7 +1,6 @@ | ... | @@ -1,7 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Log2Int = std.math.Log2Int; | 3 | const Log2Int = std.math.Log2Int; |
| 4 | const native_endian = builtin.cpu.arch.endian(); | ||
| 5 | const common = @import("common.zig"); | 4 | const common = @import("common.zig"); |
| 6 | 5 | ||
| 7 | pub const panic = common.panic; | 6 | pub const panic = common.panic; |
| ... | @@ -27,39 +26,24 @@ comptime { | ... | @@ -27,39 +26,24 @@ comptime { |
| 27 | } | 26 | } |
| 28 | } | 27 | } |
| 29 | 28 | ||
| 30 | fn Dwords(comptime T: type, comptime signed_half: bool) type { | ||
| 31 | return extern union { | ||
| 32 | const bits = @divExact(@typeInfo(T).Int.bits, 2); | ||
| 33 | const HalfTU = std.meta.Int(.unsigned, bits); | ||
| 34 | const HalfTS = std.meta.Int(.signed, bits); | ||
| 35 | const HalfT = if (signed_half) HalfTS else HalfTU; | ||
| 36 | |||
| 37 | all: T, | ||
| 38 | s: if (native_endian == .Little) | ||
| 39 | extern struct { low: HalfT, high: HalfT } | ||
| 40 | else | ||
| 41 | extern struct { high: HalfT, low: HalfT }, | ||
| 42 | }; | ||
| 43 | } | ||
| 44 | |||
| 45 | // Arithmetic shift left: shift in 0 from right to left | 29 | // Arithmetic shift left: shift in 0 from right to left |
| 46 | // Precondition: 0 <= b < bits_in_dword | 30 | // Precondition: 0 <= b < bits_in_dword |
| 47 | inline fn ashlXi3(comptime T: type, a: T, b: i32) T { | 31 | inline fn ashlXi3(comptime T: type, a: T, b: i32) T { |
| 48 | const dwords = Dwords(T, false); | 32 | const word_t = common.HalveInt(T, false); |
| 49 | const S = Log2Int(dwords.HalfT); | 33 | const S = Log2Int(word_t.HalfT); |
| 50 | 34 | ||
| 51 | const input = dwords{ .all = a }; | 35 | const input = word_t{ .all = a }; |
| 52 | var output: dwords = undefined; | 36 | var output: word_t = undefined; |
| 53 | 37 | ||
| 54 | if (b >= dwords.bits) { | 38 | if (b >= word_t.bits) { |
| 55 | output.s.low = 0; | 39 | output.s.low = 0; |
| 56 | output.s.high = input.s.low << @intCast(S, b - dwords.bits); | 40 | output.s.high = input.s.low << @intCast(S, b - word_t.bits); |
| 57 | } else if (b == 0) { | 41 | } else if (b == 0) { |
| 58 | return a; | 42 | return a; |
| 59 | } else { | 43 | } else { |
| 60 | output.s.low = input.s.low << @intCast(S, b); | 44 | output.s.low = input.s.low << @intCast(S, b); |
| 61 | output.s.high = input.s.high << @intCast(S, b); | 45 | output.s.high = input.s.high << @intCast(S, b); |
| 62 | output.s.high |= input.s.low >> @intCast(S, dwords.bits - b); | 46 | output.s.high |= input.s.low >> @intCast(S, word_t.bits - b); |
| 63 | } | 47 | } |
| 64 | 48 | ||
| 65 | return output.all; | 49 | return output.all; |
| ... | @@ -68,24 +52,24 @@ inline fn ashlXi3(comptime T: type, a: T, b: i32) T { | ... | @@ -68,24 +52,24 @@ inline fn ashlXi3(comptime T: type, a: T, b: i32) T { |
| 68 | // Arithmetic shift right: shift in 1 from left to right | 52 | // Arithmetic shift right: shift in 1 from left to right |
| 69 | // Precondition: 0 <= b < T.bit_count | 53 | // Precondition: 0 <= b < T.bit_count |
| 70 | inline fn ashrXi3(comptime T: type, a: T, b: i32) T { | 54 | inline fn ashrXi3(comptime T: type, a: T, b: i32) T { |
| 71 | const dwords = Dwords(T, true); | 55 | const word_t = common.HalveInt(T, true); |
| 72 | const S = Log2Int(dwords.HalfT); | 56 | const S = Log2Int(word_t.HalfT); |
| 73 | 57 | ||
| 74 | const input = dwords{ .all = a }; | 58 | const input = word_t{ .all = a }; |
| 75 | var output: dwords = undefined; | 59 | var output: word_t = undefined; |
| 76 | 60 | ||
| 77 | if (b >= dwords.bits) { | 61 | if (b >= word_t.bits) { |
| 78 | output.s.high = input.s.high >> (dwords.bits - 1); | 62 | output.s.high = input.s.high >> (word_t.bits - 1); |
| 79 | output.s.low = input.s.high >> @intCast(S, b - dwords.bits); | 63 | output.s.low = input.s.high >> @intCast(S, b - word_t.bits); |
| 80 | } else if (b == 0) { | 64 | } else if (b == 0) { |
| 81 | return a; | 65 | return a; |
| 82 | } else { | 66 | } else { |
| 83 | output.s.high = input.s.high >> @intCast(S, b); | 67 | output.s.high = input.s.high >> @intCast(S, b); |
| 84 | output.s.low = input.s.high << @intCast(S, dwords.bits - b); | 68 | output.s.low = input.s.high << @intCast(S, word_t.bits - b); |
| 85 | // Avoid sign-extension here | 69 | // Avoid sign-extension here |
| 86 | output.s.low |= @bitCast( | 70 | output.s.low |= @bitCast( |
| 87 | dwords.HalfT, | 71 | word_t.HalfT, |
| 88 | @bitCast(dwords.HalfTU, input.s.low) >> @intCast(S, b), | 72 | @bitCast(word_t.HalfTU, input.s.low) >> @intCast(S, b), |
| 89 | ); | 73 | ); |
| 90 | } | 74 | } |
| 91 | 75 | ||
| ... | @@ -95,20 +79,20 @@ inline fn ashrXi3(comptime T: type, a: T, b: i32) T { | ... | @@ -95,20 +79,20 @@ inline fn ashrXi3(comptime T: type, a: T, b: i32) T { |
| 95 | // Logical shift right: shift in 0 from left to right | 79 | // Logical shift right: shift in 0 from left to right |
| 96 | // Precondition: 0 <= b < T.bit_count | 80 | // Precondition: 0 <= b < T.bit_count |
| 97 | inline fn lshrXi3(comptime T: type, a: T, b: i32) T { | 81 | inline fn lshrXi3(comptime T: type, a: T, b: i32) T { |
| 98 | const dwords = Dwords(T, false); | 82 | const word_t = common.HalveInt(T, false); |
| 99 | const S = Log2Int(dwords.HalfT); | 83 | const S = Log2Int(word_t.HalfT); |
| 100 | 84 | ||
| 101 | const input = dwords{ .all = a }; | 85 | const input = word_t{ .all = a }; |
| 102 | var output: dwords = undefined; | 86 | var output: word_t = undefined; |
| 103 | 87 | ||
| 104 | if (b >= dwords.bits) { | 88 | if (b >= word_t.bits) { |
| 105 | output.s.high = 0; | 89 | output.s.high = 0; |
| 106 | output.s.low = input.s.high >> @intCast(S, b - dwords.bits); | 90 | output.s.low = input.s.high >> @intCast(S, b - word_t.bits); |
| 107 | } else if (b == 0) { | 91 | } else if (b == 0) { |
| 108 | return a; | 92 | return a; |
| 109 | } else { | 93 | } else { |
| 110 | output.s.high = input.s.high >> @intCast(S, b); | 94 | output.s.high = input.s.high >> @intCast(S, b); |
| 111 | output.s.low = input.s.high << @intCast(S, dwords.bits - b); | 95 | output.s.low = input.s.high << @intCast(S, word_t.bits - b); |
| 112 | output.s.low |= input.s.low >> @intCast(S, b); | 96 | output.s.low |= input.s.low >> @intCast(S, b); |
| 113 | } | 97 | } |
| 114 | 98 |
lib/docs/main.js+89-91| ... | @@ -106,7 +106,7 @@ const NAV_MODES = { | ... | @@ -106,7 +106,7 @@ const NAV_MODES = { |
| 106 | // empty array means refers to the package itself | 106 | // empty array means refers to the package itself |
| 107 | declNames: [], | 107 | declNames: [], |
| 108 | // these will be all types, except the last one may be a type or a decl | 108 | // these will be all types, except the last one may be a type or a decl |
| 109 | declObjs: [], | 109 | declObjs: [], |
| 110 | // (a, b, c, d) comptime call; result is the value the docs refer to | 110 | // (a, b, c, d) comptime call; result is the value the docs refer to |
| 111 | callName: null, | 111 | callName: null, |
| 112 | }; | 112 | }; |
| ... | @@ -200,7 +200,7 @@ const NAV_MODES = { | ... | @@ -200,7 +200,7 @@ const NAV_MODES = { |
| 200 | case NAV_MODES.GUIDES: | 200 | case NAV_MODES.GUIDES: |
| 201 | document.title = "[G] " + curNav.activeGuide + suffix; | 201 | document.title = "[G] " + curNav.activeGuide + suffix; |
| 202 | return; | 202 | return; |
| 203 | } | 203 | } |
| 204 | } | 204 | } |
| 205 | 205 | ||
| 206 | function isDecl(x) { | 206 | function isDecl(x) { |
| ... | @@ -401,7 +401,7 @@ const NAV_MODES = { | ... | @@ -401,7 +401,7 @@ const NAV_MODES = { |
| 401 | domGuideSwitch.classList.add("active"); | 401 | domGuideSwitch.classList.add("active"); |
| 402 | domApiSwitch.classList.remove("active"); | 402 | domApiSwitch.classList.remove("active"); |
| 403 | domDocs.classList.add("hidden"); | 403 | domDocs.classList.add("hidden"); |
| 404 | domGuides.classList.remove("hidden"); | 404 | domGuides.classList.remove("hidden"); |
| 405 | domApiMenu.classList.add("hidden"); | 405 | domApiMenu.classList.add("hidden"); |
| 406 | 406 | ||
| 407 | // sidebar guides list | 407 | // sidebar guides list |
| ... | @@ -422,7 +422,7 @@ const NAV_MODES = { | ... | @@ -422,7 +422,7 @@ const NAV_MODES = { |
| 422 | if (list.length > 0) { | 422 | if (list.length > 0) { |
| 423 | domGuidesMenu.classList.remove("hidden"); | 423 | domGuidesMenu.classList.remove("hidden"); |
| 424 | } | 424 | } |
| 425 | 425 | ||
| 426 | // main content | 426 | // main content |
| 427 | const activeGuide = zigAnalysis.guides[curNav.activeGuide]; | 427 | const activeGuide = zigAnalysis.guides[curNav.activeGuide]; |
| 428 | if (activeGuide == undefined) { | 428 | if (activeGuide == undefined) { |
| ... | @@ -454,7 +454,7 @@ const NAV_MODES = { | ... | @@ -454,7 +454,7 @@ const NAV_MODES = { |
| 454 | 454 | ||
| 455 | Happy writing! | 455 | Happy writing! |
| 456 | `); | 456 | `); |
| 457 | } else { | 457 | } else { |
| 458 | domGuides.innerHTML = markdown(activeGuide); | 458 | domGuides.innerHTML = markdown(activeGuide); |
| 459 | } | 459 | } |
| 460 | } | 460 | } |
| ... | @@ -467,7 +467,7 @@ const NAV_MODES = { | ... | @@ -467,7 +467,7 @@ const NAV_MODES = { |
| 467 | domDocs.classList.remove("hidden"); | 467 | domDocs.classList.remove("hidden"); |
| 468 | domApiMenu.classList.remove("hidden"); | 468 | domApiMenu.classList.remove("hidden"); |
| 469 | domGuidesMenu.classList.add("hidden"); | 469 | domGuidesMenu.classList.add("hidden"); |
| 470 | 470 | ||
| 471 | domStatus.classList.add("hidden"); | 471 | domStatus.classList.add("hidden"); |
| 472 | domFnProto.classList.add("hidden"); | 472 | domFnProto.classList.add("hidden"); |
| 473 | domSectParams.classList.add("hidden"); | 473 | domSectParams.classList.add("hidden"); |
| ... | @@ -537,9 +537,9 @@ const NAV_MODES = { | ... | @@ -537,9 +537,9 @@ const NAV_MODES = { |
| 537 | currentType = childDecl; | 537 | currentType = childDecl; |
| 538 | curNav.declObjs.push(currentType); | 538 | curNav.declObjs.push(currentType); |
| 539 | } | 539 | } |
| 540 | 540 | ||
| 541 | 541 | ||
| 542 | 542 | ||
| 543 | window.x = currentType; | 543 | window.x = currentType; |
| 544 | 544 | ||
| 545 | renderNav(); | 545 | renderNav(); |
| ... | @@ -586,15 +586,15 @@ const NAV_MODES = { | ... | @@ -586,15 +586,15 @@ const NAV_MODES = { |
| 586 | switch (curNav.mode) { | 586 | switch (curNav.mode) { |
| 587 | case NAV_MODES.API: | 587 | case NAV_MODES.API: |
| 588 | case NAV_MODES.API_INTERNAL: | 588 | case NAV_MODES.API_INTERNAL: |
| 589 | return renderApi(); | 589 | return renderApi(); |
| 590 | case NAV_MODES.GUIDES: | 590 | case NAV_MODES.GUIDES: |
| 591 | return renderGuides(); | 591 | return renderGuides(); |
| 592 | default: | 592 | default: |
| 593 | throw "?"; | 593 | throw "?"; |
| 594 | } | 594 | } |
| 595 | } | 595 | } |
| 596 | 596 | ||
| 597 | 597 | ||
| 598 | function renderDocTest(decl) { | 598 | function renderDocTest(decl) { |
| 599 | if (!decl.decltest) return; | 599 | if (!decl.decltest) return; |
| 600 | const astNode = getAstNode(decl.decltest); | 600 | const astNode = getAstNode(decl.decltest); |
| ... | @@ -651,7 +651,7 @@ const NAV_MODES = { | ... | @@ -651,7 +651,7 @@ const NAV_MODES = { |
| 651 | wantLink: true, | 651 | wantLink: true, |
| 652 | fnDecl, | 652 | fnDecl, |
| 653 | }); | 653 | }); |
| 654 | 654 | ||
| 655 | domFnSourceLink.innerHTML = "[<a target=\"_blank\" href=\"" + sourceFileLink(fnDecl) + "\">src</a>]"; | 655 | domFnSourceLink.innerHTML = "[<a target=\"_blank\" href=\"" + sourceFileLink(fnDecl) + "\">src</a>]"; |
| 656 | 656 | ||
| 657 | let docsSource = null; | 657 | let docsSource = null; |
| ... | @@ -895,7 +895,7 @@ const NAV_MODES = { | ... | @@ -895,7 +895,7 @@ const NAV_MODES = { |
| 895 | 895 | ||
| 896 | function navLink(pkgNames, declNames, callName) { | 896 | function navLink(pkgNames, declNames, callName) { |
| 897 | let base = curNav.mode; | 897 | let base = curNav.mode; |
| 898 | 898 | ||
| 899 | if (pkgNames.length === 0 && declNames.length === 0) { | 899 | if (pkgNames.length === 0 && declNames.length === 0) { |
| 900 | return base; | 900 | return base; |
| 901 | } else if (declNames.length === 0 && callName == null) { | 901 | } else if (declNames.length === 0 && callName == null) { |
| ... | @@ -919,18 +919,18 @@ const NAV_MODES = { | ... | @@ -919,18 +919,18 @@ const NAV_MODES = { |
| 919 | 919 | ||
| 920 | function findDeclNavLink(declName) { | 920 | function findDeclNavLink(declName) { |
| 921 | if (curNav.declObjs.length == 0) return null; | 921 | if (curNav.declObjs.length == 0) return null; |
| 922 | const curFile = getAstNode(curNav.declObjs[curNav.declObjs.length-1].src).file; | 922 | const curFile = getAstNode(curNav.declObjs[curNav.declObjs.length - 1].src).file; |
| 923 | 923 | ||
| 924 | for (let i = curNav.declObjs.length -1; i >= 0; i--) { | 924 | for (let i = curNav.declObjs.length - 1; i >= 0; i--) { |
| 925 | const curDecl = curNav.declObjs[i]; | 925 | const curDecl = curNav.declObjs[i]; |
| 926 | const curDeclName = curNav.declNames[i-1]; | 926 | const curDeclName = curNav.declNames[i - 1]; |
| 927 | if (curDeclName == declName) { | 927 | if (curDeclName == declName) { |
| 928 | const declPath = curNav.declNames.slice(0,i); | 928 | const declPath = curNav.declNames.slice(0, i); |
| 929 | return navLink(curNav.pkgNames, declPath); | 929 | return navLink(curNav.pkgNames, declPath); |
| 930 | } | 930 | } |
| 931 | 931 | ||
| 932 | if (findSubDecl(curDecl, declName) != null) { | 932 | if (findSubDecl(curDecl, declName) != null) { |
| 933 | const declPath = curNav.declNames.slice(0,i).concat([declName]); | 933 | const declPath = curNav.declNames.slice(0, i).concat([declName]); |
| 934 | return navLink(curNav.pkgNames, declPath); | 934 | return navLink(curNav.pkgNames, declPath); |
| 935 | } | 935 | } |
| 936 | } | 936 | } |
| ... | @@ -1366,10 +1366,6 @@ const NAV_MODES = { | ... | @@ -1366,10 +1366,6 @@ const NAV_MODES = { |
| 1366 | payloadHtml += "truncate"; | 1366 | payloadHtml += "truncate"; |
| 1367 | break; | 1367 | break; |
| 1368 | } | 1368 | } |
| 1369 | case "align_cast": { | ||
| 1370 | payloadHtml += "alignCast"; | ||
| 1371 | break; | ||
| 1372 | } | ||
| 1373 | case "has_decl": { | 1369 | case "has_decl": { |
| 1374 | payloadHtml += "hasDecl"; | 1370 | payloadHtml += "hasDecl"; |
| 1375 | break; | 1371 | break; |
| ... | @@ -1700,15 +1696,15 @@ const NAV_MODES = { | ... | @@ -1700,15 +1696,15 @@ const NAV_MODES = { |
| 1700 | } | 1696 | } |
| 1701 | case "declRef": { | 1697 | case "declRef": { |
| 1702 | const name = getDecl(expr.declRef).name; | 1698 | const name = getDecl(expr.declRef).name; |
| 1703 | 1699 | ||
| 1704 | if (opts.wantHtml) { | 1700 | if (opts.wantHtml) { |
| 1705 | let payloadHtml = ""; | 1701 | let payloadHtml = ""; |
| 1706 | if (opts.wantLink) { | 1702 | if (opts.wantLink) { |
| 1707 | payloadHtml += '<a href="'+ findDeclNavLink(name) +'">'; | 1703 | payloadHtml += '<a href="' + findDeclNavLink(name) + '">'; |
| 1708 | } | 1704 | } |
| 1709 | payloadHtml += | 1705 | payloadHtml += |
| 1710 | '<span class="tok-kw" style="color:lightblue;">' + | 1706 | '<span class="tok-kw" style="color:lightblue;">' + |
| 1711 | name + | 1707 | name + |
| 1712 | "</span>"; | 1708 | "</span>"; |
| 1713 | if (opts.wantLink) payloadHtml += "</a>"; | 1709 | if (opts.wantLink) payloadHtml += "</a>"; |
| 1714 | return payloadHtml; | 1710 | return payloadHtml; |
| ... | @@ -1728,12 +1724,12 @@ const NAV_MODES = { | ... | @@ -1728,12 +1724,12 @@ const NAV_MODES = { |
| 1728 | if ("string" in expr.refPath[i]) { | 1724 | if ("string" in expr.refPath[i]) { |
| 1729 | component = expr.refPath[i].string; | 1725 | component = expr.refPath[i].string; |
| 1730 | } else { | 1726 | } else { |
| 1731 | component = exprName(expr.refPath[i], {...opts, wantLink: false}); | 1727 | component = exprName(expr.refPath[i], { ...opts, wantLink: false }); |
| 1732 | if (opts.wantLink && "declRef" in expr.refPath[i]) { | 1728 | if (opts.wantLink && "declRef" in expr.refPath[i]) { |
| 1733 | url += "." + getDecl(expr.refPath[i].declRef).name; | 1729 | url += "." + getDecl(expr.refPath[i].declRef).name; |
| 1734 | component = '<a href="'+ url +'">' + | 1730 | component = '<a href="' + url + '">' + |
| 1735 | component + | 1731 | component + |
| 1736 | "</a>"; | 1732 | "</a>"; |
| 1737 | } | 1733 | } |
| 1738 | } | 1734 | } |
| 1739 | name += "." + component; | 1735 | name += "." + component; |
| ... | @@ -1792,12 +1788,12 @@ const NAV_MODES = { | ... | @@ -1792,12 +1788,12 @@ const NAV_MODES = { |
| 1792 | name = "struct { "; | 1788 | name = "struct { "; |
| 1793 | } | 1789 | } |
| 1794 | } | 1790 | } |
| 1795 | if (structObj.fields.length > 1 && opts.wantHtml) {name += "</br>";} | 1791 | if (structObj.fields.length > 1 && opts.wantHtml) { name += "</br>"; } |
| 1796 | let indent = ""; | 1792 | let indent = ""; |
| 1797 | if (structObj.fields.length > 1 && opts.wantHtml) { | 1793 | if (structObj.fields.length > 1 && opts.wantHtml) { |
| 1798 | indent = "&nbsp;&nbsp;&nbsp;&nbsp;" | 1794 | indent = "&nbsp;&nbsp;&nbsp;&nbsp;" |
| 1799 | } | 1795 | } |
| 1800 | if (opts.indent) { | 1796 | if (opts.indent && structObj.fields.length > 1) { |
| 1801 | indent = opts.indent + indent; | 1797 | indent = opts.indent + indent; |
| 1802 | } | 1798 | } |
| 1803 | let structNode = getAstNode(structObj.src); | 1799 | let structNode = getAstNode(structObj.src); |
| ... | @@ -1808,7 +1804,7 @@ const NAV_MODES = { | ... | @@ -1808,7 +1804,7 @@ const NAV_MODES = { |
| 1808 | field_end += " "; | 1804 | field_end += " "; |
| 1809 | } | 1805 | } |
| 1810 | 1806 | ||
| 1811 | for(let i = 0; i < structObj.fields.length; i += 1) { | 1807 | for (let i = 0; i < structObj.fields.length; i += 1) { |
| 1812 | let fieldNode = getAstNode(structNode.fields[i]); | 1808 | let fieldNode = getAstNode(structNode.fields[i]); |
| 1813 | let fieldName = fieldNode.name; | 1809 | let fieldName = fieldNode.name; |
| 1814 | let html = indent; | 1810 | let html = indent; |
| ... | @@ -1817,17 +1813,17 @@ const NAV_MODES = { | ... | @@ -1817,17 +1813,17 @@ const NAV_MODES = { |
| 1817 | } | 1813 | } |
| 1818 | 1814 | ||
| 1819 | let fieldTypeExpr = structObj.fields[i]; | 1815 | let fieldTypeExpr = structObj.fields[i]; |
| 1820 | if(!structObj.is_tuple) { | 1816 | if (!structObj.is_tuple) { |
| 1821 | html += ": "; | 1817 | html += ": "; |
| 1822 | } | 1818 | } |
| 1823 | 1819 | ||
| 1824 | html += exprName(fieldTypeExpr, {...opts, indent: indent}); | 1820 | html += exprName(fieldTypeExpr, { ...opts, indent: indent }); |
| 1825 | 1821 | ||
| 1826 | html += field_end; | 1822 | html += field_end; |
| 1827 | 1823 | ||
| 1828 | name += html; | 1824 | name += html; |
| 1829 | } | 1825 | } |
| 1830 | if (opts.indent) { | 1826 | if (opts.indent && structObj.fields.length > 1) { |
| 1831 | name += opts.indent; | 1827 | name += opts.indent; |
| 1832 | } | 1828 | } |
| 1833 | name += "}"; | 1829 | name += "}"; |
| ... | @@ -1850,7 +1846,7 @@ const NAV_MODES = { | ... | @@ -1850,7 +1846,7 @@ const NAV_MODES = { |
| 1850 | if (enumObj.nonexhaustive) { | 1846 | if (enumObj.nonexhaustive) { |
| 1851 | fields_len += 1; | 1847 | fields_len += 1; |
| 1852 | } | 1848 | } |
| 1853 | if (fields_len > 1 && opts.wantHtml) {name += "</br>";} | 1849 | if (fields_len > 1 && opts.wantHtml) { name += "</br>"; } |
| 1854 | let indent = ""; | 1850 | let indent = ""; |
| 1855 | if (fields_len > 1) { | 1851 | if (fields_len > 1) { |
| 1856 | if (opts.wantHtml) { | 1852 | if (opts.wantHtml) { |
| ... | @@ -1868,10 +1864,10 @@ const NAV_MODES = { | ... | @@ -1868,10 +1864,10 @@ const NAV_MODES = { |
| 1868 | } else { | 1864 | } else { |
| 1869 | field_end += " "; | 1865 | field_end += " "; |
| 1870 | } | 1866 | } |
| 1871 | for(let i = 0; i < enumNode.fields.length; i += 1) { | 1867 | for (let i = 0; i < enumNode.fields.length; i += 1) { |
| 1872 | let fieldNode = getAstNode(enumNode.fields[i]); | 1868 | let fieldNode = getAstNode(enumNode.fields[i]); |
| 1873 | let fieldName = fieldNode.name; | 1869 | let fieldName = fieldNode.name; |
| 1874 | let html = indent + escapeHtml(fieldName); | 1870 | let html = indent + escapeHtml(fieldName); |
| 1875 | 1871 | ||
| 1876 | html += field_end; | 1872 | html += field_end; |
| 1877 | 1873 | ||
| ... | @@ -1895,16 +1891,16 @@ const NAV_MODES = { | ... | @@ -1895,16 +1891,16 @@ const NAV_MODES = { |
| 1895 | name = "union"; | 1891 | name = "union"; |
| 1896 | } | 1892 | } |
| 1897 | if (unionObj.auto_tag) { | 1893 | if (unionObj.auto_tag) { |
| 1898 | if (opts.wantHtml) { | 1894 | if (opts.wantHtml) { |
| 1899 | name += " (<span class='tok-kw'>enum</span>"; | 1895 | name += " (<span class='tok-kw'>enum</span>"; |
| 1900 | } else { | 1896 | } else { |
| 1901 | name += " (enum"; | 1897 | name += " (enum"; |
| 1902 | } | 1898 | } |
| 1903 | if (unionObj.tag) { | 1899 | if (unionObj.tag) { |
| 1904 | name += "(" + exprName(unionObj.tag, opts) + "))"; | 1900 | name += "(" + exprName(unionObj.tag, opts) + "))"; |
| 1905 | } else { | 1901 | } else { |
| 1906 | name += ")"; | 1902 | name += ")"; |
| 1907 | } | 1903 | } |
| 1908 | } else if (unionObj.tag) { | 1904 | } else if (unionObj.tag) { |
| 1909 | name += " (" + exprName(unionObj.tag, opts) + ")"; | 1905 | name += " (" + exprName(unionObj.tag, opts) + ")"; |
| 1910 | } | 1906 | } |
| ... | @@ -1926,7 +1922,7 @@ const NAV_MODES = { | ... | @@ -1926,7 +1922,7 @@ const NAV_MODES = { |
| 1926 | } else { | 1922 | } else { |
| 1927 | field_end += " "; | 1923 | field_end += " "; |
| 1928 | } | 1924 | } |
| 1929 | for(let i = 0; i < unionObj.fields.length; i += 1) { | 1925 | for (let i = 0; i < unionObj.fields.length; i += 1) { |
| 1930 | let fieldNode = getAstNode(unionNode.fields[i]); | 1926 | let fieldNode = getAstNode(unionNode.fields[i]); |
| 1931 | let fieldName = fieldNode.name; | 1927 | let fieldName = fieldNode.name; |
| 1932 | let html = indent + escapeHtml(fieldName); | 1928 | let html = indent + escapeHtml(fieldName); |
| ... | @@ -1934,7 +1930,7 @@ const NAV_MODES = { | ... | @@ -1934,7 +1930,7 @@ const NAV_MODES = { |
| 1934 | let fieldTypeExpr = unionObj.fields[i]; | 1930 | let fieldTypeExpr = unionObj.fields[i]; |
| 1935 | html += ": "; | 1931 | html += ": "; |
| 1936 | 1932 | ||
| 1937 | html += exprName(fieldTypeExpr, {...opts, indent: indent}); | 1933 | html += exprName(fieldTypeExpr, { ...opts, indent: indent }); |
| 1938 | 1934 | ||
| 1939 | html += field_end; | 1935 | html += field_end; |
| 1940 | 1936 | ||
| ... | @@ -2163,7 +2159,7 @@ const NAV_MODES = { | ... | @@ -2163,7 +2159,7 @@ const NAV_MODES = { |
| 2163 | opts.fnDecl = null; | 2159 | opts.fnDecl = null; |
| 2164 | opts.linkFnNameDecl = null; | 2160 | opts.linkFnNameDecl = null; |
| 2165 | let payloadHtml = ""; | 2161 | let payloadHtml = ""; |
| 2166 | if (opts.addParensIfFnSignature && fnObj.src == 0){ | 2162 | if (opts.addParensIfFnSignature && fnObj.src == 0) { |
| 2167 | payloadHtml += "("; | 2163 | payloadHtml += "("; |
| 2168 | } | 2164 | } |
| 2169 | if (opts.wantHtml) { | 2165 | if (opts.wantHtml) { |
| ... | @@ -2179,7 +2175,7 @@ const NAV_MODES = { | ... | @@ -2179,7 +2175,7 @@ const NAV_MODES = { |
| 2179 | if (linkFnNameDecl) { | 2175 | if (linkFnNameDecl) { |
| 2180 | payloadHtml += | 2176 | payloadHtml += |
| 2181 | '<a href="' + linkFnNameDecl + '">' + | 2177 | '<a href="' + linkFnNameDecl + '">' + |
| 2182 | escapeHtml(fnDecl.name) + | 2178 | escapeHtml(fnDecl.name) + |
| 2183 | "</a>"; | 2179 | "</a>"; |
| 2184 | } else { | 2180 | } else { |
| 2185 | payloadHtml += escapeHtml(fnDecl.name); | 2181 | payloadHtml += escapeHtml(fnDecl.name); |
| ... | @@ -2198,7 +2194,7 @@ const NAV_MODES = { | ... | @@ -2198,7 +2194,7 @@ const NAV_MODES = { |
| 2198 | fields = fnNode.fields; | 2194 | fields = fnNode.fields; |
| 2199 | isVarArgs = fnNode.varArgs; | 2195 | isVarArgs = fnNode.varArgs; |
| 2200 | } | 2196 | } |
| 2201 | 2197 | ||
| 2202 | for (let i = 0; i < fnObj.params.length; i += 1) { | 2198 | for (let i = 0; i < fnObj.params.length; i += 1) { |
| 2203 | if (i != 0) { | 2199 | if (i != 0) { |
| 2204 | payloadHtml += ", "; | 2200 | payloadHtml += ", "; |
| ... | @@ -2251,13 +2247,13 @@ const NAV_MODES = { | ... | @@ -2251,13 +2247,13 @@ const NAV_MODES = { |
| 2251 | } else if ("typeOf" in value) { | 2247 | } else if ("typeOf" in value) { |
| 2252 | payloadHtml += exprName(value, opts); | 2248 | payloadHtml += exprName(value, opts); |
| 2253 | } else if ("typeOf_peer" in value) { | 2249 | } else if ("typeOf_peer" in value) { |
| 2254 | payloadHtml += exprName(value, opts); | 2250 | payloadHtml += exprName(value, opts); |
| 2255 | } else if ("declRef" in value) { | 2251 | } else if ("declRef" in value) { |
| 2256 | payloadHtml += exprName(value, opts); | 2252 | payloadHtml += exprName(value, opts); |
| 2257 | } else if ("call" in value) { | 2253 | } else if ("call" in value) { |
| 2258 | payloadHtml += exprName(value, opts); | 2254 | payloadHtml += exprName(value, opts); |
| 2259 | } else if ("refPath" in value) { | 2255 | } else if ("refPath" in value) { |
| 2260 | payloadHtml += exprName(value, opts); | 2256 | payloadHtml += exprName(value, opts); |
| 2261 | } else if ("type" in value) { | 2257 | } else if ("type" in value) { |
| 2262 | payloadHtml += exprName(value, opts); | 2258 | payloadHtml += exprName(value, opts); |
| 2263 | //payloadHtml += '<span class="tok-kw">' + name + "</span>"; | 2259 | //payloadHtml += '<span class="tok-kw">' + name + "</span>"; |
| ... | @@ -2301,7 +2297,7 @@ const NAV_MODES = { | ... | @@ -2301,7 +2297,7 @@ const NAV_MODES = { |
| 2301 | } | 2297 | } |
| 2302 | if (fnObj.ret != null) { | 2298 | if (fnObj.ret != null) { |
| 2303 | payloadHtml += exprName(fnObj.ret, { | 2299 | payloadHtml += exprName(fnObj.ret, { |
| 2304 | ...opts, | 2300 | ...opts, |
| 2305 | addParensIfFnSignature: true, | 2301 | addParensIfFnSignature: true, |
| 2306 | }); | 2302 | }); |
| 2307 | } else if (opts.wantHtml) { | 2303 | } else if (opts.wantHtml) { |
| ... | @@ -2310,7 +2306,7 @@ const NAV_MODES = { | ... | @@ -2310,7 +2306,7 @@ const NAV_MODES = { |
| 2310 | payloadHtml += "anytype"; | 2306 | payloadHtml += "anytype"; |
| 2311 | } | 2307 | } |
| 2312 | 2308 | ||
| 2313 | if (opts.addParensIfFnSignature && fnObj.src == 0){ | 2309 | if (opts.addParensIfFnSignature && fnObj.src == 0) { |
| 2314 | payloadHtml += ")"; | 2310 | payloadHtml += ")"; |
| 2315 | } | 2311 | } |
| 2316 | return payloadHtml; | 2312 | return payloadHtml; |
| ... | @@ -2353,7 +2349,7 @@ const NAV_MODES = { | ... | @@ -2353,7 +2349,7 @@ const NAV_MODES = { |
| 2353 | ) { | 2349 | ) { |
| 2354 | name = "std"; | 2350 | name = "std"; |
| 2355 | } else { | 2351 | } else { |
| 2356 | name = exprName({ type: typeObj }, {wantHtml: false, wantLink: false}); | 2352 | name = exprName({ type: typeObj }, { wantHtml: false, wantLink: false }); |
| 2357 | } | 2353 | } |
| 2358 | if (name != null && name != "") { | 2354 | if (name != null && name != "") { |
| 2359 | domHdrName.innerText = | 2355 | domHdrName.innerText = |
| ... | @@ -2644,10 +2640,10 @@ const NAV_MODES = { | ... | @@ -2644,10 +2640,10 @@ const NAV_MODES = { |
| 2644 | } | 2640 | } |
| 2645 | 2641 | ||
| 2646 | function sourceFileLink(decl) { | 2642 | function sourceFileLink(decl) { |
| 2647 | const srcNode = getAstNode(decl.src); | 2643 | const srcNode = getAstNode(decl.src); |
| 2648 | return sourceFileUrlTemplate. | 2644 | return sourceFileUrlTemplate. |
| 2649 | replace("{{file}}", zigAnalysis.files[srcNode.file]). | 2645 | replace("{{file}}", zigAnalysis.files[srcNode.file]). |
| 2650 | replace("{{line}}", srcNode.line + 1); | 2646 | replace("{{line}}", srcNode.line + 1); |
| 2651 | } | 2647 | } |
| 2652 | 2648 | ||
| 2653 | function renderContainer(container) { | 2649 | function renderContainer(container) { |
| ... | @@ -2783,8 +2779,8 @@ const NAV_MODES = { | ... | @@ -2783,8 +2779,8 @@ const NAV_MODES = { |
| 2783 | if (short != docs) { | 2779 | if (short != docs) { |
| 2784 | short = markdown(short); | 2780 | short = markdown(short); |
| 2785 | var long = markdown(docs); | 2781 | var long = markdown(docs); |
| 2786 | tdDesc.innerHTML = | 2782 | tdDesc.innerHTML = |
| 2787 | "<div class=\"expand\" ><span class=\"button\" onclick=\"toggleExpand(event)\"></span><div class=\"sum-less\">" + short + "</div>" + "<div class=\"sum-more\">" + long + "</div></details>"; | 2783 | "<div class=\"expand\" ><span class=\"button\" onclick=\"toggleExpand(event)\"></span><div class=\"sum-less\">" + short + "</div>" + "<div class=\"sum-more\">" + long + "</div></details>"; |
| 2788 | } | 2784 | } |
| 2789 | else { | 2785 | else { |
| 2790 | tdDesc.innerHTML = markdown(short); | 2786 | tdDesc.innerHTML = markdown(short); |
| ... | @@ -2818,10 +2814,10 @@ const NAV_MODES = { | ... | @@ -2818,10 +2814,10 @@ const NAV_MODES = { |
| 2818 | html += ' = <span class="tok-number">' + fieldName + "</span>"; | 2814 | html += ' = <span class="tok-number">' + fieldName + "</span>"; |
| 2819 | } else { | 2815 | } else { |
| 2820 | let fieldTypeExpr = container.fields[i]; | 2816 | let fieldTypeExpr = container.fields[i]; |
| 2821 | if(container.kind ==! typeKinds.Struct || !container.is_tuple) { | 2817 | if (container.kind !== typeKinds.Struct || !container.is_tuple) { |
| 2822 | html += ": "; | 2818 | html += ": "; |
| 2823 | } | 2819 | } |
| 2824 | html += exprName(fieldTypeExpr, {wantHtml:true, wantLink:true}); | 2820 | html += exprName(fieldTypeExpr, { wantHtml: true, wantLink: true }); |
| 2825 | let tsn = typeShorthandName(fieldTypeExpr); | 2821 | let tsn = typeShorthandName(fieldTypeExpr); |
| 2826 | if (tsn) { | 2822 | if (tsn) { |
| 2827 | html += "<span> (" + tsn + ")</span>"; | 2823 | html += "<span> (" + tsn + ")</span>"; |
| ... | @@ -3007,8 +3003,8 @@ const NAV_MODES = { | ... | @@ -3007,8 +3003,8 @@ const NAV_MODES = { |
| 3007 | throw new Error("No type 'type' found"); | 3003 | throw new Error("No type 'type' found"); |
| 3008 | } | 3004 | } |
| 3009 | 3005 | ||
| 3010 | 3006 | ||
| 3011 | function updateCurNav() { | 3007 | function updateCurNav() { |
| 3012 | curNav = { | 3008 | curNav = { |
| 3013 | mode: NAV_MODES.API, | 3009 | mode: NAV_MODES.API, |
| 3014 | pkgNames: [], | 3010 | pkgNames: [], |
| ... | @@ -3021,7 +3017,7 @@ const NAV_MODES = { | ... | @@ -3021,7 +3017,7 @@ const NAV_MODES = { |
| 3021 | 3017 | ||
| 3022 | const mode = location.hash.substring(0, 3); | 3018 | const mode = location.hash.substring(0, 3); |
| 3023 | let query = location.hash.substring(3); | 3019 | let query = location.hash.substring(3); |
| 3024 | 3020 | ||
| 3025 | const DEFAULT_HASH = NAV_MODES.API + zigAnalysis.packages[zigAnalysis.rootPkg].name; | 3021 | const DEFAULT_HASH = NAV_MODES.API + zigAnalysis.packages[zigAnalysis.rootPkg].name; |
| 3026 | switch (mode) { | 3022 | switch (mode) { |
| 3027 | case NAV_MODES.API: | 3023 | case NAV_MODES.API: |
| ... | @@ -3037,7 +3033,7 @@ const NAV_MODES = { | ... | @@ -3037,7 +3033,7 @@ const NAV_MODES = { |
| 3037 | nonSearchPart = query.substring(0, qpos); | 3033 | nonSearchPart = query.substring(0, qpos); |
| 3038 | curNavSearch = decodeURIComponent(query.substring(qpos + 1)); | 3034 | curNavSearch = decodeURIComponent(query.substring(qpos + 1)); |
| 3039 | } | 3035 | } |
| 3040 | 3036 | ||
| 3041 | let parts = nonSearchPart.split(":"); | 3037 | let parts = nonSearchPart.split(":"); |
| 3042 | if (parts[0] == "") { | 3038 | if (parts[0] == "") { |
| 3043 | location.hash = DEFAULT_HASH; | 3039 | location.hash = DEFAULT_HASH; |
| ... | @@ -3059,14 +3055,14 @@ const NAV_MODES = { | ... | @@ -3059,14 +3055,14 @@ const NAV_MODES = { |
| 3059 | 3055 | ||
| 3060 | curNav.mode = mode; | 3056 | curNav.mode = mode; |
| 3061 | curNav.activeGuide = query; | 3057 | curNav.activeGuide = query; |
| 3062 | 3058 | ||
| 3063 | return; | 3059 | return; |
| 3064 | default: | 3060 | default: |
| 3065 | location.hash = DEFAULT_HASH; | 3061 | location.hash = DEFAULT_HASH; |
| 3066 | return; | 3062 | return; |
| 3067 | } | 3063 | } |
| 3068 | } | 3064 | } |
| 3069 | 3065 | ||
| 3070 | function onHashChange() { | 3066 | function onHashChange() { |
| 3071 | updateCurNav(); | 3067 | updateCurNav(); |
| 3072 | if (domSearch.value !== curNavSearch) { | 3068 | if (domSearch.value !== curNavSearch) { |
| ... | @@ -3103,7 +3099,7 @@ const NAV_MODES = { | ... | @@ -3103,7 +3099,7 @@ const NAV_MODES = { |
| 3103 | if (!callee.generic_ret) return null; | 3099 | if (!callee.generic_ret) return null; |
| 3104 | resolvedGenericRet = resolveValue({ expr: callee.generic_ret }); | 3100 | resolvedGenericRet = resolveValue({ expr: callee.generic_ret }); |
| 3105 | } | 3101 | } |
| 3106 | 3102 | ||
| 3107 | if ("type" in resolvedGenericRet.expr) { | 3103 | if ("type" in resolvedGenericRet.expr) { |
| 3108 | parentType = getType(resolvedGenericRet.expr.type); | 3104 | parentType = getType(resolvedGenericRet.expr.type); |
| 3109 | } | 3105 | } |
| ... | @@ -3248,7 +3244,7 @@ const NAV_MODES = { | ... | @@ -3248,7 +3244,7 @@ const NAV_MODES = { |
| 3248 | }); | 3244 | }); |
| 3249 | } | 3245 | } |
| 3250 | 3246 | ||
| 3251 | function shortDesc(docs){ | 3247 | function shortDesc(docs) { |
| 3252 | const trimmed_docs = docs.trim(); | 3248 | const trimmed_docs = docs.trim(); |
| 3253 | let index = trimmed_docs.indexOf("\n\n"); | 3249 | let index = trimmed_docs.indexOf("\n\n"); |
| 3254 | let cut = false; | 3250 | let cut = false; |
| ... | @@ -3319,14 +3315,16 @@ const NAV_MODES = { | ... | @@ -3319,14 +3315,16 @@ const NAV_MODES = { |
| 3319 | } else if (line.text.startsWith("#")) { | 3315 | } else if (line.text.startsWith("#")) { |
| 3320 | line.type = "h1"; | 3316 | line.type = "h1"; |
| 3321 | line.text = line.text.substr(1); | 3317 | line.text = line.text.substr(1); |
| 3322 | } else if (line.text.startsWith("-")) { | 3318 | } else if (line.text.match(/^-[ \t]+.*$/)) { |
| 3323 | line.type = "ul"; | 3319 | // line starts with a hyphen, followed by spaces or tabs |
| 3324 | line.text = line.text.substr(1); | 3320 | const match = line.text.match(/^-[ \t]+/); |
| 3325 | } else if (line.text.match(/^\d+\..*$/)) { | ||
| 3326 | // if line starts with {number}{dot} | ||
| 3327 | const match = line.text.match(/(\d+)\./); | ||
| 3328 | line.type = "ul"; | 3321 | line.type = "ul"; |
| 3329 | line.text = line.text.substr(match[0].length); | 3322 | line.text = line.text.substr(match[0].length); |
| 3323 | } else if (line.text.match(/^\d+\.[ \t]+.*$/)) { | ||
| 3324 | // line starts with {number}{dot}{spaces or tabs} | ||
| 3325 | const match = line.text.match(/(\d+)\.[ \t]+/); | ||
| 3326 | line.type = "ol"; | ||
| 3327 | line.text = line.text.substr(match[0].length); | ||
| 3330 | line.ordered_number = Number(match[1].length); | 3328 | line.ordered_number = Number(match[1].length); |
| 3331 | } else if (line.text == "```") { | 3329 | } else if (line.text == "```") { |
| 3332 | line.type = "skip"; | 3330 | line.type = "skip"; |
| ... | @@ -3536,7 +3534,7 @@ const NAV_MODES = { | ... | @@ -3536,7 +3534,7 @@ const NAV_MODES = { |
| 3536 | case "ul": | 3534 | case "ul": |
| 3537 | case "ol": | 3535 | case "ol": |
| 3538 | if ( | 3536 | if ( |
| 3539 | !previousLineIs("ul", line_no) || | 3537 | !previousLineIs(line.type, line_no) || |
| 3540 | getPreviousLineIndent(line_no) < line.indent | 3538 | getPreviousLineIndent(line_no) < line.indent |
| 3541 | ) { | 3539 | ) { |
| 3542 | html += "<" + line.type + ">\n"; | 3540 | html += "<" + line.type + ">\n"; |
| ... | @@ -3545,7 +3543,7 @@ const NAV_MODES = { | ... | @@ -3545,7 +3543,7 @@ const NAV_MODES = { |
| 3545 | html += "<li>" + markdownInlines(line.text) + "</li>\n"; | 3543 | html += "<li>" + markdownInlines(line.text) + "</li>\n"; |
| 3546 | 3544 | ||
| 3547 | if ( | 3545 | if ( |
| 3548 | !nextLineIs("ul", line_no) || | 3546 | !nextLineIs(line.type, line_no) || |
| 3549 | getNextLineIndent(line_no) < line.indent | 3547 | getNextLineIndent(line_no) < line.indent |
| 3550 | ) { | 3548 | ) { |
| 3551 | html += "</" + line.type + ">\n"; | 3549 | html += "</" + line.type + ">\n"; |
| ... | @@ -4067,4 +4065,4 @@ function toggleExpand(event) { | ... | @@ -4067,4 +4065,4 @@ function toggleExpand(event) { |
| 4067 | if (!parent.open && parent.getBoundingClientRect().top < 0) { | 4065 | if (!parent.open && parent.getBoundingClientRect().top < 0) { |
| 4068 | parent.parentElement.parentElement.scrollIntoView(true); | 4066 | parent.parentElement.parentElement.scrollIntoView(true); |
| 4069 | } | 4067 | } |
| 4070 | } | ||
| \ No newline at end of file | |||
| 4068 | } | ||
lib/std/Build.zig+4-11| ... | @@ -37,7 +37,7 @@ pub const FmtStep = @import("Build/FmtStep.zig"); | ... | @@ -37,7 +37,7 @@ pub const FmtStep = @import("Build/FmtStep.zig"); |
| 37 | pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); | 37 | pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); |
| 38 | pub const InstallDirStep = @import("Build/InstallDirStep.zig"); | 38 | pub const InstallDirStep = @import("Build/InstallDirStep.zig"); |
| 39 | pub const InstallFileStep = @import("Build/InstallFileStep.zig"); | 39 | pub const InstallFileStep = @import("Build/InstallFileStep.zig"); |
| 40 | pub const InstallRawStep = @import("Build/InstallRawStep.zig"); | 40 | pub const ObjCopyStep = @import("Build/ObjCopyStep.zig"); |
| 41 | pub const CompileStep = @import("Build/CompileStep.zig"); | 41 | pub const CompileStep = @import("Build/CompileStep.zig"); |
| 42 | pub const LogStep = @import("Build/LogStep.zig"); | 42 | pub const LogStep = @import("Build/LogStep.zig"); |
| 43 | pub const OptionsStep = @import("Build/OptionsStep.zig"); | 43 | pub const OptionsStep = @import("Build/OptionsStep.zig"); |
| ... | @@ -1254,11 +1254,8 @@ pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const | ... | @@ -1254,11 +1254,8 @@ pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const |
| 1254 | self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step); | 1254 | self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step); |
| 1255 | } | 1255 | } |
| 1256 | 1256 | ||
| 1257 | /// Output format (BIN vs Intel HEX) determined by filename | 1257 | pub fn addObjCopy(b: *Build, source: FileSource, options: ObjCopyStep.Options) *ObjCopyStep { |
| 1258 | pub fn installRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { | 1258 | return ObjCopyStep.create(b, source, options); |
| 1259 | const raw = self.addInstallRaw(artifact, dest_filename, options); | ||
| 1260 | self.getInstallStep().dependOn(&raw.step); | ||
| 1261 | return raw; | ||
| 1262 | } | 1259 | } |
| 1263 | 1260 | ||
| 1264 | ///`dest_rel_path` is relative to install prefix path | 1261 | ///`dest_rel_path` is relative to install prefix path |
| ... | @@ -1280,10 +1277,6 @@ pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []co | ... | @@ -1280,10 +1277,6 @@ pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []co |
| 1280 | return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path); | 1277 | return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path); |
| 1281 | } | 1278 | } |
| 1282 | 1279 | ||
| 1283 | pub fn addInstallRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { | ||
| 1284 | return InstallRawStep.create(self, artifact, dest_filename, options); | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | pub fn addInstallFileWithDir( | 1280 | pub fn addInstallFileWithDir( |
| 1288 | self: *Build, | 1281 | self: *Build, |
| 1289 | source: FileSource, | 1282 | source: FileSource, |
| ... | @@ -1771,7 +1764,7 @@ test { | ... | @@ -1771,7 +1764,7 @@ test { |
| 1771 | _ = InstallArtifactStep; | 1764 | _ = InstallArtifactStep; |
| 1772 | _ = InstallDirStep; | 1765 | _ = InstallDirStep; |
| 1773 | _ = InstallFileStep; | 1766 | _ = InstallFileStep; |
| 1774 | _ = InstallRawStep; | 1767 | _ = ObjCopyStep; |
| 1775 | _ = CompileStep; | 1768 | _ = CompileStep; |
| 1776 | _ = LogStep; | 1769 | _ = LogStep; |
| 1777 | _ = OptionsStep; | 1770 | _ = OptionsStep; |
lib/std/Build/CompileStep.zig+121-25| ... | @@ -21,7 +21,7 @@ const VcpkgRoot = std.Build.VcpkgRoot; | ... | @@ -21,7 +21,7 @@ const VcpkgRoot = std.Build.VcpkgRoot; |
| 21 | const InstallDir = std.Build.InstallDir; | 21 | const InstallDir = std.Build.InstallDir; |
| 22 | const InstallArtifactStep = std.Build.InstallArtifactStep; | 22 | const InstallArtifactStep = std.Build.InstallArtifactStep; |
| 23 | const GeneratedFile = std.Build.GeneratedFile; | 23 | const GeneratedFile = std.Build.GeneratedFile; |
| 24 | const InstallRawStep = std.Build.InstallRawStep; | 24 | const ObjCopyStep = std.Build.ObjCopyStep; |
| 25 | const EmulatableRunStep = std.Build.EmulatableRunStep; | 25 | const EmulatableRunStep = std.Build.EmulatableRunStep; |
| 26 | const CheckObjectStep = std.Build.CheckObjectStep; | 26 | const CheckObjectStep = std.Build.CheckObjectStep; |
| 27 | const RunStep = std.Build.RunStep; | 27 | const RunStep = std.Build.RunStep; |
| ... | @@ -432,10 +432,6 @@ pub fn install(self: *CompileStep) void { | ... | @@ -432,10 +432,6 @@ pub fn install(self: *CompileStep) void { |
| 432 | self.builder.installArtifact(self); | 432 | self.builder.installArtifact(self); |
| 433 | } | 433 | } |
| 434 | 434 | ||
| 435 | pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { | ||
| 436 | return self.builder.installRaw(self, dest_filename, options); | ||
| 437 | } | ||
| 438 | |||
| 439 | pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { | 435 | pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { |
| 440 | const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); | 436 | const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); |
| 441 | a.builder.getInstallStep().dependOn(&install_file.step); | 437 | a.builder.getInstallStep().dependOn(&install_file.step); |
| ... | @@ -458,6 +454,7 @@ pub fn installConfigHeader( | ... | @@ -458,6 +454,7 @@ pub fn installConfigHeader( |
| 458 | options.install_dir, | 454 | options.install_dir, |
| 459 | dest_rel_path, | 455 | dest_rel_path, |
| 460 | ); | 456 | ); |
| 457 | install_file.step.dependOn(&config_header.step); | ||
| 461 | cs.builder.getInstallStep().dependOn(&install_file.step); | 458 | cs.builder.getInstallStep().dependOn(&install_file.step); |
| 462 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); | 459 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); |
| 463 | } | 460 | } |
| ... | @@ -506,6 +503,18 @@ pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { | ... | @@ -506,6 +503,18 @@ pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { |
| 506 | a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); | 503 | a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); |
| 507 | } | 504 | } |
| 508 | 505 | ||
| 506 | pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep { | ||
| 507 | var copy = options; | ||
| 508 | if (copy.basename == null) { | ||
| 509 | if (options.format) |f| { | ||
| 510 | copy.basename = cs.builder.fmt("{s}.{s}", .{ cs.name, @tagName(f) }); | ||
| 511 | } else { | ||
| 512 | copy.basename = cs.name; | ||
| 513 | } | ||
| 514 | } | ||
| 515 | return cs.builder.addObjCopy(cs.getOutputSource(), copy); | ||
| 516 | } | ||
| 517 | |||
| 509 | /// Deprecated: use `std.Build.addRunArtifact` | 518 | /// Deprecated: use `std.Build.addRunArtifact` |
| 510 | /// This function will run in the context of the package that created the executable, | 519 | /// This function will run in the context of the package that created the executable, |
| 511 | /// which is undesirable when running an executable provided by a dependency package. | 520 | /// which is undesirable when running an executable provided by a dependency package. |
| ... | @@ -955,7 +964,10 @@ pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { | ... | @@ -955,7 +964,10 @@ pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { |
| 955 | /// package's module table using `name`. | 964 | /// package's module table using `name`. |
| 956 | pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { | 965 | pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { |
| 957 | cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM"); | 966 | cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM"); |
| 958 | cs.addRecursiveBuildDeps(module); | 967 | |
| 968 | var done = std.AutoHashMap(*Module, void).init(cs.builder.allocator); | ||
| 969 | defer done.deinit(); | ||
| 970 | cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM"); | ||
| 959 | } | 971 | } |
| 960 | 972 | ||
| 961 | /// Adds a module to be used with `@import` without exposing it in the current | 973 | /// Adds a module to be used with `@import` without exposing it in the current |
| ... | @@ -969,10 +981,12 @@ pub fn addOptions(cs: *CompileStep, module_name: []const u8, options: *OptionsSt | ... | @@ -969,10 +981,12 @@ pub fn addOptions(cs: *CompileStep, module_name: []const u8, options: *OptionsSt |
| 969 | addModule(cs, module_name, options.createModule()); | 981 | addModule(cs, module_name, options.createModule()); |
| 970 | } | 982 | } |
| 971 | 983 | ||
| 972 | fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module) void { | 984 | fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashMap(*Module, void)) !void { |
| 985 | if (done.contains(module)) return; | ||
| 986 | try done.put(module, {}); | ||
| 973 | module.source_file.addStepDependencies(&cs.step); | 987 | module.source_file.addStepDependencies(&cs.step); |
| 974 | for (module.dependencies.values()) |dep| { | 988 | for (module.dependencies.values()) |dep| { |
| 975 | cs.addRecursiveBuildDeps(dep); | 989 | try cs.addRecursiveBuildDeps(dep, done); |
| 976 | } | 990 | } |
| 977 | } | 991 | } |
| 978 | 992 | ||
| ... | @@ -1031,22 +1045,110 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { | ... | @@ -1031,22 +1045,110 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { |
| 1031 | fn appendModuleArgs( | 1045 | fn appendModuleArgs( |
| 1032 | cs: *CompileStep, | 1046 | cs: *CompileStep, |
| 1033 | zig_args: *ArrayList([]const u8), | 1047 | zig_args: *ArrayList([]const u8), |
| 1034 | name: []const u8, | ||
| 1035 | module: *Module, | ||
| 1036 | ) error{OutOfMemory}!void { | 1048 | ) error{OutOfMemory}!void { |
| 1037 | try zig_args.append("--pkg-begin"); | 1049 | // First, traverse the whole dependency graph and give every module a unique name, ideally one |
| 1038 | try zig_args.append(name); | 1050 | // named after what it's called somewhere in the graph. It will help here to have both a mapping |
| 1039 | try zig_args.append(module.builder.pathFromRoot(module.source_file.getPath(module.builder))); | 1051 | // from module to name and a set of all the currently-used names. |
| 1052 | var mod_names = std.AutoHashMap(*Module, []const u8).init(cs.builder.allocator); | ||
| 1053 | var names = std.StringHashMap(void).init(cs.builder.allocator); | ||
| 1054 | |||
| 1055 | var to_name = std.ArrayList(struct { | ||
| 1056 | name: []const u8, | ||
| 1057 | mod: *Module, | ||
| 1058 | }).init(cs.builder.allocator); | ||
| 1059 | { | ||
| 1060 | var it = cs.modules.iterator(); | ||
| 1061 | while (it.next()) |kv| { | ||
| 1062 | // While we're traversing the root dependencies, let's make sure that no module names | ||
| 1063 | // have colons in them, since the CLI forbids it. We handle this for transitive | ||
| 1064 | // dependencies further down. | ||
| 1065 | if (std.mem.indexOfScalar(u8, kv.key_ptr.*, ':') != null) { | ||
| 1066 | @panic("Module names cannot contain colons"); | ||
| 1067 | } | ||
| 1068 | try to_name.append(.{ | ||
| 1069 | .name = kv.key_ptr.*, | ||
| 1070 | .mod = kv.value_ptr.*, | ||
| 1071 | }); | ||
| 1072 | } | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | while (to_name.popOrNull()) |dep| { | ||
| 1076 | if (mod_names.contains(dep.mod)) continue; | ||
| 1077 | |||
| 1078 | // We'll use this buffer to store the name we decide on | ||
| 1079 | var buf = try cs.builder.allocator.alloc(u8, dep.name.len + 32); | ||
| 1080 | // First, try just the exposed dependency name | ||
| 1081 | std.mem.copy(u8, buf, dep.name); | ||
| 1082 | var name = buf[0..dep.name.len]; | ||
| 1083 | var n: usize = 0; | ||
| 1084 | while (names.contains(name)) { | ||
| 1085 | // If that failed, append an incrementing number to the end | ||
| 1086 | name = std.fmt.bufPrint(buf, "{s}{}", .{ dep.name, n }) catch unreachable; | ||
| 1087 | n += 1; | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | try mod_names.put(dep.mod, name); | ||
| 1091 | try names.put(name, {}); | ||
| 1040 | 1092 | ||
| 1093 | var it = dep.mod.dependencies.iterator(); | ||
| 1094 | while (it.next()) |kv| { | ||
| 1095 | // Same colon-in-name check as above, but for transitive dependencies. | ||
| 1096 | if (std.mem.indexOfScalar(u8, kv.key_ptr.*, ':') != null) { | ||
| 1097 | @panic("Module names cannot contain colons"); | ||
| 1098 | } | ||
| 1099 | try to_name.append(.{ | ||
| 1100 | .name = kv.key_ptr.*, | ||
| 1101 | .mod = kv.value_ptr.*, | ||
| 1102 | }); | ||
| 1103 | } | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | // Since the module names given to the CLI are based off of the exposed names, we already know | ||
| 1107 | // that none of the CLI names have colons in them, so there's no need to check that explicitly. | ||
| 1108 | |||
| 1109 | // Every module in the graph is now named; output their definitions | ||
| 1041 | { | 1110 | { |
| 1042 | const keys = module.dependencies.keys(); | 1111 | var it = mod_names.iterator(); |
| 1043 | for (module.dependencies.values(), 0..) |sub_module, i| { | 1112 | while (it.next()) |kv| { |
| 1044 | const sub_name = keys[i]; | 1113 | const mod = kv.key_ptr.*; |
| 1045 | try cs.appendModuleArgs(zig_args, sub_name, sub_module); | 1114 | const name = kv.value_ptr.*; |
| 1115 | |||
| 1116 | const deps_str = try constructDepString(cs.builder.allocator, mod_names, mod.dependencies); | ||
| 1117 | const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder)); | ||
| 1118 | try zig_args.append("--mod"); | ||
| 1119 | try zig_args.append(try std.fmt.allocPrint(cs.builder.allocator, "{s}:{s}:{s}", .{ name, deps_str, src })); | ||
| 1046 | } | 1120 | } |
| 1047 | } | 1121 | } |
| 1048 | 1122 | ||
| 1049 | try zig_args.append("--pkg-end"); | 1123 | // Lastly, output the root dependencies |
| 1124 | const deps_str = try constructDepString(cs.builder.allocator, mod_names, cs.modules); | ||
| 1125 | if (deps_str.len > 0) { | ||
| 1126 | try zig_args.append("--deps"); | ||
| 1127 | try zig_args.append(deps_str); | ||
| 1128 | } | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | fn constructDepString( | ||
| 1132 | allocator: std.mem.Allocator, | ||
| 1133 | mod_names: std.AutoHashMap(*Module, []const u8), | ||
| 1134 | deps: std.StringArrayHashMap(*Module), | ||
| 1135 | ) ![]const u8 { | ||
| 1136 | var deps_str = std.ArrayList(u8).init(allocator); | ||
| 1137 | var it = deps.iterator(); | ||
| 1138 | while (it.next()) |kv| { | ||
| 1139 | const expose = kv.key_ptr.*; | ||
| 1140 | const name = mod_names.get(kv.value_ptr.*).?; | ||
| 1141 | if (std.mem.eql(u8, expose, name)) { | ||
| 1142 | try deps_str.writer().print("{s},", .{name}); | ||
| 1143 | } else { | ||
| 1144 | try deps_str.writer().print("{s}={s},", .{ expose, name }); | ||
| 1145 | } | ||
| 1146 | } | ||
| 1147 | if (deps_str.items.len > 0) { | ||
| 1148 | return deps_str.items[0 .. deps_str.items.len - 1]; // omit trailing comma | ||
| 1149 | } else { | ||
| 1150 | return ""; | ||
| 1151 | } | ||
| 1050 | } | 1152 | } |
| 1051 | 1153 | ||
| 1052 | fn make(step: *Step) !void { | 1154 | fn make(step: *Step) !void { |
| ... | @@ -1573,13 +1675,7 @@ fn make(step: *Step) !void { | ... | @@ -1573,13 +1675,7 @@ fn make(step: *Step) !void { |
| 1573 | try zig_args.append("--test-no-exec"); | 1675 | try zig_args.append("--test-no-exec"); |
| 1574 | } | 1676 | } |
| 1575 | 1677 | ||
| 1576 | { | 1678 | try self.appendModuleArgs(&zig_args); |
| 1577 | const keys = self.modules.keys(); | ||
| 1578 | for (self.modules.values(), 0..) |module, i| { | ||
| 1579 | const name = keys[i]; | ||
| 1580 | try self.appendModuleArgs(&zig_args, name, module); | ||
| 1581 | } | ||
| 1582 | } | ||
| 1583 | 1679 | ||
| 1584 | for (self.include_dirs.items) |include_dir| { | 1680 | for (self.include_dirs.items) |include_dir| { |
| 1585 | switch (include_dir) { | 1681 | switch (include_dir) { |
lib/std/Build/EmulatableRunStep.zig+2-2| ... | @@ -26,7 +26,7 @@ builder: *std.Build, | ... | @@ -26,7 +26,7 @@ builder: *std.Build, |
| 26 | exe: *CompileStep, | 26 | exe: *CompileStep, |
| 27 | 27 | ||
| 28 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution | 28 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution |
| 29 | expected_exit_code: ?u8 = 0, | 29 | expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 }, |
| 30 | 30 | ||
| 31 | /// Override this field to modify the environment | 31 | /// Override this field to modify the environment |
| 32 | env_map: ?*EnvMap, | 32 | env_map: ?*EnvMap, |
| ... | @@ -131,7 +131,7 @@ fn make(step: *Step) !void { | ... | @@ -131,7 +131,7 @@ fn make(step: *Step) !void { |
| 131 | try RunStep.runCommand( | 131 | try RunStep.runCommand( |
| 132 | argv_list.items, | 132 | argv_list.items, |
| 133 | self.builder, | 133 | self.builder, |
| 134 | self.expected_exit_code, | 134 | self.expected_term, |
| 135 | self.stdout_action, | 135 | self.stdout_action, |
| 136 | self.stderr_action, | 136 | self.stderr_action, |
| 137 | .Inherit, | 137 | .Inherit, |
lib/std/Build/InstallRawStep.zig deleted-110| ... | @@ -1,110 +0,0 @@ | ||
| 1 | //! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy` | ||
| 2 | //! subcommand rather than containing an implementation directly. | ||
| 3 | |||
| 4 | const std = @import("std"); | ||
| 5 | const InstallRawStep = @This(); | ||
| 6 | |||
| 7 | const Allocator = std.mem.Allocator; | ||
| 8 | const ArenaAllocator = std.heap.ArenaAllocator; | ||
| 9 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | ||
| 10 | const File = std.fs.File; | ||
| 11 | const InstallDir = std.Build.InstallDir; | ||
| 12 | const CompileStep = std.Build.CompileStep; | ||
| 13 | const Step = std.Build.Step; | ||
| 14 | const elf = std.elf; | ||
| 15 | const fs = std.fs; | ||
| 16 | const io = std.io; | ||
| 17 | const sort = std.sort; | ||
| 18 | |||
| 19 | pub const base_id = .install_raw; | ||
| 20 | |||
| 21 | pub const RawFormat = enum { | ||
| 22 | bin, | ||
| 23 | hex, | ||
| 24 | }; | ||
| 25 | |||
| 26 | step: Step, | ||
| 27 | builder: *std.Build, | ||
| 28 | artifact: *CompileStep, | ||
| 29 | dest_dir: InstallDir, | ||
| 30 | dest_filename: []const u8, | ||
| 31 | options: CreateOptions, | ||
| 32 | output_file: std.Build.GeneratedFile, | ||
| 33 | |||
| 34 | pub const CreateOptions = struct { | ||
| 35 | format: ?RawFormat = null, | ||
| 36 | dest_dir: ?InstallDir = null, | ||
| 37 | only_section: ?[]const u8 = null, | ||
| 38 | pad_to: ?u64 = null, | ||
| 39 | }; | ||
| 40 | |||
| 41 | pub fn create( | ||
| 42 | builder: *std.Build, | ||
| 43 | artifact: *CompileStep, | ||
| 44 | dest_filename: []const u8, | ||
| 45 | options: CreateOptions, | ||
| 46 | ) *InstallRawStep { | ||
| 47 | const self = builder.allocator.create(InstallRawStep) catch @panic("OOM"); | ||
| 48 | self.* = InstallRawStep{ | ||
| 49 | .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), | ||
| 50 | .builder = builder, | ||
| 51 | .artifact = artifact, | ||
| 52 | .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) { | ||
| 53 | .obj => unreachable, | ||
| 54 | .@"test" => unreachable, | ||
| 55 | .exe, .test_exe => .bin, | ||
| 56 | .lib => unreachable, | ||
| 57 | }, | ||
| 58 | .dest_filename = dest_filename, | ||
| 59 | .options = options, | ||
| 60 | .output_file = std.Build.GeneratedFile{ .step = &self.step }, | ||
| 61 | }; | ||
| 62 | self.step.dependOn(&artifact.step); | ||
| 63 | |||
| 64 | builder.pushInstalledFile(self.dest_dir, dest_filename); | ||
| 65 | return self; | ||
| 66 | } | ||
| 67 | |||
| 68 | pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource { | ||
| 69 | return std.Build.FileSource{ .generated = &self.output_file }; | ||
| 70 | } | ||
| 71 | |||
| 72 | fn make(step: *Step) !void { | ||
| 73 | const self = @fieldParentPtr(InstallRawStep, "step", step); | ||
| 74 | const b = self.builder; | ||
| 75 | |||
| 76 | if (self.artifact.target.getObjectFormat() != .elf) { | ||
| 77 | std.debug.print("InstallRawStep only works with ELF format.\n", .{}); | ||
| 78 | return error.InvalidObjectFormat; | ||
| 79 | } | ||
| 80 | |||
| 81 | const full_src_path = self.artifact.getOutputSource().getPath(b); | ||
| 82 | const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename); | ||
| 83 | self.output_file.path = full_dest_path; | ||
| 84 | |||
| 85 | try fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")); | ||
| 86 | |||
| 87 | var argv_list = std.ArrayList([]const u8).init(b.allocator); | ||
| 88 | try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" }); | ||
| 89 | |||
| 90 | if (self.options.only_section) |only_section| { | ||
| 91 | try argv_list.appendSlice(&.{ "-j", only_section }); | ||
| 92 | } | ||
| 93 | if (self.options.pad_to) |pad_to| { | ||
| 94 | try argv_list.appendSlice(&.{ | ||
| 95 | "--pad-to", | ||
| 96 | b.fmt("{d}", .{pad_to}), | ||
| 97 | }); | ||
| 98 | } | ||
| 99 | if (self.options.format) |format| switch (format) { | ||
| 100 | .bin => try argv_list.appendSlice(&.{ "-O", "binary" }), | ||
| 101 | .hex => try argv_list.appendSlice(&.{ "-O", "hex" }), | ||
| 102 | }; | ||
| 103 | |||
| 104 | try argv_list.appendSlice(&.{ full_src_path, full_dest_path }); | ||
| 105 | _ = try self.builder.execFromStep(argv_list.items, &self.step); | ||
| 106 | } | ||
| 107 | |||
| 108 | test { | ||
| 109 | std.testing.refAllDecls(InstallRawStep); | ||
| 110 | } | ||
lib/std/Build/ObjCopyStep.zig created+138| ... | @@ -0,0 +1,138 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const ObjCopyStep = @This(); | ||
| 3 | |||
| 4 | const Allocator = std.mem.Allocator; | ||
| 5 | const ArenaAllocator = std.heap.ArenaAllocator; | ||
| 6 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | ||
| 7 | const File = std.fs.File; | ||
| 8 | const InstallDir = std.Build.InstallDir; | ||
| 9 | const CompileStep = std.Build.CompileStep; | ||
| 10 | const Step = std.Build.Step; | ||
| 11 | const elf = std.elf; | ||
| 12 | const fs = std.fs; | ||
| 13 | const io = std.io; | ||
| 14 | const sort = std.sort; | ||
| 15 | |||
| 16 | pub const base_id: Step.Id = .objcopy; | ||
| 17 | |||
| 18 | pub const RawFormat = enum { | ||
| 19 | bin, | ||
| 20 | hex, | ||
| 21 | }; | ||
| 22 | |||
| 23 | step: Step, | ||
| 24 | builder: *std.Build, | ||
| 25 | file_source: std.Build.FileSource, | ||
| 26 | basename: []const u8, | ||
| 27 | output_file: std.Build.GeneratedFile, | ||
| 28 | |||
| 29 | format: ?RawFormat, | ||
| 30 | only_section: ?[]const u8, | ||
| 31 | pad_to: ?u64, | ||
| 32 | |||
| 33 | pub const Options = struct { | ||
| 34 | basename: ?[]const u8 = null, | ||
| 35 | format: ?RawFormat = null, | ||
| 36 | only_section: ?[]const u8 = null, | ||
| 37 | pad_to: ?u64 = null, | ||
| 38 | }; | ||
| 39 | |||
| 40 | pub fn create( | ||
| 41 | builder: *std.Build, | ||
| 42 | file_source: std.Build.FileSource, | ||
| 43 | options: Options, | ||
| 44 | ) *ObjCopyStep { | ||
| 45 | const self = builder.allocator.create(ObjCopyStep) catch @panic("OOM"); | ||
| 46 | self.* = ObjCopyStep{ | ||
| 47 | .step = Step.init( | ||
| 48 | base_id, | ||
| 49 | builder.fmt("objcopy {s}", .{file_source.getDisplayName()}), | ||
| 50 | builder.allocator, | ||
| 51 | make, | ||
| 52 | ), | ||
| 53 | .builder = builder, | ||
| 54 | .file_source = file_source, | ||
| 55 | .basename = options.basename orelse file_source.getDisplayName(), | ||
| 56 | .output_file = std.Build.GeneratedFile{ .step = &self.step }, | ||
| 57 | |||
| 58 | .format = options.format, | ||
| 59 | .only_section = options.only_section, | ||
| 60 | .pad_to = options.pad_to, | ||
| 61 | }; | ||
| 62 | file_source.addStepDependencies(&self.step); | ||
| 63 | return self; | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource { | ||
| 67 | return .{ .generated = &self.output_file }; | ||
| 68 | } | ||
| 69 | |||
| 70 | fn make(step: *Step) !void { | ||
| 71 | const self = @fieldParentPtr(ObjCopyStep, "step", step); | ||
| 72 | const b = self.builder; | ||
| 73 | |||
| 74 | var man = b.cache.obtain(); | ||
| 75 | defer man.deinit(); | ||
| 76 | |||
| 77 | // Random bytes to make ObjCopyStep unique. Refresh this with new random | ||
| 78 | // bytes when ObjCopyStep implementation is modified incompatibly. | ||
| 79 | man.hash.add(@as(u32, 0xe18b7baf)); | ||
| 80 | |||
| 81 | const full_src_path = self.file_source.getPath(b); | ||
| 82 | _ = try man.addFile(full_src_path, null); | ||
| 83 | man.hash.addOptionalBytes(self.only_section); | ||
| 84 | man.hash.addOptional(self.pad_to); | ||
| 85 | man.hash.addOptional(self.format); | ||
| 86 | |||
| 87 | if (man.hit() catch |err| failWithCacheError(man, err)) { | ||
| 88 | // Cache hit, skip subprocess execution. | ||
| 89 | const digest = man.final(); | ||
| 90 | self.output_file.path = try b.cache_root.join(b.allocator, &.{ | ||
| 91 | "o", &digest, self.basename, | ||
| 92 | }); | ||
| 93 | return; | ||
| 94 | } | ||
| 95 | |||
| 96 | const digest = man.final(); | ||
| 97 | const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename }); | ||
| 98 | const cache_path = "o" ++ fs.path.sep_str ++ digest; | ||
| 99 | b.cache_root.handle.makePath(cache_path) catch |err| { | ||
| 100 | std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); | ||
| 101 | return err; | ||
| 102 | }; | ||
| 103 | |||
| 104 | var argv = std.ArrayList([]const u8).init(b.allocator); | ||
| 105 | try argv.appendSlice(&.{ b.zig_exe, "objcopy" }); | ||
| 106 | |||
| 107 | if (self.only_section) |only_section| { | ||
| 108 | try argv.appendSlice(&.{ "-j", only_section }); | ||
| 109 | } | ||
| 110 | if (self.pad_to) |pad_to| { | ||
| 111 | try argv.appendSlice(&.{ "--pad-to", b.fmt("{d}", .{pad_to}) }); | ||
| 112 | } | ||
| 113 | if (self.format) |format| switch (format) { | ||
| 114 | .bin => try argv.appendSlice(&.{ "-O", "binary" }), | ||
| 115 | .hex => try argv.appendSlice(&.{ "-O", "hex" }), | ||
| 116 | }; | ||
| 117 | |||
| 118 | try argv.appendSlice(&.{ full_src_path, full_dest_path }); | ||
| 119 | _ = try self.builder.execFromStep(argv.items, &self.step); | ||
| 120 | |||
| 121 | self.output_file.path = full_dest_path; | ||
| 122 | try man.writeManifest(); | ||
| 123 | } | ||
| 124 | |||
| 125 | /// TODO consolidate this with the same function in RunStep? | ||
| 126 | /// Also properly deal with concurrency (see open PR) | ||
| 127 | fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { | ||
| 128 | const i = man.failed_file_index orelse failWithSimpleError(err); | ||
| 129 | const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); | ||
| 130 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; | ||
| 131 | std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); | ||
| 132 | std.process.exit(1); | ||
| 133 | } | ||
| 134 | |||
| 135 | fn failWithSimpleError(err: anyerror) noreturn { | ||
| 136 | std.debug.print("{s}\n", .{@errorName(err)}); | ||
| 137 | std.process.exit(1); | ||
| 138 | } | ||
lib/std/Build/RunStep.zig+55-28| ... | @@ -35,7 +35,7 @@ stderr_action: StdIoAction = .inherit, | ... | @@ -35,7 +35,7 @@ stderr_action: StdIoAction = .inherit, |
| 35 | stdin_behavior: std.ChildProcess.StdIo = .Inherit, | 35 | stdin_behavior: std.ChildProcess.StdIo = .Inherit, |
| 36 | 36 | ||
| 37 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution | 37 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution |
| 38 | expected_exit_code: ?u8 = 0, | 38 | expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 }, |
| 39 | 39 | ||
| 40 | /// Print the command before running it | 40 | /// Print the command before running it |
| 41 | print: bool, | 41 | print: bool, |
| ... | @@ -290,7 +290,7 @@ fn make(step: *Step) !void { | ... | @@ -290,7 +290,7 @@ fn make(step: *Step) !void { |
| 290 | try runCommand( | 290 | try runCommand( |
| 291 | argv_list.items, | 291 | argv_list.items, |
| 292 | self.builder, | 292 | self.builder, |
| 293 | self.expected_exit_code, | 293 | self.expected_term, |
| 294 | self.stdout_action, | 294 | self.stdout_action, |
| 295 | self.stderr_action, | 295 | self.stderr_action, |
| 296 | self.stdin_behavior, | 296 | self.stdin_behavior, |
| ... | @@ -304,10 +304,55 @@ fn make(step: *Step) !void { | ... | @@ -304,10 +304,55 @@ fn make(step: *Step) !void { |
| 304 | } | 304 | } |
| 305 | } | 305 | } |
| 306 | 306 | ||
| 307 | fn formatTerm( | ||
| 308 | term: ?std.ChildProcess.Term, | ||
| 309 | comptime fmt: []const u8, | ||
| 310 | options: std.fmt.FormatOptions, | ||
| 311 | writer: anytype, | ||
| 312 | ) !void { | ||
| 313 | _ = fmt; | ||
| 314 | _ = options; | ||
| 315 | if (term) |t| switch (t) { | ||
| 316 | .Exited => |code| try writer.print("exited with code {}", .{code}), | ||
| 317 | .Signal => |sig| try writer.print("terminated with signal {}", .{sig}), | ||
| 318 | .Stopped => |sig| try writer.print("stopped with signal {}", .{sig}), | ||
| 319 | .Unknown => |code| try writer.print("terminated for unknown reason with code {}", .{code}), | ||
| 320 | } else { | ||
| 321 | try writer.writeAll("exited with any code"); | ||
| 322 | } | ||
| 323 | } | ||
| 324 | fn fmtTerm(term: ?std.ChildProcess.Term) std.fmt.Formatter(formatTerm) { | ||
| 325 | return .{ .data = term }; | ||
| 326 | } | ||
| 327 | |||
| 328 | fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) bool { | ||
| 329 | return if (expected) |e| switch (e) { | ||
| 330 | .Exited => |expected_code| switch (actual) { | ||
| 331 | .Exited => |actual_code| expected_code == actual_code, | ||
| 332 | else => false, | ||
| 333 | }, | ||
| 334 | .Signal => |expected_sig| switch (actual) { | ||
| 335 | .Signal => |actual_sig| expected_sig == actual_sig, | ||
| 336 | else => false, | ||
| 337 | }, | ||
| 338 | .Stopped => |expected_sig| switch (actual) { | ||
| 339 | .Stopped => |actual_sig| expected_sig == actual_sig, | ||
| 340 | else => false, | ||
| 341 | }, | ||
| 342 | .Unknown => |expected_code| switch (actual) { | ||
| 343 | .Unknown => |actual_code| expected_code == actual_code, | ||
| 344 | else => false, | ||
| 345 | }, | ||
| 346 | } else switch (actual) { | ||
| 347 | .Exited => true, | ||
| 348 | else => false, | ||
| 349 | }; | ||
| 350 | } | ||
| 351 | |||
| 307 | pub fn runCommand( | 352 | pub fn runCommand( |
| 308 | argv: []const []const u8, | 353 | argv: []const []const u8, |
| 309 | builder: *std.Build, | 354 | builder: *std.Build, |
| 310 | expected_exit_code: ?u8, | 355 | expected_term: ?std.ChildProcess.Term, |
| 311 | stdout_action: StdIoAction, | 356 | stdout_action: StdIoAction, |
| 312 | stderr_action: StdIoAction, | 357 | stderr_action: StdIoAction, |
| 313 | stdin_behavior: std.ChildProcess.StdIo, | 358 | stdin_behavior: std.ChildProcess.StdIo, |
| ... | @@ -369,32 +414,14 @@ pub fn runCommand( | ... | @@ -369,32 +414,14 @@ pub fn runCommand( |
| 369 | return err; | 414 | return err; |
| 370 | }; | 415 | }; |
| 371 | 416 | ||
| 372 | switch (term) { | 417 | if (!termMatches(expected_term, term)) { |
| 373 | .Exited => |code| blk: { | 418 | if (builder.prominent_compile_errors) { |
| 374 | const expected_code = expected_exit_code orelse break :blk; | 419 | std.debug.print("Run step {} (expected {})\n", .{ fmtTerm(term), fmtTerm(expected_term) }); |
| 375 | 420 | } else { | |
| 376 | if (code != expected_code) { | 421 | std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) }); |
| 377 | if (builder.prominent_compile_errors) { | ||
| 378 | std.debug.print("Run step exited with error code {} (expected {})\n", .{ | ||
| 379 | code, | ||
| 380 | expected_code, | ||
| 381 | }); | ||
| 382 | } else { | ||
| 383 | std.debug.print("The following command exited with error code {} (expected {}):\n", .{ | ||
| 384 | code, | ||
| 385 | expected_code, | ||
| 386 | }); | ||
| 387 | printCmd(cwd, argv); | ||
| 388 | } | ||
| 389 | |||
| 390 | return error.UnexpectedExitCode; | ||
| 391 | } | ||
| 392 | }, | ||
| 393 | else => { | ||
| 394 | std.debug.print("The following command terminated unexpectedly:\n", .{}); | ||
| 395 | printCmd(cwd, argv); | 422 | printCmd(cwd, argv); |
| 396 | return error.UncleanExit; | 423 | } |
| 397 | }, | 424 | return error.UnexpectedExit; |
| 398 | } | 425 | } |
| 399 | 426 | ||
| 400 | switch (stderr_action) { | 427 | switch (stderr_action) { |
lib/std/Build/Step.zig+2-2| ... | @@ -21,7 +21,7 @@ pub const Id = enum { | ... | @@ -21,7 +21,7 @@ pub const Id = enum { |
| 21 | check_file, | 21 | check_file, |
| 22 | check_object, | 22 | check_object, |
| 23 | config_header, | 23 | config_header, |
| 24 | install_raw, | 24 | objcopy, |
| 25 | options, | 25 | options, |
| 26 | custom, | 26 | custom, |
| 27 | 27 | ||
| ... | @@ -42,7 +42,7 @@ pub const Id = enum { | ... | @@ -42,7 +42,7 @@ pub const Id = enum { |
| 42 | .check_file => Build.CheckFileStep, | 42 | .check_file => Build.CheckFileStep, |
| 43 | .check_object => Build.CheckObjectStep, | 43 | .check_object => Build.CheckObjectStep, |
| 44 | .config_header => Build.ConfigHeaderStep, | 44 | .config_header => Build.ConfigHeaderStep, |
| 45 | .install_raw => Build.InstallRawStep, | 45 | .objcopy => Build.ObjCopyStep, |
| 46 | .options => Build.OptionsStep, | 46 | .options => Build.OptionsStep, |
| 47 | .custom => @compileError("no type available for custom step"), | 47 | .custom => @compileError("no type available for custom step"), |
| 48 | }; | 48 | }; |
lib/std/Build/WriteFileStep.zig+173-71| ... | @@ -1,55 +1,117 @@ | ... | @@ -1,55 +1,117 @@ |
| 1 | const std = @import("../std.zig"); | 1 | //! WriteFileStep is primarily used to create a directory in an appropriate |
| 2 | const Step = std.Build.Step; | 2 | //! location inside the local cache which has a set of files that have either |
| 3 | const fs = std.fs; | 3 | //! been generated during the build, or are copied from the source package. |
| 4 | const ArrayList = std.ArrayList; | 4 | //! |
| 5 | 5 | //! However, this step has an additional capability of writing data to paths | |
| 6 | const WriteFileStep = @This(); | 6 | //! relative to the package root, effectively mutating the package's source |
| 7 | 7 | //! files. Be careful with the latter functionality; it should not be used | |
| 8 | pub const base_id = .write_file; | 8 | //! during the normal build process, but as a utility run by a developer with |
| 9 | //! intention to update source files, which will then be committed to version | ||
| 10 | //! control. | ||
| 9 | 11 | ||
| 10 | step: Step, | 12 | step: Step, |
| 11 | builder: *std.Build, | 13 | builder: *std.Build, |
| 12 | files: std.TailQueue(File), | 14 | /// The elements here are pointers because we need stable pointers for the |
| 15 | /// GeneratedFile field. | ||
| 16 | files: std.ArrayListUnmanaged(*File), | ||
| 17 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), | ||
| 18 | |||
| 19 | pub const base_id = .write_file; | ||
| 13 | 20 | ||
| 14 | pub const File = struct { | 21 | pub const File = struct { |
| 15 | source: std.Build.GeneratedFile, | 22 | generated_file: std.Build.GeneratedFile, |
| 16 | basename: []const u8, | 23 | sub_path: []const u8, |
| 24 | contents: Contents, | ||
| 25 | }; | ||
| 26 | |||
| 27 | pub const OutputSourceFile = struct { | ||
| 28 | contents: Contents, | ||
| 29 | sub_path: []const u8, | ||
| 30 | }; | ||
| 31 | |||
| 32 | pub const Contents = union(enum) { | ||
| 17 | bytes: []const u8, | 33 | bytes: []const u8, |
| 34 | copy: std.Build.FileSource, | ||
| 18 | }; | 35 | }; |
| 19 | 36 | ||
| 20 | pub fn init(builder: *std.Build) WriteFileStep { | 37 | pub fn init(builder: *std.Build) WriteFileStep { |
| 21 | return WriteFileStep{ | 38 | return .{ |
| 22 | .builder = builder, | 39 | .builder = builder, |
| 23 | .step = Step.init(.write_file, "writefile", builder.allocator, make), | 40 | .step = Step.init(.write_file, "writefile", builder.allocator, make), |
| 24 | .files = .{}, | 41 | .files = .{}, |
| 42 | .output_source_files = .{}, | ||
| 25 | }; | 43 | }; |
| 26 | } | 44 | } |
| 27 | 45 | ||
| 28 | pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { | 46 | pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { |
| 29 | const node = self.builder.allocator.create(std.TailQueue(File).Node) catch @panic("unhandled error"); | 47 | const gpa = wf.builder.allocator; |
| 30 | node.* = .{ | 48 | const file = gpa.create(File) catch @panic("OOM"); |
| 31 | .data = .{ | 49 | file.* = .{ |
| 32 | .source = std.Build.GeneratedFile{ .step = &self.step }, | 50 | .generated_file = .{ .step = &wf.step }, |
| 33 | .basename = self.builder.dupePath(basename), | 51 | .sub_path = wf.builder.dupePath(sub_path), |
| 34 | .bytes = self.builder.dupe(bytes), | 52 | .contents = .{ .bytes = wf.builder.dupe(bytes) }, |
| 35 | }, | 53 | }; |
| 54 | wf.files.append(gpa, file) catch @panic("OOM"); | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Place the file into the generated directory within the local cache, | ||
| 58 | /// along with all the rest of the files added to this step. The parameter | ||
| 59 | /// here is the destination path relative to the local cache directory | ||
| 60 | /// associated with this WriteFileStep. It may be a basename, or it may | ||
| 61 | /// include sub-directories, in which case this step will ensure the | ||
| 62 | /// required sub-path exists. | ||
| 63 | /// This is the option expected to be used most commonly with `addCopyFile`. | ||
| 64 | pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { | ||
| 65 | const gpa = wf.builder.allocator; | ||
| 66 | const file = gpa.create(File) catch @panic("OOM"); | ||
| 67 | file.* = .{ | ||
| 68 | .generated_file = .{ .step = &wf.step }, | ||
| 69 | .sub_path = wf.builder.dupePath(sub_path), | ||
| 70 | .contents = .{ .copy = source }, | ||
| 36 | }; | 71 | }; |
| 72 | wf.files.append(gpa, file) catch @panic("OOM"); | ||
| 73 | } | ||
| 37 | 74 | ||
| 38 | self.files.append(node); | 75 | /// A path relative to the package root. |
| 76 | /// Be careful with this because it updates source files. This should not be | ||
| 77 | /// used as part of the normal build process, but as a utility occasionally | ||
| 78 | /// run by a developer with intent to modify source files and then commit | ||
| 79 | /// those changes to version control. | ||
| 80 | /// A file added this way is not available with `getFileSource`. | ||
| 81 | pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { | ||
| 82 | wf.output_source_files.append(wf.builder.allocator, .{ | ||
| 83 | .contents = .{ .copy = source }, | ||
| 84 | .sub_path = sub_path, | ||
| 85 | }) catch @panic("OOM"); | ||
| 39 | } | 86 | } |
| 40 | 87 | ||
| 41 | /// Gets a file source for the given basename. If the file does not exist, returns `null`. | 88 | /// Gets a file source for the given sub_path. If the file does not exist, returns `null`. |
| 42 | pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?std.Build.FileSource { | 89 | pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSource { |
| 43 | var it = step.files.first; | 90 | for (wf.files.items) |file| { |
| 44 | while (it) |node| : (it = node.next) { | 91 | if (std.mem.eql(u8, file.sub_path, sub_path)) { |
| 45 | if (std.mem.eql(u8, node.data.basename, basename)) | 92 | return .{ .generated = &file.generated_file }; |
| 46 | return std.Build.FileSource{ .generated = &node.data.source }; | 93 | } |
| 47 | } | 94 | } |
| 48 | return null; | 95 | return null; |
| 49 | } | 96 | } |
| 50 | 97 | ||
| 51 | fn make(step: *Step) !void { | 98 | fn make(step: *Step) !void { |
| 52 | const self = @fieldParentPtr(WriteFileStep, "step", step); | 99 | const wf = @fieldParentPtr(WriteFileStep, "step", step); |
| 100 | |||
| 101 | // Writing to source files is kind of an extra capability of this | ||
| 102 | // WriteFileStep - arguably it should be a different step. But anyway here | ||
| 103 | // it is, it happens unconditionally and does not interact with the other | ||
| 104 | // files here. | ||
| 105 | for (wf.output_source_files.items) |output_source_file| { | ||
| 106 | const basename = fs.path.basename(output_source_file.sub_path); | ||
| 107 | if (fs.path.dirname(output_source_file.sub_path)) |dirname| { | ||
| 108 | var dir = try wf.builder.build_root.handle.makeOpenPath(dirname, .{}); | ||
| 109 | defer dir.close(); | ||
| 110 | try writeFile(wf, dir, output_source_file.contents, basename); | ||
| 111 | } else { | ||
| 112 | try writeFile(wf, wf.builder.build_root.handle, output_source_file.contents, basename); | ||
| 113 | } | ||
| 114 | } | ||
| 53 | 115 | ||
| 54 | // The cache is used here not really as a way to speed things up - because writing | 116 | // The cache is used here not really as a way to speed things up - because writing |
| 55 | // the data to a file would probably be very fast - but as a way to find a canonical | 117 | // the data to a file would probably be very fast - but as a way to find a canonical |
| ... | @@ -58,56 +120,96 @@ fn make(step: *Step) !void { | ... | @@ -58,56 +120,96 @@ fn make(step: *Step) !void { |
| 58 | // If, for example, a hard-coded path was used as the location to put WriteFileStep | 120 | // If, for example, a hard-coded path was used as the location to put WriteFileStep |
| 59 | // files, then two WriteFileSteps executing in parallel might clobber each other. | 121 | // files, then two WriteFileSteps executing in parallel might clobber each other. |
| 60 | 122 | ||
| 61 | // TODO port the cache system from the compiler to zig std lib. Until then | 123 | var man = wf.builder.cache.obtain(); |
| 62 | // we directly construct the path, and no "cache hit" detection happens; | 124 | defer man.deinit(); |
| 63 | // the files are always written. | 125 | |
| 64 | // Note there is similar code over in ConfigHeaderStep. | ||
| 65 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | ||
| 66 | // Random bytes to make WriteFileStep unique. Refresh this with | 126 | // Random bytes to make WriteFileStep unique. Refresh this with |
| 67 | // new random bytes when WriteFileStep implementation is modified | 127 | // new random bytes when WriteFileStep implementation is modified |
| 68 | // in a non-backwards-compatible way. | 128 | // in a non-backwards-compatible way. |
| 69 | var hash = Hasher.init("eagVR1dYXoE7ARDP"); | 129 | man.hash.add(@as(u32, 0xd767ee59)); |
| 70 | 130 | ||
| 71 | { | 131 | for (wf.files.items) |file| { |
| 72 | var it = self.files.first; | 132 | man.hash.addBytes(file.sub_path); |
| 73 | while (it) |node| : (it = node.next) { | 133 | switch (file.contents) { |
| 74 | hash.update(node.data.basename); | 134 | .bytes => |bytes| { |
| 75 | hash.update(node.data.bytes); | 135 | man.hash.addBytes(bytes); |
| 76 | hash.update("|"); | 136 | }, |
| 137 | .copy => |file_source| { | ||
| 138 | _ = try man.addFile(file_source.getPath(wf.builder), null); | ||
| 139 | }, | ||
| 77 | } | 140 | } |
| 78 | } | 141 | } |
| 79 | var digest: [16]u8 = undefined; | 142 | |
| 80 | hash.final(&digest); | 143 | if (man.hit() catch |err| failWithCacheError(man, err)) { |
| 81 | var hash_basename: [digest.len * 2]u8 = undefined; | 144 | // Cache hit, skip writing file data. |
| 82 | _ = std.fmt.bufPrint( | 145 | const digest = man.final(); |
| 83 | &hash_basename, | 146 | for (wf.files.items) |file| { |
| 84 | "{s}", | 147 | file.generated_file.path = try wf.builder.cache_root.join( |
| 85 | .{std.fmt.fmtSliceHexLower(&digest)}, | 148 | wf.builder.allocator, |
| 86 | ) catch unreachable; | 149 | &.{ "o", &digest, file.sub_path }, |
| 87 | 150 | ); | |
| 88 | const output_dir = try self.builder.cache_root.join(self.builder.allocator, &.{ | 151 | } |
| 89 | "o", &hash_basename, | 152 | return; |
| 90 | }); | 153 | } |
| 91 | var dir = fs.cwd().makeOpenPath(output_dir, .{}) catch |err| { | 154 | |
| 92 | std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); | 155 | const digest = man.final(); |
| 156 | const cache_path = "o" ++ fs.path.sep_str ++ digest; | ||
| 157 | |||
| 158 | var cache_dir = wf.builder.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { | ||
| 159 | std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); | ||
| 93 | return err; | 160 | return err; |
| 94 | }; | 161 | }; |
| 95 | defer dir.close(); | 162 | defer cache_dir.close(); |
| 96 | { | 163 | |
| 97 | var it = self.files.first; | 164 | for (wf.files.items) |file| { |
| 98 | while (it) |node| : (it = node.next) { | 165 | const basename = fs.path.basename(file.sub_path); |
| 99 | dir.writeFile(node.data.basename, node.data.bytes) catch |err| { | 166 | if (fs.path.dirname(file.sub_path)) |dirname| { |
| 100 | std.debug.print("unable to write {s} into {s}: {s}\n", .{ | 167 | var dir = try wf.builder.cache_root.handle.makeOpenPath(dirname, .{}); |
| 101 | node.data.basename, | 168 | defer dir.close(); |
| 102 | output_dir, | 169 | try writeFile(wf, dir, file.contents, basename); |
| 103 | @errorName(err), | 170 | } else { |
| 104 | }); | 171 | try writeFile(wf, cache_dir, file.contents, basename); |
| 105 | return err; | ||
| 106 | }; | ||
| 107 | node.data.source.path = try fs.path.join( | ||
| 108 | self.builder.allocator, | ||
| 109 | &[_][]const u8{ output_dir, node.data.basename }, | ||
| 110 | ); | ||
| 111 | } | 172 | } |
| 173 | |||
| 174 | file.generated_file.path = try wf.builder.cache_root.join( | ||
| 175 | wf.builder.allocator, | ||
| 176 | &.{ cache_path, file.sub_path }, | ||
| 177 | ); | ||
| 112 | } | 178 | } |
| 179 | |||
| 180 | try man.writeManifest(); | ||
| 113 | } | 181 | } |
| 182 | |||
| 183 | fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void { | ||
| 184 | // TODO after landing concurrency PR, improve error reporting here | ||
| 185 | switch (contents) { | ||
| 186 | .bytes => |bytes| return dir.writeFile(basename, bytes), | ||
| 187 | .copy => |file_source| { | ||
| 188 | const source_path = file_source.getPath(wf.builder); | ||
| 189 | const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{}); | ||
| 190 | _ = prev_status; // TODO logging (affected by open PR regarding concurrency) | ||
| 191 | }, | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | /// TODO consolidate this with the same function in RunStep? | ||
| 196 | /// Also properly deal with concurrency (see open PR) | ||
| 197 | fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { | ||
| 198 | const i = man.failed_file_index orelse failWithSimpleError(err); | ||
| 199 | const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); | ||
| 200 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; | ||
| 201 | std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); | ||
| 202 | std.process.exit(1); | ||
| 203 | } | ||
| 204 | |||
| 205 | fn failWithSimpleError(err: anyerror) noreturn { | ||
| 206 | std.debug.print("{s}\n", .{@errorName(err)}); | ||
| 207 | std.process.exit(1); | ||
| 208 | } | ||
| 209 | |||
| 210 | const std = @import("../std.zig"); | ||
| 211 | const Step = std.Build.Step; | ||
| 212 | const fs = std.fs; | ||
| 213 | const ArrayList = std.ArrayList; | ||
| 214 | |||
| 215 | const WriteFileStep = @This(); |
lib/std/RingBuffer.zig created+136| ... | @@ -0,0 +1,136 @@ | ||
| 1 | //! This ring buffer stores read and write indices while being able to utilise | ||
| 2 | //! the full backing slice by incrementing the indices modulo twice the slice's | ||
| 3 | //! length and reducing indices modulo the slice's length on slice access. This | ||
| 4 | //! means that whether the ring buffer if full or empty can be distinguished by | ||
| 5 | //! looking at the difference between the read and write indices without adding | ||
| 6 | //! an extra boolean flag or having to reserve a slot in the buffer. | ||
| 7 | //! | ||
| 8 | //! This ring buffer has not been implemented with thread safety in mind, and | ||
| 9 | //! therefore should not be assumed to be suitable for use cases involving | ||
| 10 | //! separate reader and writer threads. | ||
| 11 | |||
| 12 | const Allocator = @import("std").mem.Allocator; | ||
| 13 | const assert = @import("std").debug.assert; | ||
| 14 | |||
| 15 | const RingBuffer = @This(); | ||
| 16 | |||
| 17 | data: []u8, | ||
| 18 | read_index: usize, | ||
| 19 | write_index: usize, | ||
| 20 | |||
| 21 | pub const Error = error{Full}; | ||
| 22 | |||
| 23 | /// Allocate a new `RingBuffer`; `deinit()` should be called to free the buffer. | ||
| 24 | pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer { | ||
| 25 | const bytes = try allocator.alloc(u8, capacity); | ||
| 26 | return RingBuffer{ | ||
| 27 | .data = bytes, | ||
| 28 | .write_index = 0, | ||
| 29 | .read_index = 0, | ||
| 30 | }; | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Free the data backing a `RingBuffer`; must be passed the same `Allocator` as | ||
| 34 | /// `init()`. | ||
| 35 | pub fn deinit(self: *RingBuffer, allocator: Allocator) void { | ||
| 36 | allocator.free(self.data); | ||
| 37 | self.* = undefined; | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Returns `index` modulo the length of the backing slice. | ||
| 41 | pub fn mask(self: RingBuffer, index: usize) usize { | ||
| 42 | return index % self.data.len; | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Returns `index` modulo twice the length of the backing slice. | ||
| 46 | pub fn mask2(self: RingBuffer, index: usize) usize { | ||
| 47 | return index % (2 * self.data.len); | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Write `byte` into the ring buffer. Returns `error.Full` if the ring | ||
| 51 | /// buffer is full. | ||
| 52 | pub fn write(self: *RingBuffer, byte: u8) Error!void { | ||
| 53 | if (self.isFull()) return error.Full; | ||
| 54 | self.writeAssumeCapacity(byte); | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Write `byte` into the ring buffer. If the ring buffer is full, the | ||
| 58 | /// oldest byte is overwritten. | ||
| 59 | pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void { | ||
| 60 | self.data[self.mask(self.write_index)] = byte; | ||
| 61 | self.write_index = self.mask2(self.write_index + 1); | ||
| 62 | } | ||
| 63 | |||
| 64 | /// Write `bytes` into the ring buffer. Returns `error.Full` if the ring | ||
| 65 | /// buffer does not have enough space, without writing any data. | ||
| 66 | pub fn writeSlice(self: *RingBuffer, bytes: []const u8) Error!void { | ||
| 67 | if (self.len() + bytes.len > self.data.len) return error.Full; | ||
| 68 | self.writeSliceAssumeCapacity(bytes); | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Write `bytes` into the ring buffer. If there is not enough space, older | ||
| 72 | /// bytes will be overwritten. | ||
| 73 | pub fn writeSliceAssumeCapacity(self: *RingBuffer, bytes: []const u8) void { | ||
| 74 | for (bytes) |b| self.writeAssumeCapacity(b); | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Consume a byte from the ring buffer and return it. Returns `null` if the | ||
| 78 | /// ring buffer is empty. | ||
| 79 | pub fn read(self: *RingBuffer) ?u8 { | ||
| 80 | if (self.isEmpty()) return null; | ||
| 81 | return self.readAssumeLength(); | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Consume a byte from the ring buffer and return it; asserts that the buffer | ||
| 85 | /// is not empty. | ||
| 86 | pub fn readAssumeLength(self: *RingBuffer) u8 { | ||
| 87 | assert(!self.isEmpty()); | ||
| 88 | const byte = self.data[self.mask(self.read_index)]; | ||
| 89 | self.read_index = self.mask2(self.read_index + 1); | ||
| 90 | return byte; | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Returns `true` if the ring buffer is empty and `false` otherwise. | ||
| 94 | pub fn isEmpty(self: RingBuffer) bool { | ||
| 95 | return self.write_index == self.read_index; | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Returns `true` if the ring buffer is full and `false` otherwise. | ||
| 99 | pub fn isFull(self: RingBuffer) bool { | ||
| 100 | return self.mask2(self.write_index + self.data.len) == self.read_index; | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Returns the length | ||
| 104 | pub fn len(self: RingBuffer) usize { | ||
| 105 | const wrap_offset = 2 * self.data.len * @boolToInt(self.write_index < self.read_index); | ||
| 106 | const adjusted_write_index = self.write_index + wrap_offset; | ||
| 107 | return adjusted_write_index - self.read_index; | ||
| 108 | } | ||
| 109 | |||
| 110 | /// A `Slice` represents a region of a ring buffer. The region is split into two | ||
| 111 | /// sections as the ring buffer data will not be contiguous if the desired | ||
| 112 | /// region wraps to the start of the backing slice. | ||
| 113 | pub const Slice = struct { | ||
| 114 | first: []u8, | ||
| 115 | second: []u8, | ||
| 116 | }; | ||
| 117 | |||
| 118 | /// Returns a `Slice` for the region of the ring buffer starting at | ||
| 119 | /// `self.mask(start_unmasked)` with the specified length. | ||
| 120 | pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice { | ||
| 121 | assert(length <= self.data.len); | ||
| 122 | const slice1_start = self.mask(start_unmasked); | ||
| 123 | const slice1_end = @min(self.data.len, slice1_start + length); | ||
| 124 | const slice1 = self.data[slice1_start..slice1_end]; | ||
| 125 | const slice2 = self.data[0 .. length - slice1.len]; | ||
| 126 | return Slice{ | ||
| 127 | .first = slice1, | ||
| 128 | .second = slice2, | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Returns a `Slice` for the last `length` bytes written to the ring buffer. | ||
| 133 | /// Does not check that any bytes have been written into the region. | ||
| 134 | pub fn sliceLast(self: RingBuffer, length: usize) Slice { | ||
| 135 | return self.sliceAt(self.write_index + self.data.len - length, length); | ||
| 136 | } | ||
lib/std/child_process.zig+33-24| ... | @@ -197,6 +197,32 @@ pub const ChildProcess = struct { | ... | @@ -197,6 +197,32 @@ pub const ChildProcess = struct { |
| 197 | stderr: []u8, | 197 | stderr: []u8, |
| 198 | }; | 198 | }; |
| 199 | 199 | ||
| 200 | /// Collect the output from the process's stdout and stderr. Will return once all output | ||
| 201 | /// has been collected. This does not mean that the process has ended. `wait` should still | ||
| 202 | /// be called to wait for and clean up the process. | ||
| 203 | /// | ||
| 204 | /// The process must be started with stdout_behavior and stderr_behavior == .Pipe | ||
| 205 | pub fn collectOutput( | ||
| 206 | child: ChildProcess, | ||
| 207 | stdout: *std.ArrayList(u8), | ||
| 208 | stderr: *std.ArrayList(u8), | ||
| 209 | max_output_bytes: usize, | ||
| 210 | ) !void { | ||
| 211 | debug.assert(child.stdout_behavior == .Pipe); | ||
| 212 | debug.assert(child.stderr_behavior == .Pipe); | ||
| 213 | if (builtin.os.tag == .haiku) { | ||
| 214 | const stdout_in = child.stdout.?.reader(); | ||
| 215 | const stderr_in = child.stderr.?.reader(); | ||
| 216 | |||
| 217 | try stdout_in.readAllArrayList(stdout, max_output_bytes); | ||
| 218 | try stderr_in.readAllArrayList(stderr, max_output_bytes); | ||
| 219 | } else if (builtin.os.tag == .windows) { | ||
| 220 | try collectOutputWindows(child, stdout, stderr, max_output_bytes); | ||
| 221 | } else { | ||
| 222 | try collectOutputPosix(child, stdout, stderr, max_output_bytes); | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 200 | fn collectOutputPosix( | 226 | fn collectOutputPosix( |
| 201 | child: ChildProcess, | 227 | child: ChildProcess, |
| 202 | stdout: *std.ArrayList(u8), | 228 | stdout: *std.ArrayList(u8), |
| ... | @@ -297,8 +323,12 @@ pub const ChildProcess = struct { | ... | @@ -297,8 +323,12 @@ pub const ChildProcess = struct { |
| 297 | } | 323 | } |
| 298 | } | 324 | } |
| 299 | 325 | ||
| 300 | fn collectOutputWindows(child: ChildProcess, outs: [2]*std.ArrayList(u8), max_output_bytes: usize) !void { | 326 | fn collectOutputWindows(child: ChildProcess, stdout: *std.ArrayList(u8), stderr: *std.ArrayList(u8), max_output_bytes: usize) !void { |
| 301 | const bump_amt = 512; | 327 | const bump_amt = 512; |
| 328 | const outs = [_]*std.ArrayList(u8){ | ||
| 329 | stdout, | ||
| 330 | stderr, | ||
| 331 | }; | ||
| 302 | const handles = [_]windows.HANDLE{ | 332 | const handles = [_]windows.HANDLE{ |
| 303 | child.stdout.?.handle, | 333 | child.stdout.?.handle, |
| 304 | child.stderr.?.handle, | 334 | child.stderr.?.handle, |
| ... | @@ -391,24 +421,6 @@ pub const ChildProcess = struct { | ... | @@ -391,24 +421,6 @@ pub const ChildProcess = struct { |
| 391 | child.env_map = args.env_map; | 421 | child.env_map = args.env_map; |
| 392 | child.expand_arg0 = args.expand_arg0; | 422 | child.expand_arg0 = args.expand_arg0; |
| 393 | 423 | ||
| 394 | try child.spawn(); | ||
| 395 | |||
| 396 | if (builtin.os.tag == .haiku) { | ||
| 397 | const stdout_in = child.stdout.?.reader(); | ||
| 398 | const stderr_in = child.stderr.?.reader(); | ||
| 399 | |||
| 400 | const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes); | ||
| 401 | errdefer args.allocator.free(stdout); | ||
| 402 | const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes); | ||
| 403 | errdefer args.allocator.free(stderr); | ||
| 404 | |||
| 405 | return ExecResult{ | ||
| 406 | .term = try child.wait(), | ||
| 407 | .stdout = stdout, | ||
| 408 | .stderr = stderr, | ||
| 409 | }; | ||
| 410 | } | ||
| 411 | |||
| 412 | var stdout = std.ArrayList(u8).init(args.allocator); | 424 | var stdout = std.ArrayList(u8).init(args.allocator); |
| 413 | var stderr = std.ArrayList(u8).init(args.allocator); | 425 | var stderr = std.ArrayList(u8).init(args.allocator); |
| 414 | errdefer { | 426 | errdefer { |
| ... | @@ -416,11 +428,8 @@ pub const ChildProcess = struct { | ... | @@ -416,11 +428,8 @@ pub const ChildProcess = struct { |
| 416 | stderr.deinit(); | 428 | stderr.deinit(); |
| 417 | } | 429 | } |
| 418 | 430 | ||
| 419 | if (builtin.os.tag == .windows) { | 431 | try child.spawn(); |
| 420 | try collectOutputWindows(child, [_]*std.ArrayList(u8){ &stdout, &stderr }, args.max_output_bytes); | 432 | try child.collectOutput(&stdout, &stderr, args.max_output_bytes); |
| 421 | } else { | ||
| 422 | try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes); | ||
| 423 | } | ||
| 424 | 433 | ||
| 425 | return ExecResult{ | 434 | return ExecResult{ |
| 426 | .term = try child.wait(), | 435 | .term = try child.wait(), |
lib/std/compress.zig+2| ... | @@ -6,6 +6,7 @@ pub const lzma = @import("compress/lzma.zig"); | ... | @@ -6,6 +6,7 @@ pub const lzma = @import("compress/lzma.zig"); |
| 6 | pub const lzma2 = @import("compress/lzma2.zig"); | 6 | pub const lzma2 = @import("compress/lzma2.zig"); |
| 7 | pub const xz = @import("compress/xz.zig"); | 7 | pub const xz = @import("compress/xz.zig"); |
| 8 | pub const zlib = @import("compress/zlib.zig"); | 8 | pub const zlib = @import("compress/zlib.zig"); |
| 9 | pub const zstd = @import("compress/zstandard.zig"); | ||
| 9 | 10 | ||
| 10 | pub fn HashedReader( | 11 | pub fn HashedReader( |
| 11 | comptime ReaderType: anytype, | 12 | comptime ReaderType: anytype, |
| ... | @@ -44,4 +45,5 @@ test { | ... | @@ -44,4 +45,5 @@ test { |
| 44 | _ = lzma2; | 45 | _ = lzma2; |
| 45 | _ = xz; | 46 | _ = xz; |
| 46 | _ = zlib; | 47 | _ = zlib; |
| 48 | _ = zstd; | ||
| 47 | } | 49 | } |
lib/std/compress/testdata/rfc8478.txt created+3027| ... | @@ -0,0 +1,3027 @@ | ||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | Internet Engineering Task Force (IETF) Y. Collet | ||
| 8 | Request for Comments: 8478 M. Kucherawy, Ed. | ||
| 9 | Category: Informational Facebook | ||
| 10 | ISSN: 2070-1721 October 2018 | ||
| 11 | |||
| 12 | |||
| 13 | Zstandard Compression and the application/zstd Media Type | ||
| 14 | |||
| 15 | Abstract | ||
| 16 | |||
| 17 | Zstandard, or "zstd" (pronounced "zee standard"), is a data | ||
| 18 | compression mechanism. This document describes the mechanism and | ||
| 19 | registers a media type and content encoding to be used when | ||
| 20 | transporting zstd-compressed content via Multipurpose Internet Mail | ||
| 21 | Extensions (MIME). | ||
| 22 | |||
| 23 | Despite use of the word "standard" as part of its name, readers are | ||
| 24 | advised that this document is not an Internet Standards Track | ||
| 25 | specification; it is being published for informational purposes only. | ||
| 26 | |||
| 27 | Status of This Memo | ||
| 28 | |||
| 29 | This document is not an Internet Standards Track specification; it is | ||
| 30 | published for informational purposes. | ||
| 31 | |||
| 32 | This document is a product of the Internet Engineering Task Force | ||
| 33 | (IETF). It represents the consensus of the IETF community. It has | ||
| 34 | received public review and has been approved for publication by the | ||
| 35 | Internet Engineering Steering Group (IESG). Not all documents | ||
| 36 | approved by the IESG are candidates for any level of Internet | ||
| 37 | Standard; see Section 2 of RFC 7841. | ||
| 38 | |||
| 39 | Information about the current status of this document, any errata, | ||
| 40 | and how to provide feedback on it may be obtained at | ||
| 41 | https://www.rfc-editor.org/info/rfc8478. | ||
| 42 | |||
| 43 | |||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | |||
| 49 | |||
| 50 | |||
| 51 | |||
| 52 | |||
| 53 | |||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | Collet & Kucherawy Informational [Page 1] | ||
| 59 | |||
| 60 | RFC 8478 application/zstd October 2018 | ||
| 61 | |||
| 62 | |||
| 63 | Copyright Notice | ||
| 64 | |||
| 65 | Copyright (c) 2018 IETF Trust and the persons identified as the | ||
| 66 | document authors. All rights reserved. | ||
| 67 | |||
| 68 | This document is subject to BCP 78 and the IETF Trust's Legal | ||
| 69 | Provisions Relating to IETF Documents | ||
| 70 | (https://trustee.ietf.org/license-info) in effect on the date of | ||
| 71 | publication of this document. Please review these documents | ||
| 72 | carefully, as they describe your rights and restrictions with respect | ||
| 73 | to this document. Code Components extracted from this document must | ||
| 74 | include Simplified BSD License text as described in Section 4.e of | ||
| 75 | the Trust Legal Provisions and are provided without warranty as | ||
| 76 | described in the Simplified BSD License. | ||
| 77 | |||
| 78 | |||
| 79 | |||
| 80 | |||
| 81 | |||
| 82 | |||
| 83 | |||
| 84 | |||
| 85 | |||
| 86 | |||
| 87 | |||
| 88 | |||
| 89 | |||
| 90 | |||
| 91 | |||
| 92 | |||
| 93 | |||
| 94 | |||
| 95 | |||
| 96 | |||
| 97 | |||
| 98 | |||
| 99 | |||
| 100 | |||
| 101 | |||
| 102 | |||
| 103 | |||
| 104 | |||
| 105 | |||
| 106 | |||
| 107 | |||
| 108 | |||
| 109 | |||
| 110 | |||
| 111 | |||
| 112 | |||
| 113 | |||
| 114 | Collet & Kucherawy Informational [Page 2] | ||
| 115 | |||
| 116 | RFC 8478 application/zstd October 2018 | ||
| 117 | |||
| 118 | |||
| 119 | Table of Contents | ||
| 120 | |||
| 121 | 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4 | ||
| 122 | 2. Definitions . . . . . . . . . . . . . . . . . . . . . . . . . 4 | ||
| 123 | 3. Compression Algorithm . . . . . . . . . . . . . . . . . . . . 5 | ||
| 124 | 3.1. Frames . . . . . . . . . . . . . . . . . . . . . . . . . 6 | ||
| 125 | 3.1.1. Zstandard Frames . . . . . . . . . . . . . . . . . . 6 | ||
| 126 | 3.1.1.1. Frame Header . . . . . . . . . . . . . . . . . . 7 | ||
| 127 | 3.1.1.2. Blocks . . . . . . . . . . . . . . . . . . . . . 12 | ||
| 128 | 3.1.1.3. Compressed Blocks . . . . . . . . . . . . . . . . 14 | ||
| 129 | 3.1.1.4. Sequence Execution . . . . . . . . . . . . . . . 28 | ||
| 130 | 3.1.1.5. Repeat Offsets . . . . . . . . . . . . . . . . . 29 | ||
| 131 | 3.1.2. Skippable Frames . . . . . . . . . . . . . . . . . . 30 | ||
| 132 | 4. Entropy Encoding . . . . . . . . . . . . . . . . . . . . . . 30 | ||
| 133 | 4.1. FSE . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 | ||
| 134 | 4.1.1. FSE Table Description . . . . . . . . . . . . . . . . 31 | ||
| 135 | 4.2. Huffman Coding . . . . . . . . . . . . . . . . . . . . . 34 | ||
| 136 | 4.2.1. Huffman Tree Description . . . . . . . . . . . . . . 35 | ||
| 137 | 4.2.1.1. Huffman Tree Header . . . . . . . . . . . . . . . 36 | ||
| 138 | 4.2.1.2. FSE Compression of Huffman Weights . . . . . . . 37 | ||
| 139 | 4.2.1.3. Conversion from Weights to Huffman Prefix Codes . 38 | ||
| 140 | 4.2.2. Huffman-Coded Streams . . . . . . . . . . . . . . . . 39 | ||
| 141 | 5. Dictionary Format . . . . . . . . . . . . . . . . . . . . . . 40 | ||
| 142 | 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 42 | ||
| 143 | 6.1. The 'application/zstd' Media Type . . . . . . . . . . . . 42 | ||
| 144 | 6.2. Content Encoding . . . . . . . . . . . . . . . . . . . . 43 | ||
| 145 | 6.3. Dictionaries . . . . . . . . . . . . . . . . . . . . . . 43 | ||
| 146 | 7. Security Considerations . . . . . . . . . . . . . . . . . . . 43 | ||
| 147 | 8. Implementation Status . . . . . . . . . . . . . . . . . . . . 44 | ||
| 148 | 9. References . . . . . . . . . . . . . . . . . . . . . . . . . 45 | ||
| 149 | 9.1. Normative References . . . . . . . . . . . . . . . . . . 45 | ||
| 150 | 9.2. Informative References . . . . . . . . . . . . . . . . . 45 | ||
| 151 | Appendix A. Decoding Tables for Predefined Codes . . . . . . . . 46 | ||
| 152 | A.1. Literal Length Code Table . . . . . . . . . . . . . . . . 46 | ||
| 153 | A.2. Match Length Code Table . . . . . . . . . . . . . . . . . 49 | ||
| 154 | A.3. Offset Code Table . . . . . . . . . . . . . . . . . . . . 52 | ||
| 155 | Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 53 | ||
| 156 | Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 54 | ||
| 157 | |||
| 158 | |||
| 159 | |||
| 160 | |||
| 161 | |||
| 162 | |||
| 163 | |||
| 164 | |||
| 165 | |||
| 166 | |||
| 167 | |||
| 168 | |||
| 169 | |||
| 170 | Collet & Kucherawy Informational [Page 3] | ||
| 171 | |||
| 172 | RFC 8478 application/zstd October 2018 | ||
| 173 | |||
| 174 | |||
| 175 | 1. Introduction | ||
| 176 | |||
| 177 | Zstandard, or "zstd" (pronounced "zee standard"), is a data | ||
| 178 | compression mechanism, akin to gzip [RFC1952]. | ||
| 179 | |||
| 180 | Despite use of the word "standard" as part of its name, readers are | ||
| 181 | advised that this document is not an Internet Standards Track | ||
| 182 | specification; it is being published for informational purposes only. | ||
| 183 | |||
| 184 | This document describes the Zstandard format. Also, to enable the | ||
| 185 | transport of a data object compressed with Zstandard, this document | ||
| 186 | registers a media type that can be used to identify such content when | ||
| 187 | it is used in a payload encoded using Multipurpose Internet Mail | ||
| 188 | Extensions (MIME). | ||
| 189 | |||
| 190 | 2. Definitions | ||
| 191 | |||
| 192 | Some terms used elsewhere in this document are defined here for | ||
| 193 | clarity. | ||
| 194 | |||
| 195 | uncompressed: Describes an arbitrary set of bytes in their original | ||
| 196 | form, prior to being subjected to compression. | ||
| 197 | |||
| 198 | compress, compression: The act of processing a set of bytes via the | ||
| 199 | compression mechanism described here. | ||
| 200 | |||
| 201 | compressed: Describes the result of passing a set of bytes through | ||
| 202 | this mechanism. The original input has thus been compressed. | ||
| 203 | |||
| 204 | decompress, decompression: The act of processing a set of bytes | ||
| 205 | through the inverse of the compression mechanism described here, | ||
| 206 | in an attempt to recover the original set of bytes prior to | ||
| 207 | compression. | ||
| 208 | |||
| 209 | decompressed: Describes the result of passing a set of bytes through | ||
| 210 | the reverse of this mechanism. When this is successful, the | ||
| 211 | decompressed payload and the uncompressed payload are | ||
| 212 | indistinguishable. | ||
| 213 | |||
| 214 | encode: The process of translating data from one form to another; | ||
| 215 | this may include compression or it may refer to other translations | ||
| 216 | done as part of this specification. | ||
| 217 | |||
| 218 | decode: The reverse of "encode"; describes a process of reversing a | ||
| 219 | prior encoding to recover the original content. | ||
| 220 | |||
| 221 | |||
| 222 | |||
| 223 | |||
| 224 | |||
| 225 | |||
| 226 | Collet & Kucherawy Informational [Page 4] | ||
| 227 | |||
| 228 | RFC 8478 application/zstd October 2018 | ||
| 229 | |||
| 230 | |||
| 231 | frame: Content compressed by Zstandard is transformed into a | ||
| 232 | Zstandard frame. Multiple frames can be appended into a single | ||
| 233 | file or stream. A frame is completely independent, has a defined | ||
| 234 | beginning and end, and has a set of parameters that tells the | ||
| 235 | decoder how to decompress it. | ||
| 236 | |||
| 237 | block: A frame encapsulates one or multiple blocks. Each block | ||
| 238 | contains arbitrary content, which is described by its header, and | ||
| 239 | has a guaranteed maximum content size that depends upon frame | ||
| 240 | parameters. Unlike frames, each block depends on previous blocks | ||
| 241 | for proper decoding. However, each block can be decompressed | ||
| 242 | without waiting for its successor, allowing streaming operations. | ||
| 243 | |||
| 244 | natural order: A sequence or ordering of objects or values that is | ||
| 245 | typical of that type of object or value. A set of unique | ||
| 246 | integers, for example, is in "natural order" if when progressing | ||
| 247 | from one element in the set or sequence to the next, there is | ||
| 248 | never a decrease in value. | ||
| 249 | |||
| 250 | The naming convention for identifiers within the specification is | ||
| 251 | Mixed_Case_With_Underscores. Identifiers inside square brackets | ||
| 252 | indicate that the identifier is optional in the presented context. | ||
| 253 | |||
| 254 | 3. Compression Algorithm | ||
| 255 | |||
| 256 | This section describes the Zstandard algorithm. | ||
| 257 | |||
| 258 | The purpose of this document is to define a lossless compressed data | ||
| 259 | format that is a) independent of the CPU type, operating system, file | ||
| 260 | system, and character set and b) is suitable for file compression and | ||
| 261 | pipe and streaming compression, using the Zstandard algorithm. The | ||
| 262 | text of the specification assumes a basic background in programming | ||
| 263 | at the level of bits and other primitive data representations. | ||
| 264 | |||
| 265 | The data can be produced or consumed, even for an arbitrarily long | ||
| 266 | sequentially presented input data stream, using only an a priori | ||
| 267 | bounded amount of intermediate storage, and hence can be used in data | ||
| 268 | communications. The format uses the Zstandard compression method, | ||
| 269 | and an optional xxHash-64 checksum method [XXHASH], for detection of | ||
| 270 | data corruption. | ||
| 271 | |||
| 272 | The data format defined by this specification does not attempt to | ||
| 273 | allow random access to compressed data. | ||
| 274 | |||
| 275 | Unless otherwise indicated below, a compliant compressor must produce | ||
| 276 | data sets that conform to the specifications presented here. | ||
| 277 | However, it does not need to support all options. | ||
| 278 | |||
| 279 | |||
| 280 | |||
| 281 | |||
| 282 | Collet & Kucherawy Informational [Page 5] | ||
| 283 | |||
| 284 | RFC 8478 application/zstd October 2018 | ||
| 285 | |||
| 286 | |||
| 287 | A compliant decompressor must be able to decompress at least one | ||
| 288 | working set of parameters that conforms to the specifications | ||
| 289 | presented here. It may also ignore informative fields, such as the | ||
| 290 | checksum. Whenever it does not support a parameter defined in the | ||
| 291 | compressed stream, it must produce a non-ambiguous error code and | ||
| 292 | associated error message explaining which parameter is unsupported. | ||
| 293 | |||
| 294 | This specification is intended for use by implementers of software to | ||
| 295 | compress data into Zstandard format and/or decompress data from | ||
| 296 | Zstandard format. The Zstandard format is supported by an open | ||
| 297 | source reference implementation, written in portable C, and available | ||
| 298 | at [ZSTD]. | ||
| 299 | |||
| 300 | 3.1. Frames | ||
| 301 | |||
| 302 | Zstandard compressed data is made up of one or more frames. Each | ||
| 303 | frame is independent and can be decompressed independently of other | ||
| 304 | frames. The decompressed content of multiple concatenated frames is | ||
| 305 | the concatenation of each frame's decompressed content. | ||
| 306 | |||
| 307 | There are two frame formats defined for Zstandard: Zstandard frames | ||
| 308 | and skippable frames. Zstandard frames contain compressed data, | ||
| 309 | while skippable frames contain custom user metadata. | ||
| 310 | |||
| 311 | 3.1.1. Zstandard Frames | ||
| 312 | |||
| 313 | The structure of a single Zstandard frame is as follows: | ||
| 314 | |||
| 315 | +--------------------+------------+ | ||
| 316 | | Magic_Number | 4 bytes | | ||
| 317 | +--------------------+------------+ | ||
| 318 | | Frame_Header | 2-14 bytes | | ||
| 319 | +--------------------+------------+ | ||
| 320 | | Data_Block | n bytes | | ||
| 321 | +--------------------+------------+ | ||
| 322 | | [More Data_Blocks] | | | ||
| 323 | +--------------------+------------+ | ||
| 324 | | [Content_Checksum] | 0-4 bytes | | ||
| 325 | +--------------------+------------+ | ||
| 326 | |||
| 327 | Magic_Number: 4 bytes, little-endian format. Value: 0xFD2FB528. | ||
| 328 | |||
| 329 | Frame_Header: 2 to 14 bytes, detailed in Section 3.1.1.1. | ||
| 330 | |||
| 331 | Data_Block: Detailed in Section 3.1.1.2. This is where data | ||
| 332 | appears. | ||
| 333 | |||
| 334 | |||
| 335 | |||
| 336 | |||
| 337 | |||
| 338 | Collet & Kucherawy Informational [Page 6] | ||
| 339 | |||
| 340 | RFC 8478 application/zstd October 2018 | ||
| 341 | |||
| 342 | |||
| 343 | Content_Checksum: An optional 32-bit checksum, only present if | ||
| 344 | Content_Checksum_Flag is set. The content checksum is the result | ||
| 345 | of the XXH64() hash function [XXHASH] digesting the original | ||
| 346 | (decoded) data as input, and a seed of zero. The low 4 bytes of | ||
| 347 | the checksum are stored in little-endian format. | ||
| 348 | |||
| 349 | The magic number was selected to be less probable to find at the | ||
| 350 | beginning of an arbitrary file. It avoids trivial patterns (0x00, | ||
| 351 | 0xFF, repeated bytes, increasing bytes, etc.), contains byte values | ||
| 352 | outside of ASCII range, and doesn't map into UTF-8 space, all of | ||
| 353 | which reduce the likelihood of its appearance at the top of a text | ||
| 354 | file. | ||
| 355 | |||
| 356 | 3.1.1.1. Frame Header | ||
| 357 | |||
| 358 | The frame header has a variable size, with a minimum of 2 bytes and | ||
| 359 | up to 14 bytes depending on optional parameters. The structure of | ||
| 360 | Frame_Header is as follows: | ||
| 361 | |||
| 362 | +-------------------------+-----------+ | ||
| 363 | | Frame_Header_Descriptor | 1 byte | | ||
| 364 | +-------------------------+-----------+ | ||
| 365 | | [Window_Descriptor] | 0-1 byte | | ||
| 366 | +-------------------------+-----------+ | ||
| 367 | | [Dictionary_ID] | 0-4 bytes | | ||
| 368 | +-------------------------+-----------+ | ||
| 369 | | [Frame_Content_Size] | 0-8 bytes | | ||
| 370 | +-------------------------+-----------+ | ||
| 371 | |||
| 372 | |||
| 373 | |||
| 374 | |||
| 375 | |||
| 376 | |||
| 377 | |||
| 378 | |||
| 379 | |||
| 380 | |||
| 381 | |||
| 382 | |||
| 383 | |||
| 384 | |||
| 385 | |||
| 386 | |||
| 387 | |||
| 388 | |||
| 389 | |||
| 390 | |||
| 391 | |||
| 392 | |||
| 393 | |||
| 394 | Collet & Kucherawy Informational [Page 7] | ||
| 395 | |||
| 396 | RFC 8478 application/zstd October 2018 | ||
| 397 | |||
| 398 | |||
| 399 | 3.1.1.1.1. Frame_Header_Descriptor | ||
| 400 | |||
| 401 | The first header's byte is called the Frame_Header_Descriptor. It | ||
| 402 | describes which other fields are present. Decoding this byte is | ||
| 403 | enough to tell the size of Frame_Header. | ||
| 404 | |||
| 405 | +------------+-------------------------+ | ||
| 406 | | Bit Number | Field Name | | ||
| 407 | +------------+-------------------------+ | ||
| 408 | | 7-6 | Frame_Content_Size_Flag | | ||
| 409 | +------------+-------------------------+ | ||
| 410 | | 5 | Single_Segment_Flag | | ||
| 411 | +------------+-------------------------+ | ||
| 412 | | 4 | (unused) | | ||
| 413 | +------------+-------------------------+ | ||
| 414 | | 3 | (reserved) | | ||
| 415 | +------------+-------------------------+ | ||
| 416 | | 2 | Content_Checksum_Flag | | ||
| 417 | +------------+-------------------------+ | ||
| 418 | | 1-0 | Dictionary_ID_Flag | | ||
| 419 | +------------+-------------------------+ | ||
| 420 | |||
| 421 | In this table, bit 7 is the highest bit, while bit 0 is the lowest | ||
| 422 | one. | ||
| 423 | |||
| 424 | 3.1.1.1.1.1. Frame_Content_Size_Flag | ||
| 425 | |||
| 426 | This is a 2-bit flag (equivalent to Frame_Header_Descriptor right- | ||
| 427 | shifted 6 bits) specifying whether Frame_Content_Size (the | ||
| 428 | decompressed data size) is provided within the header. Flag_Value | ||
| 429 | provides FCS_Field_Size, which is the number of bytes used by | ||
| 430 | Frame_Content_Size according to the following table: | ||
| 431 | |||
| 432 | +----------------+--------+---+---+---+ | ||
| 433 | | Flag_Value | 0 | 1 | 2 | 3 | | ||
| 434 | +----------------+--------+---+---+---+ | ||
| 435 | | FCS_Field_Size | 0 or 1 | 2 | 4 | 8 | | ||
| 436 | +----------------+--------+---+---+---+ | ||
| 437 | |||
| 438 | When Flag_Value is 0, FCS_Field_Size depends on Single_Segment_Flag: | ||
| 439 | If Single_Segment_Flag is set, FCS_Field_Size is 1. Otherwise, | ||
| 440 | FCS_Field_Size is 0; Frame_Content_Size is not provided. | ||
| 441 | |||
| 442 | |||
| 443 | |||
| 444 | |||
| 445 | |||
| 446 | |||
| 447 | |||
| 448 | |||
| 449 | |||
| 450 | Collet & Kucherawy Informational [Page 8] | ||
| 451 | |||
| 452 | RFC 8478 application/zstd October 2018 | ||
| 453 | |||
| 454 | |||
| 455 | 3.1.1.1.1.2. Single_Segment_Flag | ||
| 456 | |||
| 457 | If this flag is set, data must be regenerated within a single | ||
| 458 | continuous memory segment. | ||
| 459 | |||
| 460 | In this case, Window_Descriptor byte is skipped, but | ||
| 461 | Frame_Content_Size is necessarily present. As a consequence, the | ||
| 462 | decoder must allocate a memory segment of size equal or larger than | ||
| 463 | Frame_Content_Size. | ||
| 464 | |||
| 465 | In order to protect the decoder from unreasonable memory | ||
| 466 | requirements, a decoder is allowed to reject a compressed frame that | ||
| 467 | requests a memory size beyond the decoder's authorized range. | ||
| 468 | |||
| 469 | For broader compatibility, decoders are recommended to support memory | ||
| 470 | sizes of at least 8 MB. This is only a recommendation; each decoder | ||
| 471 | is free to support higher or lower limits, depending on local | ||
| 472 | limitations. | ||
| 473 | |||
| 474 | 3.1.1.1.1.3. Unused Bit | ||
| 475 | |||
| 476 | A decoder compliant with this specification version shall not | ||
| 477 | interpret this bit. It might be used in a future version, to signal | ||
| 478 | a property that is not mandatory to properly decode the frame. An | ||
| 479 | encoder compliant with this specification must set this bit to zero. | ||
| 480 | |||
| 481 | 3.1.1.1.1.4. Reserved Bit | ||
| 482 | |||
| 483 | This bit is reserved for some future feature. Its value must be | ||
| 484 | zero. A decoder compliant with this specification version must | ||
| 485 | ensure it is not set. This bit may be used in a future revision, to | ||
| 486 | signal a feature that must be interpreted to decode the frame | ||
| 487 | correctly. | ||
| 488 | |||
| 489 | 3.1.1.1.1.5. Content_Checksum_Flag | ||
| 490 | |||
| 491 | If this flag is set, a 32-bit Content_Checksum will be present at the | ||
| 492 | frame's end. See the description of Content_Checksum above. | ||
| 493 | |||
| 494 | |||
| 495 | |||
| 496 | |||
| 497 | |||
| 498 | |||
| 499 | |||
| 500 | |||
| 501 | |||
| 502 | |||
| 503 | |||
| 504 | |||
| 505 | |||
| 506 | Collet & Kucherawy Informational [Page 9] | ||
| 507 | |||
| 508 | RFC 8478 application/zstd October 2018 | ||
| 509 | |||
| 510 | |||
| 511 | 3.1.1.1.1.6. Dictionary_ID_Flag | ||
| 512 | |||
| 513 | This is a 2-bit flag (= Frame_Header_Descriptor & 0x3) indicating | ||
| 514 | whether a dictionary ID is provided within the header. It also | ||
| 515 | specifies the size of this field as DID_Field_Size: | ||
| 516 | |||
| 517 | +----------------+---+---+---+---+ | ||
| 518 | | Flag_Value | 0 | 1 | 2 | 3 | | ||
| 519 | +----------------+---+---+---+---+ | ||
| 520 | | DID_Field_Size | 0 | 1 | 2 | 4 | | ||
| 521 | +----------------+---+---+---+---+ | ||
| 522 | |||
| 523 | 3.1.1.1.2. Window Descriptor | ||
| 524 | |||
| 525 | This provides guarantees about the minimum memory buffer required to | ||
| 526 | decompress a frame. This information is important for decoders to | ||
| 527 | allocate enough memory. | ||
| 528 | |||
| 529 | The Window_Descriptor byte is optional. When Single_Segment_Flag is | ||
| 530 | set, Window_Descriptor is not present. In this case, Window_Size is | ||
| 531 | Frame_Content_Size, which can be any value from 0 to 2^64-1 bytes (16 | ||
| 532 | ExaBytes). | ||
| 533 | |||
| 534 | +------------+----------+----------+ | ||
| 535 | | Bit Number | 7-3 | 2-0 | | ||
| 536 | +------------+----------+----------+ | ||
| 537 | | Field Name | Exponent | Mantissa | | ||
| 538 | +------------+----------+----------+ | ||
| 539 | |||
| 540 | The minimum memory buffer size is called Window_Size. It is | ||
| 541 | described by the following formulae: | ||
| 542 | |||
| 543 | windowLog = 10 + Exponent; | ||
| 544 | windowBase = 1 << windowLog; | ||
| 545 | windowAdd = (windowBase / 8) * Mantissa; | ||
| 546 | Window_Size = windowBase + windowAdd; | ||
| 547 | |||
| 548 | The minimum Window_Size is 1 KB. The maximum Window_Size is (1<<41) | ||
| 549 | + 7*(1<<38) bytes, which is 3.75 TB. | ||
| 550 | |||
| 551 | In general, larger Window_Size values tend to improve the compression | ||
| 552 | ratio, but at the cost of increased memory usage. | ||
| 553 | |||
| 554 | To properly decode compressed data, a decoder will need to allocate a | ||
| 555 | buffer of at least Window_Size bytes. | ||
| 556 | |||
| 557 | |||
| 558 | |||
| 559 | |||
| 560 | |||
| 561 | |||
| 562 | Collet & Kucherawy Informational [Page 10] | ||
| 563 | |||
| 564 | RFC 8478 application/zstd October 2018 | ||
| 565 | |||
| 566 | |||
| 567 | In order to protect decoders from unreasonable memory requirements, a | ||
| 568 | decoder is allowed to reject a compressed frame that requests a | ||
| 569 | memory size beyond decoder's authorized range. | ||
| 570 | |||
| 571 | For improved interoperability, it's recommended for decoders to | ||
| 572 | support values of Window_Size up to 8 MB and for encoders not to | ||
| 573 | generate frames requiring a Window_Size larger than 8 MB. It's | ||
| 574 | merely a recommendation though, and decoders are free to support | ||
| 575 | larger or lower limits, depending on local limitations. | ||
| 576 | |||
| 577 | 3.1.1.1.3. Dictionary_ID | ||
| 578 | |||
| 579 | This is a variable size field, which contains the ID of the | ||
| 580 | dictionary required to properly decode the frame. This field is | ||
| 581 | optional. When it's not present, it's up to the decoder to know | ||
| 582 | which dictionary to use. | ||
| 583 | |||
| 584 | Dictionary_ID field size is provided by DID_Field_Size. | ||
| 585 | DID_Field_Size is directly derived from the value of | ||
| 586 | Dictionary_ID_Flag. One byte can represent an ID 0-255; 2 bytes can | ||
| 587 | represent an ID 0-65535; 4 bytes can represent an ID 0-4294967295. | ||
| 588 | Format is little-endian. | ||
| 589 | |||
| 590 | It is permitted to represent a small ID (for example, 13) with a | ||
| 591 | large 4-byte dictionary ID, even if it is less efficient. | ||
| 592 | |||
| 593 | Within private environments, any dictionary ID can be used. However, | ||
| 594 | for frames and dictionaries distributed in public space, | ||
| 595 | Dictionary_ID must be attributed carefully. The following ranges are | ||
| 596 | reserved for use only with dictionaries that have been registered | ||
| 597 | with IANA (see Section 6.3): | ||
| 598 | |||
| 599 | low range: <= 32767 | ||
| 600 | high range: >= (1 << 31) | ||
| 601 | |||
| 602 | Any other value for Dictionary_ID can be used by private arrangement | ||
| 603 | between participants. | ||
| 604 | |||
| 605 | Any payload presented for decompression that references an | ||
| 606 | unregistered reserved dictionary ID results in an error. | ||
| 607 | |||
| 608 | |||
| 609 | |||
| 610 | |||
| 611 | |||
| 612 | |||
| 613 | |||
| 614 | |||
| 615 | |||
| 616 | |||
| 617 | |||
| 618 | Collet & Kucherawy Informational [Page 11] | ||
| 619 | |||
| 620 | RFC 8478 application/zstd October 2018 | ||
| 621 | |||
| 622 | |||
| 623 | 3.1.1.1.4. Frame Content Size | ||
| 624 | |||
| 625 | This is the original (uncompressed) size. This information is | ||
| 626 | optional. Frame_Content_Size uses a variable number of bytes, | ||
| 627 | provided by FCS_Field_Size. FCS_Field_Size is provided by the value | ||
| 628 | of Frame_Content_Size_Flag. FCS_Field_Size can be equal to 0 (not | ||
| 629 | present), 1, 2, 4, or 8 bytes. | ||
| 630 | |||
| 631 | +----------------+--------------+ | ||
| 632 | | FCS Field Size | Range | | ||
| 633 | +----------------+--------------+ | ||
| 634 | | 0 | unknown | | ||
| 635 | +----------------+--------------+ | ||
| 636 | | 1 | 0 - 255 | | ||
| 637 | +----------------+--------------+ | ||
| 638 | | 2 | 256 - 65791 | | ||
| 639 | +----------------+--------------+ | ||
| 640 | | 4 | 0 - 2^32 - 1 | | ||
| 641 | +----------------+--------------+ | ||
| 642 | | 8 | 0 - 2^64 - 1 | | ||
| 643 | +----------------+--------------+ | ||
| 644 | |||
| 645 | Frame_Content_Size format is little-endian. When FCS_Field_Size is | ||
| 646 | 1, 4, or 8 bytes, the value is read directly. When FCS_Field_Size is | ||
| 647 | 2, the offset of 256 is added. It's allowed to represent a small | ||
| 648 | size (for example 18) using any compatible variant. | ||
| 649 | |||
| 650 | 3.1.1.2. Blocks | ||
| 651 | |||
| 652 | After Magic_Number and Frame_Header, there are some number of blocks. | ||
| 653 | Each frame must have at least 1 block, but there is no upper limit on | ||
| 654 | the number of blocks per frame. | ||
| 655 | |||
| 656 | The structure of a block is as follows: | ||
| 657 | |||
| 658 | +--------------+---------------+ | ||
| 659 | | Block_Header | Block_Content | | ||
| 660 | +--------------+---------------+ | ||
| 661 | | 3 bytes | n bytes | | ||
| 662 | +--------------+---------------+ | ||
| 663 | |||
| 664 | |||
| 665 | |||
| 666 | |||
| 667 | |||
| 668 | |||
| 669 | |||
| 670 | |||
| 671 | |||
| 672 | |||
| 673 | |||
| 674 | Collet & Kucherawy Informational [Page 12] | ||
| 675 | |||
| 676 | RFC 8478 application/zstd October 2018 | ||
| 677 | |||
| 678 | |||
| 679 | Block_Header uses 3 bytes, written using little-endian convention. | ||
| 680 | It contains three fields: | ||
| 681 | |||
| 682 | +------------+------------+------------+ | ||
| 683 | | Last_Block | Block_Type | Block_Size | | ||
| 684 | +------------+------------+------------+ | ||
| 685 | | bit 0 | bits 1-2 | bits 3-23 | | ||
| 686 | +------------+------------+------------+ | ||
| 687 | |||
| 688 | 3.1.1.2.1. Last_Block | ||
| 689 | |||
| 690 | The lowest bit (Last_Block) signals whether this block is the last | ||
| 691 | one. The frame will end after this last block. It may be followed | ||
| 692 | by an optional Content_Checksum (see Section 3.1.1). | ||
| 693 | |||
| 694 | 3.1.1.2.2. Block_Type | ||
| 695 | |||
| 696 | The next 2 bits represent the Block_Type. There are four block | ||
| 697 | types: | ||
| 698 | |||
| 699 | +-----------+------------------+ | ||
| 700 | | Value | Block_Type | | ||
| 701 | +-----------+------------------+ | ||
| 702 | | 0 | Raw_Block | | ||
| 703 | +-----------+------------------+ | ||
| 704 | | 1 | RLE_Block | | ||
| 705 | +-----------+------------------+ | ||
| 706 | | 2 | Compressed_Block | | ||
| 707 | +-----------+------------------+ | ||
| 708 | | 3 | Reserved | | ||
| 709 | +-----------+------------------+ | ||
| 710 | |||
| 711 | Raw_Block: This is an uncompressed block. Block_Content contains | ||
| 712 | Block_Size bytes. | ||
| 713 | |||
| 714 | RLE_Block: This is a single byte, repeated Block_Size times. | ||
| 715 | Block_Content consists of a single byte. On the decompression | ||
| 716 | side, this byte must be repeated Block_Size times. | ||
| 717 | |||
| 718 | Compressed_Block: This is a compressed block as described in | ||
| 719 | Section 3.1.1.3. Block_Size is the length of Block_Content, | ||
| 720 | namely the compressed data. The decompressed size is not known, | ||
| 721 | but its maximum possible value is guaranteed (see below). | ||
| 722 | |||
| 723 | Reserved: This is not a block. This value cannot be used with the | ||
| 724 | current specification. If such a value is present, it is | ||
| 725 | considered to be corrupt data. | ||
| 726 | |||
| 727 | |||
| 728 | |||
| 729 | |||
| 730 | Collet & Kucherawy Informational [Page 13] | ||
| 731 | |||
| 732 | RFC 8478 application/zstd October 2018 | ||
| 733 | |||
| 734 | |||
| 735 | 3.1.1.2.3. Block_Size | ||
| 736 | |||
| 737 | The upper 21 bits of Block_Header represent the Block_Size. | ||
| 738 | Block_Size is the size of the block excluding the header. A block | ||
| 739 | can contain any number of bytes (even zero), up to | ||
| 740 | Block_Maximum_Decompressed_Size, which is the smallest of: | ||
| 741 | |||
| 742 | o Window_Size | ||
| 743 | |||
| 744 | o 128 KB | ||
| 745 | |||
| 746 | A Compressed_Block has the extra restriction that Block_Size is | ||
| 747 | always strictly less than the decompressed size. If this condition | ||
| 748 | cannot be respected, the block must be sent uncompressed instead | ||
| 749 | (i.e., treated as a Raw_Block). | ||
| 750 | |||
| 751 | 3.1.1.3. Compressed Blocks | ||
| 752 | |||
| 753 | To decompress a compressed block, the compressed size must be | ||
| 754 | provided from the Block_Size field within Block_Header. | ||
| 755 | |||
| 756 | A compressed block consists of two sections: a Literals | ||
| 757 | Section (Section 3.1.1.3.1) and a | ||
| 758 | Sequences_Section (Section 3.1.1.3.2). The results of the two | ||
| 759 | sections are then combined to produce the decompressed data in | ||
| 760 | Sequence Execution (Section 3.1.1.4). | ||
| 761 | |||
| 762 | To decode a compressed block, the following elements are necessary: | ||
| 763 | |||
| 764 | o Previous decoded data, up to a distance of Window_Size, or the | ||
| 765 | beginning of the Frame, whichever is smaller. Single_Segment_Flag | ||
| 766 | will be set in the latter case. | ||
| 767 | |||
| 768 | o List of "recent offsets" from the previous Compressed_Block. | ||
| 769 | |||
| 770 | o The previous Huffman tree, required by Treeless_Literals_Block | ||
| 771 | type. | ||
| 772 | |||
| 773 | o Previous Finite State Entropy (FSE) decoding tables, required by | ||
| 774 | Repeat_Mode, for each symbol type (literals lengths, match | ||
| 775 | lengths, offsets). | ||
| 776 | |||
| 777 | Note that decoding tables are not always from the previous | ||
| 778 | Compressed_Block: | ||
| 779 | |||
| 780 | o Every decoding table can come from a dictionary. | ||
| 781 | |||
| 782 | |||
| 783 | |||
| 784 | |||
| 785 | |||
| 786 | Collet & Kucherawy Informational [Page 14] | ||
| 787 | |||
| 788 | RFC 8478 application/zstd October 2018 | ||
| 789 | |||
| 790 | |||
| 791 | o The Huffman tree comes from the previous | ||
| 792 | Compressed_Literals_Block. | ||
| 793 | |||
| 794 | 3.1.1.3.1. Literals_Section_Header | ||
| 795 | |||
| 796 | All literals are regrouped in the first part of the block. They can | ||
| 797 | be decoded first and then copied during Sequence Execution (see | ||
| 798 | Section 3.1.1.4), or they can be decoded on the flow during Sequence | ||
| 799 | Execution. | ||
| 800 | |||
| 801 | Literals can be stored uncompressed or compressed using Huffman | ||
| 802 | prefix codes. When compressed, an optional tree description can be | ||
| 803 | present, followed by 1 or 4 streams. | ||
| 804 | |||
| 805 | +----------------------------+ | ||
| 806 | | Literals_Section_Header | | ||
| 807 | +----------------------------+ | ||
| 808 | | [Huffman_Tree_Description] | | ||
| 809 | +----------------------------+ | ||
| 810 | | [Jump_Table] | | ||
| 811 | +----------------------------+ | ||
| 812 | | Stream_1 | | ||
| 813 | +----------------------------+ | ||
| 814 | | [Stream_2] | | ||
| 815 | +----------------------------+ | ||
| 816 | | [Stream_3] | | ||
| 817 | +----------------------------+ | ||
| 818 | | [Stream_4] | | ||
| 819 | +----------------------------+ | ||
| 820 | |||
| 821 | 3.1.1.3.1.1. Literals_Section_Header | ||
| 822 | |||
| 823 | This field describes how literals are packed. It's a byte-aligned | ||
| 824 | variable-size bit field, ranging from 1 to 5 bytes, using little- | ||
| 825 | endian convention. | ||
| 826 | |||
| 827 | +---------------------+-----------+ | ||
| 828 | | Literals_Block_Type | 2 bits | | ||
| 829 | +---------------------+-----------+ | ||
| 830 | | Size_Format | 1-2 bits | | ||
| 831 | +---------------------+-----------+ | ||
| 832 | | Regenerated_Size | 5-20 bits | | ||
| 833 | +---------------------+-----------+ | ||
| 834 | | [Compressed_Size] | 0-18 bits | | ||
| 835 | +---------------------+-----------+ | ||
| 836 | |||
| 837 | In this representation, bits at the top are the lowest bits. | ||
| 838 | |||
| 839 | |||
| 840 | |||
| 841 | |||
| 842 | Collet & Kucherawy Informational [Page 15] | ||
| 843 | |||
| 844 | RFC 8478 application/zstd October 2018 | ||
| 845 | |||
| 846 | |||
| 847 | The Literals_Block_Type field uses the two lowest bits of the first | ||
| 848 | byte, describing four different block types: | ||
| 849 | |||
| 850 | +---------------------------+-------+ | ||
| 851 | | Literals_Block_Type | Value | | ||
| 852 | +---------------------------+-------+ | ||
| 853 | | Raw_Literals_Block | 0 | | ||
| 854 | +---------------------------+-------+ | ||
| 855 | | RLE_Literals_Block | 1 | | ||
| 856 | +---------------------------+-------+ | ||
| 857 | | Compressed_Literals_Block | 2 | | ||
| 858 | +---------------------------+-------+ | ||
| 859 | | Treeless_Literals_Block | 3 | | ||
| 860 | +---------------------------+-------+ | ||
| 861 | |||
| 862 | Raw_Literals_Block: Literals are stored uncompressed. | ||
| 863 | Literals_Section_Content is Regenerated_Size. | ||
| 864 | |||
| 865 | RLE_Literals_Block: Literals consist of a single-byte value repeated | ||
| 866 | Regenerated_Size times. Literals_Section_Content is 1. | ||
| 867 | |||
| 868 | Compressed_Literals_Block: This is a standard Huffman-compressed | ||
| 869 | block, starting with a Huffman tree description. See details | ||
| 870 | below. Literals_Section_Content is Compressed_Size. | ||
| 871 | |||
| 872 | Treeless_Literals_Block: This is a Huffman-compressed block, using | ||
| 873 | the Huffman tree from the previous Compressed_Literals_Block, or a | ||
| 874 | dictionary if there is no previous Huffman-compressed literals | ||
| 875 | block. Huffman_Tree_Description will be skipped. Note that if | ||
| 876 | this mode is triggered without any previous Huffman-table in the | ||
| 877 | frame (or dictionary, per Section 5), it should be treated as data | ||
| 878 | corruption. Literals_Section_Content is Compressed_Size. | ||
| 879 | |||
| 880 | The Size_Format is divided into two families: | ||
| 881 | |||
| 882 | o For Raw_Literals_Block and RLE_Literals_Block, it's only necessary | ||
| 883 | to decode Regenerated_Size. There is no Compressed_Size field. | ||
| 884 | |||
| 885 | o For Compressed_Block and Treeless_Literals_Block, it's required to | ||
| 886 | decode both Compressed_Size and Regenerated_Size (the decompressed | ||
| 887 | size). It's also necessary to decode the number of streams (1 or | ||
| 888 | 4). | ||
| 889 | |||
| 890 | For values spanning several bytes, the convention is little endian. | ||
| 891 | |||
| 892 | Size_Format for Raw_Literals_Block and RLE_Literals_Block uses 1 or 2 | ||
| 893 | bits. Its value is (Literals_Section_Header[0]>>2) & 0x3. | ||
| 894 | |||
| 895 | |||
| 896 | |||
| 897 | |||
| 898 | Collet & Kucherawy Informational [Page 16] | ||
| 899 | |||
| 900 | RFC 8478 application/zstd October 2018 | ||
| 901 | |||
| 902 | |||
| 903 | Size_Format == 00 or 10: Size_Format uses 1 bit. Regenerated_Size | ||
| 904 | uses 5 bits (value 0-31). Literals_Section_Header uses 1 byte. | ||
| 905 | Regenerated_Size = Literal_Section_Header[0]>>3. | ||
| 906 | |||
| 907 | Size_Format == 01: Size_Format uses 2 bits. Regenerated_Size uses | ||
| 908 | 12 bits (values 0-4095). Literals_Section_Header uses 2 bytes. | ||
| 909 | Regenerated_Size = (Literals_Section_Header[0]>>4) + | ||
| 910 | (Literals_Section_Header[1]<<4). | ||
| 911 | |||
| 912 | Size_Format == 11: Size_Format uses 2 bits. Regenerated_Size uses | ||
| 913 | 20 bits (values 0-1048575). Literals_Section_Header uses 3 bytes. | ||
| 914 | Regenerated_Size = (Literals_Section_Header[0]>>4) + | ||
| 915 | (Literals_Section_Header[1]<<4) + (Literals_Section_Header[2]<<12) | ||
| 916 | |||
| 917 | Only Stream_1 is present for these cases. Note that it is permitted | ||
| 918 | to represent a short value (for example, 13) using a long format, | ||
| 919 | even if it's less efficient. | ||
| 920 | |||
| 921 | Size_Format for Compressed_Literals_Block and Treeless_Literals_Block | ||
| 922 | always uses 2 bits. | ||
| 923 | |||
| 924 | Size_Format == 00: A single stream. Both Regenerated_Size and | ||
| 925 | Compressed_Size use 10 bits (values 0-1023). | ||
| 926 | Literals_Section_Header uses 3 bytes. | ||
| 927 | |||
| 928 | Size_Format == 01: 4 streams. Both Regenerated_Size and | ||
| 929 | Compressed_Size use 10 bits (values 0-1023). | ||
| 930 | Literals_Section_Header uses 3 bytes. | ||
| 931 | |||
| 932 | Size_Format == 10: 4 streams. Both Regenerated_Size and | ||
| 933 | Compressed_Size use 14 bits (values 0-16383). | ||
| 934 | Literals_Section_Header uses 4 bytes. | ||
| 935 | |||
| 936 | Size_Format == 11: 4 streams. Both Regenerated_Size and | ||
| 937 | Compressed_Size use 18 bits (values 0-262143). | ||
| 938 | Literals_Section_Header uses 5 bytes. | ||
| 939 | |||
| 940 | Both the Compressed_Size and Regenerated_Size fields follow little- | ||
| 941 | endian convention. Note that Compressed_Size includes the size of | ||
| 942 | the Huffman_Tree_Description when it is present. | ||
| 943 | |||
| 944 | 3.1.1.3.1.2. Raw_Literals_Block | ||
| 945 | |||
| 946 | The data in Stream_1 is Regenerated_Size bytes long. It contains the | ||
| 947 | raw literals data to be used during Sequence Execution | ||
| 948 | (Section 3.1.1.3.2). | ||
| 949 | |||
| 950 | |||
| 951 | |||
| 952 | |||
| 953 | |||
| 954 | Collet & Kucherawy Informational [Page 17] | ||
| 955 | |||
| 956 | RFC 8478 application/zstd October 2018 | ||
| 957 | |||
| 958 | |||
| 959 | 3.1.1.3.1.3. RLE_Literals_Block | ||
| 960 | |||
| 961 | Stream_1 consists of a single byte that should be repeated | ||
| 962 | Regenerated_Size times to generate the decoded literals. | ||
| 963 | |||
| 964 | 3.1.1.3.1.4. Compressed_Literals_Block and Treeless_Literals_Block | ||
| 965 | |||
| 966 | Both of these modes contain Huffman-encoded data. For | ||
| 967 | Treeless_Literals_Block, the Huffman table comes from the previously | ||
| 968 | compressed literals block, or from a dictionary; see Section 5. | ||
| 969 | |||
| 970 | 3.1.1.3.1.5. Huffman_Tree_Description | ||
| 971 | |||
| 972 | This section is only present when the Literals_Block_Type type is | ||
| 973 | Compressed_Literals_Block (2). The format of | ||
| 974 | Huffman_Tree_Description can be found in Section 4.2.1. The size of | ||
| 975 | Huffman_Tree_Description is determined during the decoding process. | ||
| 976 | It must be used to determine where streams begin. | ||
| 977 | |||
| 978 | Total_Streams_Size = Compressed_Size | ||
| 979 | - Huffman_Tree_Description_Size | ||
| 980 | |||
| 981 | 3.1.1.3.1.6. Jump_Table | ||
| 982 | |||
| 983 | The Jump_Table is only present when there are 4 Huffman-coded | ||
| 984 | streams. | ||
| 985 | |||
| 986 | (Reminder: Huffman-compressed data consists of either 1 or 4 Huffman- | ||
| 987 | coded streams.) | ||
| 988 | |||
| 989 | If only 1 stream is present, it is a single bitstream occupying the | ||
| 990 | entire remaining portion of the literals block, encoded as described | ||
| 991 | within Section 4.2.2. | ||
| 992 | |||
| 993 | If there are 4 streams, Literals_Section_Header only provides enough | ||
| 994 | information to know the decompressed and compressed sizes of all 4 | ||
| 995 | streams combined. The decompressed size of each stream is equal to | ||
| 996 | (Regenerated_Size+3)/4, except for the last stream, which may be up | ||
| 997 | to 3 bytes smaller, to reach a total decompressed size as specified | ||
| 998 | in Regenerated_Size. | ||
| 999 | |||
| 1000 | The compressed size of each stream is provided explicitly in the | ||
| 1001 | Jump_Table. The Jump_Table is 6 bytes long and consists of three | ||
| 1002 | 2-byte little-endian fields, describing the compressed sizes of the | ||
| 1003 | first 3 streams. Stream4_Size is computed from Total_Streams_Size | ||
| 1004 | minus sizes of other streams. | ||
| 1005 | |||
| 1006 | |||
| 1007 | |||
| 1008 | |||
| 1009 | |||
| 1010 | Collet & Kucherawy Informational [Page 18] | ||
| 1011 | |||
| 1012 | RFC 8478 application/zstd October 2018 | ||
| 1013 | |||
| 1014 | |||
| 1015 | Stream4_Size = Total_Streams_Size - 6 | ||
| 1016 | - Stream1_Size - Stream2_Size | ||
| 1017 | - Stream3_Size | ||
| 1018 | |||
| 1019 | Note that if Stream1_Size + Stream2_Size + Stream3_Size exceeds | ||
| 1020 | Total_Streams_Size, the data are considered corrupted. | ||
| 1021 | |||
| 1022 | Each of these 4 bitstreams is then decoded independently as a | ||
| 1023 | Huffman-Coded stream, as described in Section 4.2.2. | ||
| 1024 | |||
| 1025 | 3.1.1.3.2. Sequences_Section | ||
| 1026 | |||
| 1027 | A compressed block is a succession of sequences. A sequence is a | ||
| 1028 | literal copy command, followed by a match copy command. A literal | ||
| 1029 | copy command specifies a length. It is the number of bytes to be | ||
| 1030 | copied (or extracted) from the Literals Section. A match copy | ||
| 1031 | command specifies an offset and a length. | ||
| 1032 | |||
| 1033 | When all sequences are decoded, if there are literals left in the | ||
| 1034 | literals section, these bytes are added at the end of the block. | ||
| 1035 | |||
| 1036 | This is described in more detail in Section 3.1.1.4. | ||
| 1037 | |||
| 1038 | The Sequences_Section regroups all symbols required to decode | ||
| 1039 | commands. There are three symbol types: literals lengths, offsets, | ||
| 1040 | and match lengths. They are encoded together, interleaved, in a | ||
| 1041 | single "bitstream". | ||
| 1042 | |||
| 1043 | The Sequences_Section starts by a header, followed by optional | ||
| 1044 | probability tables for each symbol type, followed by the bitstream. | ||
| 1045 | |||
| 1046 | Sequences_Section_Header | ||
| 1047 | [Literals_Length_Table] | ||
| 1048 | [Offset_Table] | ||
| 1049 | [Match_Length_Table] | ||
| 1050 | bitStream | ||
| 1051 | |||
| 1052 | To decode the Sequences_Section, it's necessary to know its size. | ||
| 1053 | This size is deduced from the size of the Literals_Section: | ||
| 1054 | Sequences_Section_Size = Block_Size - Literals_Section_Header - | ||
| 1055 | Literals_Section_Content | ||
| 1056 | |||
| 1057 | |||
| 1058 | |||
| 1059 | |||
| 1060 | |||
| 1061 | |||
| 1062 | |||
| 1063 | |||
| 1064 | |||
| 1065 | |||
| 1066 | Collet & Kucherawy Informational [Page 19] | ||
| 1067 | |||
| 1068 | RFC 8478 application/zstd October 2018 | ||
| 1069 | |||
| 1070 | |||
| 1071 | 3.1.1.3.2.1. Sequences_Section_Header | ||
| 1072 | |||
| 1073 | This header consists of two items: | ||
| 1074 | |||
| 1075 | o Number_of_Sequences | ||
| 1076 | |||
| 1077 | o Symbol_Compression_Modes | ||
| 1078 | |||
| 1079 | Number_of_Sequences is a variable size field using between 1 and 3 | ||
| 1080 | bytes. If the first byte is "byte0": | ||
| 1081 | |||
| 1082 | o if (byte0 == 0): there are no sequences. The sequence section | ||
| 1083 | stops here. Decompressed content is defined entirely as Literals | ||
| 1084 | Section content. The FSE tables used in Repeat_Mode are not | ||
| 1085 | updated. | ||
| 1086 | |||
| 1087 | o if (byte0 < 128): Number_of_Sequences = byte0. Uses 1 byte. | ||
| 1088 | |||
| 1089 | o if (byte0 < 255): Number_of_Sequences = ((byte0 - 128) << 8) + | ||
| 1090 | byte1. Uses 2 bytes. | ||
| 1091 | |||
| 1092 | o if (byte0 == 255): Number_of_Sequences = byte1 + (byte2 << 8) + | ||
| 1093 | 0x7F00. Uses 3 bytes. | ||
| 1094 | |||
| 1095 | Symbol_Compression_Modes is a single byte, defining the compression | ||
| 1096 | mode of each symbol type. | ||
| 1097 | |||
| 1098 | +-------------+----------------------+ | ||
| 1099 | | Bit Number | Field Name | | ||
| 1100 | +-------------+----------------------+ | ||
| 1101 | | 7-6 | Literal_Lengths_Mode | | ||
| 1102 | +-------------+----------------------+ | ||
| 1103 | | 5-4 | Offsets_Mode | | ||
| 1104 | +-------------+----------------------+ | ||
| 1105 | | 3-2 | Match_Lengths_Mode | | ||
| 1106 | +-------------+----------------------+ | ||
| 1107 | | 1-0 | Reserved | | ||
| 1108 | +-------------+----------------------+ | ||
| 1109 | |||
| 1110 | The last field, Reserved, must be all zeroes. | ||
| 1111 | |||
| 1112 | |||
| 1113 | |||
| 1114 | |||
| 1115 | |||
| 1116 | |||
| 1117 | |||
| 1118 | |||
| 1119 | |||
| 1120 | |||
| 1121 | |||
| 1122 | Collet & Kucherawy Informational [Page 20] | ||
| 1123 | |||
| 1124 | RFC 8478 application/zstd October 2018 | ||
| 1125 | |||
| 1126 | |||
| 1127 | Literals_Lengths_Mode, Offsets_Mode, and Match_Lengths_Mode define | ||
| 1128 | the Compression_Mode of literals lengths, offsets, and match lengths | ||
| 1129 | symbols, respectively. They follow the same enumeration: | ||
| 1130 | |||
| 1131 | +-------+---------------------+ | ||
| 1132 | | Value | Compression_Mode | | ||
| 1133 | +-------+---------------------+ | ||
| 1134 | | 0 | Predefined_Mode | | ||
| 1135 | +-------+---------------------+ | ||
| 1136 | | 1 | RLE_Mode | | ||
| 1137 | +-------+---------------------+ | ||
| 1138 | | 2 | FSE_Compressed_Mode | | ||
| 1139 | +-------+---------------------+ | ||
| 1140 | | 3 | Repeat_Mode | | ||
| 1141 | +-------+---------------------+ | ||
| 1142 | |||
| 1143 | Predefined_Mode: A predefined FSE (see Section 4.1) distribution | ||
| 1144 | table is used, as defined in Section 3.1.1.3.2.2. No distribution | ||
| 1145 | table will be present. | ||
| 1146 | |||
| 1147 | RLE_Mode: The table description consists of a single byte, which | ||
| 1148 | contains the symbol's value. This symbol will be used for all | ||
| 1149 | sequences. | ||
| 1150 | |||
| 1151 | FSE_Compressed_Mode: Standard FSE compression. A distribution table | ||
| 1152 | will be present. The format of this distribution table is | ||
| 1153 | described in Section 4.1.1. Note that the maximum allowed | ||
| 1154 | accuracy log for literals length and match length tables is 9, and | ||
| 1155 | the maximum accuracy log for the offsets table is 8. This mode | ||
| 1156 | must not be used when only one symbol is present; RLE_Mode should | ||
| 1157 | be used instead (although any other mode will work). | ||
| 1158 | |||
| 1159 | Repeat_Mode: The table used in the previous Compressed_Block with | ||
| 1160 | Number_Of_Sequences > 0 will be used again, or if this is the | ||
| 1161 | first block, the table in the dictionary will be used. Note that | ||
| 1162 | this includes RLE_Mode, so if Repeat_Mode follows RLE_Mode, the | ||
| 1163 | same symbol will be repeated. It also includes Predefined_Mode, | ||
| 1164 | in which case Repeat_Mode will have the same outcome as | ||
| 1165 | Predefined_Mode. No distribution table will be present. If this | ||
| 1166 | mode is used without any previous sequence table in the frame (or | ||
| 1167 | dictionary; see Section 5) to repeat, this should be treated as | ||
| 1168 | corruption. | ||
| 1169 | |||
| 1170 | |||
| 1171 | |||
| 1172 | |||
| 1173 | |||
| 1174 | |||
| 1175 | |||
| 1176 | |||
| 1177 | |||
| 1178 | Collet & Kucherawy Informational [Page 21] | ||
| 1179 | |||
| 1180 | RFC 8478 application/zstd October 2018 | ||
| 1181 | |||
| 1182 | |||
| 1183 | 3.1.1.3.2.1.1. Sequence Codes for Lengths and Offsets | ||
| 1184 | |||
| 1185 | Each symbol is a code in its own context, which specifies Baseline | ||
| 1186 | and Number_of_Bits to add. Codes are FSE compressed and interleaved | ||
| 1187 | with raw additional bits in the same bitstream. | ||
| 1188 | |||
| 1189 | Literals length codes are values ranging from 0 to 35 inclusive. | ||
| 1190 | They define lengths from 0 to 131071 bytes. The literals length is | ||
| 1191 | equal to the decoded Baseline plus the result of reading | ||
| 1192 | Number_of_Bits bits from the bitstream, as a little-endian value. | ||
| 1193 | |||
| 1194 | |||
| 1195 | |||
| 1196 | |||
| 1197 | |||
| 1198 | |||
| 1199 | |||
| 1200 | |||
| 1201 | |||
| 1202 | |||
| 1203 | |||
| 1204 | |||
| 1205 | |||
| 1206 | |||
| 1207 | |||
| 1208 | |||
| 1209 | |||
| 1210 | |||
| 1211 | |||
| 1212 | |||
| 1213 | |||
| 1214 | |||
| 1215 | |||
| 1216 | |||
| 1217 | |||
| 1218 | |||
| 1219 | |||
| 1220 | |||
| 1221 | |||
| 1222 | |||
| 1223 | |||
| 1224 | |||
| 1225 | |||
| 1226 | |||
| 1227 | |||
| 1228 | |||
| 1229 | |||
| 1230 | |||
| 1231 | |||
| 1232 | |||
| 1233 | |||
| 1234 | Collet & Kucherawy Informational [Page 22] | ||
| 1235 | |||
| 1236 | RFC 8478 application/zstd October 2018 | ||
| 1237 | |||
| 1238 | |||
| 1239 | +----------------------+----------+----------------+ | ||
| 1240 | | Literals_Length_Code | Baseline | Number_of_Bits | | ||
| 1241 | +----------------------+----------+----------------+ | ||
| 1242 | | 0-15 | length | 0 | | ||
| 1243 | +----------------------+----------+----------------+ | ||
| 1244 | | 16 | 16 | 1 | | ||
| 1245 | +----------------------+----------+----------------+ | ||
| 1246 | | 17 | 18 | 1 | | ||
| 1247 | +----------------------+----------+----------------+ | ||
| 1248 | | 18 | 20 | 1 | | ||
| 1249 | +----------------------+----------+----------------+ | ||
| 1250 | | 19 | 22 | 1 | | ||
| 1251 | +----------------------+----------+----------------+ | ||
| 1252 | | 20 | 24 | 2 | | ||
| 1253 | +----------------------+----------+----------------+ | ||
| 1254 | | 21 | 28 | 2 | | ||
| 1255 | +----------------------+----------+----------------+ | ||
| 1256 | | 22 | 32 | 3 | | ||
| 1257 | +----------------------+----------+----------------+ | ||
| 1258 | | 23 | 40 | 3 | | ||
| 1259 | +----------------------+----------+----------------+ | ||
| 1260 | | 24 | 48 | 4 | | ||
| 1261 | +----------------------+----------+----------------+ | ||
| 1262 | | 25 | 64 | 6 | | ||
| 1263 | +----------------------+----------+----------------+ | ||
| 1264 | | 26 | 128 | 7 | | ||
| 1265 | +----------------------+----------+----------------+ | ||
| 1266 | | 27 | 256 | 8 | | ||
| 1267 | +----------------------+----------+----------------+ | ||
| 1268 | | 28 | 512 | 9 | | ||
| 1269 | +----------------------+----------+----------------+ | ||
| 1270 | | 29 | 1024 | 10 | | ||
| 1271 | +----------------------+----------+----------------+ | ||
| 1272 | | 30 | 2048 | 11 | | ||
| 1273 | +----------------------+----------+----------------+ | ||
| 1274 | | 31 | 4096 | 12 | | ||
| 1275 | +----------------------+----------+----------------+ | ||
| 1276 | | 32 | 8192 | 13 | | ||
| 1277 | +----------------------+----------+----------------+ | ||
| 1278 | | 33 | 16384 | 14 | | ||
| 1279 | +----------------------+----------+----------------+ | ||
| 1280 | | 34 | 32768 | 15 | | ||
| 1281 | +----------------------+----------+----------------+ | ||
| 1282 | | 35 | 65536 | 16 | | ||
| 1283 | +----------------------+----------+----------------+ | ||
| 1284 | |||
| 1285 | |||
| 1286 | |||
| 1287 | |||
| 1288 | |||
| 1289 | |||
| 1290 | Collet & Kucherawy Informational [Page 23] | ||
| 1291 | |||
| 1292 | RFC 8478 application/zstd October 2018 | ||
| 1293 | |||
| 1294 | |||
| 1295 | Match length codes are values ranging from 0 to 52 inclusive. They | ||
| 1296 | define lengths from 3 to 131074 bytes. The match length is equal to | ||
| 1297 | the decoded Baseline plus the result of reading Number_of_Bits bits | ||
| 1298 | from the bitstream, as a little-endian value. | ||
| 1299 | |||
| 1300 | |||
| 1301 | |||
| 1302 | |||
| 1303 | |||
| 1304 | |||
| 1305 | |||
| 1306 | |||
| 1307 | |||
| 1308 | |||
| 1309 | |||
| 1310 | |||
| 1311 | |||
| 1312 | |||
| 1313 | |||
| 1314 | |||
| 1315 | |||
| 1316 | |||
| 1317 | |||
| 1318 | |||
| 1319 | |||
| 1320 | |||
| 1321 | |||
| 1322 | |||
| 1323 | |||
| 1324 | |||
| 1325 | |||
| 1326 | |||
| 1327 | |||
| 1328 | |||
| 1329 | |||
| 1330 | |||
| 1331 | |||
| 1332 | |||
| 1333 | |||
| 1334 | |||
| 1335 | |||
| 1336 | |||
| 1337 | |||
| 1338 | |||
| 1339 | |||
| 1340 | |||
| 1341 | |||
| 1342 | |||
| 1343 | |||
| 1344 | |||
| 1345 | |||
| 1346 | Collet & Kucherawy Informational [Page 24] | ||
| 1347 | |||
| 1348 | RFC 8478 application/zstd October 2018 | ||
| 1349 | |||
| 1350 | |||
| 1351 | +-------------------+-----------------------+----------------+ | ||
| 1352 | | Match_Length_Code | Baseline | Number_of_Bits | | ||
| 1353 | +-------------------+-----------------------+----------------+ | ||
| 1354 | | 0-31 | Match_Length_Code + 3 | 0 | | ||
| 1355 | +-------------------+-----------------------+----------------+ | ||
| 1356 | | 32 | 35 | 1 | | ||
| 1357 | +-------------------+-----------------------+----------------+ | ||
| 1358 | | 33 | 37 | 1 | | ||
| 1359 | +-------------------+-----------------------+----------------+ | ||
| 1360 | | 34 | 39 | 1 | | ||
| 1361 | +-------------------+-----------------------+----------------+ | ||
| 1362 | | 35 | 41 | 1 | | ||
| 1363 | +-------------------+-----------------------+----------------+ | ||
| 1364 | | 36 | 43 | 2 | | ||
| 1365 | +-------------------+-----------------------+----------------+ | ||
| 1366 | | 37 | 47 | 2 | | ||
| 1367 | +-------------------+-----------------------+----------------+ | ||
| 1368 | | 38 | 51 | 3 | | ||
| 1369 | +-------------------+-----------------------+----------------+ | ||
| 1370 | | 39 | 59 | 3 | | ||
| 1371 | +-------------------+-----------------------+----------------+ | ||
| 1372 | | 40 | 67 | 4 | | ||
| 1373 | +-------------------+-----------------------+----------------+ | ||
| 1374 | | 41 | 83 | 4 | | ||
| 1375 | +-------------------+-----------------------+----------------+ | ||
| 1376 | | 42 | 99 | 5 | | ||
| 1377 | +-------------------+-----------------------+----------------+ | ||
| 1378 | | 43 | 131 | 7 | | ||
| 1379 | +-------------------+-----------------------+----------------+ | ||
| 1380 | | 44 | 259 | 8 | | ||
| 1381 | +-------------------+-----------------------+----------------+ | ||
| 1382 | | 45 | 515 | 9 | | ||
| 1383 | +-------------------+-----------------------+----------------+ | ||
| 1384 | | 46 | 1027 | 10 | | ||
| 1385 | +-------------------+-----------------------+----------------+ | ||
| 1386 | | 47 | 2051 | 11 | | ||
| 1387 | +-------------------+-----------------------+----------------+ | ||
| 1388 | | 48 | 4099 | 12 | | ||
| 1389 | +-------------------+-----------------------+----------------+ | ||
| 1390 | | 49 | 8195 | 13 | | ||
| 1391 | +-------------------+-----------------------+----------------+ | ||
| 1392 | | 50 | 16387 | 14 | | ||
| 1393 | +-------------------+-----------------------+----------------+ | ||
| 1394 | | 51 | 32771 | 15 | | ||
| 1395 | +-------------------+-----------------------+----------------+ | ||
| 1396 | | 52 | 65539 | 16 | | ||
| 1397 | +-------------------+-----------------------+----------------+ | ||
| 1398 | |||
| 1399 | |||
| 1400 | |||
| 1401 | |||
| 1402 | Collet & Kucherawy Informational [Page 25] | ||
| 1403 | |||
| 1404 | RFC 8478 application/zstd October 2018 | ||
| 1405 | |||
| 1406 | |||
| 1407 | Offset codes are values ranging from 0 to N. | ||
| 1408 | |||
| 1409 | A decoder is free to limit its maximum supported value for N. | ||
| 1410 | Support for values of at least 22 is recommended. At the time of | ||
| 1411 | this writing, the reference decoder supports a maximum N value of 31. | ||
| 1412 | |||
| 1413 | An offset code is also the number of additional bits to read in | ||
| 1414 | little-endian fashion and can be translated into an Offset_Value | ||
| 1415 | using the following formulas: | ||
| 1416 | |||
| 1417 | Offset_Value = (1 << offsetCode) + readNBits(offsetCode); | ||
| 1418 | if (Offset_Value > 3) Offset = Offset_Value - 3; | ||
| 1419 | |||
| 1420 | This means that maximum Offset_Value is (2^(N+1))-1, supporting back- | ||
| 1421 | reference distance up to (2^(N+1))-4, but it is limited by the | ||
| 1422 | maximum back-reference distance (see Section 3.1.1.1.2). | ||
| 1423 | |||
| 1424 | Offset_Value from 1 to 3 are special: they define "repeat codes". | ||
| 1425 | This is described in more detail in Section 3.1.1.5. | ||
| 1426 | |||
| 1427 | 3.1.1.3.2.1.2. Decoding Sequences | ||
| 1428 | |||
| 1429 | FSE bitstreams are read in reverse of the direction they are written. | ||
| 1430 | In zstd, the compressor writes bits forward into a block, and the | ||
| 1431 | decompressor must read the bitstream backwards. | ||
| 1432 | |||
| 1433 | To find the start of the bitstream, it is therefore necessary to know | ||
| 1434 | the offset of the last byte of the block, which can be found by | ||
| 1435 | counting Block_Size bytes after the block header. | ||
| 1436 | |||
| 1437 | After writing the last bit containing information, the compressor | ||
| 1438 | writes a single 1 bit and then fills the byte with 0-7 zero bits of | ||
| 1439 | padding. The last byte of the compressed bitstream cannot be zero | ||
| 1440 | for that reason. | ||
| 1441 | |||
| 1442 | When decompressing, the last byte containing the padding is the first | ||
| 1443 | byte to read. The decompressor needs to skip 0-7 initial zero bits | ||
| 1444 | until the first 1 bit occurs. Afterwards, the useful part of the | ||
| 1445 | bitstream begins. | ||
| 1446 | |||
| 1447 | FSE decoding requires a 'state' to be carried from symbol to symbol. | ||
| 1448 | For more explanation on FSE decoding, see Section 4.1. | ||
| 1449 | |||
| 1450 | For sequence decoding, a separate state keeps track of each literal | ||
| 1451 | lengths, offsets, and match lengths symbols. Some FSE primitives are | ||
| 1452 | also used. For more details on the operation of these primitives, | ||
| 1453 | see Section 4.1. | ||
| 1454 | |||
| 1455 | |||
| 1456 | |||
| 1457 | |||
| 1458 | Collet & Kucherawy Informational [Page 26] | ||
| 1459 | |||
| 1460 | RFC 8478 application/zstd October 2018 | ||
| 1461 | |||
| 1462 | |||
| 1463 | The bitstream starts with initial FSE state values, each using the | ||
| 1464 | required number of bits in their respective accuracy, decoded | ||
| 1465 | previously from their normalized distribution. It starts with | ||
| 1466 | Literals_Length_State, followed by Offset_State, and finally | ||
| 1467 | Match_Length_State. | ||
| 1468 | |||
| 1469 | Note that all values are read backward, so the 'start' of the | ||
| 1470 | bitstream is at the highest position in memory, immediately before | ||
| 1471 | the last 1 bit for padding. | ||
| 1472 | |||
| 1473 | After decoding the starting states, a single sequence is decoded | ||
| 1474 | Number_Of_Sequences times. These sequences are decoded in order from | ||
| 1475 | first to last. Since the compressor writes the bitstream in the | ||
| 1476 | forward direction, this means the compressor must encode the | ||
| 1477 | sequences starting with the last one and ending with the first. | ||
| 1478 | |||
| 1479 | For each of the symbol types, the FSE state can be used to determine | ||
| 1480 | the appropriate code. The code then defines the Baseline and | ||
| 1481 | Number_of_Bits to read for each type. The description of the codes | ||
| 1482 | for how to determine these values can be found in | ||
| 1483 | Section 3.1.1.3.2.1. | ||
| 1484 | |||
| 1485 | Decoding starts by reading the Number_of_Bits required to decode | ||
| 1486 | offset. It does the same for Match_Length and then for | ||
| 1487 | Literals_Length. This sequence is then used for Sequence Execution | ||
| 1488 | (see Section 3.1.1.4). | ||
| 1489 | |||
| 1490 | If it is not the last sequence in the block, the next operation is to | ||
| 1491 | update states. Using the rules pre-calculated in the decoding | ||
| 1492 | tables, Literals_Length_State is updated, followed by | ||
| 1493 | Match_Length_State, and then Offset_State. See Section 4.1 for | ||
| 1494 | details on how to update states from the bitstream. | ||
| 1495 | |||
| 1496 | This operation will be repeated Number_of_Sequences times. At the | ||
| 1497 | end, the bitstream shall be entirely consumed; otherwise, the | ||
| 1498 | bitstream is considered corrupted. | ||
| 1499 | |||
| 1500 | 3.1.1.3.2.2. Default Distributions | ||
| 1501 | |||
| 1502 | If Predefined_Mode is selected for a symbol type, its FSE decoding | ||
| 1503 | table is generated from a predefined distribution table defined here. | ||
| 1504 | For details on how to convert this distribution into a decoding | ||
| 1505 | table, see Section 4.1. | ||
| 1506 | |||
| 1507 | |||
| 1508 | |||
| 1509 | |||
| 1510 | |||
| 1511 | |||
| 1512 | |||
| 1513 | |||
| 1514 | Collet & Kucherawy Informational [Page 27] | ||
| 1515 | |||
| 1516 | RFC 8478 application/zstd October 2018 | ||
| 1517 | |||
| 1518 | |||
| 1519 | 3.1.1.3.2.2.1. Literals Length | ||
| 1520 | |||
| 1521 | The decoding table uses an accuracy log of 6 bits (64 states). | ||
| 1522 | |||
| 1523 | short literalsLength_defaultDistribution[36] = | ||
| 1524 | { 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, | ||
| 1525 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, | ||
| 1526 | -1,-1,-1,-1 | ||
| 1527 | }; | ||
| 1528 | |||
| 1529 | 3.1.1.3.2.2.2. Match Length | ||
| 1530 | |||
| 1531 | The decoding table uses an accuracy log of 6 bits (64 states). | ||
| 1532 | |||
| 1533 | short matchLengths_defaultDistribution[53] = | ||
| 1534 | { 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, | ||
| 1535 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1536 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-1,-1, | ||
| 1537 | -1,-1,-1,-1,-1 | ||
| 1538 | }; | ||
| 1539 | |||
| 1540 | 3.1.1.3.2.2.3. Offset Codes | ||
| 1541 | |||
| 1542 | The decoding table uses an accuracy log of 5 bits (32 states), and | ||
| 1543 | supports a maximum N value of 28, allowing offset values up to | ||
| 1544 | 536,870,908. | ||
| 1545 | |||
| 1546 | If any sequence in the compressed block requires a larger offset than | ||
| 1547 | this, it's not possible to use the default distribution to represent | ||
| 1548 | it. | ||
| 1549 | |||
| 1550 | short offsetCodes_defaultDistribution[29] = | ||
| 1551 | { 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, | ||
| 1552 | 1, 1, 1, 1, 1, 1, 1, 1,-1,-1,-1,-1,-1 | ||
| 1553 | }; | ||
| 1554 | |||
| 1555 | 3.1.1.4. Sequence Execution | ||
| 1556 | |||
| 1557 | Once literals and sequences have been decoded, they are combined to | ||
| 1558 | produce the decoded content of a block. | ||
| 1559 | |||
| 1560 | Each sequence consists of a tuple of (literals_length, offset_value, | ||
| 1561 | match_length), decoded as described in the | ||
| 1562 | Sequences_Section (Section 3.1.1.3.2). To execute a sequence, first | ||
| 1563 | copy literals_length bytes from the decoded literals to the output. | ||
| 1564 | |||
| 1565 | |||
| 1566 | |||
| 1567 | |||
| 1568 | |||
| 1569 | |||
| 1570 | Collet & Kucherawy Informational [Page 28] | ||
| 1571 | |||
| 1572 | RFC 8478 application/zstd October 2018 | ||
| 1573 | |||
| 1574 | |||
| 1575 | Then, match_length bytes are copied from previous decoded data. The | ||
| 1576 | offset to copy from is determined by offset_value: | ||
| 1577 | |||
| 1578 | o if Offset_Value > 3, then the offset is Offset_Value - 3; | ||
| 1579 | |||
| 1580 | o if Offset_Value is from 1-3, the offset is a special repeat offset | ||
| 1581 | value. See Section 3.1.1.5 for how the offset is determined in | ||
| 1582 | this case. | ||
| 1583 | |||
| 1584 | The offset is defined as from the current position (after copying the | ||
| 1585 | literals), so an offset of 6 and a match length of 3 means that 3 | ||
| 1586 | bytes should be copied from 6 bytes back. Note that all offsets | ||
| 1587 | leading to previously decoded data must be smaller than Window_Size | ||
| 1588 | defined in Frame_Header_Descriptor (Section 3.1.1.1.1). | ||
| 1589 | |||
| 1590 | 3.1.1.5. Repeat Offsets | ||
| 1591 | |||
| 1592 | As seen above, the first three values define a repeated offset; we | ||
| 1593 | will call them Repeated_Offset1, Repeated_Offset2, and | ||
| 1594 | Repeated_Offset3. They are sorted in recency order, with | ||
| 1595 | Repeated_Offset1 meaning "most recent one". | ||
| 1596 | |||
| 1597 | If offset_value is 1, then the offset used is Repeated_Offset1, etc. | ||
| 1598 | |||
| 1599 | There is one exception: When the current sequence's literals_length | ||
| 1600 | is 0, repeated offsets are shifted by 1, so an offset_value of 1 | ||
| 1601 | means Repeated_Offset2, an offset_value of 2 means Repeated_Offset3, | ||
| 1602 | and an offset_value of 3 means Repeated_Offset1 - 1_byte. | ||
| 1603 | |||
| 1604 | For the first block, the starting offset history is populated with | ||
| 1605 | the following values: Repeated_Offset1 (1), Repeated_Offset2 (4), and | ||
| 1606 | Repeated_Offset3 (8), unless a dictionary is used, in which case they | ||
| 1607 | come from the dictionary. | ||
| 1608 | |||
| 1609 | Then each block gets its starting offset history from the ending | ||
| 1610 | values of the most recent Compressed_Block. Note that blocks that | ||
| 1611 | are not Compressed_Block are skipped; they do not contribute to | ||
| 1612 | offset history. | ||
| 1613 | |||
| 1614 | The newest offset takes the lead in offset history, shifting others | ||
| 1615 | back (up to its previous place if it was already present). This | ||
| 1616 | means that when Repeated_Offset1 (most recent) is used, history is | ||
| 1617 | unmodified. When Repeated_Offset2 is used, it is swapped with | ||
| 1618 | Repeated_Offset1. If any other offset is used, it becomes | ||
| 1619 | Repeated_Offset1, and the rest are shifted back by 1. | ||
| 1620 | |||
| 1621 | |||
| 1622 | |||
| 1623 | |||
| 1624 | |||
| 1625 | |||
| 1626 | Collet & Kucherawy Informational [Page 29] | ||
| 1627 | |||
| 1628 | RFC 8478 application/zstd October 2018 | ||
| 1629 | |||
| 1630 | |||
| 1631 | 3.1.2. Skippable Frames | ||
| 1632 | |||
| 1633 | +--------------+------------+-----------+ | ||
| 1634 | | Magic_Number | Frame_Size | User_Data | | ||
| 1635 | +--------------+------------+-----------+ | ||
| 1636 | | 4 bytes | 4 bytes | n bytes | | ||
| 1637 | +--------------+------------+-----------+ | ||
| 1638 | |||
| 1639 | Skippable frames allow the insertion of user-defined metadata into a | ||
| 1640 | flow of concatenated frames. | ||
| 1641 | |||
| 1642 | Skippable frames defined in this specification are compatible with | ||
| 1643 | skippable frames in [LZ4]. | ||
| 1644 | |||
| 1645 | From a compliant decoder perspective, skippable frames simply need to | ||
| 1646 | be skipped, and their content ignored, resuming decoding after the | ||
| 1647 | skippable frame. | ||
| 1648 | |||
| 1649 | It should be noted that a skippable frame can be used to watermark a | ||
| 1650 | stream of concatenated frames embedding any kind of tracking | ||
| 1651 | information (even just a Universally Unique Identifier (UUID)). | ||
| 1652 | Users wary of such possibility should scan the stream of concatenated | ||
| 1653 | frames in an attempt to detect such frames for analysis or removal. | ||
| 1654 | |||
| 1655 | The fields are: | ||
| 1656 | |||
| 1657 | Magic_Number: 4 bytes, little-endian format. Value: 0x184D2A5?, | ||
| 1658 | which means any value from 0x184D2A50 to 0x184D2A5F. All 16 | ||
| 1659 | values are valid to identify a skippable frame. This | ||
| 1660 | specification does not detail any specific tagging methods for | ||
| 1661 | skippable frames. | ||
| 1662 | |||
| 1663 | Frame_Size: This is the size, in bytes, of the following User_Data | ||
| 1664 | (without including the magic number nor the size field itself). | ||
| 1665 | This field is represented using 4 bytes, little-endian format, | ||
| 1666 | unsigned 32 bits. This means User_Data can't be bigger than | ||
| 1667 | (2^32-1) bytes. | ||
| 1668 | |||
| 1669 | User_Data: This field can be anything. Data will just be skipped by | ||
| 1670 | the decoder. | ||
| 1671 | |||
| 1672 | 4. Entropy Encoding | ||
| 1673 | |||
| 1674 | Two types of entropy encoding are used by the Zstandard format: FSE | ||
| 1675 | and Huffman coding. Huffman is used to compress literals, while FSE | ||
| 1676 | is used for all other symbols (Literals_Length_Code, | ||
| 1677 | Match_Length_Code, and offset codes) and to compress Huffman headers. | ||
| 1678 | |||
| 1679 | |||
| 1680 | |||
| 1681 | |||
| 1682 | Collet & Kucherawy Informational [Page 30] | ||
| 1683 | |||
| 1684 | RFC 8478 application/zstd October 2018 | ||
| 1685 | |||
| 1686 | |||
| 1687 | 4.1. FSE | ||
| 1688 | |||
| 1689 | FSE, short for Finite State Entropy, is an entropy codec based on | ||
| 1690 | [ANS]. FSE encoding/decoding involves a state that is carried over | ||
| 1691 | between symbols, so decoding must be done in the opposite direction | ||
| 1692 | as encoding. Therefore, all FSE bitstreams are read from end to | ||
| 1693 | beginning. Note that the order of the bits in the stream is not | ||
| 1694 | reversed; they are simply read in the reverse order from which they | ||
| 1695 | were written. | ||
| 1696 | |||
| 1697 | For additional details on FSE, see Finite State Entropy [FSE]. | ||
| 1698 | |||
| 1699 | FSE decoding involves a decoding table that has a power of 2 size and | ||
| 1700 | contains three elements: Symbol, Num_Bits, and Baseline. The base 2 | ||
| 1701 | logarithm of the table size is its Accuracy_Log. An FSE state value | ||
| 1702 | represents an index in this table. | ||
| 1703 | |||
| 1704 | To obtain the initial state value, consume Accuracy_Log bits from the | ||
| 1705 | stream as a little-endian value. The next symbol in the stream is | ||
| 1706 | the Symbol indicated in the table for that state. To obtain the next | ||
| 1707 | state value, the decoder should consume Num_Bits bits from the stream | ||
| 1708 | as a little-endian value and add it to Baseline. | ||
| 1709 | |||
| 1710 | 4.1.1. FSE Table Description | ||
| 1711 | |||
| 1712 | To decode FSE streams, it is necessary to construct the decoding | ||
| 1713 | table. The Zstandard format encodes FSE table descriptions as | ||
| 1714 | described here. | ||
| 1715 | |||
| 1716 | An FSE distribution table describes the probabilities of all symbols | ||
| 1717 | from 0 to the last present one (included) on a normalized scale of | ||
| 1718 | (1 << Accuracy_Log). Note that there must be two or more symbols | ||
| 1719 | with non-zero probability. | ||
| 1720 | |||
| 1721 | A bitstream is read forward, in little-endian fashion. It is not | ||
| 1722 | necessary to know its exact size, since the size will be discovered | ||
| 1723 | and reported by the decoding process. The bitstream starts by | ||
| 1724 | reporting on which scale it operates. If low4bits designates the | ||
| 1725 | lowest 4 bits of the first byte, then Accuracy_Log = low4bits + 5. | ||
| 1726 | |||
| 1727 | |||
| 1728 | |||
| 1729 | |||
| 1730 | |||
| 1731 | |||
| 1732 | |||
| 1733 | |||
| 1734 | |||
| 1735 | |||
| 1736 | |||
| 1737 | |||
| 1738 | Collet & Kucherawy Informational [Page 31] | ||
| 1739 | |||
| 1740 | RFC 8478 application/zstd October 2018 | ||
| 1741 | |||
| 1742 | |||
| 1743 | This is followed by each symbol value, from 0 to the last present | ||
| 1744 | one. The number of bits used by each field is variable and depends | ||
| 1745 | on: | ||
| 1746 | |||
| 1747 | Remaining probabilities + 1: For example, presuming an Accuracy_Log | ||
| 1748 | of 8, and presuming 100 probabilities points have already been | ||
| 1749 | distributed, the decoder may read any value from 0 to | ||
| 1750 | (256 - 100 + 1) == 157, inclusive. Therefore, it must read | ||
| 1751 | log2sup(157) == 8 bits. | ||
| 1752 | |||
| 1753 | Value decoded: Small values use 1 fewer bit. For example, presuming | ||
| 1754 | values from 0 to 157 (inclusive) are possible, 255 - 157 = 98 | ||
| 1755 | values are remaining in an 8-bit field. The first 98 values | ||
| 1756 | (hence from 0 to 97) use only 7 bits, and values from 98 to 157 | ||
| 1757 | use 8 bits. This is achieved through this scheme: | ||
| 1758 | |||
| 1759 | +------------+---------------+-----------+ | ||
| 1760 | | Value Read | Value Decoded | Bits Used | | ||
| 1761 | +------------+---------------+-----------+ | ||
| 1762 | | 0 - 97 | 0 - 97 | 7 | | ||
| 1763 | +------------+---------------+-----------+ | ||
| 1764 | | 98 - 127 | 98 - 127 | 8 | | ||
| 1765 | +------------+---------------+-----------+ | ||
| 1766 | | 128 - 225 | 0 - 97 | 7 | | ||
| 1767 | +------------+---------------+-----------+ | ||
| 1768 | | 226 - 255 | 128 - 157 | 8 | | ||
| 1769 | +------------+---------------+-----------+ | ||
| 1770 | |||
| 1771 | Symbol probabilities are read one by one, in order. The probability | ||
| 1772 | is obtained from Value decoded using the formula P = Value - 1. This | ||
| 1773 | means the value 0 becomes the negative probability -1. This is a | ||
| 1774 | special probability that means "less than 1". Its effect on the | ||
| 1775 | distribution table is described below. For the purpose of | ||
| 1776 | calculating total allocated probability points, it counts as 1. | ||
| 1777 | |||
| 1778 | When a symbol has a probability of zero, it is followed by a 2-bit | ||
| 1779 | repeat flag. This repeat flag tells how many probabilities of zeroes | ||
| 1780 | follow the current one. It provides a number ranging from 0 to 3. | ||
| 1781 | If it is a 3, another 2-bit repeat flag follows, and so on. | ||
| 1782 | |||
| 1783 | When the last symbol reaches a cumulated total of | ||
| 1784 | (1 << Accuracy_Log), decoding is complete. If the last symbol makes | ||
| 1785 | the cumulated total go above (1 << Accuracy_Log), distribution is | ||
| 1786 | considered corrupted. | ||
| 1787 | |||
| 1788 | |||
| 1789 | |||
| 1790 | |||
| 1791 | |||
| 1792 | |||
| 1793 | |||
| 1794 | Collet & Kucherawy Informational [Page 32] | ||
| 1795 | |||
| 1796 | RFC 8478 application/zstd October 2018 | ||
| 1797 | |||
| 1798 | |||
| 1799 | Finally, the decoder can tell how many bytes were used in this | ||
| 1800 | process and how many symbols are present. The bitstream consumes a | ||
| 1801 | round number of bytes. Any remaining bit within the last byte is | ||
| 1802 | simply unused. | ||
| 1803 | |||
| 1804 | The distribution of normalized probabilities is enough to create a | ||
| 1805 | unique decoding table. The table has a size of (1 << Accuracy_Log). | ||
| 1806 | Each cell describes the symbol decoded and instructions to get the | ||
| 1807 | next state. | ||
| 1808 | |||
| 1809 | Symbols are scanned in their natural order for "less than 1" | ||
| 1810 | probabilities as described above. Symbols with this probability are | ||
| 1811 | being attributed a single cell, starting from the end of the table | ||
| 1812 | and retreating. These symbols define a full state reset, reading | ||
| 1813 | Accuracy_Log bits. | ||
| 1814 | |||
| 1815 | All remaining symbols are allocated in their natural order. Starting | ||
| 1816 | from symbol 0 and table position 0, each symbol gets allocated as | ||
| 1817 | many cells as its probability. Cell allocation is spread, not | ||
| 1818 | linear; each successor position follows this rule: | ||
| 1819 | |||
| 1820 | position += (tableSize >> 1) + (tableSize >> 3) + 3; | ||
| 1821 | position &= tableSize - 1; | ||
| 1822 | |||
| 1823 | A position is skipped if it is already occupied by a "less than 1" | ||
| 1824 | probability symbol. Position does not reset between symbols; it | ||
| 1825 | simply iterates through each position in the table, switching to the | ||
| 1826 | next symbol when enough states have been allocated to the current | ||
| 1827 | one. | ||
| 1828 | |||
| 1829 | The result is a list of state values. Each state will decode the | ||
| 1830 | current symbol. | ||
| 1831 | |||
| 1832 | To get the Number_of_Bits and Baseline required for the next state, | ||
| 1833 | it is first necessary to sort all states in their natural order. The | ||
| 1834 | lower states will need 1 more bit than higher ones. The process is | ||
| 1835 | repeated for each symbol. | ||
| 1836 | |||
| 1837 | For example, presuming a symbol has a probability of 5, it receives | ||
| 1838 | five state values. States are sorted in natural order. The next | ||
| 1839 | power of 2 is 8. The space of probabilities is divided into 8 equal | ||
| 1840 | parts. Presuming the Accuracy_Log is 7, this defines 128 states, and | ||
| 1841 | each share (divided by 8) is 16 in size. In order to reach 8, 8 - 5 | ||
| 1842 | = 3 lowest states will count "double", doubling the number of shares | ||
| 1843 | (32 in width), requiring 1 more bit in the process. | ||
| 1844 | |||
| 1845 | |||
| 1846 | |||
| 1847 | |||
| 1848 | |||
| 1849 | |||
| 1850 | Collet & Kucherawy Informational [Page 33] | ||
| 1851 | |||
| 1852 | RFC 8478 application/zstd October 2018 | ||
| 1853 | |||
| 1854 | |||
| 1855 | Baseline is assigned starting from the higher states using fewer | ||
| 1856 | bits, and proceeding naturally, then resuming at the first state, | ||
| 1857 | each taking its allocated width from Baseline. | ||
| 1858 | |||
| 1859 | +----------------+-------+-------+--------+------+-------+ | ||
| 1860 | | state order | 0 | 1 | 2 | 3 | 4 | | ||
| 1861 | +----------------+-------+-------+--------+------+-------+ | ||
| 1862 | | width | 32 | 32 | 32 | 16 | 16 | | ||
| 1863 | +----------------+-------+-------+--------+------+-------+ | ||
| 1864 | | Number_of_Bits | 5 | 5 | 5 | 4 | 4 | | ||
| 1865 | +----------------+-------+-------+--------+------+-------+ | ||
| 1866 | | range number | 2 | 4 | 6 | 0 | 1 | | ||
| 1867 | +----------------+-------+-------+--------+------+-------+ | ||
| 1868 | | Baseline | 32 | 64 | 96 | 0 | 16 | | ||
| 1869 | +----------------+-------+-------+--------+------+-------+ | ||
| 1870 | | range | 32-63 | 64-95 | 96-127 | 0-15 | 16-31 | | ||
| 1871 | +----------------+-------+-------+--------+------+-------+ | ||
| 1872 | |||
| 1873 | The next state is determined from the current state by reading the | ||
| 1874 | required Number_of_Bits and adding the specified Baseline. | ||
| 1875 | |||
| 1876 | See Appendix A for the results of this process that are applied to | ||
| 1877 | the default distributions. | ||
| 1878 | |||
| 1879 | 4.2. Huffman Coding | ||
| 1880 | |||
| 1881 | Zstandard Huffman-coded streams are read backwards, similar to the | ||
| 1882 | FSE bitstreams. Therefore, to find the start of the bitstream, it is | ||
| 1883 | necessary to know the offset of the last byte of the Huffman-coded | ||
| 1884 | stream. | ||
| 1885 | |||
| 1886 | After writing the last bit containing information, the compressor | ||
| 1887 | writes a single 1 bit and then fills the byte with 0-7 0 bits of | ||
| 1888 | padding. The last byte of the compressed bitstream cannot be 0 for | ||
| 1889 | that reason. | ||
| 1890 | |||
| 1891 | When decompressing, the last byte containing the padding is the first | ||
| 1892 | byte to read. The decompressor needs to skip 0-7 initial 0 bits and | ||
| 1893 | the first 1 bit that occurs. Afterwards, the useful part of the | ||
| 1894 | bitstream begins. | ||
| 1895 | |||
| 1896 | The bitstream contains Huffman-coded symbols in little-endian order, | ||
| 1897 | with the codes defined by the method below. | ||
| 1898 | |||
| 1899 | |||
| 1900 | |||
| 1901 | |||
| 1902 | |||
| 1903 | |||
| 1904 | |||
| 1905 | |||
| 1906 | Collet & Kucherawy Informational [Page 34] | ||
| 1907 | |||
| 1908 | RFC 8478 application/zstd October 2018 | ||
| 1909 | |||
| 1910 | |||
| 1911 | 4.2.1. Huffman Tree Description | ||
| 1912 | |||
| 1913 | Prefix coding represents symbols from an a priori known alphabet by | ||
| 1914 | bit sequences (codewords), one codeword for each symbol, in a manner | ||
| 1915 | such that different symbols may be represented by bit sequences of | ||
| 1916 | different lengths, but a parser can always parse an encoded string | ||
| 1917 | unambiguously symbol by symbol. | ||
| 1918 | |||
| 1919 | Given an alphabet with known symbol frequencies, the Huffman | ||
| 1920 | algorithm allows the construction of an optimal prefix code using the | ||
| 1921 | fewest bits of any possible prefix codes for that alphabet. | ||
| 1922 | |||
| 1923 | The prefix code must not exceed a maximum code length. More bits | ||
| 1924 | improve accuracy but yield a larger header size and require more | ||
| 1925 | memory or more complex decoding operations. This specification | ||
| 1926 | limits the maximum code length to 11 bits. | ||
| 1927 | |||
| 1928 | All literal values from zero (included) to the last present one | ||
| 1929 | (excluded) are represented by Weight with values from 0 to | ||
| 1930 | Max_Number_of_Bits. Transformation from Weight to Number_of_Bits | ||
| 1931 | follows this pseudocode: | ||
| 1932 | |||
| 1933 | if Weight == 0 | ||
| 1934 | Number_of_Bits = 0 | ||
| 1935 | else | ||
| 1936 | Number_of_Bits = Max_Number_of_Bits + 1 - Weight | ||
| 1937 | |||
| 1938 | The last symbol's Weight is deduced from previously decoded ones, by | ||
| 1939 | completing to the nearest power of 2. This power of 2 gives | ||
| 1940 | Max_Number_of_Bits the depth of the current tree. | ||
| 1941 | |||
| 1942 | For example, presume the following Huffman tree must be described: | ||
| 1943 | |||
| 1944 | +---------------+----------------+ | ||
| 1945 | | Literal Value | Number_of_Bits | | ||
| 1946 | +---------------+----------------+ | ||
| 1947 | | 0 | 1 | | ||
| 1948 | +---------------+----------------+ | ||
| 1949 | | 1 | 2 | | ||
| 1950 | +---------------+----------------+ | ||
| 1951 | | 2 | 3 | | ||
| 1952 | +---------------+----------------+ | ||
| 1953 | | 3 | 0 | | ||
| 1954 | +---------------+----------------+ | ||
| 1955 | | 4 | 4 | | ||
| 1956 | +---------------+----------------+ | ||
| 1957 | | 5 | 4 | | ||
| 1958 | +---------------+----------------+ | ||
| 1959 | |||
| 1960 | |||
| 1961 | |||
| 1962 | Collet & Kucherawy Informational [Page 35] | ||
| 1963 | |||
| 1964 | RFC 8478 application/zstd October 2018 | ||
| 1965 | |||
| 1966 | |||
| 1967 | The tree depth is 4, since its longest element uses 4 bits. (The | ||
| 1968 | longest elements are those with the smallest frequencies.) Value 5 | ||
| 1969 | will not be listed as it can be determined from the values for 0-4, | ||
| 1970 | nor will values above 5 as they are all 0. Values from 0 to 4 will | ||
| 1971 | be listed using Weight instead of Number_of_Bits. The pseudocode to | ||
| 1972 | determine Weight is: | ||
| 1973 | |||
| 1974 | if Number_of_Bits == 0 | ||
| 1975 | Weight = 0 | ||
| 1976 | else | ||
| 1977 | Weight = Max_Number_of_Bits + 1 - Number_of_Bits | ||
| 1978 | |||
| 1979 | It gives the following series of weights: | ||
| 1980 | |||
| 1981 | +---------------+--------+ | ||
| 1982 | | Literal Value | Weight | | ||
| 1983 | +---------------+--------+ | ||
| 1984 | | 0 | 4 | | ||
| 1985 | +---------------+--------+ | ||
| 1986 | | 1 | 3 | | ||
| 1987 | +---------------+--------+ | ||
| 1988 | | 2 | 2 | | ||
| 1989 | +---------------+--------+ | ||
| 1990 | | 3 | 0 | | ||
| 1991 | +---------------+--------+ | ||
| 1992 | | 4 | 1 | | ||
| 1993 | +---------------+--------+ | ||
| 1994 | |||
| 1995 | The decoder will do the inverse operation: having collected weights | ||
| 1996 | of literals from 0 to 4, it knows the last literal, 5, is present | ||
| 1997 | with a non-zero Weight. The Weight of 5 can be determined by | ||
| 1998 | advancing to the next power of 2. The sum of 2^(Weight-1) (excluding | ||
| 1999 | 0's) is 15. The nearest power of 2 is 16. Therefore, | ||
| 2000 | Max_Number_of_Bits = 4 and Weight[5] = 16 - 15 = 1. | ||
| 2001 | |||
| 2002 | 4.2.1.1. Huffman Tree Header | ||
| 2003 | |||
| 2004 | This is a single byte value (0-255), which describes how the series | ||
| 2005 | of weights is encoded. | ||
| 2006 | |||
| 2007 | headerByte < 128: The series of weights is compressed using FSE (see | ||
| 2008 | below). The length of the FSE-compressed series is equal to | ||
| 2009 | headerByte (0-127). | ||
| 2010 | |||
| 2011 | |||
| 2012 | |||
| 2013 | |||
| 2014 | |||
| 2015 | |||
| 2016 | |||
| 2017 | |||
| 2018 | Collet & Kucherawy Informational [Page 36] | ||
| 2019 | |||
| 2020 | RFC 8478 application/zstd October 2018 | ||
| 2021 | |||
| 2022 | |||
| 2023 | headerByte >= 128: This is a direct representation, where each | ||
| 2024 | Weight is written directly as a 4-bit field (0-15). They are | ||
| 2025 | encoded forward, 2 weights to a byte with the first weight taking | ||
| 2026 | the top 4 bits and the second taking the bottom 4; for example, | ||
| 2027 | the following operations could be used to read the weights: | ||
| 2028 | |||
| 2029 | Weight[0] = (Byte[0] >> 4) | ||
| 2030 | Weight[1] = (Byte[0] & 0xf), | ||
| 2031 | etc. | ||
| 2032 | |||
| 2033 | The full representation occupies ceiling(Number_of_Symbols/2) | ||
| 2034 | bytes, meaning it uses only full bytes even if Number_of_Symbols | ||
| 2035 | is odd. Number_of_Symbols = headerByte - 127. Note that maximum | ||
| 2036 | Number_of_Symbols is 255 - 127 = 128. If any literal has a value | ||
| 2037 | over 128, raw header mode is not possible, and it is necessary to | ||
| 2038 | use FSE compression. | ||
| 2039 | |||
| 2040 | 4.2.1.2. FSE Compression of Huffman Weights | ||
| 2041 | |||
| 2042 | In this case, the series of Huffman weights is compressed using FSE | ||
| 2043 | compression. It is a single bitstream with two interleaved states, | ||
| 2044 | sharing a single distribution table. | ||
| 2045 | |||
| 2046 | To decode an FSE bitstream, it is necessary to know its compressed | ||
| 2047 | size. Compressed size is provided by headerByte. It's also | ||
| 2048 | necessary to know its maximum possible decompressed size, which is | ||
| 2049 | 255, since literal values span from 0 to 255, and the last symbol's | ||
| 2050 | Weight is not represented. | ||
| 2051 | |||
| 2052 | An FSE bitstream starts by a header, describing probabilities | ||
| 2053 | distribution. It will create a decoding table. For a list of | ||
| 2054 | Huffman weights, the maximum accuracy log is 6 bits. For more | ||
| 2055 | details, see Section 4.1.1. | ||
| 2056 | |||
| 2057 | The Huffman header compression uses two states, which share the same | ||
| 2058 | FSE distribution table. The first state (State1) encodes the even- | ||
| 2059 | numbered index symbols, and the second (State2) encodes the odd- | ||
| 2060 | numbered index symbols. State1 is initialized first, and then | ||
| 2061 | State2, and they take turns decoding a single symbol and updating | ||
| 2062 | their state. For more details on these FSE operations, see | ||
| 2063 | Section 4.1. | ||
| 2064 | |||
| 2065 | The number of symbols to be decoded is determined by tracking the | ||
| 2066 | bitStream overflow condition: If updating state after decoding a | ||
| 2067 | symbol would require more bits than remain in the stream, it is | ||
| 2068 | assumed that extra bits are zero. Then, symbols for each of the | ||
| 2069 | final states are decoded and the process is complete. | ||
| 2070 | |||
| 2071 | |||
| 2072 | |||
| 2073 | |||
| 2074 | Collet & Kucherawy Informational [Page 37] | ||
| 2075 | |||
| 2076 | RFC 8478 application/zstd October 2018 | ||
| 2077 | |||
| 2078 | |||
| 2079 | 4.2.1.3. Conversion from Weights to Huffman Prefix Codes | ||
| 2080 | |||
| 2081 | All present symbols will now have a Weight value. It is possible to | ||
| 2082 | transform weights into Number_of_Bits, using this formula: | ||
| 2083 | |||
| 2084 | if Weight > 0 | ||
| 2085 | Number_of_Bits = Max_Number_of_Bits + 1 - Weight | ||
| 2086 | else | ||
| 2087 | Number_of_Bits = 0 | ||
| 2088 | |||
| 2089 | Symbols are sorted by Weight. Within the same Weight, symbols keep | ||
| 2090 | natural sequential order. Symbols with a Weight of zero are removed. | ||
| 2091 | Then, starting from the lowest Weight, prefix codes are distributed | ||
| 2092 | in sequential order. | ||
| 2093 | |||
| 2094 | For example, assume the following list of weights has been decoded: | ||
| 2095 | |||
| 2096 | +---------+--------+ | ||
| 2097 | | Literal | Weight | | ||
| 2098 | +---------+--------+ | ||
| 2099 | | 0 | 4 | | ||
| 2100 | +---------+--------+ | ||
| 2101 | | 1 | 3 | | ||
| 2102 | +---------+--------+ | ||
| 2103 | | 2 | 2 | | ||
| 2104 | +---------+--------+ | ||
| 2105 | | 3 | 0 | | ||
| 2106 | +---------+--------+ | ||
| 2107 | | 4 | 1 | | ||
| 2108 | +---------+--------+ | ||
| 2109 | | 5 | 1 | | ||
| 2110 | +---------+--------+ | ||
| 2111 | |||
| 2112 | |||
| 2113 | |||
| 2114 | |||
| 2115 | |||
| 2116 | |||
| 2117 | |||
| 2118 | |||
| 2119 | |||
| 2120 | |||
| 2121 | |||
| 2122 | |||
| 2123 | |||
| 2124 | |||
| 2125 | |||
| 2126 | |||
| 2127 | |||
| 2128 | |||
| 2129 | |||
| 2130 | Collet & Kucherawy Informational [Page 38] | ||
| 2131 | |||
| 2132 | RFC 8478 application/zstd October 2018 | ||
| 2133 | |||
| 2134 | |||
| 2135 | Sorting by weight and then the natural sequential order yields the | ||
| 2136 | following distribution: | ||
| 2137 | |||
| 2138 | +---------+--------+----------------+--------------+ | ||
| 2139 | | Literal | Weight | Number_Of_Bits | Prefix Codes | | ||
| 2140 | +---------+--------+----------------|--------------+ | ||
| 2141 | | 3 | 0 | 0 | N/A | | ||
| 2142 | +---------+--------+----------------|--------------+ | ||
| 2143 | | 4 | 1 | 4 | 0000 | | ||
| 2144 | +---------+--------+----------------|--------------+ | ||
| 2145 | | 5 | 1 | 4 | 0001 | | ||
| 2146 | +---------+--------+----------------|--------------+ | ||
| 2147 | | 2 | 2 | 3 | 001 | | ||
| 2148 | +---------+--------+----------------|--------------+ | ||
| 2149 | | 1 | 3 | 2 | 01 | | ||
| 2150 | +---------+--------+----------------|--------------+ | ||
| 2151 | | 0 | 4 | 1 | 1 | | ||
| 2152 | +---------+--------+----------------|--------------+ | ||
| 2153 | |||
| 2154 | 4.2.2. Huffman-Coded Streams | ||
| 2155 | |||
| 2156 | Given a Huffman decoding table, it is possible to decode a Huffman- | ||
| 2157 | coded stream. | ||
| 2158 | |||
| 2159 | Each bitstream must be read backward, which starts from the end and | ||
| 2160 | goes up to the beginning. Therefore, it is necessary to know the | ||
| 2161 | size of each bitstream. | ||
| 2162 | |||
| 2163 | It is also necessary to know exactly which bit is the last. This is | ||
| 2164 | detected by a final bit flag: the highest bit of the last byte is a | ||
| 2165 | final-bit-flag. Consequently, a last byte of 0 is not possible. And | ||
| 2166 | the final-bit-flag itself is not part of the useful bitstream. | ||
| 2167 | Hence, the last byte contains between 0 and 7 useful bits. | ||
| 2168 | |||
| 2169 | Starting from the end, it is possible to read the bitstream in a | ||
| 2170 | little-endian fashion, keeping track of already used bits. Since the | ||
| 2171 | bitstream is encoded in reverse order, starting from the end, read | ||
| 2172 | symbols in forward order. | ||
| 2173 | |||
| 2174 | |||
| 2175 | |||
| 2176 | |||
| 2177 | |||
| 2178 | |||
| 2179 | |||
| 2180 | |||
| 2181 | |||
| 2182 | |||
| 2183 | |||
| 2184 | |||
| 2185 | |||
| 2186 | Collet & Kucherawy Informational [Page 39] | ||
| 2187 | |||
| 2188 | RFC 8478 application/zstd October 2018 | ||
| 2189 | |||
| 2190 | |||
| 2191 | For example, if the literal sequence "0145" was encoded using the | ||
| 2192 | above prefix code, it would be encoded (in reverse order) as: | ||
| 2193 | |||
| 2194 | +---------+----------+ | ||
| 2195 | | Symbol | Encoding | | ||
| 2196 | +---------+----------+ | ||
| 2197 | | 5 | 0000 | | ||
| 2198 | +---------+----------+ | ||
| 2199 | | 4 | 0001 | | ||
| 2200 | +---------+----------+ | ||
| 2201 | | 1 | 01 | | ||
| 2202 | +---------+----------+ | ||
| 2203 | | 0 | 1 | | ||
| 2204 | +---------+----------+ | ||
| 2205 | | Padding | 00001 | | ||
| 2206 | +---------+----------+ | ||
| 2207 | |||
| 2208 | This results in the following 2-byte bitstream: | ||
| 2209 | |||
| 2210 | 00010000 00001101 | ||
| 2211 | |||
| 2212 | Here is an alternative representation with the symbol codes separated | ||
| 2213 | by underscores: | ||
| 2214 | |||
| 2215 | 0001_0000 00001_1_01 | ||
| 2216 | |||
| 2217 | Reading the highest Max_Number_of_Bits bits, it's possible to compare | ||
| 2218 | the extracted value to the decoding table, determining the symbol to | ||
| 2219 | decode and number of bits to discard. | ||
| 2220 | |||
| 2221 | The process continues reading up to the required number of symbols | ||
| 2222 | per stream. If a bitstream is not entirely and exactly consumed, | ||
| 2223 | hence reaching exactly its beginning position with all bits consumed, | ||
| 2224 | the decoding process is considered faulty. | ||
| 2225 | |||
| 2226 | 5. Dictionary Format | ||
| 2227 | |||
| 2228 | Zstandard is compatible with "raw content" dictionaries, free of any | ||
| 2229 | format restriction, except that they must be at least 8 bytes. These | ||
| 2230 | dictionaries function as if they were just the content part of a | ||
| 2231 | formatted dictionary. | ||
| 2232 | |||
| 2233 | However, dictionaries created by "zstd --train" in the reference | ||
| 2234 | implementation follow a specific format, described here. | ||
| 2235 | |||
| 2236 | Dictionaries are not included in the compressed content but rather | ||
| 2237 | are provided out of band. That is, the Dictionary_ID identifies | ||
| 2238 | which should be used, but this specification does not describe the | ||
| 2239 | |||
| 2240 | |||
| 2241 | |||
| 2242 | Collet & Kucherawy Informational [Page 40] | ||
| 2243 | |||
| 2244 | RFC 8478 application/zstd October 2018 | ||
| 2245 | |||
| 2246 | |||
| 2247 | mechanism by which the dictionary is obtained prior to use during | ||
| 2248 | compression or decompression. | ||
| 2249 | |||
| 2250 | A dictionary has a size, defined either by a buffer limit or a file | ||
| 2251 | size. The general format is: | ||
| 2252 | |||
| 2253 | +--------------+---------------+----------------+---------+ | ||
| 2254 | | Magic_Number | Dictionary_ID | Entropy_Tables | Content | | ||
| 2255 | +--------------+---------------+----------------+---------+ | ||
| 2256 | |||
| 2257 | Magic_Number: 4 bytes ID, value 0xEC30A437, little-endian format. | ||
| 2258 | |||
| 2259 | Dictionary_ID: 4 bytes, stored in little-endian format. | ||
| 2260 | Dictionary_ID can be any value, except 0 (which means no | ||
| 2261 | Dictionary_ID). It is used by decoders to check if they use the | ||
| 2262 | correct dictionary. If the frame is going to be distributed in a | ||
| 2263 | private environment, any Dictionary_ID can be used. However, for | ||
| 2264 | public distribution of compressed frames, the following ranges are | ||
| 2265 | reserved and shall not be used: | ||
| 2266 | |||
| 2267 | low range: <= 32767 | ||
| 2268 | high range: >= (2^31) | ||
| 2269 | |||
| 2270 | Entropy_Tables: Follow the same format as the tables in compressed | ||
| 2271 | blocks. See the relevant FSE and Huffman sections for how to | ||
| 2272 | decode these tables. They are stored in the following order: | ||
| 2273 | Huffman table for literals, FSE table for offsets, FSE table for | ||
| 2274 | match lengths, and FSE table for literals lengths. These tables | ||
| 2275 | populate the Repeat Stats literals mode and Repeat distribution | ||
| 2276 | mode for sequence decoding. It is finally followed by 3 offset | ||
| 2277 | values, populating repeat offsets (instead of using {1,4,8}), | ||
| 2278 | stored in order, 4-bytes little-endian each, for a total of 12 | ||
| 2279 | bytes. Each repeat offset must have a value less than the | ||
| 2280 | dictionary size. | ||
| 2281 | |||
| 2282 | Content: The rest of the dictionary is its content. The content | ||
| 2283 | acts as a "past" in front of data to be compressed or | ||
| 2284 | decompressed, so it can be referenced in sequence commands. As | ||
| 2285 | long as the amount of data decoded from this frame is less than or | ||
| 2286 | equal to Window_Size, sequence commands may specify offsets longer | ||
| 2287 | than the total length of decoded output so far to reference back | ||
| 2288 | to the dictionary, even parts of the dictionary with offsets | ||
| 2289 | larger than Window_Size. After the total output has surpassed | ||
| 2290 | Window_Size, however, this is no longer allowed, and the | ||
| 2291 | dictionary is no longer accessible. | ||
| 2292 | |||
| 2293 | |||
| 2294 | |||
| 2295 | |||
| 2296 | |||
| 2297 | |||
| 2298 | Collet & Kucherawy Informational [Page 41] | ||
| 2299 | |||
| 2300 | RFC 8478 application/zstd October 2018 | ||
| 2301 | |||
| 2302 | |||
| 2303 | 6. IANA Considerations | ||
| 2304 | |||
| 2305 | IANA has made two registrations, as described below. | ||
| 2306 | |||
| 2307 | 6.1. The 'application/zstd' Media Type | ||
| 2308 | |||
| 2309 | The 'application/zstd' media type identifies a block of data that is | ||
| 2310 | compressed using zstd compression. The data is a stream of bytes as | ||
| 2311 | described in this document. IANA has added the following to the | ||
| 2312 | "Media Types" registry: | ||
| 2313 | |||
| 2314 | Type name: application | ||
| 2315 | |||
| 2316 | Subtype name: zstd | ||
| 2317 | |||
| 2318 | Required parameters: N/A | ||
| 2319 | |||
| 2320 | Optional parameters: N/A | ||
| 2321 | |||
| 2322 | Encoding considerations: binary | ||
| 2323 | |||
| 2324 | Security considerations: See Section 7 of RFC 8478 | ||
| 2325 | |||
| 2326 | Interoperability considerations: N/A | ||
| 2327 | |||
| 2328 | Published specification: RFC 8478 | ||
| 2329 | |||
| 2330 | Applications that use this media type: anywhere data size is an | ||
| 2331 | issue | ||
| 2332 | |||
| 2333 | Additional information: | ||
| 2334 | |||
| 2335 | Magic number(s): 4 bytes, little-endian format. | ||
| 2336 | Value: 0xFD2FB528 | ||
| 2337 | |||
| 2338 | File extension(s): zst | ||
| 2339 | |||
| 2340 | Macintosh file type code(s): N/A | ||
| 2341 | |||
| 2342 | For further information: See [ZSTD] | ||
| 2343 | |||
| 2344 | Intended usage: common | ||
| 2345 | |||
| 2346 | Restrictions on usage: N/A | ||
| 2347 | |||
| 2348 | Author: Murray S. Kucherawy | ||
| 2349 | |||
| 2350 | Change Controller: IETF | ||
| 2351 | |||
| 2352 | |||
| 2353 | |||
| 2354 | Collet & Kucherawy Informational [Page 42] | ||
| 2355 | |||
| 2356 | RFC 8478 application/zstd October 2018 | ||
| 2357 | |||
| 2358 | |||
| 2359 | Provisional registration: no | ||
| 2360 | |||
| 2361 | 6.2. Content Encoding | ||
| 2362 | |||
| 2363 | IANA has added the following entry to the "HTTP Content Coding | ||
| 2364 | Registry" within the "Hypertext Transfer Protocol (HTTP) Parameters" | ||
| 2365 | registry: | ||
| 2366 | |||
| 2367 | Name: zstd | ||
| 2368 | |||
| 2369 | Description: A stream of bytes compressed using the Zstandard | ||
| 2370 | protocol | ||
| 2371 | |||
| 2372 | Pointer to specification text: RFC 8478 | ||
| 2373 | |||
| 2374 | 6.3. Dictionaries | ||
| 2375 | |||
| 2376 | Work in progress includes development of dictionaries that will | ||
| 2377 | optimize compression and decompression of particular types of data. | ||
| 2378 | Specification of such dictionaries for public use will necessitate | ||
| 2379 | registration of a code point from the reserved range described in | ||
| 2380 | Section 3.1.1.1.3 and its association with a specific dictionary. | ||
| 2381 | |||
| 2382 | However, there are at present no such dictionaries published for | ||
| 2383 | public use, so this document makes no immediate request of IANA to | ||
| 2384 | create such a registry. | ||
| 2385 | |||
| 2386 | 7. Security Considerations | ||
| 2387 | |||
| 2388 | Any data compression method involves the reduction of redundancy in | ||
| 2389 | the data. Zstandard is no exception, and the usual precautions | ||
| 2390 | apply. | ||
| 2391 | |||
| 2392 | One should never compress a message whose content must remain secret | ||
| 2393 | with a message generated by a third party. Such a compression can be | ||
| 2394 | used to guess the content of the secret message through analysis of | ||
| 2395 | entropy reduction. This was demonstrated in the Compression Ratio | ||
| 2396 | Info-leak Made Easy (CRIME) attack [CRIME], for example. | ||
| 2397 | |||
| 2398 | A decoder has to demonstrate capabilities to detect and prevent any | ||
| 2399 | kind of data tampering in the compressed frame from triggering system | ||
| 2400 | faults, such as reading or writing beyond allowed memory ranges. | ||
| 2401 | This can be guaranteed by either the implementation language or | ||
| 2402 | careful bound checkings. Of particular note is the encoding of | ||
| 2403 | Number_of_Sequences values that cause the decoder to read into the | ||
| 2404 | block header (and beyond), as well as the indication of a | ||
| 2405 | Frame_Content_Size that is smaller than the actual decompressed data, | ||
| 2406 | in an attempt to trigger a buffer overflow. It is highly recommended | ||
| 2407 | |||
| 2408 | |||
| 2409 | |||
| 2410 | Collet & Kucherawy Informational [Page 43] | ||
| 2411 | |||
| 2412 | RFC 8478 application/zstd October 2018 | ||
| 2413 | |||
| 2414 | |||
| 2415 | to fuzz-test (i.e., provide invalid, unexpected, or random input and | ||
| 2416 | verify safe operation of) decoder implementations to test and harden | ||
| 2417 | their capability to detect bad frames and deal with them without any | ||
| 2418 | adverse system side effect. | ||
| 2419 | |||
| 2420 | An attacker may provide correctly formed compressed frames with | ||
| 2421 | unreasonable memory requirements. A decoder must always control | ||
| 2422 | memory requirements and enforce some (system-specific) limits in | ||
| 2423 | order to protect memory usage from such scenarios. | ||
| 2424 | |||
| 2425 | Compression can be optimized by training a dictionary on a variety of | ||
| 2426 | related content payloads. This dictionary must then be available at | ||
| 2427 | the decoder for decompression of the payload to be possible. While | ||
| 2428 | this document does not specify how to acquire a dictionary for a | ||
| 2429 | given compressed payload, it is worth noting that third-party | ||
| 2430 | dictionaries may interact unexpectedly with a decoder, leading to | ||
| 2431 | possible memory or other resource exhaustion attacks. We expect such | ||
| 2432 | topics to be discussed in further detail in the Security | ||
| 2433 | Considerations section of a forthcoming RFC for dictionary | ||
| 2434 | acquisition and transmission, but highlight this issue now out of an | ||
| 2435 | abundance of caution. | ||
| 2436 | |||
| 2437 | As discussed in Section 3.1.2, it is possible to store arbitrary user | ||
| 2438 | metadata in skippable frames. While such frames are ignored during | ||
| 2439 | decompression of the data, they can be used as a watermark to track | ||
| 2440 | the path of the compressed payload. | ||
| 2441 | |||
| 2442 | 8. Implementation Status | ||
| 2443 | |||
| 2444 | Source code for a C language implementation of a Zstandard-compliant | ||
| 2445 | library is available at [ZSTD-GITHUB]. This implementation is | ||
| 2446 | considered to be the reference implementation and is production | ||
| 2447 | ready; it implements the full range of the specification. It is | ||
| 2448 | routinely tested against security hazards and widely deployed within | ||
| 2449 | Facebook infrastructure. | ||
| 2450 | |||
| 2451 | The reference version is optimized for speed and is highly portable. | ||
| 2452 | It has been proven to run safely on multiple architectures (e.g., | ||
| 2453 | x86, x64, ARM, MIPS, PowerPC, IA64) featuring 32- or 64-bit | ||
| 2454 | addressing schemes, a little- or big-endian storage scheme, a number | ||
| 2455 | of different operating systems (e.g., UNIX (including Linux, BSD, | ||
| 2456 | OS-X, and Solaris) and Windows), and a number of compilers (e.g., | ||
| 2457 | gcc, clang, visual, and icc). | ||
| 2458 | |||
| 2459 | |||
| 2460 | |||
| 2461 | |||
| 2462 | |||
| 2463 | |||
| 2464 | |||
| 2465 | |||
| 2466 | Collet & Kucherawy Informational [Page 44] | ||
| 2467 | |||
| 2468 | RFC 8478 application/zstd October 2018 | ||
| 2469 | |||
| 2470 | |||
| 2471 | 9. References | ||
| 2472 | |||
| 2473 | 9.1. Normative References | ||
| 2474 | |||
| 2475 | [ZSTD] "Zstandard", <http://www.zstd.net>. | ||
| 2476 | |||
| 2477 | 9.2. Informative References | ||
| 2478 | |||
| 2479 | [ANS] Duda, J., "Asymmetric numeral systems: entropy coding | ||
| 2480 | combining speed of Huffman coding with compression rate of | ||
| 2481 | arithmetic coding", January 2014, | ||
| 2482 | <https://arxiv.org/pdf/1311.2540>. | ||
| 2483 | |||
| 2484 | [CRIME] "CRIME", June 2018, <https://en.wikipedia.org/w/ | ||
| 2485 | index.php?title=CRIME&oldid=844538656>. | ||
| 2486 | |||
| 2487 | [FSE] "FiniteStateEntropy", commit 6efa78a, June 2018, | ||
| 2488 | <https://github.com/Cyan4973/FiniteStateEntropy/>. | ||
| 2489 | |||
| 2490 | [LZ4] "LZ4 Frame Format Description", commit d03224b, January | ||
| 2491 | 2018, <https://github.com/lz4/lz4/blob/master/doc/ | ||
| 2492 | lz4_Frame_format.md>. | ||
| 2493 | |||
| 2494 | [RFC1952] Deutsch, P., "GZIP file format specification version 4.3", | ||
| 2495 | RFC 1952, DOI 10.17487/RFC1952, May 1996, | ||
| 2496 | <https://www.rfc-editor.org/info/rfc1952>. | ||
| 2497 | |||
| 2498 | [XXHASH] "XXHASH Algorithm", <http://www.xxhash.org>. | ||
| 2499 | |||
| 2500 | [ZSTD-GITHUB] | ||
| 2501 | "zstd", commit 8514bd8, August 2018, | ||
| 2502 | <https://github.com/facebook/zstd>. | ||
| 2503 | |||
| 2504 | |||
| 2505 | |||
| 2506 | |||
| 2507 | |||
| 2508 | |||
| 2509 | |||
| 2510 | |||
| 2511 | |||
| 2512 | |||
| 2513 | |||
| 2514 | |||
| 2515 | |||
| 2516 | |||
| 2517 | |||
| 2518 | |||
| 2519 | |||
| 2520 | |||
| 2521 | |||
| 2522 | Collet & Kucherawy Informational [Page 45] | ||
| 2523 | |||
| 2524 | RFC 8478 application/zstd October 2018 | ||
| 2525 | |||
| 2526 | |||
| 2527 | Appendix A. Decoding Tables for Predefined Codes | ||
| 2528 | |||
| 2529 | This appendix contains FSE decoding tables for the predefined literal | ||
| 2530 | length, match length, and offset codes. The tables have been | ||
| 2531 | constructed using the algorithm as given above in Section 4.1.1. The | ||
| 2532 | tables here can be used as examples to crosscheck that an | ||
| 2533 | implementation has built its decoding tables correctly. | ||
| 2534 | |||
| 2535 | A.1. Literal Length Code Table | ||
| 2536 | |||
| 2537 | +-------+--------+----------------+------+ | ||
| 2538 | | State | Symbol | Number_Of_Bits | Base | | ||
| 2539 | +-------+--------+----------------+------+ | ||
| 2540 | | 0 | 0 | 0 | 0 | | ||
| 2541 | +-------+--------+----------------+------+ | ||
| 2542 | | 0 | 0 | 4 | 0 | | ||
| 2543 | +-------+--------+----------------+------+ | ||
| 2544 | | 1 | 0 | 4 | 16 | | ||
| 2545 | +-------+--------+----------------+------+ | ||
| 2546 | | 2 | 1 | 5 | 32 | | ||
| 2547 | +-------+--------+----------------+------+ | ||
| 2548 | | 3 | 3 | 5 | 0 | | ||
| 2549 | +-------+--------+----------------+------+ | ||
| 2550 | | 4 | 4 | 5 | 0 | | ||
| 2551 | +-------+--------+----------------+------+ | ||
| 2552 | | 5 | 6 | 5 | 0 | | ||
| 2553 | +-------+--------+----------------+------+ | ||
| 2554 | | 6 | 7 | 5 | 0 | | ||
| 2555 | +-------+--------+----------------+------+ | ||
| 2556 | | 7 | 9 | 5 | 0 | | ||
| 2557 | +-------+--------+----------------+------+ | ||
| 2558 | | 8 | 10 | 5 | 0 | | ||
| 2559 | +-------+--------+----------------+------+ | ||
| 2560 | | 9 | 12 | 5 | 0 | | ||
| 2561 | +-------+--------+----------------+------+ | ||
| 2562 | | 10 | 14 | 6 | 0 | | ||
| 2563 | +-------+--------+----------------+------+ | ||
| 2564 | | 11 | 16 | 5 | 0 | | ||
| 2565 | +-------+--------+----------------+------+ | ||
| 2566 | | 12 | 18 | 5 | 0 | | ||
| 2567 | +-------+--------+----------------+------+ | ||
| 2568 | | 13 | 19 | 5 | 0 | | ||
| 2569 | +-------+--------+----------------+------+ | ||
| 2570 | | 14 | 21 | 5 | 0 | | ||
| 2571 | +-------+--------+----------------+------+ | ||
| 2572 | | 15 | 22 | 5 | 0 | | ||
| 2573 | +-------+--------+----------------+------+ | ||
| 2574 | | 16 | 24 | 5 | 0 | | ||
| 2575 | |||
| 2576 | |||
| 2577 | |||
| 2578 | Collet & Kucherawy Informational [Page 46] | ||
| 2579 | |||
| 2580 | RFC 8478 application/zstd October 2018 | ||
| 2581 | |||
| 2582 | |||
| 2583 | +-------+--------+----------------+------+ | ||
| 2584 | | 17 | 25 | 5 | 32 | | ||
| 2585 | +-------+--------+----------------+------+ | ||
| 2586 | | 18 | 26 | 5 | 0 | | ||
| 2587 | +-------+--------+----------------+------+ | ||
| 2588 | | 19 | 27 | 6 | 0 | | ||
| 2589 | +-------+--------+----------------+------+ | ||
| 2590 | | 20 | 29 | 6 | 0 | | ||
| 2591 | +-------+--------+----------------+------+ | ||
| 2592 | | 21 | 31 | 6 | 0 | | ||
| 2593 | +-------+--------+----------------+------+ | ||
| 2594 | | 22 | 0 | 4 | 32 | | ||
| 2595 | +-------+--------+----------------+------+ | ||
| 2596 | | 23 | 1 | 4 | 0 | | ||
| 2597 | +-------+--------+----------------+------+ | ||
| 2598 | | 24 | 2 | 5 | 0 | | ||
| 2599 | +-------+--------+----------------+------+ | ||
| 2600 | | 25 | 4 | 5 | 32 | | ||
| 2601 | +-------+--------+----------------+------+ | ||
| 2602 | | 26 | 5 | 5 | 0 | | ||
| 2603 | +-------+--------+----------------+------+ | ||
| 2604 | | 27 | 7 | 5 | 32 | | ||
| 2605 | +-------+--------+----------------+------+ | ||
| 2606 | | 28 | 8 | 5 | 0 | | ||
| 2607 | +-------+--------+----------------+------+ | ||
| 2608 | | 29 | 10 | 5 | 32 | | ||
| 2609 | +-------+--------+----------------+------+ | ||
| 2610 | | 30 | 11 | 5 | 0 | | ||
| 2611 | +-------+--------+----------------+------+ | ||
| 2612 | | 31 | 13 | 6 | 0 | | ||
| 2613 | +-------+--------+----------------+------+ | ||
| 2614 | | 32 | 16 | 5 | 32 | | ||
| 2615 | +-------+--------+----------------+------+ | ||
| 2616 | | 33 | 17 | 5 | 0 | | ||
| 2617 | +-------+--------+----------------+------+ | ||
| 2618 | | 34 | 19 | 5 | 32 | | ||
| 2619 | +-------+--------+----------------+------+ | ||
| 2620 | | 35 | 20 | 5 | 0 | | ||
| 2621 | +-------+--------+----------------+------+ | ||
| 2622 | | 36 | 22 | 5 | 32 | | ||
| 2623 | +-------+--------+----------------+------+ | ||
| 2624 | | 37 | 23 | 5 | 0 | | ||
| 2625 | +-------+--------+----------------+------+ | ||
| 2626 | | 38 | 25 | 4 | 0 | | ||
| 2627 | +-------+--------+----------------+------+ | ||
| 2628 | | 39 | 25 | 4 | 16 | | ||
| 2629 | +-------+--------+----------------+------+ | ||
| 2630 | | 40 | 26 | 5 | 32 | | ||
| 2631 | |||
| 2632 | |||
| 2633 | |||
| 2634 | Collet & Kucherawy Informational [Page 47] | ||
| 2635 | |||
| 2636 | RFC 8478 application/zstd October 2018 | ||
| 2637 | |||
| 2638 | |||
| 2639 | +-------+--------+----------------+------+ | ||
| 2640 | | 41 | 28 | 6 | 0 | | ||
| 2641 | +-------+--------+----------------+------+ | ||
| 2642 | | 42 | 30 | 6 | 0 | | ||
| 2643 | +-------+--------+----------------+------+ | ||
| 2644 | | 43 | 0 | 4 | 48 | | ||
| 2645 | +-------+--------+----------------+------+ | ||
| 2646 | | 44 | 1 | 4 | 16 | | ||
| 2647 | +-------+--------+----------------+------+ | ||
| 2648 | | 45 | 2 | 5 | 32 | | ||
| 2649 | +-------+--------+----------------+------+ | ||
| 2650 | | 46 | 3 | 5 | 32 | | ||
| 2651 | +-------+--------+----------------+------+ | ||
| 2652 | | 47 | 5 | 5 | 32 | | ||
| 2653 | +-------+--------+----------------+------+ | ||
| 2654 | | 48 | 6 | 5 | 32 | | ||
| 2655 | +-------+--------+----------------+------+ | ||
| 2656 | | 49 | 8 | 5 | 32 | | ||
| 2657 | +-------+--------+----------------+------+ | ||
| 2658 | | 50 | 9 | 5 | 32 | | ||
| 2659 | +-------+--------+----------------+------+ | ||
| 2660 | | 51 | 11 | 5 | 32 | | ||
| 2661 | +-------+--------+----------------+------+ | ||
| 2662 | | 52 | 12 | 5 | 32 | | ||
| 2663 | +-------+--------+----------------+------+ | ||
| 2664 | | 53 | 15 | 6 | 0 | | ||
| 2665 | +-------+--------+----------------+------+ | ||
| 2666 | | 54 | 17 | 5 | 32 | | ||
| 2667 | +-------+--------+----------------+------+ | ||
| 2668 | | 55 | 18 | 5 | 32 | | ||
| 2669 | +-------+--------+----------------+------+ | ||
| 2670 | | 56 | 20 | 5 | 32 | | ||
| 2671 | +-------+--------+----------------+------+ | ||
| 2672 | | 57 | 21 | 5 | 32 | | ||
| 2673 | +-------+--------+----------------+------+ | ||
| 2674 | | 58 | 23 | 5 | 32 | | ||
| 2675 | +-------+--------+----------------+------+ | ||
| 2676 | | 59 | 24 | 5 | 32 | | ||
| 2677 | +-------+--------+----------------+------+ | ||
| 2678 | | 60 | 35 | 6 | 0 | | ||
| 2679 | +-------+--------+----------------+------+ | ||
| 2680 | | 61 | 34 | 6 | 0 | | ||
| 2681 | +-------+--------+----------------+------+ | ||
| 2682 | | 62 | 33 | 6 | 0 | | ||
| 2683 | +-------+--------+----------------+------+ | ||
| 2684 | | 63 | 32 | 6 | 0 | | ||
| 2685 | +-------+--------+----------------+------+ | ||
| 2686 | |||
| 2687 | |||
| 2688 | |||
| 2689 | |||
| 2690 | Collet & Kucherawy Informational [Page 48] | ||
| 2691 | |||
| 2692 | RFC 8478 application/zstd October 2018 | ||
| 2693 | |||
| 2694 | |||
| 2695 | A.2. Match Length Code Table | ||
| 2696 | |||
| 2697 | +-------+--------+----------------+------+ | ||
| 2698 | | State | Symbol | Number_Of_Bits | Base | | ||
| 2699 | +-------+--------+----------------+------+ | ||
| 2700 | | 0 | 0 | 0 | 0 | | ||
| 2701 | +-------+--------+----------------+------+ | ||
| 2702 | | 0 | 0 | 6 | 0 | | ||
| 2703 | +-------+--------+----------------+------+ | ||
| 2704 | | 1 | 1 | 4 | 0 | | ||
| 2705 | +-------+--------+----------------+------+ | ||
| 2706 | | 2 | 2 | 5 | 32 | | ||
| 2707 | +-------+--------+----------------+------+ | ||
| 2708 | | 3 | 3 | 5 | 0 | | ||
| 2709 | +-------+--------+----------------+------+ | ||
| 2710 | | 4 | 5 | 5 | 0 | | ||
| 2711 | +-------+--------+----------------+------+ | ||
| 2712 | | 5 | 6 | 5 | 0 | | ||
| 2713 | +-------+--------+----------------+------+ | ||
| 2714 | | 6 | 8 | 5 | 0 | | ||
| 2715 | +-------+--------+----------------+------+ | ||
| 2716 | | 7 | 10 | 6 | 0 | | ||
| 2717 | +-------+--------+----------------+------+ | ||
| 2718 | | 8 | 13 | 6 | 0 | | ||
| 2719 | +-------+--------+----------------+------+ | ||
| 2720 | | 9 | 16 | 6 | 0 | | ||
| 2721 | +-------+--------+----------------+------+ | ||
| 2722 | | 10 | 19 | 6 | 0 | | ||
| 2723 | +-------+--------+----------------+------+ | ||
| 2724 | | 11 | 22 | 6 | 0 | | ||
| 2725 | +-------+--------+----------------+------+ | ||
| 2726 | | 12 | 25 | 6 | 0 | | ||
| 2727 | +-------+--------+----------------+------+ | ||
| 2728 | | 13 | 28 | 6 | 0 | | ||
| 2729 | +-------+--------+----------------+------+ | ||
| 2730 | | 14 | 31 | 6 | 0 | | ||
| 2731 | +-------+--------+----------------+------+ | ||
| 2732 | | 15 | 33 | 6 | 0 | | ||
| 2733 | +-------+--------+----------------+------+ | ||
| 2734 | | 16 | 35 | 6 | 0 | | ||
| 2735 | +-------+--------+----------------+------+ | ||
| 2736 | | 17 | 37 | 6 | 0 | | ||
| 2737 | +-------+--------+----------------+------+ | ||
| 2738 | | 18 | 39 | 6 | 0 | | ||
| 2739 | +-------+--------+----------------+------+ | ||
| 2740 | | 19 | 41 | 6 | 0 | | ||
| 2741 | +-------+--------+----------------+------+ | ||
| 2742 | | 20 | 43 | 6 | 0 | | ||
| 2743 | |||
| 2744 | |||
| 2745 | |||
| 2746 | Collet & Kucherawy Informational [Page 49] | ||
| 2747 | |||
| 2748 | RFC 8478 application/zstd October 2018 | ||
| 2749 | |||
| 2750 | |||
| 2751 | +-------+--------+----------------+------+ | ||
| 2752 | | 21 | 45 | 6 | 0 | | ||
| 2753 | +-------+--------+----------------+------+ | ||
| 2754 | | 22 | 1 | 4 | 16 | | ||
| 2755 | +-------+--------+----------------+------+ | ||
| 2756 | | 23 | 2 | 4 | 0 | | ||
| 2757 | +-------+--------+----------------+------+ | ||
| 2758 | | 24 | 3 | 5 | 32 | | ||
| 2759 | +-------+--------+----------------+------+ | ||
| 2760 | | 25 | 4 | 5 | 0 | | ||
| 2761 | +-------+--------+----------------+------+ | ||
| 2762 | | 26 | 6 | 5 | 32 | | ||
| 2763 | +-------+--------+----------------+------+ | ||
| 2764 | | 27 | 7 | 5 | 0 | | ||
| 2765 | +-------+--------+----------------+------+ | ||
| 2766 | | 28 | 9 | 6 | 0 | | ||
| 2767 | +-------+--------+----------------+------+ | ||
| 2768 | | 29 | 12 | 6 | 0 | | ||
| 2769 | +-------+--------+----------------+------+ | ||
| 2770 | | 30 | 15 | 6 | 0 | | ||
| 2771 | +-------+--------+----------------+------+ | ||
| 2772 | | 31 | 18 | 6 | 0 | | ||
| 2773 | +-------+--------+----------------+------+ | ||
| 2774 | | 32 | 21 | 6 | 0 | | ||
| 2775 | +-------+--------+----------------+------+ | ||
| 2776 | | 33 | 24 | 6 | 0 | | ||
| 2777 | +-------+--------+----------------+------+ | ||
| 2778 | | 34 | 27 | 6 | 0 | | ||
| 2779 | +-------+--------+----------------+------+ | ||
| 2780 | | 35 | 30 | 6 | 0 | | ||
| 2781 | +-------+--------+----------------+------+ | ||
| 2782 | | 36 | 32 | 6 | 0 | | ||
| 2783 | +-------+--------+----------------+------+ | ||
| 2784 | | 37 | 34 | 6 | 0 | | ||
| 2785 | +-------+--------+----------------+------+ | ||
| 2786 | | 38 | 36 | 6 | 0 | | ||
| 2787 | +-------+--------+----------------+------+ | ||
| 2788 | | 39 | 38 | 6 | 0 | | ||
| 2789 | +-------+--------+----------------+------+ | ||
| 2790 | | 40 | 40 | 6 | 0 | | ||
| 2791 | +-------+--------+----------------+------+ | ||
| 2792 | | 41 | 42 | 6 | 0 | | ||
| 2793 | +-------+--------+----------------+------+ | ||
| 2794 | | 42 | 44 | 6 | 0 | | ||
| 2795 | +-------+--------+----------------+------+ | ||
| 2796 | | 43 | 1 | 4 | 32 | | ||
| 2797 | +-------+--------+----------------+------+ | ||
| 2798 | | 44 | 1 | 4 | 48 | | ||
| 2799 | |||
| 2800 | |||
| 2801 | |||
| 2802 | Collet & Kucherawy Informational [Page 50] | ||
| 2803 | |||
| 2804 | RFC 8478 application/zstd October 2018 | ||
| 2805 | |||
| 2806 | |||
| 2807 | +-------+--------+----------------+------+ | ||
| 2808 | | 45 | 2 | 4 | 16 | | ||
| 2809 | +-------+--------+----------------+------+ | ||
| 2810 | | 46 | 4 | 5 | 32 | | ||
| 2811 | +-------+--------+----------------+------+ | ||
| 2812 | | 47 | 5 | 5 | 32 | | ||
| 2813 | +-------+--------+----------------+------+ | ||
| 2814 | | 48 | 7 | 5 | 32 | | ||
| 2815 | +-------+--------+----------------+------+ | ||
| 2816 | | 49 | 8 | 5 | 32 | | ||
| 2817 | +-------+--------+----------------+------+ | ||
| 2818 | | 50 | 11 | 6 | 0 | | ||
| 2819 | +-------+--------+----------------+------+ | ||
| 2820 | | 51 | 14 | 6 | 0 | | ||
| 2821 | +-------+--------+----------------+------+ | ||
| 2822 | | 52 | 17 | 6 | 0 | | ||
| 2823 | +-------+--------+----------------+------+ | ||
| 2824 | | 53 | 20 | 6 | 0 | | ||
| 2825 | +-------+--------+----------------+------+ | ||
| 2826 | | 54 | 23 | 6 | 0 | | ||
| 2827 | +-------+--------+----------------+------+ | ||
| 2828 | | 55 | 26 | 6 | 0 | | ||
| 2829 | +-------+--------+----------------+------+ | ||
| 2830 | | 56 | 29 | 6 | 0 | | ||
| 2831 | +-------+--------+----------------+------+ | ||
| 2832 | | 57 | 52 | 6 | 0 | | ||
| 2833 | +-------+--------+----------------+------+ | ||
| 2834 | | 58 | 51 | 6 | 0 | | ||
| 2835 | +-------+--------+----------------+------+ | ||
| 2836 | | 59 | 50 | 6 | 0 | | ||
| 2837 | +-------+--------+----------------+------+ | ||
| 2838 | | 60 | 49 | 6 | 0 | | ||
| 2839 | +-------+--------+----------------+------+ | ||
| 2840 | | 61 | 48 | 6 | 0 | | ||
| 2841 | +-------+--------+----------------+------+ | ||
| 2842 | | 62 | 47 | 6 | 0 | | ||
| 2843 | +-------+--------+----------------+------+ | ||
| 2844 | | 63 | 46 | 6 | 0 | | ||
| 2845 | +-------+--------+----------------+------+ | ||
| 2846 | |||
| 2847 | |||
| 2848 | |||
| 2849 | |||
| 2850 | |||
| 2851 | |||
| 2852 | |||
| 2853 | |||
| 2854 | |||
| 2855 | |||
| 2856 | |||
| 2857 | |||
| 2858 | Collet & Kucherawy Informational [Page 51] | ||
| 2859 | |||
| 2860 | RFC 8478 application/zstd October 2018 | ||
| 2861 | |||
| 2862 | |||
| 2863 | A.3. Offset Code Table | ||
| 2864 | |||
| 2865 | +-------+--------+----------------+------+ | ||
| 2866 | | State | Symbol | Number_Of_Bits | Base | | ||
| 2867 | +-------+--------+----------------+------+ | ||
| 2868 | | 0 | 0 | 0 | 0 | | ||
| 2869 | +-------+--------+----------------+------+ | ||
| 2870 | | 0 | 0 | 5 | 0 | | ||
| 2871 | +-------+--------+----------------+------+ | ||
| 2872 | | 1 | 6 | 4 | 0 | | ||
| 2873 | +-------+--------+----------------+------+ | ||
| 2874 | | 2 | 9 | 5 | 0 | | ||
| 2875 | +-------+--------+----------------+------+ | ||
| 2876 | | 3 | 15 | 5 | 0 | | ||
| 2877 | +-------+--------+----------------+------+ | ||
| 2878 | | 4 | 21 | 5 | 0 | | ||
| 2879 | +-------+--------+----------------+------+ | ||
| 2880 | | 5 | 3 | 5 | 0 | | ||
| 2881 | +-------+--------+----------------+------+ | ||
| 2882 | | 6 | 7 | 4 | 0 | | ||
| 2883 | +-------+--------+----------------+------+ | ||
| 2884 | | 7 | 12 | 5 | 0 | | ||
| 2885 | +-------+--------+----------------+------+ | ||
| 2886 | | 8 | 18 | 5 | 0 | | ||
| 2887 | +-------+--------+----------------+------+ | ||
| 2888 | | 9 | 23 | 5 | 0 | | ||
| 2889 | +-------+--------+----------------+------+ | ||
| 2890 | | 10 | 5 | 5 | 0 | | ||
| 2891 | +-------+--------+----------------+------+ | ||
| 2892 | | 11 | 8 | 4 | 0 | | ||
| 2893 | +-------+--------+----------------+------+ | ||
| 2894 | | 12 | 14 | 5 | 0 | | ||
| 2895 | +-------+--------+----------------+------+ | ||
| 2896 | | 13 | 20 | 5 | 0 | | ||
| 2897 | +-------+--------+----------------+------+ | ||
| 2898 | | 14 | 2 | 5 | 0 | | ||
| 2899 | +-------+--------+----------------+------+ | ||
| 2900 | | 15 | 7 | 4 | 16 | | ||
| 2901 | +-------+--------+----------------+------+ | ||
| 2902 | | 16 | 11 | 5 | 0 | | ||
| 2903 | +-------+--------+----------------+------+ | ||
| 2904 | | 17 | 17 | 5 | 0 | | ||
| 2905 | +-------+--------+----------------+------+ | ||
| 2906 | | 18 | 22 | 5 | 0 | | ||
| 2907 | +-------+--------+----------------+------+ | ||
| 2908 | | 19 | 4 | 5 | 0 | | ||
| 2909 | +-------+--------+----------------+------+ | ||
| 2910 | | 20 | 8 | 4 | 16 | | ||
| 2911 | |||
| 2912 | |||
| 2913 | |||
| 2914 | Collet & Kucherawy Informational [Page 52] | ||
| 2915 | |||
| 2916 | RFC 8478 application/zstd October 2018 | ||
| 2917 | |||
| 2918 | |||
| 2919 | +-------+--------+----------------+------+ | ||
| 2920 | | 21 | 13 | 5 | 0 | | ||
| 2921 | +-------+--------+----------------+------+ | ||
| 2922 | | 22 | 19 | 5 | 0 | | ||
| 2923 | +-------+--------+----------------+------+ | ||
| 2924 | | 23 | 1 | 5 | 0 | | ||
| 2925 | +-------+--------+----------------+------+ | ||
| 2926 | | 24 | 6 | 4 | 16 | | ||
| 2927 | +-------+--------+----------------+------+ | ||
| 2928 | | 25 | 10 | 5 | 0 | | ||
| 2929 | +-------+--------+----------------+------+ | ||
| 2930 | | 26 | 16 | 5 | 0 | | ||
| 2931 | +-------+--------+----------------+------+ | ||
| 2932 | | 27 | 28 | 5 | 0 | | ||
| 2933 | +-------+--------+----------------+------+ | ||
| 2934 | | 28 | 27 | 5 | 0 | | ||
| 2935 | +-------+--------+----------------+------+ | ||
| 2936 | | 29 | 26 | 5 | 0 | | ||
| 2937 | +-------+--------+----------------+------+ | ||
| 2938 | | 30 | 25 | 5 | 0 | | ||
| 2939 | +-------+--------+----------------+------+ | ||
| 2940 | | 31 | 24 | 5 | 0 | | ||
| 2941 | +-------+--------+----------------+------+ | ||
| 2942 | |||
| 2943 | Acknowledgments | ||
| 2944 | |||
| 2945 | zstd was developed by Yann Collet. | ||
| 2946 | |||
| 2947 | Bobo Bose-Kolanu, Felix Handte, Kyle Nekritz, Nick Terrell, and David | ||
| 2948 | Schleimer provided helpful feedback during the development of this | ||
| 2949 | document. | ||
| 2950 | |||
| 2951 | |||
| 2952 | |||
| 2953 | |||
| 2954 | |||
| 2955 | |||
| 2956 | |||
| 2957 | |||
| 2958 | |||
| 2959 | |||
| 2960 | |||
| 2961 | |||
| 2962 | |||
| 2963 | |||
| 2964 | |||
| 2965 | |||
| 2966 | |||
| 2967 | |||
| 2968 | |||
| 2969 | |||
| 2970 | Collet & Kucherawy Informational [Page 53] | ||
| 2971 | |||
| 2972 | RFC 8478 application/zstd October 2018 | ||
| 2973 | |||
| 2974 | |||
| 2975 | Authors' Addresses | ||
| 2976 | |||
| 2977 | Yann Collet | ||
| 2978 | |||
| 2979 | 1 Hacker Way | ||
| 2980 | Menlo Park, CA 94025 | ||
| 2981 | United States of America | ||
| 2982 | |||
| 2983 | Email: cyan@fb.com | ||
| 2984 | |||
| 2985 | |||
| 2986 | Murray S. Kucherawy (editor) | ||
| 2987 | |||
| 2988 | 1 Hacker Way | ||
| 2989 | Menlo Park, CA 94025 | ||
| 2990 | United States of America | ||
| 2991 | |||
| 2992 | Email: msk@fb.com | ||
| 2993 | |||
| 2994 | |||
| 2995 | |||
| 2996 | |||
| 2997 | |||
| 2998 | |||
| 2999 | |||
| 3000 | |||
| 3001 | |||
| 3002 | |||
| 3003 | |||
| 3004 | |||
| 3005 | |||
| 3006 | |||
| 3007 | |||
| 3008 | |||
| 3009 | |||
| 3010 | |||
| 3011 | |||
| 3012 | |||
| 3013 | |||
| 3014 | |||
| 3015 | |||
| 3016 | |||
| 3017 | |||
| 3018 | |||
| 3019 | |||
| 3020 | |||
| 3021 | |||
| 3022 | |||
| 3023 | |||
| 3024 | |||
| 3025 | |||
| 3026 | Collet & Kucherawy Informational [Page 54] | ||
| 3027 | |||
lib/std/compress/testdata/rfc8478.txt.zst.19 created| Binary files /dev/null and b/lib/std/compress/testdata/rfc8478.txt.zst.19 differ | |||
lib/std/compress/testdata/rfc8478.txt.zst.3 created| Binary files /dev/null and b/lib/std/compress/testdata/rfc8478.txt.zst.3 differ | |||
lib/std/compress/zstandard.zig created+286| ... | @@ -0,0 +1,286 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Allocator = std.mem.Allocator; | ||
| 3 | const RingBuffer = std.RingBuffer; | ||
| 4 | |||
| 5 | const types = @import("zstandard/types.zig"); | ||
| 6 | pub const frame = types.frame; | ||
| 7 | pub const compressed_block = types.compressed_block; | ||
| 8 | |||
| 9 | pub const decompress = @import("zstandard/decompress.zig"); | ||
| 10 | |||
| 11 | pub const DecompressStreamOptions = struct { | ||
| 12 | verify_checksum: bool = true, | ||
| 13 | window_size_max: usize = 1 << 23, // 8MiB default maximum window size, | ||
| 14 | }; | ||
| 15 | |||
| 16 | pub fn DecompressStream( | ||
| 17 | comptime ReaderType: type, | ||
| 18 | comptime options: DecompressStreamOptions, | ||
| 19 | ) type { | ||
| 20 | return struct { | ||
| 21 | const Self = @This(); | ||
| 22 | |||
| 23 | allocator: Allocator, | ||
| 24 | source: std.io.CountingReader(ReaderType), | ||
| 25 | state: enum { NewFrame, InFrame, LastBlock }, | ||
| 26 | decode_state: decompress.block.DecodeState, | ||
| 27 | frame_context: decompress.FrameContext, | ||
| 28 | buffer: RingBuffer, | ||
| 29 | literal_fse_buffer: []types.compressed_block.Table.Fse, | ||
| 30 | match_fse_buffer: []types.compressed_block.Table.Fse, | ||
| 31 | offset_fse_buffer: []types.compressed_block.Table.Fse, | ||
| 32 | literals_buffer: []u8, | ||
| 33 | sequence_buffer: []u8, | ||
| 34 | checksum: if (options.verify_checksum) ?u32 else void, | ||
| 35 | current_frame_decompressed_size: usize, | ||
| 36 | |||
| 37 | pub const Error = ReaderType.Error || error{ | ||
| 38 | ChecksumFailure, | ||
| 39 | DictionaryIdFlagUnsupported, | ||
| 40 | MalformedBlock, | ||
| 41 | MalformedFrame, | ||
| 42 | OutOfMemory, | ||
| 43 | }; | ||
| 44 | |||
| 45 | pub const Reader = std.io.Reader(*Self, Error, read); | ||
| 46 | |||
| 47 | pub fn init(allocator: Allocator, source: ReaderType) Self { | ||
| 48 | return Self{ | ||
| 49 | .allocator = allocator, | ||
| 50 | .source = std.io.countingReader(source), | ||
| 51 | .state = .NewFrame, | ||
| 52 | .decode_state = undefined, | ||
| 53 | .frame_context = undefined, | ||
| 54 | .buffer = undefined, | ||
| 55 | .literal_fse_buffer = undefined, | ||
| 56 | .match_fse_buffer = undefined, | ||
| 57 | .offset_fse_buffer = undefined, | ||
| 58 | .literals_buffer = undefined, | ||
| 59 | .sequence_buffer = undefined, | ||
| 60 | .checksum = undefined, | ||
| 61 | .current_frame_decompressed_size = undefined, | ||
| 62 | }; | ||
| 63 | } | ||
| 64 | |||
| 65 | fn frameInit(self: *Self) !void { | ||
| 66 | const source_reader = self.source.reader(); | ||
| 67 | switch (try decompress.decodeFrameHeader(source_reader)) { | ||
| 68 | .skippable => |header| { | ||
| 69 | try source_reader.skipBytes(header.frame_size, .{}); | ||
| 70 | self.state = .NewFrame; | ||
| 71 | }, | ||
| 72 | .zstandard => |header| { | ||
| 73 | const frame_context = context: { | ||
| 74 | break :context try decompress.FrameContext.init( | ||
| 75 | header, | ||
| 76 | options.window_size_max, | ||
| 77 | options.verify_checksum, | ||
| 78 | ); | ||
| 79 | }; | ||
| 80 | |||
| 81 | const literal_fse_buffer = try self.allocator.alloc( | ||
| 82 | types.compressed_block.Table.Fse, | ||
| 83 | types.compressed_block.table_size_max.literal, | ||
| 84 | ); | ||
| 85 | errdefer self.allocator.free(literal_fse_buffer); | ||
| 86 | |||
| 87 | const match_fse_buffer = try self.allocator.alloc( | ||
| 88 | types.compressed_block.Table.Fse, | ||
| 89 | types.compressed_block.table_size_max.match, | ||
| 90 | ); | ||
| 91 | errdefer self.allocator.free(match_fse_buffer); | ||
| 92 | |||
| 93 | const offset_fse_buffer = try self.allocator.alloc( | ||
| 94 | types.compressed_block.Table.Fse, | ||
| 95 | types.compressed_block.table_size_max.offset, | ||
| 96 | ); | ||
| 97 | errdefer self.allocator.free(offset_fse_buffer); | ||
| 98 | |||
| 99 | const decode_state = decompress.block.DecodeState.init( | ||
| 100 | literal_fse_buffer, | ||
| 101 | match_fse_buffer, | ||
| 102 | offset_fse_buffer, | ||
| 103 | ); | ||
| 104 | const buffer = try RingBuffer.init(self.allocator, frame_context.window_size); | ||
| 105 | |||
| 106 | const literals_data = try self.allocator.alloc(u8, options.window_size_max); | ||
| 107 | errdefer self.allocator.free(literals_data); | ||
| 108 | |||
| 109 | const sequence_data = try self.allocator.alloc(u8, options.window_size_max); | ||
| 110 | errdefer self.allocator.free(sequence_data); | ||
| 111 | |||
| 112 | self.literal_fse_buffer = literal_fse_buffer; | ||
| 113 | self.match_fse_buffer = match_fse_buffer; | ||
| 114 | self.offset_fse_buffer = offset_fse_buffer; | ||
| 115 | self.literals_buffer = literals_data; | ||
| 116 | self.sequence_buffer = sequence_data; | ||
| 117 | |||
| 118 | self.buffer = buffer; | ||
| 119 | |||
| 120 | self.decode_state = decode_state; | ||
| 121 | self.frame_context = frame_context; | ||
| 122 | |||
| 123 | self.checksum = if (options.verify_checksum) null else {}; | ||
| 124 | self.current_frame_decompressed_size = 0; | ||
| 125 | |||
| 126 | self.state = .InFrame; | ||
| 127 | }, | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | pub fn deinit(self: *Self) void { | ||
| 132 | if (self.state == .NewFrame) return; | ||
| 133 | self.allocator.free(self.decode_state.literal_fse_buffer); | ||
| 134 | self.allocator.free(self.decode_state.match_fse_buffer); | ||
| 135 | self.allocator.free(self.decode_state.offset_fse_buffer); | ||
| 136 | self.allocator.free(self.literals_buffer); | ||
| 137 | self.allocator.free(self.sequence_buffer); | ||
| 138 | self.buffer.deinit(self.allocator); | ||
| 139 | } | ||
| 140 | |||
| 141 | pub fn reader(self: *Self) Reader { | ||
| 142 | return .{ .context = self }; | ||
| 143 | } | ||
| 144 | |||
| 145 | pub fn read(self: *Self, buffer: []u8) Error!usize { | ||
| 146 | if (buffer.len == 0) return 0; | ||
| 147 | |||
| 148 | var size: usize = 0; | ||
| 149 | while (size == 0) { | ||
| 150 | while (self.state == .NewFrame) { | ||
| 151 | const initial_count = self.source.bytes_read; | ||
| 152 | self.frameInit() catch |err| switch (err) { | ||
| 153 | error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported, | ||
| 154 | error.EndOfStream => return if (self.source.bytes_read == initial_count) | ||
| 155 | 0 | ||
| 156 | else | ||
| 157 | error.MalformedFrame, | ||
| 158 | error.OutOfMemory => return error.OutOfMemory, | ||
| 159 | else => return error.MalformedFrame, | ||
| 160 | }; | ||
| 161 | } | ||
| 162 | size = try self.readInner(buffer); | ||
| 163 | } | ||
| 164 | return size; | ||
| 165 | } | ||
| 166 | |||
| 167 | fn readInner(self: *Self, buffer: []u8) Error!usize { | ||
| 168 | std.debug.assert(self.state != .NewFrame); | ||
| 169 | |||
| 170 | const source_reader = self.source.reader(); | ||
| 171 | while (self.buffer.isEmpty() and self.state != .LastBlock) { | ||
| 172 | const header_bytes = source_reader.readBytesNoEof(3) catch | ||
| 173 | return error.MalformedFrame; | ||
| 174 | const block_header = decompress.block.decodeBlockHeader(&header_bytes); | ||
| 175 | |||
| 176 | decompress.block.decodeBlockReader( | ||
| 177 | &self.buffer, | ||
| 178 | source_reader, | ||
| 179 | block_header, | ||
| 180 | &self.decode_state, | ||
| 181 | self.frame_context.block_size_max, | ||
| 182 | self.literals_buffer, | ||
| 183 | self.sequence_buffer, | ||
| 184 | ) catch | ||
| 185 | return error.MalformedBlock; | ||
| 186 | |||
| 187 | if (self.frame_context.content_size) |size| { | ||
| 188 | if (self.current_frame_decompressed_size > size) return error.MalformedFrame; | ||
| 189 | } | ||
| 190 | |||
| 191 | const size = self.buffer.len(); | ||
| 192 | self.current_frame_decompressed_size += size; | ||
| 193 | |||
| 194 | if (self.frame_context.hasher_opt) |*hasher| { | ||
| 195 | if (size > 0) { | ||
| 196 | const written_slice = self.buffer.sliceLast(size); | ||
| 197 | hasher.update(written_slice.first); | ||
| 198 | hasher.update(written_slice.second); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | if (block_header.last_block) { | ||
| 202 | self.state = .LastBlock; | ||
| 203 | if (self.frame_context.has_checksum) { | ||
| 204 | const checksum = source_reader.readIntLittle(u32) catch | ||
| 205 | return error.MalformedFrame; | ||
| 206 | if (comptime options.verify_checksum) { | ||
| 207 | if (self.frame_context.hasher_opt) |*hasher| { | ||
| 208 | if (checksum != decompress.computeChecksum(hasher)) | ||
| 209 | return error.ChecksumFailure; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | } | ||
| 213 | if (self.frame_context.content_size) |content_size| { | ||
| 214 | if (content_size != self.current_frame_decompressed_size) { | ||
| 215 | return error.MalformedFrame; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | const size = @min(self.buffer.len(), buffer.len); | ||
| 222 | for (0..size) |i| { | ||
| 223 | buffer[i] = self.buffer.read().?; | ||
| 224 | } | ||
| 225 | if (self.state == .LastBlock and self.buffer.len() == 0) { | ||
| 226 | self.state = .NewFrame; | ||
| 227 | self.allocator.free(self.literal_fse_buffer); | ||
| 228 | self.allocator.free(self.match_fse_buffer); | ||
| 229 | self.allocator.free(self.offset_fse_buffer); | ||
| 230 | self.allocator.free(self.literals_buffer); | ||
| 231 | self.allocator.free(self.sequence_buffer); | ||
| 232 | self.buffer.deinit(self.allocator); | ||
| 233 | } | ||
| 234 | return size; | ||
| 235 | } | ||
| 236 | }; | ||
| 237 | } | ||
| 238 | |||
| 239 | pub fn decompressStreamOptions( | ||
| 240 | allocator: Allocator, | ||
| 241 | reader: anytype, | ||
| 242 | comptime options: DecompressStreamOptions, | ||
| 243 | ) DecompressStream(@TypeOf(reader, options)) { | ||
| 244 | return DecompressStream(@TypeOf(reader), options).init(allocator, reader); | ||
| 245 | } | ||
| 246 | |||
| 247 | pub fn decompressStream( | ||
| 248 | allocator: Allocator, | ||
| 249 | reader: anytype, | ||
| 250 | ) DecompressStream(@TypeOf(reader), .{}) { | ||
| 251 | return DecompressStream(@TypeOf(reader), .{}).init(allocator, reader); | ||
| 252 | } | ||
| 253 | |||
| 254 | fn testDecompress(data: []const u8) ![]u8 { | ||
| 255 | var in_stream = std.io.fixedBufferStream(data); | ||
| 256 | var zstd_stream = decompressStream(std.testing.allocator, in_stream.reader()); | ||
| 257 | defer zstd_stream.deinit(); | ||
| 258 | const result = zstd_stream.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); | ||
| 259 | return result; | ||
| 260 | } | ||
| 261 | |||
| 262 | fn testReader(data: []const u8, comptime expected: []const u8) !void { | ||
| 263 | const buf = try testDecompress(data); | ||
| 264 | defer std.testing.allocator.free(buf); | ||
| 265 | try std.testing.expectEqualSlices(u8, expected, buf); | ||
| 266 | } | ||
| 267 | |||
| 268 | test "zstandard decompression" { | ||
| 269 | const uncompressed = @embedFile("testdata/rfc8478.txt"); | ||
| 270 | const compressed3 = @embedFile("testdata/rfc8478.txt.zst.3"); | ||
| 271 | const compressed19 = @embedFile("testdata/rfc8478.txt.zst.19"); | ||
| 272 | |||
| 273 | var buffer = try std.testing.allocator.alloc(u8, uncompressed.len); | ||
| 274 | defer std.testing.allocator.free(buffer); | ||
| 275 | |||
| 276 | const res3 = try decompress.decode(buffer, compressed3, true); | ||
| 277 | try std.testing.expectEqual(uncompressed.len, res3); | ||
| 278 | try std.testing.expectEqualSlices(u8, uncompressed, buffer); | ||
| 279 | |||
| 280 | const res19 = try decompress.decode(buffer, compressed19, true); | ||
| 281 | try std.testing.expectEqual(uncompressed.len, res19); | ||
| 282 | try std.testing.expectEqualSlices(u8, uncompressed, buffer); | ||
| 283 | |||
| 284 | try testReader(compressed3, uncompressed); | ||
| 285 | try testReader(compressed19, uncompressed); | ||
| 286 | } | ||
lib/std/compress/zstandard/decode/block.zig created+1149| ... | @@ -0,0 +1,1149 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const RingBuffer = std.RingBuffer; | ||
| 4 | |||
| 5 | const types = @import("../types.zig"); | ||
| 6 | const frame = types.frame; | ||
| 7 | const Table = types.compressed_block.Table; | ||
| 8 | const LiteralsSection = types.compressed_block.LiteralsSection; | ||
| 9 | const SequencesSection = types.compressed_block.SequencesSection; | ||
| 10 | |||
| 11 | const huffman = @import("huffman.zig"); | ||
| 12 | const readers = @import("../readers.zig"); | ||
| 13 | |||
| 14 | const decodeFseTable = @import("fse.zig").decodeFseTable; | ||
| 15 | |||
| 16 | const readInt = std.mem.readIntLittle; | ||
| 17 | |||
| 18 | pub const Error = error{ | ||
| 19 | BlockSizeOverMaximum, | ||
| 20 | MalformedBlockSize, | ||
| 21 | ReservedBlock, | ||
| 22 | MalformedRleBlock, | ||
| 23 | MalformedCompressedBlock, | ||
| 24 | }; | ||
| 25 | |||
| 26 | pub const DecodeState = struct { | ||
| 27 | repeat_offsets: [3]u32, | ||
| 28 | |||
| 29 | offset: StateData(8), | ||
| 30 | match: StateData(9), | ||
| 31 | literal: StateData(9), | ||
| 32 | |||
| 33 | offset_fse_buffer: []Table.Fse, | ||
| 34 | match_fse_buffer: []Table.Fse, | ||
| 35 | literal_fse_buffer: []Table.Fse, | ||
| 36 | |||
| 37 | fse_tables_undefined: bool, | ||
| 38 | |||
| 39 | literal_stream_reader: readers.ReverseBitReader, | ||
| 40 | literal_stream_index: usize, | ||
| 41 | literal_streams: LiteralsSection.Streams, | ||
| 42 | literal_header: LiteralsSection.Header, | ||
| 43 | huffman_tree: ?LiteralsSection.HuffmanTree, | ||
| 44 | |||
| 45 | literal_written_count: usize, | ||
| 46 | written_count: usize = 0, | ||
| 47 | |||
| 48 | fn StateData(comptime max_accuracy_log: comptime_int) type { | ||
| 49 | return struct { | ||
| 50 | state: State, | ||
| 51 | table: Table, | ||
| 52 | accuracy_log: u8, | ||
| 53 | |||
| 54 | const State = std.meta.Int(.unsigned, max_accuracy_log); | ||
| 55 | }; | ||
| 56 | } | ||
| 57 | |||
| 58 | pub fn init( | ||
| 59 | literal_fse_buffer: []Table.Fse, | ||
| 60 | match_fse_buffer: []Table.Fse, | ||
| 61 | offset_fse_buffer: []Table.Fse, | ||
| 62 | ) DecodeState { | ||
| 63 | return DecodeState{ | ||
| 64 | .repeat_offsets = .{ | ||
| 65 | types.compressed_block.start_repeated_offset_1, | ||
| 66 | types.compressed_block.start_repeated_offset_2, | ||
| 67 | types.compressed_block.start_repeated_offset_3, | ||
| 68 | }, | ||
| 69 | |||
| 70 | .offset = undefined, | ||
| 71 | .match = undefined, | ||
| 72 | .literal = undefined, | ||
| 73 | |||
| 74 | .literal_fse_buffer = literal_fse_buffer, | ||
| 75 | .match_fse_buffer = match_fse_buffer, | ||
| 76 | .offset_fse_buffer = offset_fse_buffer, | ||
| 77 | |||
| 78 | .fse_tables_undefined = true, | ||
| 79 | |||
| 80 | .literal_written_count = 0, | ||
| 81 | .literal_header = undefined, | ||
| 82 | .literal_streams = undefined, | ||
| 83 | .literal_stream_reader = undefined, | ||
| 84 | .literal_stream_index = undefined, | ||
| 85 | .huffman_tree = null, | ||
| 86 | |||
| 87 | .written_count = 0, | ||
| 88 | }; | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Prepare the decoder to decode a compressed block. Loads the literals | ||
| 92 | /// stream and Huffman tree from `literals` and reads the FSE tables from | ||
| 93 | /// `source`. | ||
| 94 | /// | ||
| 95 | /// Errors returned: | ||
| 96 | /// - `error.BitStreamHasNoStartBit` if the (reversed) literal bitstream's | ||
| 97 | /// first byte does not have any bits set | ||
| 98 | /// - `error.TreelessLiteralsFirst` `literals` is a treeless literals | ||
| 99 | /// section and the decode state does not have a Huffman tree from a | ||
| 100 | /// previous block | ||
| 101 | /// - `error.RepeatModeFirst` on the first call if one of the sequence FSE | ||
| 102 | /// tables is set to repeat mode | ||
| 103 | /// - `error.MalformedAccuracyLog` if an FSE table has an invalid accuracy | ||
| 104 | /// - `error.MalformedFseTable` if there are errors decoding an FSE table | ||
| 105 | /// - `error.EndOfStream` if `source` ends before all FSE tables are read | ||
| 106 | pub fn prepare( | ||
| 107 | self: *DecodeState, | ||
| 108 | source: anytype, | ||
| 109 | literals: LiteralsSection, | ||
| 110 | sequences_header: SequencesSection.Header, | ||
| 111 | ) !void { | ||
| 112 | self.literal_written_count = 0; | ||
| 113 | self.literal_header = literals.header; | ||
| 114 | self.literal_streams = literals.streams; | ||
| 115 | |||
| 116 | if (literals.huffman_tree) |tree| { | ||
| 117 | self.huffman_tree = tree; | ||
| 118 | } else if (literals.header.block_type == .treeless and self.huffman_tree == null) { | ||
| 119 | return error.TreelessLiteralsFirst; | ||
| 120 | } | ||
| 121 | |||
| 122 | switch (literals.header.block_type) { | ||
| 123 | .raw, .rle => {}, | ||
| 124 | .compressed, .treeless => { | ||
| 125 | self.literal_stream_index = 0; | ||
| 126 | switch (literals.streams) { | ||
| 127 | .one => |slice| try self.initLiteralStream(slice), | ||
| 128 | .four => |streams| try self.initLiteralStream(streams[0]), | ||
| 129 | } | ||
| 130 | }, | ||
| 131 | } | ||
| 132 | |||
| 133 | if (sequences_header.sequence_count > 0) { | ||
| 134 | try self.updateFseTable(source, .literal, sequences_header.literal_lengths); | ||
| 135 | try self.updateFseTable(source, .offset, sequences_header.offsets); | ||
| 136 | try self.updateFseTable(source, .match, sequences_header.match_lengths); | ||
| 137 | self.fse_tables_undefined = false; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Read initial FSE states for sequence decoding. | ||
| 142 | /// | ||
| 143 | /// Errors returned: | ||
| 144 | /// - `error.EndOfStream` if `bit_reader` does not contain enough bits. | ||
| 145 | pub fn readInitialFseState(self: *DecodeState, bit_reader: *readers.ReverseBitReader) error{EndOfStream}!void { | ||
| 146 | self.literal.state = try bit_reader.readBitsNoEof(u9, self.literal.accuracy_log); | ||
| 147 | self.offset.state = try bit_reader.readBitsNoEof(u8, self.offset.accuracy_log); | ||
| 148 | self.match.state = try bit_reader.readBitsNoEof(u9, self.match.accuracy_log); | ||
| 149 | } | ||
| 150 | |||
| 151 | fn updateRepeatOffset(self: *DecodeState, offset: u32) void { | ||
| 152 | self.repeat_offsets[2] = self.repeat_offsets[1]; | ||
| 153 | self.repeat_offsets[1] = self.repeat_offsets[0]; | ||
| 154 | self.repeat_offsets[0] = offset; | ||
| 155 | } | ||
| 156 | |||
| 157 | fn useRepeatOffset(self: *DecodeState, index: usize) u32 { | ||
| 158 | if (index == 1) | ||
| 159 | std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[1]) | ||
| 160 | else if (index == 2) { | ||
| 161 | std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[2]); | ||
| 162 | std.mem.swap(u32, &self.repeat_offsets[1], &self.repeat_offsets[2]); | ||
| 163 | } | ||
| 164 | return self.repeat_offsets[0]; | ||
| 165 | } | ||
| 166 | |||
| 167 | const DataType = enum { offset, match, literal }; | ||
| 168 | |||
| 169 | fn updateState( | ||
| 170 | self: *DecodeState, | ||
| 171 | comptime choice: DataType, | ||
| 172 | bit_reader: *readers.ReverseBitReader, | ||
| 173 | ) error{ MalformedFseBits, EndOfStream }!void { | ||
| 174 | switch (@field(self, @tagName(choice)).table) { | ||
| 175 | .rle => {}, | ||
| 176 | .fse => |table| { | ||
| 177 | const data = table[@field(self, @tagName(choice)).state]; | ||
| 178 | const T = @TypeOf(@field(self, @tagName(choice))).State; | ||
| 179 | const bits_summand = try bit_reader.readBitsNoEof(T, data.bits); | ||
| 180 | const next_state = std.math.cast( | ||
| 181 | @TypeOf(@field(self, @tagName(choice))).State, | ||
| 182 | data.baseline + bits_summand, | ||
| 183 | ) orelse return error.MalformedFseBits; | ||
| 184 | @field(self, @tagName(choice)).state = next_state; | ||
| 185 | }, | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | const FseTableError = error{ | ||
| 190 | MalformedFseTable, | ||
| 191 | MalformedAccuracyLog, | ||
| 192 | RepeatModeFirst, | ||
| 193 | EndOfStream, | ||
| 194 | }; | ||
| 195 | |||
| 196 | fn updateFseTable( | ||
| 197 | self: *DecodeState, | ||
| 198 | source: anytype, | ||
| 199 | comptime choice: DataType, | ||
| 200 | mode: SequencesSection.Header.Mode, | ||
| 201 | ) !void { | ||
| 202 | const field_name = @tagName(choice); | ||
| 203 | switch (mode) { | ||
| 204 | .predefined => { | ||
| 205 | @field(self, field_name).accuracy_log = | ||
| 206 | @field(types.compressed_block.default_accuracy_log, field_name); | ||
| 207 | |||
| 208 | @field(self, field_name).table = | ||
| 209 | @field(types.compressed_block, "predefined_" ++ field_name ++ "_fse_table"); | ||
| 210 | }, | ||
| 211 | .rle => { | ||
| 212 | @field(self, field_name).accuracy_log = 0; | ||
| 213 | @field(self, field_name).table = .{ .rle = try source.readByte() }; | ||
| 214 | }, | ||
| 215 | .fse => { | ||
| 216 | var bit_reader = readers.bitReader(source); | ||
| 217 | |||
| 218 | const table_size = try decodeFseTable( | ||
| 219 | &bit_reader, | ||
| 220 | @field(types.compressed_block.table_symbol_count_max, field_name), | ||
| 221 | @field(types.compressed_block.table_accuracy_log_max, field_name), | ||
| 222 | @field(self, field_name ++ "_fse_buffer"), | ||
| 223 | ); | ||
| 224 | @field(self, field_name).table = .{ | ||
| 225 | .fse = @field(self, field_name ++ "_fse_buffer")[0..table_size], | ||
| 226 | }; | ||
| 227 | @field(self, field_name).accuracy_log = std.math.log2_int_ceil(usize, table_size); | ||
| 228 | }, | ||
| 229 | .repeat => if (self.fse_tables_undefined) return error.RepeatModeFirst, | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | const Sequence = struct { | ||
| 234 | literal_length: u32, | ||
| 235 | match_length: u32, | ||
| 236 | offset: u32, | ||
| 237 | }; | ||
| 238 | |||
| 239 | fn nextSequence( | ||
| 240 | self: *DecodeState, | ||
| 241 | bit_reader: *readers.ReverseBitReader, | ||
| 242 | ) error{ InvalidBitStream, EndOfStream }!Sequence { | ||
| 243 | const raw_code = self.getCode(.offset); | ||
| 244 | const offset_code = std.math.cast(u5, raw_code) orelse { | ||
| 245 | return error.InvalidBitStream; | ||
| 246 | }; | ||
| 247 | const offset_value = (@as(u32, 1) << offset_code) + try bit_reader.readBitsNoEof(u32, offset_code); | ||
| 248 | |||
| 249 | const match_code = self.getCode(.match); | ||
| 250 | if (match_code >= types.compressed_block.match_length_code_table.len) | ||
| 251 | return error.InvalidBitStream; | ||
| 252 | const match = types.compressed_block.match_length_code_table[match_code]; | ||
| 253 | const match_length = match[0] + try bit_reader.readBitsNoEof(u32, match[1]); | ||
| 254 | |||
| 255 | const literal_code = self.getCode(.literal); | ||
| 256 | if (literal_code >= types.compressed_block.literals_length_code_table.len) | ||
| 257 | return error.InvalidBitStream; | ||
| 258 | const literal = types.compressed_block.literals_length_code_table[literal_code]; | ||
| 259 | const literal_length = literal[0] + try bit_reader.readBitsNoEof(u32, literal[1]); | ||
| 260 | |||
| 261 | const offset = if (offset_value > 3) offset: { | ||
| 262 | const offset = offset_value - 3; | ||
| 263 | self.updateRepeatOffset(offset); | ||
| 264 | break :offset offset; | ||
| 265 | } else offset: { | ||
| 266 | if (literal_length == 0) { | ||
| 267 | if (offset_value == 3) { | ||
| 268 | const offset = self.repeat_offsets[0] - 1; | ||
| 269 | self.updateRepeatOffset(offset); | ||
| 270 | break :offset offset; | ||
| 271 | } | ||
| 272 | break :offset self.useRepeatOffset(offset_value); | ||
| 273 | } | ||
| 274 | break :offset self.useRepeatOffset(offset_value - 1); | ||
| 275 | }; | ||
| 276 | |||
| 277 | if (offset == 0) return error.InvalidBitStream; | ||
| 278 | |||
| 279 | return .{ | ||
| 280 | .literal_length = literal_length, | ||
| 281 | .match_length = match_length, | ||
| 282 | .offset = offset, | ||
| 283 | }; | ||
| 284 | } | ||
| 285 | |||
| 286 | fn executeSequenceSlice( | ||
| 287 | self: *DecodeState, | ||
| 288 | dest: []u8, | ||
| 289 | write_pos: usize, | ||
| 290 | sequence: Sequence, | ||
| 291 | ) (error{MalformedSequence} || DecodeLiteralsError)!void { | ||
| 292 | if (sequence.offset > write_pos + sequence.literal_length) return error.MalformedSequence; | ||
| 293 | |||
| 294 | try self.decodeLiteralsSlice(dest[write_pos..], sequence.literal_length); | ||
| 295 | const copy_start = write_pos + sequence.literal_length - sequence.offset; | ||
| 296 | const copy_end = copy_start + sequence.match_length; | ||
| 297 | // NOTE: we ignore the usage message for std.mem.copy and copy with dest.ptr >= src.ptr | ||
| 298 | // to allow repeats | ||
| 299 | std.mem.copy(u8, dest[write_pos + sequence.literal_length ..], dest[copy_start..copy_end]); | ||
| 300 | self.written_count += sequence.match_length; | ||
| 301 | } | ||
| 302 | |||
| 303 | fn executeSequenceRingBuffer( | ||
| 304 | self: *DecodeState, | ||
| 305 | dest: *RingBuffer, | ||
| 306 | sequence: Sequence, | ||
| 307 | ) (error{MalformedSequence} || DecodeLiteralsError)!void { | ||
| 308 | if (sequence.offset > @min(dest.data.len, self.written_count + sequence.literal_length)) | ||
| 309 | return error.MalformedSequence; | ||
| 310 | |||
| 311 | try self.decodeLiteralsRingBuffer(dest, sequence.literal_length); | ||
| 312 | const copy_start = dest.write_index + dest.data.len - sequence.offset; | ||
| 313 | const copy_slice = dest.sliceAt(copy_start, sequence.match_length); | ||
| 314 | // TODO: would std.mem.copy and figuring out dest slice be better/faster? | ||
| 315 | for (copy_slice.first) |b| dest.writeAssumeCapacity(b); | ||
| 316 | for (copy_slice.second) |b| dest.writeAssumeCapacity(b); | ||
| 317 | self.written_count += sequence.match_length; | ||
| 318 | } | ||
| 319 | |||
| 320 | const DecodeSequenceError = error{ | ||
| 321 | InvalidBitStream, | ||
| 322 | EndOfStream, | ||
| 323 | MalformedSequence, | ||
| 324 | MalformedFseBits, | ||
| 325 | } || DecodeLiteralsError; | ||
| 326 | |||
| 327 | /// Decode one sequence from `bit_reader` into `dest`, written starting at | ||
| 328 | /// `write_pos` and update FSE states if `last_sequence` is `false`. | ||
| 329 | /// `prepare()` must be called for the block before attempting to decode | ||
| 330 | /// sequences. | ||
| 331 | /// | ||
| 332 | /// Errors returned: | ||
| 333 | /// - `error.MalformedSequence` if the decompressed sequence would be | ||
| 334 | /// longer than `sequence_size_limit` or the sequence's offset is too | ||
| 335 | /// large | ||
| 336 | /// - `error.UnexpectedEndOfLiteralStream` if the decoder state's literal | ||
| 337 | /// streams do not contain enough literals for the sequence (this may | ||
| 338 | /// mean the literal stream or the sequence is malformed). | ||
| 339 | /// - `error.InvalidBitStream` if the FSE sequence bitstream is malformed | ||
| 340 | /// - `error.EndOfStream` if `bit_reader` does not contain enough bits | ||
| 341 | /// - `error.DestTooSmall` if `dest` is not large enough to holde the | ||
| 342 | /// decompressed sequence | ||
| 343 | pub fn decodeSequenceSlice( | ||
| 344 | self: *DecodeState, | ||
| 345 | dest: []u8, | ||
| 346 | write_pos: usize, | ||
| 347 | bit_reader: *readers.ReverseBitReader, | ||
| 348 | sequence_size_limit: usize, | ||
| 349 | last_sequence: bool, | ||
| 350 | ) (error{DestTooSmall} || DecodeSequenceError)!usize { | ||
| 351 | const sequence = try self.nextSequence(bit_reader); | ||
| 352 | const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; | ||
| 353 | if (sequence_length > sequence_size_limit) return error.MalformedSequence; | ||
| 354 | if (sequence_length > dest[write_pos..].len) return error.DestTooSmall; | ||
| 355 | |||
| 356 | try self.executeSequenceSlice(dest, write_pos, sequence); | ||
| 357 | if (!last_sequence) { | ||
| 358 | try self.updateState(.literal, bit_reader); | ||
| 359 | try self.updateState(.match, bit_reader); | ||
| 360 | try self.updateState(.offset, bit_reader); | ||
| 361 | } | ||
| 362 | return sequence_length; | ||
| 363 | } | ||
| 364 | |||
| 365 | /// Decode one sequence from `bit_reader` into `dest`; see | ||
| 366 | /// `decodeSequenceSlice`. | ||
| 367 | pub fn decodeSequenceRingBuffer( | ||
| 368 | self: *DecodeState, | ||
| 369 | dest: *RingBuffer, | ||
| 370 | bit_reader: anytype, | ||
| 371 | sequence_size_limit: usize, | ||
| 372 | last_sequence: bool, | ||
| 373 | ) DecodeSequenceError!usize { | ||
| 374 | const sequence = try self.nextSequence(bit_reader); | ||
| 375 | const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; | ||
| 376 | if (sequence_length > sequence_size_limit) return error.MalformedSequence; | ||
| 377 | |||
| 378 | try self.executeSequenceRingBuffer(dest, sequence); | ||
| 379 | if (!last_sequence) { | ||
| 380 | try self.updateState(.literal, bit_reader); | ||
| 381 | try self.updateState(.match, bit_reader); | ||
| 382 | try self.updateState(.offset, bit_reader); | ||
| 383 | } | ||
| 384 | return sequence_length; | ||
| 385 | } | ||
| 386 | |||
| 387 | fn nextLiteralMultiStream( | ||
| 388 | self: *DecodeState, | ||
| 389 | ) error{BitStreamHasNoStartBit}!void { | ||
| 390 | self.literal_stream_index += 1; | ||
| 391 | try self.initLiteralStream(self.literal_streams.four[self.literal_stream_index]); | ||
| 392 | } | ||
| 393 | |||
| 394 | fn initLiteralStream(self: *DecodeState, bytes: []const u8) error{BitStreamHasNoStartBit}!void { | ||
| 395 | try self.literal_stream_reader.init(bytes); | ||
| 396 | } | ||
| 397 | |||
| 398 | fn isLiteralStreamEmpty(self: *DecodeState) bool { | ||
| 399 | switch (self.literal_streams) { | ||
| 400 | .one => return self.literal_stream_reader.isEmpty(), | ||
| 401 | .four => return self.literal_stream_index == 3 and self.literal_stream_reader.isEmpty(), | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | const LiteralBitsError = error{ | ||
| 406 | BitStreamHasNoStartBit, | ||
| 407 | UnexpectedEndOfLiteralStream, | ||
| 408 | }; | ||
| 409 | fn readLiteralsBits( | ||
| 410 | self: *DecodeState, | ||
| 411 | bit_count_to_read: usize, | ||
| 412 | ) LiteralBitsError!u16 { | ||
| 413 | return self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch bits: { | ||
| 414 | if (self.literal_streams == .four and self.literal_stream_index < 3) { | ||
| 415 | try self.nextLiteralMultiStream(); | ||
| 416 | break :bits self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch | ||
| 417 | return error.UnexpectedEndOfLiteralStream; | ||
| 418 | } else { | ||
| 419 | return error.UnexpectedEndOfLiteralStream; | ||
| 420 | } | ||
| 421 | }; | ||
| 422 | } | ||
| 423 | |||
| 424 | const DecodeLiteralsError = error{ | ||
| 425 | MalformedLiteralsLength, | ||
| 426 | NotFound, | ||
| 427 | } || LiteralBitsError; | ||
| 428 | |||
| 429 | /// Decode `len` bytes of literals into `dest`. | ||
| 430 | /// | ||
| 431 | /// Errors returned: | ||
| 432 | /// - `error.MalformedLiteralsLength` if the number of literal bytes | ||
| 433 | /// decoded by `self` plus `len` is greater than the regenerated size of | ||
| 434 | /// `literals` | ||
| 435 | /// - `error.UnexpectedEndOfLiteralStream` and `error.NotFound` if there | ||
| 436 | /// are problems decoding Huffman compressed literals | ||
| 437 | pub fn decodeLiteralsSlice( | ||
| 438 | self: *DecodeState, | ||
| 439 | dest: []u8, | ||
| 440 | len: usize, | ||
| 441 | ) DecodeLiteralsError!void { | ||
| 442 | if (self.literal_written_count + len > self.literal_header.regenerated_size) | ||
| 443 | return error.MalformedLiteralsLength; | ||
| 444 | |||
| 445 | switch (self.literal_header.block_type) { | ||
| 446 | .raw => { | ||
| 447 | const literals_end = self.literal_written_count + len; | ||
| 448 | const literal_data = self.literal_streams.one[self.literal_written_count..literals_end]; | ||
| 449 | std.mem.copy(u8, dest, literal_data); | ||
| 450 | self.literal_written_count += len; | ||
| 451 | self.written_count += len; | ||
| 452 | }, | ||
| 453 | .rle => { | ||
| 454 | for (0..len) |i| { | ||
| 455 | dest[i] = self.literal_streams.one[0]; | ||
| 456 | } | ||
| 457 | self.literal_written_count += len; | ||
| 458 | self.written_count += len; | ||
| 459 | }, | ||
| 460 | .compressed, .treeless => { | ||
| 461 | // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; | ||
| 462 | const huffman_tree = self.huffman_tree orelse unreachable; | ||
| 463 | const max_bit_count = huffman_tree.max_bit_count; | ||
| 464 | const starting_bit_count = LiteralsSection.HuffmanTree.weightToBitCount( | ||
| 465 | huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, | ||
| 466 | max_bit_count, | ||
| 467 | ); | ||
| 468 | var bits_read: u4 = 0; | ||
| 469 | var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; | ||
| 470 | var bit_count_to_read: u4 = starting_bit_count; | ||
| 471 | for (0..len) |i| { | ||
| 472 | var prefix: u16 = 0; | ||
| 473 | while (true) { | ||
| 474 | const new_bits = self.readLiteralsBits(bit_count_to_read) catch |err| { | ||
| 475 | return err; | ||
| 476 | }; | ||
| 477 | prefix <<= bit_count_to_read; | ||
| 478 | prefix |= new_bits; | ||
| 479 | bits_read += bit_count_to_read; | ||
| 480 | const result = huffman_tree.query(huffman_tree_index, prefix) catch |err| { | ||
| 481 | return err; | ||
| 482 | }; | ||
| 483 | |||
| 484 | switch (result) { | ||
| 485 | .symbol => |sym| { | ||
| 486 | dest[i] = sym; | ||
| 487 | bit_count_to_read = starting_bit_count; | ||
| 488 | bits_read = 0; | ||
| 489 | huffman_tree_index = huffman_tree.symbol_count_minus_one; | ||
| 490 | break; | ||
| 491 | }, | ||
| 492 | .index => |index| { | ||
| 493 | huffman_tree_index = index; | ||
| 494 | const bit_count = LiteralsSection.HuffmanTree.weightToBitCount( | ||
| 495 | huffman_tree.nodes[index].weight, | ||
| 496 | max_bit_count, | ||
| 497 | ); | ||
| 498 | bit_count_to_read = bit_count - bits_read; | ||
| 499 | }, | ||
| 500 | } | ||
| 501 | } | ||
| 502 | } | ||
| 503 | self.literal_written_count += len; | ||
| 504 | self.written_count += len; | ||
| 505 | }, | ||
| 506 | } | ||
| 507 | } | ||
| 508 | |||
| 509 | /// Decode literals into `dest`; see `decodeLiteralsSlice()`. | ||
| 510 | pub fn decodeLiteralsRingBuffer( | ||
| 511 | self: *DecodeState, | ||
| 512 | dest: *RingBuffer, | ||
| 513 | len: usize, | ||
| 514 | ) DecodeLiteralsError!void { | ||
| 515 | if (self.literal_written_count + len > self.literal_header.regenerated_size) | ||
| 516 | return error.MalformedLiteralsLength; | ||
| 517 | |||
| 518 | switch (self.literal_header.block_type) { | ||
| 519 | .raw => { | ||
| 520 | const literals_end = self.literal_written_count + len; | ||
| 521 | const literal_data = self.literal_streams.one[self.literal_written_count..literals_end]; | ||
| 522 | dest.writeSliceAssumeCapacity(literal_data); | ||
| 523 | self.literal_written_count += len; | ||
| 524 | self.written_count += len; | ||
| 525 | }, | ||
| 526 | .rle => { | ||
| 527 | for (0..len) |_| { | ||
| 528 | dest.writeAssumeCapacity(self.literal_streams.one[0]); | ||
| 529 | } | ||
| 530 | self.literal_written_count += len; | ||
| 531 | self.written_count += len; | ||
| 532 | }, | ||
| 533 | .compressed, .treeless => { | ||
| 534 | // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; | ||
| 535 | const huffman_tree = self.huffman_tree orelse unreachable; | ||
| 536 | const max_bit_count = huffman_tree.max_bit_count; | ||
| 537 | const starting_bit_count = LiteralsSection.HuffmanTree.weightToBitCount( | ||
| 538 | huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, | ||
| 539 | max_bit_count, | ||
| 540 | ); | ||
| 541 | var bits_read: u4 = 0; | ||
| 542 | var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; | ||
| 543 | var bit_count_to_read: u4 = starting_bit_count; | ||
| 544 | for (0..len) |_| { | ||
| 545 | var prefix: u16 = 0; | ||
| 546 | while (true) { | ||
| 547 | const new_bits = try self.readLiteralsBits(bit_count_to_read); | ||
| 548 | prefix <<= bit_count_to_read; | ||
| 549 | prefix |= new_bits; | ||
| 550 | bits_read += bit_count_to_read; | ||
| 551 | const result = try huffman_tree.query(huffman_tree_index, prefix); | ||
| 552 | |||
| 553 | switch (result) { | ||
| 554 | .symbol => |sym| { | ||
| 555 | dest.writeAssumeCapacity(sym); | ||
| 556 | bit_count_to_read = starting_bit_count; | ||
| 557 | bits_read = 0; | ||
| 558 | huffman_tree_index = huffman_tree.symbol_count_minus_one; | ||
| 559 | break; | ||
| 560 | }, | ||
| 561 | .index => |index| { | ||
| 562 | huffman_tree_index = index; | ||
| 563 | const bit_count = LiteralsSection.HuffmanTree.weightToBitCount( | ||
| 564 | huffman_tree.nodes[index].weight, | ||
| 565 | max_bit_count, | ||
| 566 | ); | ||
| 567 | bit_count_to_read = bit_count - bits_read; | ||
| 568 | }, | ||
| 569 | } | ||
| 570 | } | ||
| 571 | } | ||
| 572 | self.literal_written_count += len; | ||
| 573 | self.written_count += len; | ||
| 574 | }, | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | fn getCode(self: *DecodeState, comptime choice: DataType) u32 { | ||
| 579 | return switch (@field(self, @tagName(choice)).table) { | ||
| 580 | .rle => |value| value, | ||
| 581 | .fse => |table| table[@field(self, @tagName(choice)).state].symbol, | ||
| 582 | }; | ||
| 583 | } | ||
| 584 | }; | ||
| 585 | |||
| 586 | /// Decode a single block from `src` into `dest`. The beginning of `src` must be | ||
| 587 | /// the start of the block content (i.e. directly after the block header). | ||
| 588 | /// Increments `consumed_count` by the number of bytes read from `src` to decode | ||
| 589 | /// the block and returns the decompressed size of the block. | ||
| 590 | /// | ||
| 591 | /// Errors returned: | ||
| 592 | /// | ||
| 593 | /// - `error.BlockSizeOverMaximum` if block's size is larger than 1 << 17 or | ||
| 594 | /// `dest[written_count..].len` | ||
| 595 | /// - `error.MalformedBlockSize` if `src.len` is smaller than the block size | ||
| 596 | /// and the block is a raw or compressed block | ||
| 597 | /// - `error.ReservedBlock` if the block is a reserved block | ||
| 598 | /// - `error.MalformedRleBlock` if the block is an RLE block and `src.len < 1` | ||
| 599 | /// - `error.MalformedCompressedBlock` if there are errors decoding a | ||
| 600 | /// compressed block | ||
| 601 | /// - `error.DestTooSmall` is `dest` is not large enough to hold the | ||
| 602 | /// decompressed block | ||
| 603 | pub fn decodeBlock( | ||
| 604 | dest: []u8, | ||
| 605 | src: []const u8, | ||
| 606 | block_header: frame.Zstandard.Block.Header, | ||
| 607 | decode_state: *DecodeState, | ||
| 608 | consumed_count: *usize, | ||
| 609 | block_size_max: usize, | ||
| 610 | written_count: usize, | ||
| 611 | ) (error{DestTooSmall} || Error)!usize { | ||
| 612 | const block_size = block_header.block_size; | ||
| 613 | if (block_size_max < block_size) return error.BlockSizeOverMaximum; | ||
| 614 | switch (block_header.block_type) { | ||
| 615 | .raw => { | ||
| 616 | if (src.len < block_size) return error.MalformedBlockSize; | ||
| 617 | if (dest[written_count..].len < block_size) return error.DestTooSmall; | ||
| 618 | const data = src[0..block_size]; | ||
| 619 | std.mem.copy(u8, dest[written_count..], data); | ||
| 620 | consumed_count.* += block_size; | ||
| 621 | decode_state.written_count += block_size; | ||
| 622 | return block_size; | ||
| 623 | }, | ||
| 624 | .rle => { | ||
| 625 | if (src.len < 1) return error.MalformedRleBlock; | ||
| 626 | if (dest[written_count..].len < block_size) return error.DestTooSmall; | ||
| 627 | for (written_count..block_size + written_count) |write_pos| { | ||
| 628 | dest[write_pos] = src[0]; | ||
| 629 | } | ||
| 630 | consumed_count.* += 1; | ||
| 631 | decode_state.written_count += block_size; | ||
| 632 | return block_size; | ||
| 633 | }, | ||
| 634 | .compressed => { | ||
| 635 | if (src.len < block_size) return error.MalformedBlockSize; | ||
| 636 | var bytes_read: usize = 0; | ||
| 637 | const literals = decodeLiteralsSectionSlice(src[0..block_size], &bytes_read) catch | ||
| 638 | return error.MalformedCompressedBlock; | ||
| 639 | var fbs = std.io.fixedBufferStream(src[bytes_read..block_size]); | ||
| 640 | const fbs_reader = fbs.reader(); | ||
| 641 | const sequences_header = decodeSequencesHeader(fbs_reader) catch | ||
| 642 | return error.MalformedCompressedBlock; | ||
| 643 | |||
| 644 | decode_state.prepare(fbs_reader, literals, sequences_header) catch | ||
| 645 | return error.MalformedCompressedBlock; | ||
| 646 | |||
| 647 | bytes_read += fbs.pos; | ||
| 648 | |||
| 649 | var bytes_written: usize = 0; | ||
| 650 | { | ||
| 651 | const bit_stream_bytes = src[bytes_read..block_size]; | ||
| 652 | var bit_stream: readers.ReverseBitReader = undefined; | ||
| 653 | bit_stream.init(bit_stream_bytes) catch return error.MalformedCompressedBlock; | ||
| 654 | |||
| 655 | if (sequences_header.sequence_count > 0) { | ||
| 656 | decode_state.readInitialFseState(&bit_stream) catch | ||
| 657 | return error.MalformedCompressedBlock; | ||
| 658 | |||
| 659 | var sequence_size_limit = block_size_max; | ||
| 660 | for (0..sequences_header.sequence_count) |i| { | ||
| 661 | const write_pos = written_count + bytes_written; | ||
| 662 | const decompressed_size = decode_state.decodeSequenceSlice( | ||
| 663 | dest, | ||
| 664 | write_pos, | ||
| 665 | &bit_stream, | ||
| 666 | sequence_size_limit, | ||
| 667 | i == sequences_header.sequence_count - 1, | ||
| 668 | ) catch |err| switch (err) { | ||
| 669 | error.DestTooSmall => return error.DestTooSmall, | ||
| 670 | else => return error.MalformedCompressedBlock, | ||
| 671 | }; | ||
| 672 | bytes_written += decompressed_size; | ||
| 673 | sequence_size_limit -= decompressed_size; | ||
| 674 | } | ||
| 675 | } | ||
| 676 | |||
| 677 | if (!bit_stream.isEmpty()) { | ||
| 678 | return error.MalformedCompressedBlock; | ||
| 679 | } | ||
| 680 | } | ||
| 681 | |||
| 682 | if (decode_state.literal_written_count < literals.header.regenerated_size) { | ||
| 683 | const len = literals.header.regenerated_size - decode_state.literal_written_count; | ||
| 684 | if (len > dest[written_count + bytes_written ..].len) return error.DestTooSmall; | ||
| 685 | decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], len) catch | ||
| 686 | return error.MalformedCompressedBlock; | ||
| 687 | bytes_written += len; | ||
| 688 | } | ||
| 689 | |||
| 690 | switch (decode_state.literal_header.block_type) { | ||
| 691 | .treeless, .compressed => { | ||
| 692 | if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; | ||
| 693 | }, | ||
| 694 | .raw, .rle => {}, | ||
| 695 | } | ||
| 696 | |||
| 697 | consumed_count.* += block_size; | ||
| 698 | return bytes_written; | ||
| 699 | }, | ||
| 700 | .reserved => return error.ReservedBlock, | ||
| 701 | } | ||
| 702 | } | ||
| 703 | |||
| 704 | /// Decode a single block from `src` into `dest`; see `decodeBlock()`. Returns | ||
| 705 | /// the size of the decompressed block, which can be used with `dest.sliceLast()` | ||
| 706 | /// to get the decompressed bytes. `error.BlockSizeOverMaximum` is returned if | ||
| 707 | /// the block's compressed or decompressed size is larger than `block_size_max`. | ||
| 708 | pub fn decodeBlockRingBuffer( | ||
| 709 | dest: *RingBuffer, | ||
| 710 | src: []const u8, | ||
| 711 | block_header: frame.Zstandard.Block.Header, | ||
| 712 | decode_state: *DecodeState, | ||
| 713 | consumed_count: *usize, | ||
| 714 | block_size_max: usize, | ||
| 715 | ) Error!usize { | ||
| 716 | const block_size = block_header.block_size; | ||
| 717 | if (block_size_max < block_size) return error.BlockSizeOverMaximum; | ||
| 718 | switch (block_header.block_type) { | ||
| 719 | .raw => { | ||
| 720 | if (src.len < block_size) return error.MalformedBlockSize; | ||
| 721 | const data = src[0..block_size]; | ||
| 722 | dest.writeSliceAssumeCapacity(data); | ||
| 723 | consumed_count.* += block_size; | ||
| 724 | decode_state.written_count += block_size; | ||
| 725 | return block_size; | ||
| 726 | }, | ||
| 727 | .rle => { | ||
| 728 | if (src.len < 1) return error.MalformedRleBlock; | ||
| 729 | for (0..block_size) |_| { | ||
| 730 | dest.writeAssumeCapacity(src[0]); | ||
| 731 | } | ||
| 732 | consumed_count.* += 1; | ||
| 733 | decode_state.written_count += block_size; | ||
| 734 | return block_size; | ||
| 735 | }, | ||
| 736 | .compressed => { | ||
| 737 | if (src.len < block_size) return error.MalformedBlockSize; | ||
| 738 | var bytes_read: usize = 0; | ||
| 739 | const literals = decodeLiteralsSectionSlice(src[0..block_size], &bytes_read) catch | ||
| 740 | return error.MalformedCompressedBlock; | ||
| 741 | var fbs = std.io.fixedBufferStream(src[bytes_read..block_size]); | ||
| 742 | const fbs_reader = fbs.reader(); | ||
| 743 | const sequences_header = decodeSequencesHeader(fbs_reader) catch | ||
| 744 | return error.MalformedCompressedBlock; | ||
| 745 | |||
| 746 | decode_state.prepare(fbs_reader, literals, sequences_header) catch | ||
| 747 | return error.MalformedCompressedBlock; | ||
| 748 | |||
| 749 | bytes_read += fbs.pos; | ||
| 750 | |||
| 751 | var bytes_written: usize = 0; | ||
| 752 | { | ||
| 753 | const bit_stream_bytes = src[bytes_read..block_size]; | ||
| 754 | var bit_stream: readers.ReverseBitReader = undefined; | ||
| 755 | bit_stream.init(bit_stream_bytes) catch return error.MalformedCompressedBlock; | ||
| 756 | |||
| 757 | if (sequences_header.sequence_count > 0) { | ||
| 758 | decode_state.readInitialFseState(&bit_stream) catch | ||
| 759 | return error.MalformedCompressedBlock; | ||
| 760 | |||
| 761 | var sequence_size_limit = block_size_max; | ||
| 762 | for (0..sequences_header.sequence_count) |i| { | ||
| 763 | const decompressed_size = decode_state.decodeSequenceRingBuffer( | ||
| 764 | dest, | ||
| 765 | &bit_stream, | ||
| 766 | sequence_size_limit, | ||
| 767 | i == sequences_header.sequence_count - 1, | ||
| 768 | ) catch return error.MalformedCompressedBlock; | ||
| 769 | bytes_written += decompressed_size; | ||
| 770 | sequence_size_limit -= decompressed_size; | ||
| 771 | } | ||
| 772 | } | ||
| 773 | |||
| 774 | if (!bit_stream.isEmpty()) { | ||
| 775 | return error.MalformedCompressedBlock; | ||
| 776 | } | ||
| 777 | } | ||
| 778 | |||
| 779 | if (decode_state.literal_written_count < literals.header.regenerated_size) { | ||
| 780 | const len = literals.header.regenerated_size - decode_state.literal_written_count; | ||
| 781 | decode_state.decodeLiteralsRingBuffer(dest, len) catch | ||
| 782 | return error.MalformedCompressedBlock; | ||
| 783 | bytes_written += len; | ||
| 784 | } | ||
| 785 | |||
| 786 | switch (decode_state.literal_header.block_type) { | ||
| 787 | .treeless, .compressed => { | ||
| 788 | if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; | ||
| 789 | }, | ||
| 790 | .raw, .rle => {}, | ||
| 791 | } | ||
| 792 | |||
| 793 | consumed_count.* += block_size; | ||
| 794 | if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; | ||
| 795 | return bytes_written; | ||
| 796 | }, | ||
| 797 | .reserved => return error.ReservedBlock, | ||
| 798 | } | ||
| 799 | } | ||
| 800 | |||
| 801 | /// Decode a single block from `source` into `dest`. Literal and sequence data | ||
| 802 | /// from the block is copied into `literals_buffer` and `sequence_buffer`, which | ||
| 803 | /// must be large enough or `error.LiteralsBufferTooSmall` and | ||
| 804 | /// `error.SequenceBufferTooSmall` are returned (the maximum block size is an | ||
| 805 | /// upper bound for the size of both buffers). See `decodeBlock` | ||
| 806 | /// and `decodeBlockRingBuffer` for function that can decode a block without | ||
| 807 | /// these extra copies. `error.EndOfStream` is returned if `source` does not | ||
| 808 | /// contain enough bytes. | ||
| 809 | pub fn decodeBlockReader( | ||
| 810 | dest: *RingBuffer, | ||
| 811 | source: anytype, | ||
| 812 | block_header: frame.Zstandard.Block.Header, | ||
| 813 | decode_state: *DecodeState, | ||
| 814 | block_size_max: usize, | ||
| 815 | literals_buffer: []u8, | ||
| 816 | sequence_buffer: []u8, | ||
| 817 | ) !void { | ||
| 818 | const block_size = block_header.block_size; | ||
| 819 | var block_reader_limited = std.io.limitedReader(source, block_size); | ||
| 820 | const block_reader = block_reader_limited.reader(); | ||
| 821 | if (block_size_max < block_size) return error.BlockSizeOverMaximum; | ||
| 822 | switch (block_header.block_type) { | ||
| 823 | .raw => { | ||
| 824 | if (block_size == 0) return; | ||
| 825 | const slice = dest.sliceAt(dest.write_index, block_size); | ||
| 826 | try source.readNoEof(slice.first); | ||
| 827 | try source.readNoEof(slice.second); | ||
| 828 | dest.write_index = dest.mask2(dest.write_index + block_size); | ||
| 829 | decode_state.written_count += block_size; | ||
| 830 | }, | ||
| 831 | .rle => { | ||
| 832 | const byte = try source.readByte(); | ||
| 833 | for (0..block_size) |_| { | ||
| 834 | dest.writeAssumeCapacity(byte); | ||
| 835 | } | ||
| 836 | decode_state.written_count += block_size; | ||
| 837 | }, | ||
| 838 | .compressed => { | ||
| 839 | const literals = try decodeLiteralsSection(block_reader, literals_buffer); | ||
| 840 | const sequences_header = try decodeSequencesHeader(block_reader); | ||
| 841 | |||
| 842 | try decode_state.prepare(block_reader, literals, sequences_header); | ||
| 843 | |||
| 844 | var bytes_written: usize = 0; | ||
| 845 | { | ||
| 846 | const size = try block_reader.readAll(sequence_buffer); | ||
| 847 | var bit_stream: readers.ReverseBitReader = undefined; | ||
| 848 | try bit_stream.init(sequence_buffer[0..size]); | ||
| 849 | |||
| 850 | if (sequences_header.sequence_count > 0) { | ||
| 851 | if (sequence_buffer.len < block_reader_limited.bytes_left) | ||
| 852 | return error.SequenceBufferTooSmall; | ||
| 853 | |||
| 854 | decode_state.readInitialFseState(&bit_stream) catch | ||
| 855 | return error.MalformedCompressedBlock; | ||
| 856 | |||
| 857 | var sequence_size_limit = block_size_max; | ||
| 858 | for (0..sequences_header.sequence_count) |i| { | ||
| 859 | const decompressed_size = decode_state.decodeSequenceRingBuffer( | ||
| 860 | dest, | ||
| 861 | &bit_stream, | ||
| 862 | sequence_size_limit, | ||
| 863 | i == sequences_header.sequence_count - 1, | ||
| 864 | ) catch return error.MalformedCompressedBlock; | ||
| 865 | sequence_size_limit -= decompressed_size; | ||
| 866 | bytes_written += decompressed_size; | ||
| 867 | } | ||
| 868 | } | ||
| 869 | |||
| 870 | if (!bit_stream.isEmpty()) { | ||
| 871 | return error.MalformedCompressedBlock; | ||
| 872 | } | ||
| 873 | } | ||
| 874 | |||
| 875 | if (decode_state.literal_written_count < literals.header.regenerated_size) { | ||
| 876 | const len = literals.header.regenerated_size - decode_state.literal_written_count; | ||
| 877 | decode_state.decodeLiteralsRingBuffer(dest, len) catch | ||
| 878 | return error.MalformedCompressedBlock; | ||
| 879 | bytes_written += len; | ||
| 880 | } | ||
| 881 | |||
| 882 | switch (decode_state.literal_header.block_type) { | ||
| 883 | .treeless, .compressed => { | ||
| 884 | if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; | ||
| 885 | }, | ||
| 886 | .raw, .rle => {}, | ||
| 887 | } | ||
| 888 | |||
| 889 | if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; | ||
| 890 | if (block_reader_limited.bytes_left != 0) return error.MalformedCompressedBlock; | ||
| 891 | decode_state.literal_written_count = 0; | ||
| 892 | }, | ||
| 893 | .reserved => return error.ReservedBlock, | ||
| 894 | } | ||
| 895 | } | ||
| 896 | |||
| 897 | /// Decode the header of a block. | ||
| 898 | pub fn decodeBlockHeader(src: *const [3]u8) frame.Zstandard.Block.Header { | ||
| 899 | const last_block = src[0] & 1 == 1; | ||
| 900 | const block_type = @intToEnum(frame.Zstandard.Block.Type, (src[0] & 0b110) >> 1); | ||
| 901 | const block_size = ((src[0] & 0b11111000) >> 3) + (@as(u21, src[1]) << 5) + (@as(u21, src[2]) << 13); | ||
| 902 | return .{ | ||
| 903 | .last_block = last_block, | ||
| 904 | .block_type = block_type, | ||
| 905 | .block_size = block_size, | ||
| 906 | }; | ||
| 907 | } | ||
| 908 | |||
| 909 | /// Decode the header of a block. | ||
| 910 | /// | ||
| 911 | /// Errors returned: | ||
| 912 | /// - `error.EndOfStream` if `src.len < 3` | ||
| 913 | pub fn decodeBlockHeaderSlice(src: []const u8) error{EndOfStream}!frame.Zstandard.Block.Header { | ||
| 914 | if (src.len < 3) return error.EndOfStream; | ||
| 915 | return decodeBlockHeader(src[0..3]); | ||
| 916 | } | ||
| 917 | |||
| 918 | /// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the | ||
| 919 | /// number of bytes the section uses. | ||
| 920 | /// | ||
| 921 | /// Errors returned: | ||
| 922 | /// - `error.MalformedLiteralsHeader` if the header is invalid | ||
| 923 | /// - `error.MalformedLiteralsSection` if there are decoding errors | ||
| 924 | /// - `error.MalformedAccuracyLog` if compressed literals have invalid | ||
| 925 | /// accuracy | ||
| 926 | /// - `error.MalformedFseTable` if compressed literals have invalid FSE table | ||
| 927 | /// - `error.MalformedHuffmanTree` if there are errors decoding a Huffamn tree | ||
| 928 | /// - `error.EndOfStream` if there are not enough bytes in `src` | ||
| 929 | pub fn decodeLiteralsSectionSlice( | ||
| 930 | src: []const u8, | ||
| 931 | consumed_count: *usize, | ||
| 932 | ) (error{ MalformedLiteralsHeader, MalformedLiteralsSection, EndOfStream } || huffman.Error)!LiteralsSection { | ||
| 933 | var bytes_read: usize = 0; | ||
| 934 | const header = header: { | ||
| 935 | var fbs = std.io.fixedBufferStream(src); | ||
| 936 | defer bytes_read = fbs.pos; | ||
| 937 | break :header decodeLiteralsHeader(fbs.reader()) catch return error.MalformedLiteralsHeader; | ||
| 938 | }; | ||
| 939 | switch (header.block_type) { | ||
| 940 | .raw => { | ||
| 941 | if (src.len < bytes_read + header.regenerated_size) return error.MalformedLiteralsSection; | ||
| 942 | const stream = src[bytes_read .. bytes_read + header.regenerated_size]; | ||
| 943 | consumed_count.* += header.regenerated_size + bytes_read; | ||
| 944 | return LiteralsSection{ | ||
| 945 | .header = header, | ||
| 946 | .huffman_tree = null, | ||
| 947 | .streams = .{ .one = stream }, | ||
| 948 | }; | ||
| 949 | }, | ||
| 950 | .rle => { | ||
| 951 | if (src.len < bytes_read + 1) return error.MalformedLiteralsSection; | ||
| 952 | const stream = src[bytes_read .. bytes_read + 1]; | ||
| 953 | consumed_count.* += 1 + bytes_read; | ||
| 954 | return LiteralsSection{ | ||
| 955 | .header = header, | ||
| 956 | .huffman_tree = null, | ||
| 957 | .streams = .{ .one = stream }, | ||
| 958 | }; | ||
| 959 | }, | ||
| 960 | .compressed, .treeless => { | ||
| 961 | const huffman_tree_start = bytes_read; | ||
| 962 | const huffman_tree = if (header.block_type == .compressed) | ||
| 963 | try huffman.decodeHuffmanTreeSlice(src[bytes_read..], &bytes_read) | ||
| 964 | else | ||
| 965 | null; | ||
| 966 | const huffman_tree_size = bytes_read - huffman_tree_start; | ||
| 967 | const total_streams_size = std.math.sub(usize, header.compressed_size.?, huffman_tree_size) catch | ||
| 968 | return error.MalformedLiteralsSection; | ||
| 969 | |||
| 970 | if (src.len < bytes_read + total_streams_size) return error.MalformedLiteralsSection; | ||
| 971 | const stream_data = src[bytes_read .. bytes_read + total_streams_size]; | ||
| 972 | |||
| 973 | const streams = try decodeStreams(header.size_format, stream_data); | ||
| 974 | consumed_count.* += bytes_read + total_streams_size; | ||
| 975 | return LiteralsSection{ | ||
| 976 | .header = header, | ||
| 977 | .huffman_tree = huffman_tree, | ||
| 978 | .streams = streams, | ||
| 979 | }; | ||
| 980 | }, | ||
| 981 | } | ||
| 982 | } | ||
| 983 | |||
| 984 | /// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the | ||
| 985 | /// number of bytes the section uses. See `decodeLiterasSectionSlice()`. | ||
| 986 | pub fn decodeLiteralsSection( | ||
| 987 | source: anytype, | ||
| 988 | buffer: []u8, | ||
| 989 | ) !LiteralsSection { | ||
| 990 | const header = try decodeLiteralsHeader(source); | ||
| 991 | switch (header.block_type) { | ||
| 992 | .raw => { | ||
| 993 | try source.readNoEof(buffer[0..header.regenerated_size]); | ||
| 994 | return LiteralsSection{ | ||
| 995 | .header = header, | ||
| 996 | .huffman_tree = null, | ||
| 997 | .streams = .{ .one = buffer }, | ||
| 998 | }; | ||
| 999 | }, | ||
| 1000 | .rle => { | ||
| 1001 | buffer[0] = try source.readByte(); | ||
| 1002 | return LiteralsSection{ | ||
| 1003 | .header = header, | ||
| 1004 | .huffman_tree = null, | ||
| 1005 | .streams = .{ .one = buffer[0..1] }, | ||
| 1006 | }; | ||
| 1007 | }, | ||
| 1008 | .compressed, .treeless => { | ||
| 1009 | var counting_reader = std.io.countingReader(source); | ||
| 1010 | const huffman_tree = if (header.block_type == .compressed) | ||
| 1011 | try huffman.decodeHuffmanTree(counting_reader.reader(), buffer) | ||
| 1012 | else | ||
| 1013 | null; | ||
| 1014 | const huffman_tree_size = @intCast(usize, counting_reader.bytes_read); | ||
| 1015 | const total_streams_size = std.math.sub(usize, header.compressed_size.?, huffman_tree_size) catch | ||
| 1016 | return error.MalformedLiteralsSection; | ||
| 1017 | |||
| 1018 | if (total_streams_size > buffer.len) return error.LiteralsBufferTooSmall; | ||
| 1019 | try source.readNoEof(buffer[0..total_streams_size]); | ||
| 1020 | const stream_data = buffer[0..total_streams_size]; | ||
| 1021 | |||
| 1022 | const streams = try decodeStreams(header.size_format, stream_data); | ||
| 1023 | return LiteralsSection{ | ||
| 1024 | .header = header, | ||
| 1025 | .huffman_tree = huffman_tree, | ||
| 1026 | .streams = streams, | ||
| 1027 | }; | ||
| 1028 | }, | ||
| 1029 | } | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | fn decodeStreams(size_format: u2, stream_data: []const u8) !LiteralsSection.Streams { | ||
| 1033 | if (size_format == 0) { | ||
| 1034 | return .{ .one = stream_data }; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | if (stream_data.len < 6) return error.MalformedLiteralsSection; | ||
| 1038 | |||
| 1039 | const stream_1_length = @as(usize, readInt(u16, stream_data[0..2])); | ||
| 1040 | const stream_2_length = @as(usize, readInt(u16, stream_data[2..4])); | ||
| 1041 | const stream_3_length = @as(usize, readInt(u16, stream_data[4..6])); | ||
| 1042 | |||
| 1043 | const stream_1_start = 6; | ||
| 1044 | const stream_2_start = stream_1_start + stream_1_length; | ||
| 1045 | const stream_3_start = stream_2_start + stream_2_length; | ||
| 1046 | const stream_4_start = stream_3_start + stream_3_length; | ||
| 1047 | |||
| 1048 | if (stream_data.len < stream_4_start) return error.MalformedLiteralsSection; | ||
| 1049 | |||
| 1050 | return .{ .four = .{ | ||
| 1051 | stream_data[stream_1_start .. stream_1_start + stream_1_length], | ||
| 1052 | stream_data[stream_2_start .. stream_2_start + stream_2_length], | ||
| 1053 | stream_data[stream_3_start .. stream_3_start + stream_3_length], | ||
| 1054 | stream_data[stream_4_start..], | ||
| 1055 | } }; | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | /// Decode a literals section header. | ||
| 1059 | /// | ||
| 1060 | /// Errors returned: | ||
| 1061 | /// - `error.EndOfStream` if there are not enough bytes in `source` | ||
| 1062 | pub fn decodeLiteralsHeader(source: anytype) !LiteralsSection.Header { | ||
| 1063 | const byte0 = try source.readByte(); | ||
| 1064 | const block_type = @intToEnum(LiteralsSection.BlockType, byte0 & 0b11); | ||
| 1065 | const size_format = @intCast(u2, (byte0 & 0b1100) >> 2); | ||
| 1066 | var regenerated_size: u20 = undefined; | ||
| 1067 | var compressed_size: ?u18 = null; | ||
| 1068 | switch (block_type) { | ||
| 1069 | .raw, .rle => { | ||
| 1070 | switch (size_format) { | ||
| 1071 | 0, 2 => { | ||
| 1072 | regenerated_size = byte0 >> 3; | ||
| 1073 | }, | ||
| 1074 | 1 => regenerated_size = (byte0 >> 4) + (@as(u20, try source.readByte()) << 4), | ||
| 1075 | 3 => regenerated_size = (byte0 >> 4) + | ||
| 1076 | (@as(u20, try source.readByte()) << 4) + | ||
| 1077 | (@as(u20, try source.readByte()) << 12), | ||
| 1078 | } | ||
| 1079 | }, | ||
| 1080 | .compressed, .treeless => { | ||
| 1081 | const byte1 = try source.readByte(); | ||
| 1082 | const byte2 = try source.readByte(); | ||
| 1083 | switch (size_format) { | ||
| 1084 | 0, 1 => { | ||
| 1085 | regenerated_size = (byte0 >> 4) + ((@as(u20, byte1) & 0b00111111) << 4); | ||
| 1086 | compressed_size = ((byte1 & 0b11000000) >> 6) + (@as(u18, byte2) << 2); | ||
| 1087 | }, | ||
| 1088 | 2 => { | ||
| 1089 | const byte3 = try source.readByte(); | ||
| 1090 | regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00000011) << 12); | ||
| 1091 | compressed_size = ((byte2 & 0b11111100) >> 2) + (@as(u18, byte3) << 6); | ||
| 1092 | }, | ||
| 1093 | 3 => { | ||
| 1094 | const byte3 = try source.readByte(); | ||
| 1095 | const byte4 = try source.readByte(); | ||
| 1096 | regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00111111) << 12); | ||
| 1097 | compressed_size = ((byte2 & 0b11000000) >> 6) + (@as(u18, byte3) << 2) + (@as(u18, byte4) << 10); | ||
| 1098 | }, | ||
| 1099 | } | ||
| 1100 | }, | ||
| 1101 | } | ||
| 1102 | return LiteralsSection.Header{ | ||
| 1103 | .block_type = block_type, | ||
| 1104 | .size_format = size_format, | ||
| 1105 | .regenerated_size = regenerated_size, | ||
| 1106 | .compressed_size = compressed_size, | ||
| 1107 | }; | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | /// Decode a sequences section header. | ||
| 1111 | /// | ||
| 1112 | /// Errors returned: | ||
| 1113 | /// - `error.ReservedBitSet` if the reserved bit is set | ||
| 1114 | /// - `error.EndOfStream` if there are not enough bytes in `source` | ||
| 1115 | pub fn decodeSequencesHeader( | ||
| 1116 | source: anytype, | ||
| 1117 | ) !SequencesSection.Header { | ||
| 1118 | var sequence_count: u24 = undefined; | ||
| 1119 | |||
| 1120 | const byte0 = try source.readByte(); | ||
| 1121 | if (byte0 == 0) { | ||
| 1122 | return SequencesSection.Header{ | ||
| 1123 | .sequence_count = 0, | ||
| 1124 | .offsets = undefined, | ||
| 1125 | .match_lengths = undefined, | ||
| 1126 | .literal_lengths = undefined, | ||
| 1127 | }; | ||
| 1128 | } else if (byte0 < 128) { | ||
| 1129 | sequence_count = byte0; | ||
| 1130 | } else if (byte0 < 255) { | ||
| 1131 | sequence_count = (@as(u24, (byte0 - 128)) << 8) + try source.readByte(); | ||
| 1132 | } else { | ||
| 1133 | sequence_count = (try source.readByte()) + (@as(u24, try source.readByte()) << 8) + 0x7F00; | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | const compression_modes = try source.readByte(); | ||
| 1137 | |||
| 1138 | const matches_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00001100) >> 2); | ||
| 1139 | const offsets_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00110000) >> 4); | ||
| 1140 | const literal_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b11000000) >> 6); | ||
| 1141 | if (compression_modes & 0b11 != 0) return error.ReservedBitSet; | ||
| 1142 | |||
| 1143 | return SequencesSection.Header{ | ||
| 1144 | .sequence_count = sequence_count, | ||
| 1145 | .offsets = offsets_mode, | ||
| 1146 | .match_lengths = matches_mode, | ||
| 1147 | .literal_lengths = literal_mode, | ||
| 1148 | }; | ||
| 1149 | } | ||
lib/std/compress/zstandard/decode/fse.zig created+153| ... | @@ -0,0 +1,153 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | |||
| 4 | const types = @import("../types.zig"); | ||
| 5 | const Table = types.compressed_block.Table; | ||
| 6 | |||
| 7 | pub fn decodeFseTable( | ||
| 8 | bit_reader: anytype, | ||
| 9 | expected_symbol_count: usize, | ||
| 10 | max_accuracy_log: u4, | ||
| 11 | entries: []Table.Fse, | ||
| 12 | ) !usize { | ||
| 13 | const accuracy_log_biased = try bit_reader.readBitsNoEof(u4, 4); | ||
| 14 | if (accuracy_log_biased > max_accuracy_log -| 5) return error.MalformedAccuracyLog; | ||
| 15 | const accuracy_log = accuracy_log_biased + 5; | ||
| 16 | |||
| 17 | var values: [256]u16 = undefined; | ||
| 18 | var value_count: usize = 0; | ||
| 19 | |||
| 20 | const total_probability = @as(u16, 1) << accuracy_log; | ||
| 21 | var accumulated_probability: u16 = 0; | ||
| 22 | |||
| 23 | while (accumulated_probability < total_probability) { | ||
| 24 | // WARNING: The RFC in poorly worded, and would suggest std.math.log2_int_ceil is correct here, | ||
| 25 | // but power of two (remaining probabilities + 1) need max bits set to 1 more. | ||
| 26 | const max_bits = std.math.log2_int(u16, total_probability - accumulated_probability + 1) + 1; | ||
| 27 | const small = try bit_reader.readBitsNoEof(u16, max_bits - 1); | ||
| 28 | |||
| 29 | const cutoff = (@as(u16, 1) << max_bits) - 1 - (total_probability - accumulated_probability + 1); | ||
| 30 | |||
| 31 | const value = if (small < cutoff) | ||
| 32 | small | ||
| 33 | else value: { | ||
| 34 | const value_read = small + (try bit_reader.readBitsNoEof(u16, 1) << (max_bits - 1)); | ||
| 35 | break :value if (value_read < @as(u16, 1) << (max_bits - 1)) | ||
| 36 | value_read | ||
| 37 | else | ||
| 38 | value_read - cutoff; | ||
| 39 | }; | ||
| 40 | |||
| 41 | accumulated_probability += if (value != 0) value - 1 else 1; | ||
| 42 | |||
| 43 | values[value_count] = value; | ||
| 44 | value_count += 1; | ||
| 45 | |||
| 46 | if (value == 1) { | ||
| 47 | while (true) { | ||
| 48 | const repeat_flag = try bit_reader.readBitsNoEof(u2, 2); | ||
| 49 | if (repeat_flag + value_count > 256) return error.MalformedFseTable; | ||
| 50 | for (0..repeat_flag) |_| { | ||
| 51 | values[value_count] = 1; | ||
| 52 | value_count += 1; | ||
| 53 | } | ||
| 54 | if (repeat_flag < 3) break; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | if (value_count == 256) break; | ||
| 58 | } | ||
| 59 | bit_reader.alignToByte(); | ||
| 60 | |||
| 61 | if (value_count < 2) return error.MalformedFseTable; | ||
| 62 | if (accumulated_probability != total_probability) return error.MalformedFseTable; | ||
| 63 | if (value_count > expected_symbol_count) return error.MalformedFseTable; | ||
| 64 | |||
| 65 | const table_size = total_probability; | ||
| 66 | |||
| 67 | try buildFseTable(values[0..value_count], entries[0..table_size]); | ||
| 68 | return table_size; | ||
| 69 | } | ||
| 70 | |||
| 71 | fn buildFseTable(values: []const u16, entries: []Table.Fse) !void { | ||
| 72 | const total_probability = @intCast(u16, entries.len); | ||
| 73 | const accuracy_log = std.math.log2_int(u16, total_probability); | ||
| 74 | assert(total_probability <= 1 << 9); | ||
| 75 | |||
| 76 | var less_than_one_count: usize = 0; | ||
| 77 | for (values, 0..) |value, i| { | ||
| 78 | if (value == 0) { | ||
| 79 | entries[entries.len - 1 - less_than_one_count] = Table.Fse{ | ||
| 80 | .symbol = @intCast(u8, i), | ||
| 81 | .baseline = 0, | ||
| 82 | .bits = accuracy_log, | ||
| 83 | }; | ||
| 84 | less_than_one_count += 1; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | var position: usize = 0; | ||
| 89 | var temp_states: [1 << 9]u16 = undefined; | ||
| 90 | for (values, 0..) |value, symbol| { | ||
| 91 | if (value == 0 or value == 1) continue; | ||
| 92 | const probability = value - 1; | ||
| 93 | |||
| 94 | const state_share_dividend = std.math.ceilPowerOfTwo(u16, probability) catch | ||
| 95 | return error.MalformedFseTable; | ||
| 96 | const share_size = @divExact(total_probability, state_share_dividend); | ||
| 97 | const double_state_count = state_share_dividend - probability; | ||
| 98 | const single_state_count = probability - double_state_count; | ||
| 99 | const share_size_log = std.math.log2_int(u16, share_size); | ||
| 100 | |||
| 101 | for (0..probability) |i| { | ||
| 102 | temp_states[i] = @intCast(u16, position); | ||
| 103 | position += (entries.len >> 1) + (entries.len >> 3) + 3; | ||
| 104 | position &= entries.len - 1; | ||
| 105 | while (position >= entries.len - less_than_one_count) { | ||
| 106 | position += (entries.len >> 1) + (entries.len >> 3) + 3; | ||
| 107 | position &= entries.len - 1; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | std.sort.sort(u16, temp_states[0..probability], {}, std.sort.asc(u16)); | ||
| 111 | for (0..probability) |i| { | ||
| 112 | entries[temp_states[i]] = if (i < double_state_count) Table.Fse{ | ||
| 113 | .symbol = @intCast(u8, symbol), | ||
| 114 | .bits = share_size_log + 1, | ||
| 115 | .baseline = single_state_count * share_size + @intCast(u16, i) * 2 * share_size, | ||
| 116 | } else Table.Fse{ | ||
| 117 | .symbol = @intCast(u8, symbol), | ||
| 118 | .bits = share_size_log, | ||
| 119 | .baseline = (@intCast(u16, i) - double_state_count) * share_size, | ||
| 120 | }; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | test buildFseTable { | ||
| 126 | const literals_length_default_values = [36]u16{ | ||
| 127 | 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, | ||
| 128 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 2, 2, 2, 2, 2, | ||
| 129 | 0, 0, 0, 0, | ||
| 130 | }; | ||
| 131 | |||
| 132 | const match_lengths_default_values = [53]u16{ | ||
| 133 | 2, 5, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, | ||
| 134 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, | ||
| 135 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, | ||
| 136 | 0, 0, 0, 0, 0, | ||
| 137 | }; | ||
| 138 | |||
| 139 | const offset_codes_default_values = [29]u16{ | ||
| 140 | 2, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, | ||
| 141 | 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, | ||
| 142 | }; | ||
| 143 | |||
| 144 | var entries: [64]Table.Fse = undefined; | ||
| 145 | try buildFseTable(&literals_length_default_values, &entries); | ||
| 146 | try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_literal_fse_table.fse, &entries); | ||
| 147 | |||
| 148 | try buildFseTable(&match_lengths_default_values, &entries); | ||
| 149 | try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_match_fse_table.fse, &entries); | ||
| 150 | |||
| 151 | try buildFseTable(&offset_codes_default_values, entries[0..32]); | ||
| 152 | try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_offset_fse_table.fse, entries[0..32]); | ||
| 153 | } | ||
lib/std/compress/zstandard/decode/huffman.zig created+234| ... | @@ -0,0 +1,234 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | const types = @import("../types.zig"); | ||
| 4 | const LiteralsSection = types.compressed_block.LiteralsSection; | ||
| 5 | const Table = types.compressed_block.Table; | ||
| 6 | |||
| 7 | const readers = @import("../readers.zig"); | ||
| 8 | |||
| 9 | const decodeFseTable = @import("fse.zig").decodeFseTable; | ||
| 10 | |||
| 11 | pub const Error = error{ | ||
| 12 | MalformedHuffmanTree, | ||
| 13 | MalformedFseTable, | ||
| 14 | MalformedAccuracyLog, | ||
| 15 | EndOfStream, | ||
| 16 | }; | ||
| 17 | |||
| 18 | fn decodeFseHuffmanTree( | ||
| 19 | source: anytype, | ||
| 20 | compressed_size: usize, | ||
| 21 | buffer: []u8, | ||
| 22 | weights: *[256]u4, | ||
| 23 | ) !usize { | ||
| 24 | var stream = std.io.limitedReader(source, compressed_size); | ||
| 25 | var bit_reader = readers.bitReader(stream.reader()); | ||
| 26 | |||
| 27 | var entries: [1 << 6]Table.Fse = undefined; | ||
| 28 | const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { | ||
| 29 | error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, | ||
| 30 | error.EndOfStream => return error.MalformedFseTable, | ||
| 31 | else => |e| return e, | ||
| 32 | }; | ||
| 33 | const accuracy_log = std.math.log2_int_ceil(usize, table_size); | ||
| 34 | |||
| 35 | const amount = try stream.reader().readAll(buffer); | ||
| 36 | var huff_bits: readers.ReverseBitReader = undefined; | ||
| 37 | huff_bits.init(buffer[0..amount]) catch return error.MalformedHuffmanTree; | ||
| 38 | |||
| 39 | return assignWeights(&huff_bits, accuracy_log, &entries, weights); | ||
| 40 | } | ||
| 41 | |||
| 42 | fn decodeFseHuffmanTreeSlice(src: []const u8, compressed_size: usize, weights: *[256]u4) !usize { | ||
| 43 | if (src.len < compressed_size) return error.MalformedHuffmanTree; | ||
| 44 | var stream = std.io.fixedBufferStream(src[0..compressed_size]); | ||
| 45 | var counting_reader = std.io.countingReader(stream.reader()); | ||
| 46 | var bit_reader = readers.bitReader(counting_reader.reader()); | ||
| 47 | |||
| 48 | var entries: [1 << 6]Table.Fse = undefined; | ||
| 49 | const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { | ||
| 50 | error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, | ||
| 51 | error.EndOfStream => return error.MalformedFseTable, | ||
| 52 | }; | ||
| 53 | const accuracy_log = std.math.log2_int_ceil(usize, table_size); | ||
| 54 | |||
| 55 | const start_index = std.math.cast(usize, counting_reader.bytes_read) orelse | ||
| 56 | return error.MalformedHuffmanTree; | ||
| 57 | var huff_data = src[start_index..compressed_size]; | ||
| 58 | var huff_bits: readers.ReverseBitReader = undefined; | ||
| 59 | huff_bits.init(huff_data) catch return error.MalformedHuffmanTree; | ||
| 60 | |||
| 61 | return assignWeights(&huff_bits, accuracy_log, &entries, weights); | ||
| 62 | } | ||
| 63 | |||
| 64 | fn assignWeights( | ||
| 65 | huff_bits: *readers.ReverseBitReader, | ||
| 66 | accuracy_log: usize, | ||
| 67 | entries: *[1 << 6]Table.Fse, | ||
| 68 | weights: *[256]u4, | ||
| 69 | ) !usize { | ||
| 70 | var i: usize = 0; | ||
| 71 | var even_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; | ||
| 72 | var odd_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; | ||
| 73 | |||
| 74 | while (i < 254) { | ||
| 75 | const even_data = entries[even_state]; | ||
| 76 | var read_bits: usize = 0; | ||
| 77 | const even_bits = huff_bits.readBits(u32, even_data.bits, &read_bits) catch unreachable; | ||
| 78 | weights[i] = std.math.cast(u4, even_data.symbol) orelse return error.MalformedHuffmanTree; | ||
| 79 | i += 1; | ||
| 80 | if (read_bits < even_data.bits) { | ||
| 81 | weights[i] = std.math.cast(u4, entries[odd_state].symbol) orelse return error.MalformedHuffmanTree; | ||
| 82 | i += 1; | ||
| 83 | break; | ||
| 84 | } | ||
| 85 | even_state = even_data.baseline + even_bits; | ||
| 86 | |||
| 87 | read_bits = 0; | ||
| 88 | const odd_data = entries[odd_state]; | ||
| 89 | const odd_bits = huff_bits.readBits(u32, odd_data.bits, &read_bits) catch unreachable; | ||
| 90 | weights[i] = std.math.cast(u4, odd_data.symbol) orelse return error.MalformedHuffmanTree; | ||
| 91 | i += 1; | ||
| 92 | if (read_bits < odd_data.bits) { | ||
| 93 | if (i == 255) return error.MalformedHuffmanTree; | ||
| 94 | weights[i] = std.math.cast(u4, entries[even_state].symbol) orelse return error.MalformedHuffmanTree; | ||
| 95 | i += 1; | ||
| 96 | break; | ||
| 97 | } | ||
| 98 | odd_state = odd_data.baseline + odd_bits; | ||
| 99 | } else return error.MalformedHuffmanTree; | ||
| 100 | |||
| 101 | if (!huff_bits.isEmpty()) { | ||
| 102 | return error.MalformedHuffmanTree; | ||
| 103 | } | ||
| 104 | |||
| 105 | return i + 1; // stream contains all but the last symbol | ||
| 106 | } | ||
| 107 | |||
| 108 | fn decodeDirectHuffmanTree(source: anytype, encoded_symbol_count: usize, weights: *[256]u4) !usize { | ||
| 109 | const weights_byte_count = (encoded_symbol_count + 1) / 2; | ||
| 110 | for (0..weights_byte_count) |i| { | ||
| 111 | const byte = try source.readByte(); | ||
| 112 | weights[2 * i] = @intCast(u4, byte >> 4); | ||
| 113 | weights[2 * i + 1] = @intCast(u4, byte & 0xF); | ||
| 114 | } | ||
| 115 | return encoded_symbol_count + 1; | ||
| 116 | } | ||
| 117 | |||
| 118 | fn assignSymbols(weight_sorted_prefixed_symbols: []LiteralsSection.HuffmanTree.PrefixedSymbol, weights: [256]u4) usize { | ||
| 119 | for (0..weight_sorted_prefixed_symbols.len) |i| { | ||
| 120 | weight_sorted_prefixed_symbols[i] = .{ | ||
| 121 | .symbol = @intCast(u8, i), | ||
| 122 | .weight = undefined, | ||
| 123 | .prefix = undefined, | ||
| 124 | }; | ||
| 125 | } | ||
| 126 | |||
| 127 | std.sort.sort( | ||
| 128 | LiteralsSection.HuffmanTree.PrefixedSymbol, | ||
| 129 | weight_sorted_prefixed_symbols, | ||
| 130 | weights, | ||
| 131 | lessThanByWeight, | ||
| 132 | ); | ||
| 133 | |||
| 134 | var prefix: u16 = 0; | ||
| 135 | var prefixed_symbol_count: usize = 0; | ||
| 136 | var sorted_index: usize = 0; | ||
| 137 | const symbol_count = weight_sorted_prefixed_symbols.len; | ||
| 138 | while (sorted_index < symbol_count) { | ||
| 139 | var symbol = weight_sorted_prefixed_symbols[sorted_index].symbol; | ||
| 140 | const weight = weights[symbol]; | ||
| 141 | if (weight == 0) { | ||
| 142 | sorted_index += 1; | ||
| 143 | continue; | ||
| 144 | } | ||
| 145 | |||
| 146 | while (sorted_index < symbol_count) : ({ | ||
| 147 | sorted_index += 1; | ||
| 148 | prefixed_symbol_count += 1; | ||
| 149 | prefix += 1; | ||
| 150 | }) { | ||
| 151 | symbol = weight_sorted_prefixed_symbols[sorted_index].symbol; | ||
| 152 | if (weights[symbol] != weight) { | ||
| 153 | prefix = ((prefix - 1) >> (weights[symbol] - weight)) + 1; | ||
| 154 | break; | ||
| 155 | } | ||
| 156 | weight_sorted_prefixed_symbols[prefixed_symbol_count].symbol = symbol; | ||
| 157 | weight_sorted_prefixed_symbols[prefixed_symbol_count].prefix = prefix; | ||
| 158 | weight_sorted_prefixed_symbols[prefixed_symbol_count].weight = weight; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | return prefixed_symbol_count; | ||
| 162 | } | ||
| 163 | |||
| 164 | fn buildHuffmanTree(weights: *[256]u4, symbol_count: usize) error{MalformedHuffmanTree}!LiteralsSection.HuffmanTree { | ||
| 165 | var weight_power_sum_big: u32 = 0; | ||
| 166 | for (weights[0 .. symbol_count - 1]) |value| { | ||
| 167 | weight_power_sum_big += (@as(u16, 1) << value) >> 1; | ||
| 168 | } | ||
| 169 | if (weight_power_sum_big >= 1 << 11) return error.MalformedHuffmanTree; | ||
| 170 | const weight_power_sum = @intCast(u16, weight_power_sum_big); | ||
| 171 | |||
| 172 | // advance to next power of two (even if weight_power_sum is a power of 2) | ||
| 173 | // TODO: is it valid to have weight_power_sum == 0? | ||
| 174 | const max_number_of_bits = if (weight_power_sum == 0) 1 else std.math.log2_int(u16, weight_power_sum) + 1; | ||
| 175 | const next_power_of_two = @as(u16, 1) << max_number_of_bits; | ||
| 176 | weights[symbol_count - 1] = std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1; | ||
| 177 | |||
| 178 | var weight_sorted_prefixed_symbols: [256]LiteralsSection.HuffmanTree.PrefixedSymbol = undefined; | ||
| 179 | const prefixed_symbol_count = assignSymbols(weight_sorted_prefixed_symbols[0..symbol_count], weights.*); | ||
| 180 | const tree = LiteralsSection.HuffmanTree{ | ||
| 181 | .max_bit_count = max_number_of_bits, | ||
| 182 | .symbol_count_minus_one = @intCast(u8, prefixed_symbol_count - 1), | ||
| 183 | .nodes = weight_sorted_prefixed_symbols, | ||
| 184 | }; | ||
| 185 | return tree; | ||
| 186 | } | ||
| 187 | |||
| 188 | pub fn decodeHuffmanTree( | ||
| 189 | source: anytype, | ||
| 190 | buffer: []u8, | ||
| 191 | ) (@TypeOf(source).Error || Error)!LiteralsSection.HuffmanTree { | ||
| 192 | const header = try source.readByte(); | ||
| 193 | var weights: [256]u4 = undefined; | ||
| 194 | const symbol_count = if (header < 128) | ||
| 195 | // FSE compressed weights | ||
| 196 | try decodeFseHuffmanTree(source, header, buffer, &weights) | ||
| 197 | else | ||
| 198 | try decodeDirectHuffmanTree(source, header - 127, &weights); | ||
| 199 | |||
| 200 | return buildHuffmanTree(&weights, symbol_count); | ||
| 201 | } | ||
| 202 | |||
| 203 | pub fn decodeHuffmanTreeSlice( | ||
| 204 | src: []const u8, | ||
| 205 | consumed_count: *usize, | ||
| 206 | ) Error!LiteralsSection.HuffmanTree { | ||
| 207 | if (src.len == 0) return error.MalformedHuffmanTree; | ||
| 208 | const header = src[0]; | ||
| 209 | var bytes_read: usize = 1; | ||
| 210 | var weights: [256]u4 = undefined; | ||
| 211 | const symbol_count = if (header < 128) count: { | ||
| 212 | // FSE compressed weights | ||
| 213 | bytes_read += header; | ||
| 214 | break :count try decodeFseHuffmanTreeSlice(src[1..], header, &weights); | ||
| 215 | } else count: { | ||
| 216 | var fbs = std.io.fixedBufferStream(src[1..]); | ||
| 217 | defer bytes_read += fbs.pos; | ||
| 218 | break :count try decodeDirectHuffmanTree(fbs.reader(), header - 127, &weights); | ||
| 219 | }; | ||
| 220 | |||
| 221 | consumed_count.* += bytes_read; | ||
| 222 | return buildHuffmanTree(&weights, symbol_count); | ||
| 223 | } | ||
| 224 | |||
| 225 | fn lessThanByWeight( | ||
| 226 | weights: [256]u4, | ||
| 227 | lhs: LiteralsSection.HuffmanTree.PrefixedSymbol, | ||
| 228 | rhs: LiteralsSection.HuffmanTree.PrefixedSymbol, | ||
| 229 | ) bool { | ||
| 230 | // NOTE: this function relies on the use of a stable sorting algorithm, | ||
| 231 | // otherwise a special case of if (weights[lhs] == weights[rhs]) return lhs < rhs; | ||
| 232 | // should be added | ||
| 233 | return weights[lhs.symbol] < weights[rhs.symbol]; | ||
| 234 | } | ||
lib/std/compress/zstandard/decompress.zig created+636| ... | @@ -0,0 +1,636 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const Allocator = std.mem.Allocator; | ||
| 4 | const RingBuffer = std.RingBuffer; | ||
| 5 | |||
| 6 | const types = @import("types.zig"); | ||
| 7 | const frame = types.frame; | ||
| 8 | const LiteralsSection = types.compressed_block.LiteralsSection; | ||
| 9 | const SequencesSection = types.compressed_block.SequencesSection; | ||
| 10 | const SkippableHeader = types.frame.Skippable.Header; | ||
| 11 | const ZstandardHeader = types.frame.Zstandard.Header; | ||
| 12 | const Table = types.compressed_block.Table; | ||
| 13 | |||
| 14 | pub const block = @import("decode/block.zig"); | ||
| 15 | |||
| 16 | const readers = @import("readers.zig"); | ||
| 17 | |||
| 18 | const readInt = std.mem.readIntLittle; | ||
| 19 | const readIntSlice = std.mem.readIntSliceLittle; | ||
| 20 | |||
| 21 | /// Returns `true` is `magic` is a valid magic number for a skippable frame | ||
| 22 | pub fn isSkippableMagic(magic: u32) bool { | ||
| 23 | return frame.Skippable.magic_number_min <= magic and magic <= frame.Skippable.magic_number_max; | ||
| 24 | } | ||
| 25 | |||
| 26 | /// Returns the kind of frame at the beginning of `source`. | ||
| 27 | /// | ||
| 28 | /// Errors returned: | ||
| 29 | /// - `error.BadMagic` if `source` begins with bytes not equal to the | ||
| 30 | /// Zstandard frame magic number, or outside the range of magic numbers for | ||
| 31 | /// skippable frames. | ||
| 32 | /// - `error.EndOfStream` if `source` contains fewer than 4 bytes | ||
| 33 | pub fn decodeFrameType(source: anytype) error{ BadMagic, EndOfStream }!frame.Kind { | ||
| 34 | const magic = try source.readIntLittle(u32); | ||
| 35 | return frameType(magic); | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Returns the kind of frame associated to `magic`. | ||
| 39 | /// | ||
| 40 | /// Errors returned: | ||
| 41 | /// - `error.BadMagic` if `magic` is not a valid magic number. | ||
| 42 | pub fn frameType(magic: u32) error{BadMagic}!frame.Kind { | ||
| 43 | return if (magic == frame.Zstandard.magic_number) | ||
| 44 | .zstandard | ||
| 45 | else if (isSkippableMagic(magic)) | ||
| 46 | .skippable | ||
| 47 | else | ||
| 48 | error.BadMagic; | ||
| 49 | } | ||
| 50 | |||
| 51 | pub const FrameHeader = union(enum) { | ||
| 52 | zstandard: ZstandardHeader, | ||
| 53 | skippable: SkippableHeader, | ||
| 54 | }; | ||
| 55 | |||
| 56 | pub const HeaderError = error{ BadMagic, EndOfStream, ReservedBitSet }; | ||
| 57 | |||
| 58 | /// Returns the header of the frame at the beginning of `source`. | ||
| 59 | /// | ||
| 60 | /// Errors returned: | ||
| 61 | /// - `error.BadMagic` if `source` begins with bytes not equal to the | ||
| 62 | /// Zstandard frame magic number, or outside the range of magic numbers for | ||
| 63 | /// skippable frames. | ||
| 64 | /// - `error.EndOfStream` if `source` contains fewer than 4 bytes | ||
| 65 | /// - `error.ReservedBitSet` if the frame is a Zstandard frame and any of the | ||
| 66 | /// reserved bits are set | ||
| 67 | pub fn decodeFrameHeader(source: anytype) (@TypeOf(source).Error || HeaderError)!FrameHeader { | ||
| 68 | const magic = try source.readIntLittle(u32); | ||
| 69 | const frame_type = try frameType(magic); | ||
| 70 | switch (frame_type) { | ||
| 71 | .zstandard => return FrameHeader{ .zstandard = try decodeZstandardHeader(source) }, | ||
| 72 | .skippable => return FrameHeader{ | ||
| 73 | .skippable = .{ | ||
| 74 | .magic_number = magic, | ||
| 75 | .frame_size = try source.readIntLittle(u32), | ||
| 76 | }, | ||
| 77 | }, | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | pub const ReadWriteCount = struct { | ||
| 82 | read_count: usize, | ||
| 83 | write_count: usize, | ||
| 84 | }; | ||
| 85 | |||
| 86 | /// Decodes frames from `src` into `dest`; returns the length of the result. | ||
| 87 | /// The stream should not have extra trailing bytes - either all bytes in `src` | ||
| 88 | /// will be decoded, or an error will be returned. An error will be returned if | ||
| 89 | /// a Zstandard frame in `src` does not declare its content size. | ||
| 90 | /// | ||
| 91 | /// Errors returned: | ||
| 92 | /// - `error.DictionaryIdFlagUnsupported` if a `src` contains a frame that | ||
| 93 | /// uses a dictionary | ||
| 94 | /// - `error.MalformedFrame` if a frame in `src` is invalid | ||
| 95 | /// - `error.UnknownContentSizeUnsupported` if a frame in `src` does not | ||
| 96 | /// declare its content size | ||
| 97 | pub fn decode(dest: []u8, src: []const u8, verify_checksum: bool) error{ | ||
| 98 | MalformedFrame, | ||
| 99 | UnknownContentSizeUnsupported, | ||
| 100 | DictionaryIdFlagUnsupported, | ||
| 101 | }!usize { | ||
| 102 | var write_count: usize = 0; | ||
| 103 | var read_count: usize = 0; | ||
| 104 | while (read_count < src.len) { | ||
| 105 | const counts = decodeFrame(dest, src[read_count..], verify_checksum) catch |err| { | ||
| 106 | switch (err) { | ||
| 107 | error.UnknownContentSizeUnsupported => return error.UnknownContentSizeUnsupported, | ||
| 108 | error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported, | ||
| 109 | else => return error.MalformedFrame, | ||
| 110 | } | ||
| 111 | }; | ||
| 112 | read_count += counts.read_count; | ||
| 113 | write_count += counts.write_count; | ||
| 114 | } | ||
| 115 | return write_count; | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Decodes a stream of frames from `src`; returns the decoded bytes. The stream | ||
| 119 | /// should not have extra trailing bytes - either all bytes in `src` will be | ||
| 120 | /// decoded, or an error will be returned. | ||
| 121 | /// | ||
| 122 | /// Errors returned: | ||
| 123 | /// - `error.DictionaryIdFlagUnsupported` if a `src` contains a frame that | ||
| 124 | /// uses a dictionary | ||
| 125 | /// - `error.MalformedFrame` if a frame in `src` is invalid | ||
| 126 | /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory | ||
| 127 | pub fn decodeAlloc( | ||
| 128 | allocator: Allocator, | ||
| 129 | src: []const u8, | ||
| 130 | verify_checksum: bool, | ||
| 131 | window_size_max: usize, | ||
| 132 | ) error{ DictionaryIdFlagUnsupported, MalformedFrame, OutOfMemory }![]u8 { | ||
| 133 | var result = std.ArrayList(u8).init(allocator); | ||
| 134 | errdefer result.deinit(); | ||
| 135 | |||
| 136 | var read_count: usize = 0; | ||
| 137 | while (read_count < src.len) { | ||
| 138 | read_count += decodeFrameArrayList( | ||
| 139 | allocator, | ||
| 140 | &result, | ||
| 141 | src[read_count..], | ||
| 142 | verify_checksum, | ||
| 143 | window_size_max, | ||
| 144 | ) catch |err| switch (err) { | ||
| 145 | error.OutOfMemory => return error.OutOfMemory, | ||
| 146 | error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported, | ||
| 147 | else => return error.MalformedFrame, | ||
| 148 | }; | ||
| 149 | } | ||
| 150 | return result.toOwnedSlice(); | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Decodes the frame at the start of `src` into `dest`. Returns the number of | ||
| 154 | /// bytes read from `src` and written to `dest`. This function can only decode | ||
| 155 | /// frames that declare the decompressed content size. | ||
| 156 | /// | ||
| 157 | /// Errors returned: | ||
| 158 | /// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic | ||
| 159 | /// number for a Zstandard or skippable frame | ||
| 160 | /// - `error.UnknownContentSizeUnsupported` if the frame does not declare the | ||
| 161 | /// uncompressed content size | ||
| 162 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size | ||
| 163 | /// - `error.ContentTooLarge` if `dest` is smaller than the uncompressed data | ||
| 164 | /// size declared by the frame header | ||
| 165 | /// - `error.ContentSizeTooLarge` if the frame header indicates a content size | ||
| 166 | /// that is larger than `std.math.maxInt(usize)` | ||
| 167 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary | ||
| 168 | /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame | ||
| 169 | /// contains a checksum that does not match the checksum of the decompressed | ||
| 170 | /// data | ||
| 171 | /// - `error.ReservedBitSet` if any of the reserved bits of the frame header | ||
| 172 | /// are set | ||
| 173 | /// - `error.EndOfStream` if `src` does not contain a complete frame | ||
| 174 | /// - `error.BadContentSize` if the content size declared by the frame does | ||
| 175 | /// not equal the actual size of decompressed data | ||
| 176 | /// - an error in `block.Error` if there are errors decoding a block | ||
| 177 | /// - `error.SkippableSizeTooLarge` if the frame is skippable and reports a | ||
| 178 | /// size greater than `src.len` | ||
| 179 | pub fn decodeFrame( | ||
| 180 | dest: []u8, | ||
| 181 | src: []const u8, | ||
| 182 | verify_checksum: bool, | ||
| 183 | ) (error{ | ||
| 184 | BadMagic, | ||
| 185 | UnknownContentSizeUnsupported, | ||
| 186 | ContentTooLarge, | ||
| 187 | ContentSizeTooLarge, | ||
| 188 | WindowSizeUnknown, | ||
| 189 | DictionaryIdFlagUnsupported, | ||
| 190 | SkippableSizeTooLarge, | ||
| 191 | } || FrameError)!ReadWriteCount { | ||
| 192 | var fbs = std.io.fixedBufferStream(src); | ||
| 193 | switch (try decodeFrameType(fbs.reader())) { | ||
| 194 | .zstandard => return decodeZstandardFrame(dest, src, verify_checksum), | ||
| 195 | .skippable => { | ||
| 196 | const content_size = try fbs.reader().readIntLittle(u32); | ||
| 197 | if (content_size > std.math.maxInt(usize) - 8) return error.SkippableSizeTooLarge; | ||
| 198 | const read_count = @as(usize, content_size) + 8; | ||
| 199 | if (read_count > src.len) return error.SkippableSizeTooLarge; | ||
| 200 | return ReadWriteCount{ | ||
| 201 | .read_count = read_count, | ||
| 202 | .write_count = 0, | ||
| 203 | }; | ||
| 204 | }, | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | /// Decodes the frame at the start of `src` into `dest`. Returns the number of | ||
| 209 | /// bytes read from `src`. | ||
| 210 | /// | ||
| 211 | /// Errors returned: | ||
| 212 | /// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic | ||
| 213 | /// number for a Zstandard or skippable frame | ||
| 214 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size | ||
| 215 | /// - `error.WindowTooLarge` if the window size is larger than | ||
| 216 | /// `window_size_max` | ||
| 217 | /// - `error.ContentSizeTooLarge` if the frame header indicates a content size | ||
| 218 | /// that is larger than `std.math.maxInt(usize)` | ||
| 219 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary | ||
| 220 | /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame | ||
| 221 | /// contains a checksum that does not match the checksum of the decompressed | ||
| 222 | /// data | ||
| 223 | /// - `error.ReservedBitSet` if any of the reserved bits of the frame header | ||
| 224 | /// are set | ||
| 225 | /// - `error.EndOfStream` if `src` does not contain a complete frame | ||
| 226 | /// - `error.BadContentSize` if the content size declared by the frame does | ||
| 227 | /// not equal the actual size of decompressed data | ||
| 228 | /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory | ||
| 229 | /// - an error in `block.Error` if there are errors decoding a block | ||
| 230 | /// - `error.SkippableSizeTooLarge` if the frame is skippable and reports a | ||
| 231 | /// size greater than `src.len` | ||
| 232 | pub fn decodeFrameArrayList( | ||
| 233 | allocator: Allocator, | ||
| 234 | dest: *std.ArrayList(u8), | ||
| 235 | src: []const u8, | ||
| 236 | verify_checksum: bool, | ||
| 237 | window_size_max: usize, | ||
| 238 | ) (error{ BadMagic, OutOfMemory, SkippableSizeTooLarge } || FrameContext.Error || FrameError)!usize { | ||
| 239 | var fbs = std.io.fixedBufferStream(src); | ||
| 240 | const reader = fbs.reader(); | ||
| 241 | const magic = try reader.readIntLittle(u32); | ||
| 242 | switch (try frameType(magic)) { | ||
| 243 | .zstandard => return decodeZstandardFrameArrayList( | ||
| 244 | allocator, | ||
| 245 | dest, | ||
| 246 | src, | ||
| 247 | verify_checksum, | ||
| 248 | window_size_max, | ||
| 249 | ), | ||
| 250 | .skippable => { | ||
| 251 | const content_size = try fbs.reader().readIntLittle(u32); | ||
| 252 | if (content_size > std.math.maxInt(usize) - 8) return error.SkippableSizeTooLarge; | ||
| 253 | const read_count = @as(usize, content_size) + 8; | ||
| 254 | if (read_count > src.len) return error.SkippableSizeTooLarge; | ||
| 255 | return read_count; | ||
| 256 | }, | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | /// Returns the frame checksum corresponding to the data fed into `hasher` | ||
| 261 | pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { | ||
| 262 | const hash = hasher.final(); | ||
| 263 | return @intCast(u32, hash & 0xFFFFFFFF); | ||
| 264 | } | ||
| 265 | |||
| 266 | const FrameError = error{ | ||
| 267 | ChecksumFailure, | ||
| 268 | BadContentSize, | ||
| 269 | EndOfStream, | ||
| 270 | ReservedBitSet, | ||
| 271 | } || block.Error; | ||
| 272 | |||
| 273 | /// Decode a Zstandard frame from `src` into `dest`, returning the number of | ||
| 274 | /// bytes read from `src` and written to `dest`. The first four bytes of `src` | ||
| 275 | /// must be the magic number for a Zstandard frame. | ||
| 276 | /// | ||
| 277 | /// Error returned: | ||
| 278 | /// - `error.UnknownContentSizeUnsupported` if the frame does not declare the | ||
| 279 | /// uncompressed content size | ||
| 280 | /// - `error.ContentTooLarge` if `dest` is smaller than the uncompressed data | ||
| 281 | /// size declared by the frame header | ||
| 282 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size | ||
| 283 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary | ||
| 284 | /// - `error.ContentSizeTooLarge` if the frame header indicates a content size | ||
| 285 | /// that is larger than `std.math.maxInt(usize)` | ||
| 286 | /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame | ||
| 287 | /// contains a checksum that does not match the checksum of the decompressed | ||
| 288 | /// data | ||
| 289 | /// - `error.ReservedBitSet` if the reserved bit of the frame header is set | ||
| 290 | /// - `error.EndOfStream` if `src` does not contain a complete frame | ||
| 291 | /// - an error in `block.Error` if there are errors decoding a block | ||
| 292 | /// - `error.BadContentSize` if the content size declared by the frame does | ||
| 293 | /// not equal the actual size of decompressed data | ||
| 294 | pub fn decodeZstandardFrame( | ||
| 295 | dest: []u8, | ||
| 296 | src: []const u8, | ||
| 297 | verify_checksum: bool, | ||
| 298 | ) (error{ | ||
| 299 | UnknownContentSizeUnsupported, | ||
| 300 | ContentTooLarge, | ||
| 301 | ContentSizeTooLarge, | ||
| 302 | WindowSizeUnknown, | ||
| 303 | DictionaryIdFlagUnsupported, | ||
| 304 | } || FrameError)!ReadWriteCount { | ||
| 305 | assert(readInt(u32, src[0..4]) == frame.Zstandard.magic_number); | ||
| 306 | var consumed_count: usize = 4; | ||
| 307 | |||
| 308 | var frame_context = context: { | ||
| 309 | var fbs = std.io.fixedBufferStream(src[consumed_count..]); | ||
| 310 | var source = fbs.reader(); | ||
| 311 | const frame_header = try decodeZstandardHeader(source); | ||
| 312 | consumed_count += fbs.pos; | ||
| 313 | break :context FrameContext.init( | ||
| 314 | frame_header, | ||
| 315 | std.math.maxInt(usize), | ||
| 316 | verify_checksum, | ||
| 317 | ) catch |err| switch (err) { | ||
| 318 | error.WindowTooLarge => unreachable, | ||
| 319 | inline else => |e| return e, | ||
| 320 | }; | ||
| 321 | }; | ||
| 322 | const counts = try decodeZStandardFrameBlocks( | ||
| 323 | dest, | ||
| 324 | src[consumed_count..], | ||
| 325 | &frame_context, | ||
| 326 | ); | ||
| 327 | return ReadWriteCount{ | ||
| 328 | .read_count = counts.read_count + consumed_count, | ||
| 329 | .write_count = counts.write_count, | ||
| 330 | }; | ||
| 331 | } | ||
| 332 | |||
| 333 | pub fn decodeZStandardFrameBlocks( | ||
| 334 | dest: []u8, | ||
| 335 | src: []const u8, | ||
| 336 | frame_context: *FrameContext, | ||
| 337 | ) (error{ ContentTooLarge, UnknownContentSizeUnsupported } || FrameError)!ReadWriteCount { | ||
| 338 | const content_size = frame_context.content_size orelse | ||
| 339 | return error.UnknownContentSizeUnsupported; | ||
| 340 | if (dest.len < content_size) return error.ContentTooLarge; | ||
| 341 | |||
| 342 | var consumed_count: usize = 0; | ||
| 343 | const written_count = decodeFrameBlocksInner( | ||
| 344 | dest[0..content_size], | ||
| 345 | src[consumed_count..], | ||
| 346 | &consumed_count, | ||
| 347 | if (frame_context.hasher_opt) |*hasher| hasher else null, | ||
| 348 | frame_context.block_size_max, | ||
| 349 | ) catch |err| switch (err) { | ||
| 350 | error.DestTooSmall => return error.BadContentSize, | ||
| 351 | inline else => |e| return e, | ||
| 352 | }; | ||
| 353 | |||
| 354 | if (written_count != content_size) return error.BadContentSize; | ||
| 355 | if (frame_context.has_checksum) { | ||
| 356 | if (src.len < consumed_count + 4) return error.EndOfStream; | ||
| 357 | const checksum = readIntSlice(u32, src[consumed_count .. consumed_count + 4]); | ||
| 358 | consumed_count += 4; | ||
| 359 | if (frame_context.hasher_opt) |*hasher| { | ||
| 360 | if (checksum != computeChecksum(hasher)) return error.ChecksumFailure; | ||
| 361 | } | ||
| 362 | } | ||
| 363 | return ReadWriteCount{ .read_count = consumed_count, .write_count = written_count }; | ||
| 364 | } | ||
| 365 | |||
| 366 | pub const FrameContext = struct { | ||
| 367 | hasher_opt: ?std.hash.XxHash64, | ||
| 368 | window_size: usize, | ||
| 369 | has_checksum: bool, | ||
| 370 | block_size_max: usize, | ||
| 371 | content_size: ?usize, | ||
| 372 | |||
| 373 | const Error = error{ | ||
| 374 | DictionaryIdFlagUnsupported, | ||
| 375 | WindowSizeUnknown, | ||
| 376 | WindowTooLarge, | ||
| 377 | ContentSizeTooLarge, | ||
| 378 | }; | ||
| 379 | /// Validates `frame_header` and returns the associated `FrameContext`. | ||
| 380 | /// | ||
| 381 | /// Errors returned: | ||
| 382 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary | ||
| 383 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window | ||
| 384 | /// size | ||
| 385 | /// - `error.WindowTooLarge` if the window size is larger than | ||
| 386 | /// `window_size_max` | ||
| 387 | /// - `error.ContentSizeTooLarge` if the frame header indicates a content | ||
| 388 | /// size larger than `std.math.maxInt(usize)` | ||
| 389 | pub fn init( | ||
| 390 | frame_header: ZstandardHeader, | ||
| 391 | window_size_max: usize, | ||
| 392 | verify_checksum: bool, | ||
| 393 | ) Error!FrameContext { | ||
| 394 | if (frame_header.descriptor.dictionary_id_flag != 0) | ||
| 395 | return error.DictionaryIdFlagUnsupported; | ||
| 396 | |||
| 397 | const window_size_raw = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; | ||
| 398 | const window_size = if (window_size_raw > window_size_max) | ||
| 399 | return error.WindowTooLarge | ||
| 400 | else | ||
| 401 | @intCast(usize, window_size_raw); | ||
| 402 | |||
| 403 | const should_compute_checksum = | ||
| 404 | frame_header.descriptor.content_checksum_flag and verify_checksum; | ||
| 405 | |||
| 406 | const content_size = if (frame_header.content_size) |size| | ||
| 407 | std.math.cast(usize, size) orelse return error.ContentSizeTooLarge | ||
| 408 | else | ||
| 409 | null; | ||
| 410 | |||
| 411 | return .{ | ||
| 412 | .hasher_opt = if (should_compute_checksum) std.hash.XxHash64.init(0) else null, | ||
| 413 | .window_size = window_size, | ||
| 414 | .has_checksum = frame_header.descriptor.content_checksum_flag, | ||
| 415 | .block_size_max = @min(1 << 17, window_size), | ||
| 416 | .content_size = content_size, | ||
| 417 | }; | ||
| 418 | } | ||
| 419 | }; | ||
| 420 | |||
| 421 | /// Decode a Zstandard from from `src` and return number of bytes read; see | ||
| 422 | /// `decodeZstandardFrame()`. The first four bytes of `src` must be the magic | ||
| 423 | /// number for a Zstandard frame. | ||
| 424 | /// | ||
| 425 | /// Errors returned: | ||
| 426 | /// - `error.WindowSizeUnknown` if the frame does not have a valid window size | ||
| 427 | /// - `error.WindowTooLarge` if the window size is larger than | ||
| 428 | /// `window_size_max` | ||
| 429 | /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary | ||
| 430 | /// - `error.ContentSizeTooLarge` if the frame header indicates a content size | ||
| 431 | /// that is larger than `std.math.maxInt(usize)` | ||
| 432 | /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame | ||
| 433 | /// contains a checksum that does not match the checksum of the decompressed | ||
| 434 | /// data | ||
| 435 | /// - `error.ReservedBitSet` if the reserved bit of the frame header is set | ||
| 436 | /// - `error.EndOfStream` if `src` does not contain a complete frame | ||
| 437 | /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory | ||
| 438 | /// - an error in `block.Error` if there are errors decoding a block | ||
| 439 | /// - `error.BadContentSize` if the content size declared by the frame does | ||
| 440 | /// not equal the size of decompressed data | ||
| 441 | pub fn decodeZstandardFrameArrayList( | ||
| 442 | allocator: Allocator, | ||
| 443 | dest: *std.ArrayList(u8), | ||
| 444 | src: []const u8, | ||
| 445 | verify_checksum: bool, | ||
| 446 | window_size_max: usize, | ||
| 447 | ) (error{OutOfMemory} || FrameContext.Error || FrameError)!usize { | ||
| 448 | assert(readInt(u32, src[0..4]) == frame.Zstandard.magic_number); | ||
| 449 | var consumed_count: usize = 4; | ||
| 450 | |||
| 451 | var frame_context = context: { | ||
| 452 | var fbs = std.io.fixedBufferStream(src[consumed_count..]); | ||
| 453 | var source = fbs.reader(); | ||
| 454 | const frame_header = try decodeZstandardHeader(source); | ||
| 455 | consumed_count += fbs.pos; | ||
| 456 | break :context try FrameContext.init(frame_header, window_size_max, verify_checksum); | ||
| 457 | }; | ||
| 458 | |||
| 459 | consumed_count += try decodeZstandardFrameBlocksArrayList( | ||
| 460 | allocator, | ||
| 461 | dest, | ||
| 462 | src[consumed_count..], | ||
| 463 | &frame_context, | ||
| 464 | ); | ||
| 465 | return consumed_count; | ||
| 466 | } | ||
| 467 | |||
| 468 | pub fn decodeZstandardFrameBlocksArrayList( | ||
| 469 | allocator: Allocator, | ||
| 470 | dest: *std.ArrayList(u8), | ||
| 471 | src: []const u8, | ||
| 472 | frame_context: *FrameContext, | ||
| 473 | ) (error{OutOfMemory} || FrameError)!usize { | ||
| 474 | const initial_len = dest.items.len; | ||
| 475 | |||
| 476 | var ring_buffer = try RingBuffer.init(allocator, frame_context.window_size); | ||
| 477 | defer ring_buffer.deinit(allocator); | ||
| 478 | |||
| 479 | // These tables take 7680 bytes | ||
| 480 | var literal_fse_data: [types.compressed_block.table_size_max.literal]Table.Fse = undefined; | ||
| 481 | var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; | ||
| 482 | var offset_fse_data: [types.compressed_block.table_size_max.offset]Table.Fse = undefined; | ||
| 483 | |||
| 484 | var block_header = try block.decodeBlockHeaderSlice(src); | ||
| 485 | var consumed_count: usize = 3; | ||
| 486 | var decode_state = block.DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); | ||
| 487 | while (true) : ({ | ||
| 488 | block_header = try block.decodeBlockHeaderSlice(src[consumed_count..]); | ||
| 489 | consumed_count += 3; | ||
| 490 | }) { | ||
| 491 | const written_size = try block.decodeBlockRingBuffer( | ||
| 492 | &ring_buffer, | ||
| 493 | src[consumed_count..], | ||
| 494 | block_header, | ||
| 495 | &decode_state, | ||
| 496 | &consumed_count, | ||
| 497 | frame_context.block_size_max, | ||
| 498 | ); | ||
| 499 | if (frame_context.content_size) |size| { | ||
| 500 | if (dest.items.len - initial_len > size) { | ||
| 501 | return error.BadContentSize; | ||
| 502 | } | ||
| 503 | } | ||
| 504 | if (written_size > 0) { | ||
| 505 | const written_slice = ring_buffer.sliceLast(written_size); | ||
| 506 | try dest.appendSlice(written_slice.first); | ||
| 507 | try dest.appendSlice(written_slice.second); | ||
| 508 | if (frame_context.hasher_opt) |*hasher| { | ||
| 509 | hasher.update(written_slice.first); | ||
| 510 | hasher.update(written_slice.second); | ||
| 511 | } | ||
| 512 | } | ||
| 513 | if (block_header.last_block) break; | ||
| 514 | } | ||
| 515 | if (frame_context.content_size) |size| { | ||
| 516 | if (dest.items.len - initial_len != size) { | ||
| 517 | return error.BadContentSize; | ||
| 518 | } | ||
| 519 | } | ||
| 520 | |||
| 521 | if (frame_context.has_checksum) { | ||
| 522 | if (src.len < consumed_count + 4) return error.EndOfStream; | ||
| 523 | const checksum = readIntSlice(u32, src[consumed_count .. consumed_count + 4]); | ||
| 524 | consumed_count += 4; | ||
| 525 | if (frame_context.hasher_opt) |*hasher| { | ||
| 526 | if (checksum != computeChecksum(hasher)) return error.ChecksumFailure; | ||
| 527 | } | ||
| 528 | } | ||
| 529 | return consumed_count; | ||
| 530 | } | ||
| 531 | |||
| 532 | fn decodeFrameBlocksInner( | ||
| 533 | dest: []u8, | ||
| 534 | src: []const u8, | ||
| 535 | consumed_count: *usize, | ||
| 536 | hash: ?*std.hash.XxHash64, | ||
| 537 | block_size_max: usize, | ||
| 538 | ) (error{ EndOfStream, DestTooSmall } || block.Error)!usize { | ||
| 539 | // These tables take 7680 bytes | ||
| 540 | var literal_fse_data: [types.compressed_block.table_size_max.literal]Table.Fse = undefined; | ||
| 541 | var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; | ||
| 542 | var offset_fse_data: [types.compressed_block.table_size_max.offset]Table.Fse = undefined; | ||
| 543 | |||
| 544 | var block_header = try block.decodeBlockHeaderSlice(src); | ||
| 545 | var bytes_read: usize = 3; | ||
| 546 | defer consumed_count.* += bytes_read; | ||
| 547 | var decode_state = block.DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); | ||
| 548 | var count: usize = 0; | ||
| 549 | while (true) : ({ | ||
| 550 | block_header = try block.decodeBlockHeaderSlice(src[bytes_read..]); | ||
| 551 | bytes_read += 3; | ||
| 552 | }) { | ||
| 553 | const written_size = try block.decodeBlock( | ||
| 554 | dest, | ||
| 555 | src[bytes_read..], | ||
| 556 | block_header, | ||
| 557 | &decode_state, | ||
| 558 | &bytes_read, | ||
| 559 | block_size_max, | ||
| 560 | count, | ||
| 561 | ); | ||
| 562 | if (hash) |hash_state| hash_state.update(dest[count .. count + written_size]); | ||
| 563 | count += written_size; | ||
| 564 | if (block_header.last_block) break; | ||
| 565 | } | ||
| 566 | return count; | ||
| 567 | } | ||
| 568 | |||
| 569 | /// Decode the header of a skippable frame. The first four bytes of `src` must | ||
| 570 | /// be a valid magic number for a skippable frame. | ||
| 571 | pub fn decodeSkippableHeader(src: *const [8]u8) SkippableHeader { | ||
| 572 | const magic = readInt(u32, src[0..4]); | ||
| 573 | assert(isSkippableMagic(magic)); | ||
| 574 | const frame_size = readInt(u32, src[4..8]); | ||
| 575 | return .{ | ||
| 576 | .magic_number = magic, | ||
| 577 | .frame_size = frame_size, | ||
| 578 | }; | ||
| 579 | } | ||
| 580 | |||
| 581 | /// Returns the window size required to decompress a frame, or `null` if it | ||
| 582 | /// cannot be determined (which indicates a malformed frame header). | ||
| 583 | pub fn frameWindowSize(header: ZstandardHeader) ?u64 { | ||
| 584 | if (header.window_descriptor) |descriptor| { | ||
| 585 | const exponent = (descriptor & 0b11111000) >> 3; | ||
| 586 | const mantissa = descriptor & 0b00000111; | ||
| 587 | const window_log = 10 + exponent; | ||
| 588 | const window_base = @as(u64, 1) << @intCast(u6, window_log); | ||
| 589 | const window_add = (window_base / 8) * mantissa; | ||
| 590 | return window_base + window_add; | ||
| 591 | } else return header.content_size; | ||
| 592 | } | ||
| 593 | |||
| 594 | /// Decode the header of a Zstandard frame. | ||
| 595 | /// | ||
| 596 | /// Errors returned: | ||
| 597 | /// - `error.ReservedBitSet` if any of the reserved bits of the header are set | ||
| 598 | /// - `error.EndOfStream` if `source` does not contain a complete header | ||
| 599 | pub fn decodeZstandardHeader( | ||
| 600 | source: anytype, | ||
| 601 | ) (@TypeOf(source).Error || error{ EndOfStream, ReservedBitSet })!ZstandardHeader { | ||
| 602 | const descriptor = @bitCast(ZstandardHeader.Descriptor, try source.readByte()); | ||
| 603 | |||
| 604 | if (descriptor.reserved) return error.ReservedBitSet; | ||
| 605 | |||
| 606 | var window_descriptor: ?u8 = null; | ||
| 607 | if (!descriptor.single_segment_flag) { | ||
| 608 | window_descriptor = try source.readByte(); | ||
| 609 | } | ||
| 610 | |||
| 611 | var dictionary_id: ?u32 = null; | ||
| 612 | if (descriptor.dictionary_id_flag > 0) { | ||
| 613 | // if flag is 3 then field_size = 4, else field_size = flag | ||
| 614 | const field_size = (@as(u4, 1) << descriptor.dictionary_id_flag) >> 1; | ||
| 615 | dictionary_id = try source.readVarInt(u32, .Little, field_size); | ||
| 616 | } | ||
| 617 | |||
| 618 | var content_size: ?u64 = null; | ||
| 619 | if (descriptor.single_segment_flag or descriptor.content_size_flag > 0) { | ||
| 620 | const field_size = @as(u4, 1) << descriptor.content_size_flag; | ||
| 621 | content_size = try source.readVarInt(u64, .Little, field_size); | ||
| 622 | if (field_size == 2) content_size.? += 256; | ||
| 623 | } | ||
| 624 | |||
| 625 | const header = ZstandardHeader{ | ||
| 626 | .descriptor = descriptor, | ||
| 627 | .window_descriptor = window_descriptor, | ||
| 628 | .dictionary_id = dictionary_id, | ||
| 629 | .content_size = content_size, | ||
| 630 | }; | ||
| 631 | return header; | ||
| 632 | } | ||
| 633 | |||
| 634 | test { | ||
| 635 | std.testing.refAllDecls(@This()); | ||
| 636 | } | ||
lib/std/compress/zstandard/readers.zig created+82| ... | @@ -0,0 +1,82 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub const ReversedByteReader = struct { | ||
| 4 | remaining_bytes: usize, | ||
| 5 | bytes: []const u8, | ||
| 6 | |||
| 7 | const Reader = std.io.Reader(*ReversedByteReader, error{}, readFn); | ||
| 8 | |||
| 9 | pub fn init(bytes: []const u8) ReversedByteReader { | ||
| 10 | return .{ | ||
| 11 | .bytes = bytes, | ||
| 12 | .remaining_bytes = bytes.len, | ||
| 13 | }; | ||
| 14 | } | ||
| 15 | |||
| 16 | pub fn reader(self: *ReversedByteReader) Reader { | ||
| 17 | return .{ .context = self }; | ||
| 18 | } | ||
| 19 | |||
| 20 | fn readFn(ctx: *ReversedByteReader, buffer: []u8) !usize { | ||
| 21 | if (ctx.remaining_bytes == 0) return 0; | ||
| 22 | const byte_index = ctx.remaining_bytes - 1; | ||
| 23 | buffer[0] = ctx.bytes[byte_index]; | ||
| 24 | // buffer[0] = @bitReverse(ctx.bytes[byte_index]); | ||
| 25 | ctx.remaining_bytes = byte_index; | ||
| 26 | return 1; | ||
| 27 | } | ||
| 28 | }; | ||
| 29 | |||
| 30 | /// A bit reader for reading the reversed bit streams used to encode | ||
| 31 | /// FSE compressed data. | ||
| 32 | pub const ReverseBitReader = struct { | ||
| 33 | byte_reader: ReversedByteReader, | ||
| 34 | bit_reader: std.io.BitReader(.Big, ReversedByteReader.Reader), | ||
| 35 | |||
| 36 | pub fn init(self: *ReverseBitReader, bytes: []const u8) error{BitStreamHasNoStartBit}!void { | ||
| 37 | self.byte_reader = ReversedByteReader.init(bytes); | ||
| 38 | self.bit_reader = std.io.bitReader(.Big, self.byte_reader.reader()); | ||
| 39 | if (bytes.len == 0) return; | ||
| 40 | var i: usize = 0; | ||
| 41 | while (i < 8 and 0 == self.readBitsNoEof(u1, 1) catch unreachable) : (i += 1) {} | ||
| 42 | if (i == 8) return error.BitStreamHasNoStartBit; | ||
| 43 | } | ||
| 44 | |||
| 45 | pub fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) error{EndOfStream}!U { | ||
| 46 | return self.bit_reader.readBitsNoEof(U, num_bits); | ||
| 47 | } | ||
| 48 | |||
| 49 | pub fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) error{}!U { | ||
| 50 | return try self.bit_reader.readBits(U, num_bits, out_bits); | ||
| 51 | } | ||
| 52 | |||
| 53 | pub fn alignToByte(self: *@This()) void { | ||
| 54 | self.bit_reader.alignToByte(); | ||
| 55 | } | ||
| 56 | |||
| 57 | pub fn isEmpty(self: ReverseBitReader) bool { | ||
| 58 | return self.byte_reader.remaining_bytes == 0 and self.bit_reader.bit_count == 0; | ||
| 59 | } | ||
| 60 | }; | ||
| 61 | |||
| 62 | pub fn BitReader(comptime Reader: type) type { | ||
| 63 | return struct { | ||
| 64 | underlying: std.io.BitReader(.Little, Reader), | ||
| 65 | |||
| 66 | pub fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) !U { | ||
| 67 | return self.underlying.readBitsNoEof(U, num_bits); | ||
| 68 | } | ||
| 69 | |||
| 70 | pub fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) !U { | ||
| 71 | return self.underlying.readBits(U, num_bits, out_bits); | ||
| 72 | } | ||
| 73 | |||
| 74 | pub fn alignToByte(self: *@This()) void { | ||
| 75 | self.underlying.alignToByte(); | ||
| 76 | } | ||
| 77 | }; | ||
| 78 | } | ||
| 79 | |||
| 80 | pub fn bitReader(reader: anytype) BitReader(@TypeOf(reader)) { | ||
| 81 | return .{ .underlying = std.io.bitReader(.Little, reader) }; | ||
| 82 | } | ||
lib/std/compress/zstandard/types.zig created+401| ... | @@ -0,0 +1,401 @@ | ||
| 1 | pub const frame = struct { | ||
| 2 | pub const Kind = enum { zstandard, skippable }; | ||
| 3 | |||
| 4 | pub const Zstandard = struct { | ||
| 5 | pub const magic_number = 0xFD2FB528; | ||
| 6 | |||
| 7 | header: Header, | ||
| 8 | data_blocks: []Block, | ||
| 9 | checksum: ?u32, | ||
| 10 | |||
| 11 | pub const Header = struct { | ||
| 12 | descriptor: Descriptor, | ||
| 13 | window_descriptor: ?u8, | ||
| 14 | dictionary_id: ?u32, | ||
| 15 | content_size: ?u64, | ||
| 16 | |||
| 17 | pub const Descriptor = packed struct { | ||
| 18 | dictionary_id_flag: u2, | ||
| 19 | content_checksum_flag: bool, | ||
| 20 | reserved: bool, | ||
| 21 | unused: bool, | ||
| 22 | single_segment_flag: bool, | ||
| 23 | content_size_flag: u2, | ||
| 24 | }; | ||
| 25 | }; | ||
| 26 | |||
| 27 | pub const Block = struct { | ||
| 28 | pub const Header = struct { | ||
| 29 | last_block: bool, | ||
| 30 | block_type: Block.Type, | ||
| 31 | block_size: u21, | ||
| 32 | }; | ||
| 33 | |||
| 34 | pub const Type = enum(u2) { | ||
| 35 | raw, | ||
| 36 | rle, | ||
| 37 | compressed, | ||
| 38 | reserved, | ||
| 39 | }; | ||
| 40 | }; | ||
| 41 | }; | ||
| 42 | |||
| 43 | pub const Skippable = struct { | ||
| 44 | pub const magic_number_min = 0x184D2A50; | ||
| 45 | pub const magic_number_max = 0x184D2A5F; | ||
| 46 | |||
| 47 | pub const Header = struct { | ||
| 48 | magic_number: u32, | ||
| 49 | frame_size: u32, | ||
| 50 | }; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | |||
| 54 | pub const compressed_block = struct { | ||
| 55 | pub const LiteralsSection = struct { | ||
| 56 | header: Header, | ||
| 57 | huffman_tree: ?HuffmanTree, | ||
| 58 | streams: Streams, | ||
| 59 | |||
| 60 | pub const Streams = union(enum) { | ||
| 61 | one: []const u8, | ||
| 62 | four: [4][]const u8, | ||
| 63 | }; | ||
| 64 | |||
| 65 | pub const Header = struct { | ||
| 66 | block_type: BlockType, | ||
| 67 | size_format: u2, | ||
| 68 | regenerated_size: u20, | ||
| 69 | compressed_size: ?u18, | ||
| 70 | }; | ||
| 71 | |||
| 72 | pub const BlockType = enum(u2) { | ||
| 73 | raw, | ||
| 74 | rle, | ||
| 75 | compressed, | ||
| 76 | treeless, | ||
| 77 | }; | ||
| 78 | |||
| 79 | pub const HuffmanTree = struct { | ||
| 80 | max_bit_count: u4, | ||
| 81 | symbol_count_minus_one: u8, | ||
| 82 | nodes: [256]PrefixedSymbol, | ||
| 83 | |||
| 84 | pub const PrefixedSymbol = struct { | ||
| 85 | symbol: u8, | ||
| 86 | prefix: u16, | ||
| 87 | weight: u4, | ||
| 88 | }; | ||
| 89 | |||
| 90 | pub const Result = union(enum) { | ||
| 91 | symbol: u8, | ||
| 92 | index: usize, | ||
| 93 | }; | ||
| 94 | |||
| 95 | pub fn query(self: HuffmanTree, index: usize, prefix: u16) error{NotFound}!Result { | ||
| 96 | var node = self.nodes[index]; | ||
| 97 | const weight = node.weight; | ||
| 98 | var i: usize = index; | ||
| 99 | while (node.weight == weight) { | ||
| 100 | if (node.prefix == prefix) return Result{ .symbol = node.symbol }; | ||
| 101 | if (i == 0) return error.NotFound; | ||
| 102 | i -= 1; | ||
| 103 | node = self.nodes[i]; | ||
| 104 | } | ||
| 105 | return Result{ .index = i }; | ||
| 106 | } | ||
| 107 | |||
| 108 | pub fn weightToBitCount(weight: u4, max_bit_count: u4) u4 { | ||
| 109 | return if (weight == 0) 0 else ((max_bit_count + 1) - weight); | ||
| 110 | } | ||
| 111 | }; | ||
| 112 | |||
| 113 | pub const StreamCount = enum { one, four }; | ||
| 114 | pub fn streamCount(size_format: u2, block_type: BlockType) StreamCount { | ||
| 115 | return switch (block_type) { | ||
| 116 | .raw, .rle => .one, | ||
| 117 | .compressed, .treeless => if (size_format == 0) .one else .four, | ||
| 118 | }; | ||
| 119 | } | ||
| 120 | }; | ||
| 121 | |||
| 122 | pub const SequencesSection = struct { | ||
| 123 | header: SequencesSection.Header, | ||
| 124 | literals_length_table: Table, | ||
| 125 | offset_table: Table, | ||
| 126 | match_length_table: Table, | ||
| 127 | |||
| 128 | pub const Header = struct { | ||
| 129 | sequence_count: u24, | ||
| 130 | match_lengths: Mode, | ||
| 131 | offsets: Mode, | ||
| 132 | literal_lengths: Mode, | ||
| 133 | |||
| 134 | pub const Mode = enum(u2) { | ||
| 135 | predefined, | ||
| 136 | rle, | ||
| 137 | fse, | ||
| 138 | repeat, | ||
| 139 | }; | ||
| 140 | }; | ||
| 141 | }; | ||
| 142 | |||
| 143 | pub const Table = union(enum) { | ||
| 144 | fse: []const Fse, | ||
| 145 | rle: u8, | ||
| 146 | |||
| 147 | pub const Fse = struct { | ||
| 148 | symbol: u8, | ||
| 149 | baseline: u16, | ||
| 150 | bits: u8, | ||
| 151 | }; | ||
| 152 | }; | ||
| 153 | |||
| 154 | pub const literals_length_code_table = [36]struct { u32, u5 }{ | ||
| 155 | .{ 0, 0 }, .{ 1, 0 }, .{ 2, 0 }, .{ 3, 0 }, | ||
| 156 | .{ 4, 0 }, .{ 5, 0 }, .{ 6, 0 }, .{ 7, 0 }, | ||
| 157 | .{ 8, 0 }, .{ 9, 0 }, .{ 10, 0 }, .{ 11, 0 }, | ||
| 158 | .{ 12, 0 }, .{ 13, 0 }, .{ 14, 0 }, .{ 15, 0 }, | ||
| 159 | .{ 16, 1 }, .{ 18, 1 }, .{ 20, 1 }, .{ 22, 1 }, | ||
| 160 | .{ 24, 2 }, .{ 28, 2 }, .{ 32, 3 }, .{ 40, 3 }, | ||
| 161 | .{ 48, 4 }, .{ 64, 6 }, .{ 128, 7 }, .{ 256, 8 }, | ||
| 162 | .{ 512, 9 }, .{ 1024, 10 }, .{ 2048, 11 }, .{ 4096, 12 }, | ||
| 163 | .{ 8192, 13 }, .{ 16384, 14 }, .{ 32768, 15 }, .{ 65536, 16 }, | ||
| 164 | }; | ||
| 165 | |||
| 166 | pub const match_length_code_table = [53]struct { u32, u5 }{ | ||
| 167 | .{ 3, 0 }, .{ 4, 0 }, .{ 5, 0 }, .{ 6, 0 }, .{ 7, 0 }, .{ 8, 0 }, | ||
| 168 | .{ 9, 0 }, .{ 10, 0 }, .{ 11, 0 }, .{ 12, 0 }, .{ 13, 0 }, .{ 14, 0 }, | ||
| 169 | .{ 15, 0 }, .{ 16, 0 }, .{ 17, 0 }, .{ 18, 0 }, .{ 19, 0 }, .{ 20, 0 }, | ||
| 170 | .{ 21, 0 }, .{ 22, 0 }, .{ 23, 0 }, .{ 24, 0 }, .{ 25, 0 }, .{ 26, 0 }, | ||
| 171 | .{ 27, 0 }, .{ 28, 0 }, .{ 29, 0 }, .{ 30, 0 }, .{ 31, 0 }, .{ 32, 0 }, | ||
| 172 | .{ 33, 0 }, .{ 34, 0 }, .{ 35, 1 }, .{ 37, 1 }, .{ 39, 1 }, .{ 41, 1 }, | ||
| 173 | .{ 43, 2 }, .{ 47, 2 }, .{ 51, 3 }, .{ 59, 3 }, .{ 67, 4 }, .{ 83, 4 }, | ||
| 174 | .{ 99, 5 }, .{ 131, 7 }, .{ 259, 8 }, .{ 515, 9 }, .{ 1027, 10 }, .{ 2051, 11 }, | ||
| 175 | .{ 4099, 12 }, .{ 8195, 13 }, .{ 16387, 14 }, .{ 32771, 15 }, .{ 65539, 16 }, | ||
| 176 | }; | ||
| 177 | |||
| 178 | pub const literals_length_default_distribution = [36]i16{ | ||
| 179 | 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, | ||
| 180 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, | ||
| 181 | -1, -1, -1, -1, | ||
| 182 | }; | ||
| 183 | |||
| 184 | pub const match_lengths_default_distribution = [53]i16{ | ||
| 185 | 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, | ||
| 186 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 187 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, | ||
| 188 | -1, -1, -1, -1, -1, | ||
| 189 | }; | ||
| 190 | |||
| 191 | pub const offset_codes_default_distribution = [29]i16{ | ||
| 192 | 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, | ||
| 193 | 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, | ||
| 194 | }; | ||
| 195 | |||
| 196 | pub const predefined_literal_fse_table = Table{ | ||
| 197 | .fse = &[64]Table.Fse{ | ||
| 198 | .{ .symbol = 0, .bits = 4, .baseline = 0 }, | ||
| 199 | .{ .symbol = 0, .bits = 4, .baseline = 16 }, | ||
| 200 | .{ .symbol = 1, .bits = 5, .baseline = 32 }, | ||
| 201 | .{ .symbol = 3, .bits = 5, .baseline = 0 }, | ||
| 202 | .{ .symbol = 4, .bits = 5, .baseline = 0 }, | ||
| 203 | .{ .symbol = 6, .bits = 5, .baseline = 0 }, | ||
| 204 | .{ .symbol = 7, .bits = 5, .baseline = 0 }, | ||
| 205 | .{ .symbol = 9, .bits = 5, .baseline = 0 }, | ||
| 206 | .{ .symbol = 10, .bits = 5, .baseline = 0 }, | ||
| 207 | .{ .symbol = 12, .bits = 5, .baseline = 0 }, | ||
| 208 | .{ .symbol = 14, .bits = 6, .baseline = 0 }, | ||
| 209 | .{ .symbol = 16, .bits = 5, .baseline = 0 }, | ||
| 210 | .{ .symbol = 18, .bits = 5, .baseline = 0 }, | ||
| 211 | .{ .symbol = 19, .bits = 5, .baseline = 0 }, | ||
| 212 | .{ .symbol = 21, .bits = 5, .baseline = 0 }, | ||
| 213 | .{ .symbol = 22, .bits = 5, .baseline = 0 }, | ||
| 214 | .{ .symbol = 24, .bits = 5, .baseline = 0 }, | ||
| 215 | .{ .symbol = 25, .bits = 5, .baseline = 32 }, | ||
| 216 | .{ .symbol = 26, .bits = 5, .baseline = 0 }, | ||
| 217 | .{ .symbol = 27, .bits = 6, .baseline = 0 }, | ||
| 218 | .{ .symbol = 29, .bits = 6, .baseline = 0 }, | ||
| 219 | .{ .symbol = 31, .bits = 6, .baseline = 0 }, | ||
| 220 | .{ .symbol = 0, .bits = 4, .baseline = 32 }, | ||
| 221 | .{ .symbol = 1, .bits = 4, .baseline = 0 }, | ||
| 222 | .{ .symbol = 2, .bits = 5, .baseline = 0 }, | ||
| 223 | .{ .symbol = 4, .bits = 5, .baseline = 32 }, | ||
| 224 | .{ .symbol = 5, .bits = 5, .baseline = 0 }, | ||
| 225 | .{ .symbol = 7, .bits = 5, .baseline = 32 }, | ||
| 226 | .{ .symbol = 8, .bits = 5, .baseline = 0 }, | ||
| 227 | .{ .symbol = 10, .bits = 5, .baseline = 32 }, | ||
| 228 | .{ .symbol = 11, .bits = 5, .baseline = 0 }, | ||
| 229 | .{ .symbol = 13, .bits = 6, .baseline = 0 }, | ||
| 230 | .{ .symbol = 16, .bits = 5, .baseline = 32 }, | ||
| 231 | .{ .symbol = 17, .bits = 5, .baseline = 0 }, | ||
| 232 | .{ .symbol = 19, .bits = 5, .baseline = 32 }, | ||
| 233 | .{ .symbol = 20, .bits = 5, .baseline = 0 }, | ||
| 234 | .{ .symbol = 22, .bits = 5, .baseline = 32 }, | ||
| 235 | .{ .symbol = 23, .bits = 5, .baseline = 0 }, | ||
| 236 | .{ .symbol = 25, .bits = 4, .baseline = 0 }, | ||
| 237 | .{ .symbol = 25, .bits = 4, .baseline = 16 }, | ||
| 238 | .{ .symbol = 26, .bits = 5, .baseline = 32 }, | ||
| 239 | .{ .symbol = 28, .bits = 6, .baseline = 0 }, | ||
| 240 | .{ .symbol = 30, .bits = 6, .baseline = 0 }, | ||
| 241 | .{ .symbol = 0, .bits = 4, .baseline = 48 }, | ||
| 242 | .{ .symbol = 1, .bits = 4, .baseline = 16 }, | ||
| 243 | .{ .symbol = 2, .bits = 5, .baseline = 32 }, | ||
| 244 | .{ .symbol = 3, .bits = 5, .baseline = 32 }, | ||
| 245 | .{ .symbol = 5, .bits = 5, .baseline = 32 }, | ||
| 246 | .{ .symbol = 6, .bits = 5, .baseline = 32 }, | ||
| 247 | .{ .symbol = 8, .bits = 5, .baseline = 32 }, | ||
| 248 | .{ .symbol = 9, .bits = 5, .baseline = 32 }, | ||
| 249 | .{ .symbol = 11, .bits = 5, .baseline = 32 }, | ||
| 250 | .{ .symbol = 12, .bits = 5, .baseline = 32 }, | ||
| 251 | .{ .symbol = 15, .bits = 6, .baseline = 0 }, | ||
| 252 | .{ .symbol = 17, .bits = 5, .baseline = 32 }, | ||
| 253 | .{ .symbol = 18, .bits = 5, .baseline = 32 }, | ||
| 254 | .{ .symbol = 20, .bits = 5, .baseline = 32 }, | ||
| 255 | .{ .symbol = 21, .bits = 5, .baseline = 32 }, | ||
| 256 | .{ .symbol = 23, .bits = 5, .baseline = 32 }, | ||
| 257 | .{ .symbol = 24, .bits = 5, .baseline = 32 }, | ||
| 258 | .{ .symbol = 35, .bits = 6, .baseline = 0 }, | ||
| 259 | .{ .symbol = 34, .bits = 6, .baseline = 0 }, | ||
| 260 | .{ .symbol = 33, .bits = 6, .baseline = 0 }, | ||
| 261 | .{ .symbol = 32, .bits = 6, .baseline = 0 }, | ||
| 262 | }, | ||
| 263 | }; | ||
| 264 | |||
| 265 | pub const predefined_match_fse_table = Table{ | ||
| 266 | .fse = &[64]Table.Fse{ | ||
| 267 | .{ .symbol = 0, .bits = 6, .baseline = 0 }, | ||
| 268 | .{ .symbol = 1, .bits = 4, .baseline = 0 }, | ||
| 269 | .{ .symbol = 2, .bits = 5, .baseline = 32 }, | ||
| 270 | .{ .symbol = 3, .bits = 5, .baseline = 0 }, | ||
| 271 | .{ .symbol = 5, .bits = 5, .baseline = 0 }, | ||
| 272 | .{ .symbol = 6, .bits = 5, .baseline = 0 }, | ||
| 273 | .{ .symbol = 8, .bits = 5, .baseline = 0 }, | ||
| 274 | .{ .symbol = 10, .bits = 6, .baseline = 0 }, | ||
| 275 | .{ .symbol = 13, .bits = 6, .baseline = 0 }, | ||
| 276 | .{ .symbol = 16, .bits = 6, .baseline = 0 }, | ||
| 277 | .{ .symbol = 19, .bits = 6, .baseline = 0 }, | ||
| 278 | .{ .symbol = 22, .bits = 6, .baseline = 0 }, | ||
| 279 | .{ .symbol = 25, .bits = 6, .baseline = 0 }, | ||
| 280 | .{ .symbol = 28, .bits = 6, .baseline = 0 }, | ||
| 281 | .{ .symbol = 31, .bits = 6, .baseline = 0 }, | ||
| 282 | .{ .symbol = 33, .bits = 6, .baseline = 0 }, | ||
| 283 | .{ .symbol = 35, .bits = 6, .baseline = 0 }, | ||
| 284 | .{ .symbol = 37, .bits = 6, .baseline = 0 }, | ||
| 285 | .{ .symbol = 39, .bits = 6, .baseline = 0 }, | ||
| 286 | .{ .symbol = 41, .bits = 6, .baseline = 0 }, | ||
| 287 | .{ .symbol = 43, .bits = 6, .baseline = 0 }, | ||
| 288 | .{ .symbol = 45, .bits = 6, .baseline = 0 }, | ||
| 289 | .{ .symbol = 1, .bits = 4, .baseline = 16 }, | ||
| 290 | .{ .symbol = 2, .bits = 4, .baseline = 0 }, | ||
| 291 | .{ .symbol = 3, .bits = 5, .baseline = 32 }, | ||
| 292 | .{ .symbol = 4, .bits = 5, .baseline = 0 }, | ||
| 293 | .{ .symbol = 6, .bits = 5, .baseline = 32 }, | ||
| 294 | .{ .symbol = 7, .bits = 5, .baseline = 0 }, | ||
| 295 | .{ .symbol = 9, .bits = 6, .baseline = 0 }, | ||
| 296 | .{ .symbol = 12, .bits = 6, .baseline = 0 }, | ||
| 297 | .{ .symbol = 15, .bits = 6, .baseline = 0 }, | ||
| 298 | .{ .symbol = 18, .bits = 6, .baseline = 0 }, | ||
| 299 | .{ .symbol = 21, .bits = 6, .baseline = 0 }, | ||
| 300 | .{ .symbol = 24, .bits = 6, .baseline = 0 }, | ||
| 301 | .{ .symbol = 27, .bits = 6, .baseline = 0 }, | ||
| 302 | .{ .symbol = 30, .bits = 6, .baseline = 0 }, | ||
| 303 | .{ .symbol = 32, .bits = 6, .baseline = 0 }, | ||
| 304 | .{ .symbol = 34, .bits = 6, .baseline = 0 }, | ||
| 305 | .{ .symbol = 36, .bits = 6, .baseline = 0 }, | ||
| 306 | .{ .symbol = 38, .bits = 6, .baseline = 0 }, | ||
| 307 | .{ .symbol = 40, .bits = 6, .baseline = 0 }, | ||
| 308 | .{ .symbol = 42, .bits = 6, .baseline = 0 }, | ||
| 309 | .{ .symbol = 44, .bits = 6, .baseline = 0 }, | ||
| 310 | .{ .symbol = 1, .bits = 4, .baseline = 32 }, | ||
| 311 | .{ .symbol = 1, .bits = 4, .baseline = 48 }, | ||
| 312 | .{ .symbol = 2, .bits = 4, .baseline = 16 }, | ||
| 313 | .{ .symbol = 4, .bits = 5, .baseline = 32 }, | ||
| 314 | .{ .symbol = 5, .bits = 5, .baseline = 32 }, | ||
| 315 | .{ .symbol = 7, .bits = 5, .baseline = 32 }, | ||
| 316 | .{ .symbol = 8, .bits = 5, .baseline = 32 }, | ||
| 317 | .{ .symbol = 11, .bits = 6, .baseline = 0 }, | ||
| 318 | .{ .symbol = 14, .bits = 6, .baseline = 0 }, | ||
| 319 | .{ .symbol = 17, .bits = 6, .baseline = 0 }, | ||
| 320 | .{ .symbol = 20, .bits = 6, .baseline = 0 }, | ||
| 321 | .{ .symbol = 23, .bits = 6, .baseline = 0 }, | ||
| 322 | .{ .symbol = 26, .bits = 6, .baseline = 0 }, | ||
| 323 | .{ .symbol = 29, .bits = 6, .baseline = 0 }, | ||
| 324 | .{ .symbol = 52, .bits = 6, .baseline = 0 }, | ||
| 325 | .{ .symbol = 51, .bits = 6, .baseline = 0 }, | ||
| 326 | .{ .symbol = 50, .bits = 6, .baseline = 0 }, | ||
| 327 | .{ .symbol = 49, .bits = 6, .baseline = 0 }, | ||
| 328 | .{ .symbol = 48, .bits = 6, .baseline = 0 }, | ||
| 329 | .{ .symbol = 47, .bits = 6, .baseline = 0 }, | ||
| 330 | .{ .symbol = 46, .bits = 6, .baseline = 0 }, | ||
| 331 | }, | ||
| 332 | }; | ||
| 333 | |||
| 334 | pub const predefined_offset_fse_table = Table{ | ||
| 335 | .fse = &[32]Table.Fse{ | ||
| 336 | .{ .symbol = 0, .bits = 5, .baseline = 0 }, | ||
| 337 | .{ .symbol = 6, .bits = 4, .baseline = 0 }, | ||
| 338 | .{ .symbol = 9, .bits = 5, .baseline = 0 }, | ||
| 339 | .{ .symbol = 15, .bits = 5, .baseline = 0 }, | ||
| 340 | .{ .symbol = 21, .bits = 5, .baseline = 0 }, | ||
| 341 | .{ .symbol = 3, .bits = 5, .baseline = 0 }, | ||
| 342 | .{ .symbol = 7, .bits = 4, .baseline = 0 }, | ||
| 343 | .{ .symbol = 12, .bits = 5, .baseline = 0 }, | ||
| 344 | .{ .symbol = 18, .bits = 5, .baseline = 0 }, | ||
| 345 | .{ .symbol = 23, .bits = 5, .baseline = 0 }, | ||
| 346 | .{ .symbol = 5, .bits = 5, .baseline = 0 }, | ||
| 347 | .{ .symbol = 8, .bits = 4, .baseline = 0 }, | ||
| 348 | .{ .symbol = 14, .bits = 5, .baseline = 0 }, | ||
| 349 | .{ .symbol = 20, .bits = 5, .baseline = 0 }, | ||
| 350 | .{ .symbol = 2, .bits = 5, .baseline = 0 }, | ||
| 351 | .{ .symbol = 7, .bits = 4, .baseline = 16 }, | ||
| 352 | .{ .symbol = 11, .bits = 5, .baseline = 0 }, | ||
| 353 | .{ .symbol = 17, .bits = 5, .baseline = 0 }, | ||
| 354 | .{ .symbol = 22, .bits = 5, .baseline = 0 }, | ||
| 355 | .{ .symbol = 4, .bits = 5, .baseline = 0 }, | ||
| 356 | .{ .symbol = 8, .bits = 4, .baseline = 16 }, | ||
| 357 | .{ .symbol = 13, .bits = 5, .baseline = 0 }, | ||
| 358 | .{ .symbol = 19, .bits = 5, .baseline = 0 }, | ||
| 359 | .{ .symbol = 1, .bits = 5, .baseline = 0 }, | ||
| 360 | .{ .symbol = 6, .bits = 4, .baseline = 16 }, | ||
| 361 | .{ .symbol = 10, .bits = 5, .baseline = 0 }, | ||
| 362 | .{ .symbol = 16, .bits = 5, .baseline = 0 }, | ||
| 363 | .{ .symbol = 28, .bits = 5, .baseline = 0 }, | ||
| 364 | .{ .symbol = 27, .bits = 5, .baseline = 0 }, | ||
| 365 | .{ .symbol = 26, .bits = 5, .baseline = 0 }, | ||
| 366 | .{ .symbol = 25, .bits = 5, .baseline = 0 }, | ||
| 367 | .{ .symbol = 24, .bits = 5, .baseline = 0 }, | ||
| 368 | }, | ||
| 369 | }; | ||
| 370 | pub const start_repeated_offset_1 = 1; | ||
| 371 | pub const start_repeated_offset_2 = 4; | ||
| 372 | pub const start_repeated_offset_3 = 8; | ||
| 373 | |||
| 374 | pub const table_accuracy_log_max = struct { | ||
| 375 | pub const literal = 9; | ||
| 376 | pub const match = 9; | ||
| 377 | pub const offset = 8; | ||
| 378 | }; | ||
| 379 | |||
| 380 | pub const table_symbol_count_max = struct { | ||
| 381 | pub const literal = 36; | ||
| 382 | pub const match = 53; | ||
| 383 | pub const offset = 32; | ||
| 384 | }; | ||
| 385 | |||
| 386 | pub const default_accuracy_log = struct { | ||
| 387 | pub const literal = 6; | ||
| 388 | pub const match = 6; | ||
| 389 | pub const offset = 5; | ||
| 390 | }; | ||
| 391 | pub const table_size_max = struct { | ||
| 392 | pub const literal = 1 << table_accuracy_log_max.literal; | ||
| 393 | pub const match = 1 << table_accuracy_log_max.match; | ||
| 394 | pub const offset = 1 << table_accuracy_log_max.match; | ||
| 395 | }; | ||
| 396 | }; | ||
| 397 | |||
| 398 | test { | ||
| 399 | const testing = @import("std").testing; | ||
| 400 | testing.refAllDeclsRecursive(@This()); | ||
| 401 | } | ||
lib/std/crypto/benchmark.zig+1-1| ... | @@ -178,7 +178,7 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime | ... | @@ -178,7 +178,7 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime |
| 178 | const sig = try key_pair.sign(&msg, null); | 178 | const sig = try key_pair.sign(&msg, null); |
| 179 | 179 | ||
| 180 | var batch: [64]Signature.BatchElement = undefined; | 180 | var batch: [64]Signature.BatchElement = undefined; |
| 181 | for (batch) |*element| { | 181 | for (&batch) |*element| { |
| 182 | element.* = Signature.BatchElement{ .sig = sig, .msg = &msg, .public_key = key_pair.public_key }; | 182 | element.* = Signature.BatchElement{ .sig = sig, .msg = &msg, .public_key = key_pair.public_key }; |
| 183 | } | 183 | } |
| 184 | 184 |
lib/std/hash.zig+5| ... | @@ -32,6 +32,10 @@ pub const CityHash64 = cityhash.CityHash64; | ... | @@ -32,6 +32,10 @@ pub const CityHash64 = cityhash.CityHash64; |
| 32 | const wyhash = @import("hash/wyhash.zig"); | 32 | const wyhash = @import("hash/wyhash.zig"); |
| 33 | pub const Wyhash = wyhash.Wyhash; | 33 | pub const Wyhash = wyhash.Wyhash; |
| 34 | 34 | ||
| 35 | const xxhash = @import("hash/xxhash.zig"); | ||
| 36 | pub const XxHash64 = xxhash.XxHash64; | ||
| 37 | pub const XxHash32 = xxhash.XxHash32; | ||
| 38 | |||
| 35 | test "hash" { | 39 | test "hash" { |
| 36 | _ = adler; | 40 | _ = adler; |
| 37 | _ = auto_hash; | 41 | _ = auto_hash; |
| ... | @@ -40,4 +44,5 @@ test "hash" { | ... | @@ -40,4 +44,5 @@ test "hash" { |
| 40 | _ = murmur; | 44 | _ = murmur; |
| 41 | _ = cityhash; | 45 | _ = cityhash; |
| 42 | _ = wyhash; | 46 | _ = wyhash; |
| 47 | _ = xxhash; | ||
| 43 | } | 48 | } |
lib/std/hash/xxhash.zig created+268| ... | @@ -0,0 +1,268 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const mem = std.mem; | ||
| 3 | const expectEqual = std.testing.expectEqual; | ||
| 4 | |||
| 5 | const rotl = std.math.rotl; | ||
| 6 | |||
| 7 | pub const XxHash64 = struct { | ||
| 8 | acc1: u64, | ||
| 9 | acc2: u64, | ||
| 10 | acc3: u64, | ||
| 11 | acc4: u64, | ||
| 12 | |||
| 13 | seed: u64, | ||
| 14 | buf: [32]u8, | ||
| 15 | buf_len: usize, | ||
| 16 | byte_count: usize, | ||
| 17 | |||
| 18 | const prime_1 = 0x9E3779B185EBCA87; // 0b1001111000110111011110011011000110000101111010111100101010000111 | ||
| 19 | const prime_2 = 0xC2B2AE3D27D4EB4F; // 0b1100001010110010101011100011110100100111110101001110101101001111 | ||
| 20 | const prime_3 = 0x165667B19E3779F9; // 0b0001011001010110011001111011000110011110001101110111100111111001 | ||
| 21 | const prime_4 = 0x85EBCA77C2B2AE63; // 0b1000010111101011110010100111011111000010101100101010111001100011 | ||
| 22 | const prime_5 = 0x27D4EB2F165667C5; // 0b0010011111010100111010110010111100010110010101100110011111000101 | ||
| 23 | |||
| 24 | pub fn init(seed: u64) XxHash64 { | ||
| 25 | return XxHash64{ | ||
| 26 | .seed = seed, | ||
| 27 | .acc1 = seed +% prime_1 +% prime_2, | ||
| 28 | .acc2 = seed +% prime_2, | ||
| 29 | .acc3 = seed, | ||
| 30 | .acc4 = seed -% prime_1, | ||
| 31 | .buf = undefined, | ||
| 32 | .buf_len = 0, | ||
| 33 | .byte_count = 0, | ||
| 34 | }; | ||
| 35 | } | ||
| 36 | |||
| 37 | pub fn update(self: *XxHash64, input: []const u8) void { | ||
| 38 | if (input.len < 32 - self.buf_len) { | ||
| 39 | mem.copy(u8, self.buf[self.buf_len..], input); | ||
| 40 | self.buf_len += input.len; | ||
| 41 | return; | ||
| 42 | } | ||
| 43 | |||
| 44 | var i: usize = 0; | ||
| 45 | |||
| 46 | if (self.buf_len > 0) { | ||
| 47 | i = 32 - self.buf_len; | ||
| 48 | mem.copy(u8, self.buf[self.buf_len..], input[0..i]); | ||
| 49 | self.processStripe(&self.buf); | ||
| 50 | self.buf_len = 0; | ||
| 51 | } | ||
| 52 | |||
| 53 | while (i + 32 <= input.len) : (i += 32) { | ||
| 54 | self.processStripe(input[i..][0..32]); | ||
| 55 | } | ||
| 56 | |||
| 57 | const remaining_bytes = input[i..]; | ||
| 58 | mem.copy(u8, &self.buf, remaining_bytes); | ||
| 59 | self.buf_len = remaining_bytes.len; | ||
| 60 | } | ||
| 61 | |||
| 62 | inline fn processStripe(self: *XxHash64, buf: *const [32]u8) void { | ||
| 63 | self.acc1 = round(self.acc1, mem.readIntLittle(u64, buf[0..8])); | ||
| 64 | self.acc2 = round(self.acc2, mem.readIntLittle(u64, buf[8..16])); | ||
| 65 | self.acc3 = round(self.acc3, mem.readIntLittle(u64, buf[16..24])); | ||
| 66 | self.acc4 = round(self.acc4, mem.readIntLittle(u64, buf[24..32])); | ||
| 67 | self.byte_count += 32; | ||
| 68 | } | ||
| 69 | |||
| 70 | inline fn round(acc: u64, lane: u64) u64 { | ||
| 71 | const a = acc +% (lane *% prime_2); | ||
| 72 | const b = rotl(u64, a, 31); | ||
| 73 | return b *% prime_1; | ||
| 74 | } | ||
| 75 | |||
| 76 | pub fn final(self: *XxHash64) u64 { | ||
| 77 | var acc: u64 = undefined; | ||
| 78 | |||
| 79 | if (self.byte_count < 32) { | ||
| 80 | acc = self.seed +% prime_5; | ||
| 81 | } else { | ||
| 82 | acc = rotl(u64, self.acc1, 1) +% rotl(u64, self.acc2, 7) +% | ||
| 83 | rotl(u64, self.acc3, 12) +% rotl(u64, self.acc4, 18); | ||
| 84 | acc = mergeAccumulator(acc, self.acc1); | ||
| 85 | acc = mergeAccumulator(acc, self.acc2); | ||
| 86 | acc = mergeAccumulator(acc, self.acc3); | ||
| 87 | acc = mergeAccumulator(acc, self.acc4); | ||
| 88 | } | ||
| 89 | |||
| 90 | acc = acc +% @as(u64, self.byte_count) +% @as(u64, self.buf_len); | ||
| 91 | |||
| 92 | var pos: usize = 0; | ||
| 93 | while (pos + 8 <= self.buf_len) : (pos += 8) { | ||
| 94 | const lane = mem.readIntLittle(u64, self.buf[pos..][0..8]); | ||
| 95 | acc ^= round(0, lane); | ||
| 96 | acc = rotl(u64, acc, 27) *% prime_1; | ||
| 97 | acc +%= prime_4; | ||
| 98 | } | ||
| 99 | |||
| 100 | if (pos + 4 <= self.buf_len) { | ||
| 101 | const lane = @as(u64, mem.readIntLittle(u32, self.buf[pos..][0..4])); | ||
| 102 | acc ^= lane *% prime_1; | ||
| 103 | acc = rotl(u64, acc, 23) *% prime_2; | ||
| 104 | acc +%= prime_3; | ||
| 105 | pos += 4; | ||
| 106 | } | ||
| 107 | |||
| 108 | while (pos < self.buf_len) : (pos += 1) { | ||
| 109 | const lane = @as(u64, self.buf[pos]); | ||
| 110 | acc ^= lane *% prime_5; | ||
| 111 | acc = rotl(u64, acc, 11) *% prime_1; | ||
| 112 | } | ||
| 113 | |||
| 114 | acc ^= acc >> 33; | ||
| 115 | acc *%= prime_2; | ||
| 116 | acc ^= acc >> 29; | ||
| 117 | acc *%= prime_3; | ||
| 118 | acc ^= acc >> 32; | ||
| 119 | |||
| 120 | return acc; | ||
| 121 | } | ||
| 122 | |||
| 123 | inline fn mergeAccumulator(acc: u64, other: u64) u64 { | ||
| 124 | const a = acc ^ round(0, other); | ||
| 125 | const b = a *% prime_1; | ||
| 126 | return b +% prime_4; | ||
| 127 | } | ||
| 128 | |||
| 129 | pub fn hash(input: []const u8) u64 { | ||
| 130 | var hasher = XxHash64.init(0); | ||
| 131 | hasher.update(input); | ||
| 132 | return hasher.final(); | ||
| 133 | } | ||
| 134 | }; | ||
| 135 | |||
| 136 | pub const XxHash32 = struct { | ||
| 137 | acc1: u32, | ||
| 138 | acc2: u32, | ||
| 139 | acc3: u32, | ||
| 140 | acc4: u32, | ||
| 141 | |||
| 142 | seed: u32, | ||
| 143 | buf: [16]u8, | ||
| 144 | buf_len: usize, | ||
| 145 | byte_count: usize, | ||
| 146 | |||
| 147 | const prime_1 = 0x9E3779B1; // 0b10011110001101110111100110110001 | ||
| 148 | const prime_2 = 0x85EBCA77; // 0b10000101111010111100101001110111 | ||
| 149 | const prime_3 = 0xC2B2AE3D; // 0b11000010101100101010111000111101 | ||
| 150 | const prime_4 = 0x27D4EB2F; // 0b00100111110101001110101100101111 | ||
| 151 | const prime_5 = 0x165667B1; // 0b00010110010101100110011110110001 | ||
| 152 | |||
| 153 | pub fn init(seed: u32) XxHash32 { | ||
| 154 | return XxHash32{ | ||
| 155 | .seed = seed, | ||
| 156 | .acc1 = seed +% prime_1 +% prime_2, | ||
| 157 | .acc2 = seed +% prime_2, | ||
| 158 | .acc3 = seed, | ||
| 159 | .acc4 = seed -% prime_1, | ||
| 160 | .buf = undefined, | ||
| 161 | .buf_len = 0, | ||
| 162 | .byte_count = 0, | ||
| 163 | }; | ||
| 164 | } | ||
| 165 | |||
| 166 | pub fn update(self: *XxHash32, input: []const u8) void { | ||
| 167 | if (input.len < 16 - self.buf_len) { | ||
| 168 | mem.copy(u8, self.buf[self.buf_len..], input); | ||
| 169 | self.buf_len += input.len; | ||
| 170 | return; | ||
| 171 | } | ||
| 172 | |||
| 173 | var i: usize = 0; | ||
| 174 | |||
| 175 | if (self.buf_len > 0) { | ||
| 176 | i = 16 - self.buf_len; | ||
| 177 | mem.copy(u8, self.buf[self.buf_len..], input[0..i]); | ||
| 178 | self.processStripe(&self.buf); | ||
| 179 | self.buf_len = 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | while (i + 16 <= input.len) : (i += 16) { | ||
| 183 | self.processStripe(input[i..][0..16]); | ||
| 184 | } | ||
| 185 | |||
| 186 | const remaining_bytes = input[i..]; | ||
| 187 | mem.copy(u8, &self.buf, remaining_bytes); | ||
| 188 | self.buf_len = remaining_bytes.len; | ||
| 189 | } | ||
| 190 | |||
| 191 | inline fn processStripe(self: *XxHash32, buf: *const [16]u8) void { | ||
| 192 | self.acc1 = round(self.acc1, mem.readIntLittle(u32, buf[0..4])); | ||
| 193 | self.acc2 = round(self.acc2, mem.readIntLittle(u32, buf[4..8])); | ||
| 194 | self.acc3 = round(self.acc3, mem.readIntLittle(u32, buf[8..12])); | ||
| 195 | self.acc4 = round(self.acc4, mem.readIntLittle(u32, buf[12..16])); | ||
| 196 | self.byte_count += 16; | ||
| 197 | } | ||
| 198 | |||
| 199 | inline fn round(acc: u32, lane: u32) u32 { | ||
| 200 | const a = acc +% (lane *% prime_2); | ||
| 201 | const b = rotl(u32, a, 13); | ||
| 202 | return b *% prime_1; | ||
| 203 | } | ||
| 204 | |||
| 205 | pub fn final(self: *XxHash32) u32 { | ||
| 206 | var acc: u32 = undefined; | ||
| 207 | |||
| 208 | if (self.byte_count < 16) { | ||
| 209 | acc = self.seed +% prime_5; | ||
| 210 | } else { | ||
| 211 | acc = rotl(u32, self.acc1, 1) +% rotl(u32, self.acc2, 7) +% | ||
| 212 | rotl(u32, self.acc3, 12) +% rotl(u32, self.acc4, 18); | ||
| 213 | } | ||
| 214 | |||
| 215 | acc = acc +% @intCast(u32, self.byte_count) +% @intCast(u32, self.buf_len); | ||
| 216 | |||
| 217 | var pos: usize = 0; | ||
| 218 | while (pos + 4 <= self.buf_len) : (pos += 4) { | ||
| 219 | const lane = mem.readIntLittle(u32, self.buf[pos..][0..4]); | ||
| 220 | acc +%= lane *% prime_3; | ||
| 221 | acc = rotl(u32, acc, 17) *% prime_4; | ||
| 222 | } | ||
| 223 | |||
| 224 | while (pos < self.buf_len) : (pos += 1) { | ||
| 225 | const lane = @as(u32, self.buf[pos]); | ||
| 226 | acc +%= lane *% prime_5; | ||
| 227 | acc = rotl(u32, acc, 11) *% prime_1; | ||
| 228 | } | ||
| 229 | |||
| 230 | acc ^= acc >> 15; | ||
| 231 | acc *%= prime_2; | ||
| 232 | acc ^= acc >> 13; | ||
| 233 | acc *%= prime_3; | ||
| 234 | acc ^= acc >> 16; | ||
| 235 | |||
| 236 | return acc; | ||
| 237 | } | ||
| 238 | |||
| 239 | pub fn hash(input: []const u8) u32 { | ||
| 240 | var hasher = XxHash32.init(0); | ||
| 241 | hasher.update(input); | ||
| 242 | return hasher.final(); | ||
| 243 | } | ||
| 244 | }; | ||
| 245 | |||
| 246 | test "xxhash64" { | ||
| 247 | const hash = XxHash64.hash; | ||
| 248 | |||
| 249 | try expectEqual(hash(""), 0xef46db3751d8e999); | ||
| 250 | try expectEqual(hash("a"), 0xd24ec4f1a98c6e5b); | ||
| 251 | try expectEqual(hash("abc"), 0x44bc2cf5ad770999); | ||
| 252 | try expectEqual(hash("message digest"), 0x066ed728fceeb3be); | ||
| 253 | try expectEqual(hash("abcdefghijklmnopqrstuvwxyz"), 0xcfe1f278fa89835c); | ||
| 254 | try expectEqual(hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0xaaa46907d3047814); | ||
| 255 | try expectEqual(hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0xe04a477f19ee145d); | ||
| 256 | } | ||
| 257 | |||
| 258 | test "xxhash32" { | ||
| 259 | const hash = XxHash32.hash; | ||
| 260 | |||
| 261 | try expectEqual(hash(""), 0x02cc5d05); | ||
| 262 | try expectEqual(hash("a"), 0x550d7456); | ||
| 263 | try expectEqual(hash("abc"), 0x32d153ff); | ||
| 264 | try expectEqual(hash("message digest"), 0x7c948494); | ||
| 265 | try expectEqual(hash("abcdefghijklmnopqrstuvwxyz"), 0x63a14d5f); | ||
| 266 | try expectEqual(hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0x9c285e64); | ||
| 267 | try expectEqual(hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0x9c05f475); | ||
| 268 | } | ||
lib/std/hash_map.zig+2-2| ... | @@ -508,7 +508,7 @@ pub fn HashMap( | ... | @@ -508,7 +508,7 @@ pub fn HashMap( |
| 508 | /// If a new entry needs to be stored, this function asserts there | 508 | /// If a new entry needs to be stored, this function asserts there |
| 509 | /// is enough capacity to store it. | 509 | /// is enough capacity to store it. |
| 510 | pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult { | 510 | pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult { |
| 511 | return self.unmanaged.getOrPutAssumeCapacityAdapted(self.allocator, key, ctx); | 511 | return self.unmanaged.getOrPutAssumeCapacityAdapted(key, ctx); |
| 512 | } | 512 | } |
| 513 | 513 | ||
| 514 | pub fn getOrPutValue(self: *Self, key: K, value: V) Allocator.Error!Entry { | 514 | pub fn getOrPutValue(self: *Self, key: K, value: V) Allocator.Error!Entry { |
| ... | @@ -2130,7 +2130,7 @@ test "std.hash_map getOrPutAdapted" { | ... | @@ -2130,7 +2130,7 @@ test "std.hash_map getOrPutAdapted" { |
| 2130 | try testing.expectEqual(map.count(), keys.len); | 2130 | try testing.expectEqual(map.count(), keys.len); |
| 2131 | 2131 | ||
| 2132 | inline for (keys, 0..) |key_str, i| { | 2132 | inline for (keys, 0..) |key_str, i| { |
| 2133 | const result = try map.getOrPutAdapted(key_str, AdaptedContext{}); | 2133 | const result = map.getOrPutAssumeCapacityAdapted(key_str, AdaptedContext{}); |
| 2134 | try testing.expect(result.found_existing); | 2134 | try testing.expect(result.found_existing); |
| 2135 | try testing.expectEqual(real_keys[i], result.key_ptr.*); | 2135 | try testing.expectEqual(real_keys[i], result.key_ptr.*); |
| 2136 | try testing.expectEqual(@as(u64, i) * 2, result.value_ptr.*); | 2136 | try testing.expectEqual(@as(u64, i) * 2, result.value_ptr.*); |
lib/std/mem.zig+3-3| ... | @@ -941,21 +941,21 @@ pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool { | ... | @@ -941,21 +941,21 @@ pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool { |
| 941 | return true; | 941 | return true; |
| 942 | } | 942 | } |
| 943 | 943 | ||
| 944 | /// Remove values from the beginning of a slice. | 944 | /// Remove a set of values from the beginning of a slice. |
| 945 | pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { | 945 | pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { |
| 946 | var begin: usize = 0; | 946 | var begin: usize = 0; |
| 947 | while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {} | 947 | while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {} |
| 948 | return slice[begin..]; | 948 | return slice[begin..]; |
| 949 | } | 949 | } |
| 950 | 950 | ||
| 951 | /// Remove values from the end of a slice. | 951 | /// Remove a set of values from the end of a slice. |
| 952 | pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { | 952 | pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { |
| 953 | var end: usize = slice.len; | 953 | var end: usize = slice.len; |
| 954 | while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {} | 954 | while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {} |
| 955 | return slice[0..end]; | 955 | return slice[0..end]; |
| 956 | } | 956 | } |
| 957 | 957 | ||
| 958 | /// Remove values from the beginning and end of a slice. | 958 | /// Remove a set of values from the beginning and end of a slice. |
| 959 | pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { | 959 | pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { |
| 960 | var begin: usize = 0; | 960 | var begin: usize = 0; |
| 961 | var end: usize = slice.len; | 961 | var end: usize = slice.len; |
lib/std/multi_array_list.zig+3-9| ... | @@ -433,15 +433,9 @@ pub fn MultiArrayList(comptime S: type) type { | ... | @@ -433,15 +433,9 @@ pub fn MultiArrayList(comptime S: type) type { |
| 433 | } | 433 | } |
| 434 | 434 | ||
| 435 | fn capacityInBytes(capacity: usize) usize { | 435 | fn capacityInBytes(capacity: usize) usize { |
| 436 | if (builtin.zig_backend == .stage2_c) { | 436 | comptime var elem_bytes: usize = 0; |
| 437 | var bytes: usize = 0; | 437 | inline for (sizes.bytes) |size| elem_bytes += size; |
| 438 | for (sizes.bytes) |size| bytes += size * capacity; | 438 | return elem_bytes * capacity; |
| 439 | return bytes; | ||
| 440 | } else { | ||
| 441 | const sizes_vector: @Vector(sizes.bytes.len, usize) = sizes.bytes; | ||
| 442 | const capacity_vector = @splat(sizes.bytes.len, capacity); | ||
| 443 | return @reduce(.Add, capacity_vector * sizes_vector); | ||
| 444 | } | ||
| 445 | } | 439 | } |
| 446 | 440 | ||
| 447 | fn allocatedBytes(self: Self) []align(@alignOf(S)) u8 { | 441 | fn allocatedBytes(self: Self) []align(@alignOf(S)) u8 { |
lib/std/os.zig+18| ... | @@ -7056,3 +7056,21 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { | ... | @@ -7056,3 +7056,21 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { |
| 7056 | else => |err| return unexpectedErrno(err), | 7056 | else => |err| return unexpectedErrno(err), |
| 7057 | }; | 7057 | }; |
| 7058 | } | 7058 | } |
| 7059 | |||
| 7060 | pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE"); | ||
| 7061 | |||
| 7062 | fn noopSigHandler(_: c_int) callconv(.C) void {} | ||
| 7063 | |||
| 7064 | pub fn maybeIgnoreSigpipe() void { | ||
| 7065 | if (have_sigpipe_support and !std.options.keep_sigpipe) { | ||
| 7066 | const act = Sigaction{ | ||
| 7067 | // We set handler to a noop function instead of SIG.IGN so we don't leak our | ||
| 7068 | // signal disposition to a child process | ||
| 7069 | .handler = .{ .handler = noopSigHandler }, | ||
| 7070 | .mask = empty_sigset, | ||
| 7071 | .flags = 0, | ||
| 7072 | }; | ||
| 7073 | sigaction(SIG.PIPE, &act, null) catch |err| | ||
| 7074 | std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)}); | ||
| 7075 | } | ||
| 7076 | } |
lib/std/os/windows/advapi32.zig+4-4| ... | @@ -21,10 +21,10 @@ pub extern "advapi32" fn RegOpenKeyExW( | ... | @@ -21,10 +21,10 @@ pub extern "advapi32" fn RegOpenKeyExW( |
| 21 | pub extern "advapi32" fn RegQueryValueExW( | 21 | pub extern "advapi32" fn RegQueryValueExW( |
| 22 | hKey: HKEY, | 22 | hKey: HKEY, |
| 23 | lpValueName: LPCWSTR, | 23 | lpValueName: LPCWSTR, |
| 24 | lpReserved: *DWORD, | 24 | lpReserved: ?*DWORD, |
| 25 | lpType: *DWORD, | 25 | lpType: ?*DWORD, |
| 26 | lpData: *BYTE, | 26 | lpData: ?*BYTE, |
| 27 | lpcbData: *DWORD, | 27 | lpcbData: ?*DWORD, |
| 28 | ) callconv(WINAPI) LSTATUS; | 28 | ) callconv(WINAPI) LSTATUS; |
| 29 | 29 | ||
| 30 | // RtlGenRandom is known as SystemFunction036 under advapi32 | 30 | // RtlGenRandom is known as SystemFunction036 under advapi32 |
lib/std/sort.zig+44-10| ... | @@ -6,10 +6,10 @@ const math = std.math; | ... | @@ -6,10 +6,10 @@ const math = std.math; |
| 6 | 6 | ||
| 7 | pub fn binarySearch( | 7 | pub fn binarySearch( |
| 8 | comptime T: type, | 8 | comptime T: type, |
| 9 | key: T, | 9 | key: anytype, |
| 10 | items: []const T, | 10 | items: []const T, |
| 11 | context: anytype, | 11 | context: anytype, |
| 12 | comptime compareFn: fn (context: @TypeOf(context), lhs: T, rhs: T) math.Order, | 12 | comptime compareFn: fn (context: @TypeOf(context), key: @TypeOf(key), mid: T) math.Order, |
| 13 | ) ?usize { | 13 | ) ?usize { |
| 14 | var left: usize = 0; | 14 | var left: usize = 0; |
| 15 | var right: usize = items.len; | 15 | var right: usize = items.len; |
| ... | @@ -41,35 +41,69 @@ test "binarySearch" { | ... | @@ -41,35 +41,69 @@ test "binarySearch" { |
| 41 | }; | 41 | }; |
| 42 | try testing.expectEqual( | 42 | try testing.expectEqual( |
| 43 | @as(?usize, null), | 43 | @as(?usize, null), |
| 44 | binarySearch(u32, 1, &[_]u32{}, {}, S.order_u32), | 44 | binarySearch(u32, @as(u32, 1), &[_]u32{}, {}, S.order_u32), |
| 45 | ); | 45 | ); |
| 46 | try testing.expectEqual( | 46 | try testing.expectEqual( |
| 47 | @as(?usize, 0), | 47 | @as(?usize, 0), |
| 48 | binarySearch(u32, 1, &[_]u32{1}, {}, S.order_u32), | 48 | binarySearch(u32, @as(u32, 1), &[_]u32{1}, {}, S.order_u32), |
| 49 | ); | 49 | ); |
| 50 | try testing.expectEqual( | 50 | try testing.expectEqual( |
| 51 | @as(?usize, null), | 51 | @as(?usize, null), |
| 52 | binarySearch(u32, 1, &[_]u32{0}, {}, S.order_u32), | 52 | binarySearch(u32, @as(u32, 1), &[_]u32{0}, {}, S.order_u32), |
| 53 | ); | 53 | ); |
| 54 | try testing.expectEqual( | 54 | try testing.expectEqual( |
| 55 | @as(?usize, null), | 55 | @as(?usize, null), |
| 56 | binarySearch(u32, 0, &[_]u32{1}, {}, S.order_u32), | 56 | binarySearch(u32, @as(u32, 0), &[_]u32{1}, {}, S.order_u32), |
| 57 | ); | 57 | ); |
| 58 | try testing.expectEqual( | 58 | try testing.expectEqual( |
| 59 | @as(?usize, 4), | 59 | @as(?usize, 4), |
| 60 | binarySearch(u32, 5, &[_]u32{ 1, 2, 3, 4, 5 }, {}, S.order_u32), | 60 | binarySearch(u32, @as(u32, 5), &[_]u32{ 1, 2, 3, 4, 5 }, {}, S.order_u32), |
| 61 | ); | 61 | ); |
| 62 | try testing.expectEqual( | 62 | try testing.expectEqual( |
| 63 | @as(?usize, 0), | 63 | @as(?usize, 0), |
| 64 | binarySearch(u32, 2, &[_]u32{ 2, 4, 8, 16, 32, 64 }, {}, S.order_u32), | 64 | binarySearch(u32, @as(u32, 2), &[_]u32{ 2, 4, 8, 16, 32, 64 }, {}, S.order_u32), |
| 65 | ); | 65 | ); |
| 66 | try testing.expectEqual( | 66 | try testing.expectEqual( |
| 67 | @as(?usize, 1), | 67 | @as(?usize, 1), |
| 68 | binarySearch(i32, -4, &[_]i32{ -7, -4, 0, 9, 10 }, {}, S.order_i32), | 68 | binarySearch(i32, @as(i32, -4), &[_]i32{ -7, -4, 0, 9, 10 }, {}, S.order_i32), |
| 69 | ); | 69 | ); |
| 70 | try testing.expectEqual( | 70 | try testing.expectEqual( |
| 71 | @as(?usize, 3), | 71 | @as(?usize, 3), |
| 72 | binarySearch(i32, 98, &[_]i32{ -100, -25, 2, 98, 99, 100 }, {}, S.order_i32), | 72 | binarySearch(i32, @as(i32, 98), &[_]i32{ -100, -25, 2, 98, 99, 100 }, {}, S.order_i32), |
| 73 | ); | ||
| 74 | const R = struct { | ||
| 75 | b: i32, | ||
| 76 | e: i32, | ||
| 77 | |||
| 78 | fn r(b: i32, e: i32) @This() { | ||
| 79 | return @This(){ .b = b, .e = e }; | ||
| 80 | } | ||
| 81 | |||
| 82 | fn order(context: void, key: i32, mid: @This()) math.Order { | ||
| 83 | _ = context; | ||
| 84 | |||
| 85 | if (key < mid.b) { | ||
| 86 | return .lt; | ||
| 87 | } | ||
| 88 | |||
| 89 | if (key > mid.e) { | ||
| 90 | return .gt; | ||
| 91 | } | ||
| 92 | |||
| 93 | return .eq; | ||
| 94 | } | ||
| 95 | }; | ||
| 96 | try testing.expectEqual( | ||
| 97 | @as(?usize, null), | ||
| 98 | binarySearch(R, @as(i32, -45), &[_]R{ R.r(-100, -50), R.r(-40, -20), R.r(-10, 20), R.r(30, 40) }, {}, R.order), | ||
| 99 | ); | ||
| 100 | try testing.expectEqual( | ||
| 101 | @as(?usize, 2), | ||
| 102 | binarySearch(R, @as(i32, 10), &[_]R{ R.r(-100, -50), R.r(-40, -20), R.r(-10, 20), R.r(30, 40) }, {}, R.order), | ||
| 103 | ); | ||
| 104 | try testing.expectEqual( | ||
| 105 | @as(?usize, 1), | ||
| 106 | binarySearch(R, @as(i32, -20), &[_]R{ R.r(-100, -50), R.r(-40, -20), R.r(-10, 20), R.r(30, 40) }, {}, R.order), | ||
| 73 | ); | 107 | ); |
| 74 | } | 108 | } |
| 75 | 109 |
lib/std/start.zig+1| ... | @@ -496,6 +496,7 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { | ... | @@ -496,6 +496,7 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { |
| 496 | std.os.environ = envp; | 496 | std.os.environ = envp; |
| 497 | 497 | ||
| 498 | std.debug.maybeEnableSegfaultHandler(); | 498 | std.debug.maybeEnableSegfaultHandler(); |
| 499 | std.os.maybeIgnoreSigpipe(); | ||
| 499 | 500 | ||
| 500 | return initEventLoopAndCallMain(); | 501 | return initEventLoopAndCallMain(); |
| 501 | } | 502 | } |
lib/std/std.zig+17| ... | @@ -31,6 +31,7 @@ pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceE | ... | @@ -31,6 +31,7 @@ pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceE |
| 31 | pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; | 31 | pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; |
| 32 | pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue; | 32 | pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue; |
| 33 | pub const Progress = @import("Progress.zig"); | 33 | pub const Progress = @import("Progress.zig"); |
| 34 | pub const RingBuffer = @import("RingBuffer.zig"); | ||
| 34 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; | 35 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; |
| 35 | pub const SemanticVersion = @import("SemanticVersion.zig"); | 36 | pub const SemanticVersion = @import("SemanticVersion.zig"); |
| 36 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; | 37 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; |
| ... | @@ -167,6 +168,22 @@ pub const options = struct { | ... | @@ -167,6 +168,22 @@ pub const options = struct { |
| 167 | options_override.crypto_always_getrandom | 168 | options_override.crypto_always_getrandom |
| 168 | else | 169 | else |
| 169 | false; | 170 | false; |
| 171 | |||
| 172 | /// By default Zig disables SIGPIPE by setting a "no-op" handler for it. Set this option | ||
| 173 | /// to `true` to prevent that. | ||
| 174 | /// | ||
| 175 | /// Note that we use a "no-op" handler instead of SIG_IGN because it will not be inherited by | ||
| 176 | /// any child process. | ||
| 177 | /// | ||
| 178 | /// SIGPIPE is triggered when a process attempts to write to a broken pipe. By default, SIGPIPE | ||
| 179 | /// will terminate the process instead of exiting. It doesn't trigger the panic handler so in many | ||
| 180 | /// cases it's unclear why the process was terminated. By capturing SIGPIPE instead, functions that | ||
| 181 | /// write to broken pipes will return the EPIPE error (error.BrokenPipe) and the program can handle | ||
| 182 | /// it like any other error. | ||
| 183 | pub const keep_sigpipe: bool = if (@hasDecl(options_override, "keep_sigpipe")) | ||
| 184 | options_override.keep_sigpipe | ||
| 185 | else | ||
| 186 | false; | ||
| 170 | }; | 187 | }; |
| 171 | 188 | ||
| 172 | // This forces the start.zig file to be imported, and the comptime logic inside that | 189 | // This forces the start.zig file to be imported, and the comptime logic inside that |
lib/std/target/loongarch.zig+1-1| ... | @@ -89,7 +89,7 @@ pub const all_features = blk: { | ... | @@ -89,7 +89,7 @@ pub const all_features = blk: { |
| 89 | .dependencies = featureSet(&[_]Feature{}), | 89 | .dependencies = featureSet(&[_]Feature{}), |
| 90 | }; | 90 | }; |
| 91 | const ti = @typeInfo(Feature); | 91 | const ti = @typeInfo(Feature); |
| 92 | for (result) |*elem, i| { | 92 | for (&result, 0..) |*elem, i| { |
| 93 | elem.index = i; | 93 | elem.index = i; |
| 94 | elem.name = ti.Enum.fields[i].name; | 94 | elem.name = ti.Enum.fields[i].name; |
| 95 | } | 95 | } |
lib/std/target/xtensa.zig+1-1| ... | @@ -23,7 +23,7 @@ pub const all_features = blk: { | ... | @@ -23,7 +23,7 @@ pub const all_features = blk: { |
| 23 | .dependencies = featureSet(&[_]Feature{}), | 23 | .dependencies = featureSet(&[_]Feature{}), |
| 24 | }; | 24 | }; |
| 25 | const ti = @typeInfo(Feature); | 25 | const ti = @typeInfo(Feature); |
| 26 | for (result) |*elem, i| { | 26 | for (&result, 0..) |*elem, i| { |
| 27 | elem.index = i; | 27 | elem.index = i; |
| 28 | elem.name = ti.Enum.fields[i].name; | 28 | elem.name = ti.Enum.fields[i].name; |
| 29 | } | 29 | } |
lib/zig.h+915-787| ... | @@ -1,8 +1,11 @@ | ... | @@ -1,8 +1,11 @@ |
| 1 | #undef linux | 1 | #undef linux |
| 2 | 2 | ||
| 3 | #ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ | ||
| 3 | #define __STDC_WANT_IEC_60559_TYPES_EXT__ | 4 | #define __STDC_WANT_IEC_60559_TYPES_EXT__ |
| 5 | #endif | ||
| 4 | #include <float.h> | 6 | #include <float.h> |
| 5 | #include <limits.h> | 7 | #include <limits.h> |
| 8 | #include <stdarg.h> | ||
| 6 | #include <stddef.h> | 9 | #include <stddef.h> |
| 7 | #include <stdint.h> | 10 | #include <stdint.h> |
| 8 | 11 | ||
| ... | @@ -75,6 +78,32 @@ typedef char bool; | ... | @@ -75,6 +78,32 @@ typedef char bool; |
| 75 | #define zig_cold | 78 | #define zig_cold |
| 76 | #endif | 79 | #endif |
| 77 | 80 | ||
| 81 | #if zig_has_attribute(flatten) | ||
| 82 | #define zig_maybe_flatten __attribute__((flatten)) | ||
| 83 | #else | ||
| 84 | #define zig_maybe_flatten | ||
| 85 | #endif | ||
| 86 | |||
| 87 | #if zig_has_attribute(noinline) | ||
| 88 | #define zig_never_inline __attribute__((noinline)) zig_maybe_flatten | ||
| 89 | #elif defined(_MSC_VER) | ||
| 90 | #define zig_never_inline __declspec(noinline) zig_maybe_flatten | ||
| 91 | #else | ||
| 92 | #define zig_never_inline zig_never_inline_unavailable | ||
| 93 | #endif | ||
| 94 | |||
| 95 | #if zig_has_attribute(not_tail_called) | ||
| 96 | #define zig_never_tail __attribute__((not_tail_called)) zig_never_inline | ||
| 97 | #else | ||
| 98 | #define zig_never_tail zig_never_tail_unavailable | ||
| 99 | #endif | ||
| 100 | |||
| 101 | #if zig_has_attribute(always_inline) | ||
| 102 | #define zig_always_tail __attribute__((musttail)) | ||
| 103 | #else | ||
| 104 | #define zig_always_tail zig_always_tail_unavailable | ||
| 105 | #endif | ||
| 106 | |||
| 78 | #if __STDC_VERSION__ >= 199901L | 107 | #if __STDC_VERSION__ >= 199901L |
| 79 | #define zig_restrict restrict | 108 | #define zig_restrict restrict |
| 80 | #elif defined(__GNUC__) | 109 | #elif defined(__GNUC__) |
| ... | @@ -286,701 +315,802 @@ typedef char bool; | ... | @@ -286,701 +315,802 @@ typedef char bool; |
| 286 | #endif | 315 | #endif |
| 287 | 316 | ||
| 288 | #if __STDC_VERSION__ >= 201112L | 317 | #if __STDC_VERSION__ >= 201112L |
| 289 | #define zig_noreturn _Noreturn void | 318 | #define zig_noreturn _Noreturn |
| 290 | #elif zig_has_attribute(noreturn) || defined(zig_gnuc) | 319 | #elif zig_has_attribute(noreturn) || defined(zig_gnuc) |
| 291 | #define zig_noreturn __attribute__((noreturn)) void | 320 | #define zig_noreturn __attribute__((noreturn)) |
| 292 | #elif _MSC_VER | 321 | #elif _MSC_VER |
| 293 | #define zig_noreturn __declspec(noreturn) void | 322 | #define zig_noreturn __declspec(noreturn) |
| 294 | #else | 323 | #else |
| 295 | #define zig_noreturn void | 324 | #define zig_noreturn |
| 296 | #endif | 325 | #endif |
| 297 | 326 | ||
| 298 | #define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) | 327 | #define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) |
| 299 | 328 | ||
| 300 | typedef uintptr_t zig_usize; | 329 | #define zig_compiler_rt_abbrev_uint32_t si |
| 301 | typedef intptr_t zig_isize; | 330 | #define zig_compiler_rt_abbrev_int32_t si |
| 302 | typedef signed short int zig_c_short; | 331 | #define zig_compiler_rt_abbrev_uint64_t di |
| 303 | typedef unsigned short int zig_c_ushort; | 332 | #define zig_compiler_rt_abbrev_int64_t di |
| 304 | typedef signed int zig_c_int; | 333 | #define zig_compiler_rt_abbrev_zig_u128 ti |
| 305 | typedef unsigned int zig_c_uint; | 334 | #define zig_compiler_rt_abbrev_zig_i128 ti |
| 306 | typedef signed long int zig_c_long; | 335 | #define zig_compiler_rt_abbrev_zig_f16 hf |
| 307 | typedef unsigned long int zig_c_ulong; | 336 | #define zig_compiler_rt_abbrev_zig_f32 sf |
| 308 | typedef signed long long int zig_c_longlong; | 337 | #define zig_compiler_rt_abbrev_zig_f64 df |
| 309 | typedef unsigned long long int zig_c_ulonglong; | 338 | #define zig_compiler_rt_abbrev_zig_f80 xf |
| 310 | 339 | #define zig_compiler_rt_abbrev_zig_f128 tf | |
| 311 | typedef uint8_t zig_u8; | 340 | |
| 312 | typedef int8_t zig_i8; | 341 | zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t); |
| 313 | typedef uint16_t zig_u16; | 342 | zig_extern void *memset (void *, int, size_t); |
| 314 | typedef int16_t zig_i16; | 343 | |
| 315 | typedef uint32_t zig_u32; | 344 | /* ===================== 8/16/32/64-bit Integer Support ===================== */ |
| 316 | typedef int32_t zig_i32; | 345 | |
| 317 | typedef uint64_t zig_u64; | 346 | #if __STDC_VERSION__ >= 199901L || _MSC_VER |
| 318 | typedef int64_t zig_i64; | 347 | #include <stdint.h> |
| 319 | 348 | #else | |
| 320 | #define zig_as_u8(val) UINT8_C(val) | 349 | |
| 321 | #define zig_as_i8(val) INT8_C(val) | 350 | #if SCHAR_MIN == ~0x7F && SCHAR_MAX == 0x7F && UCHAR_MAX == 0xFF |
| 322 | #define zig_as_u16(val) UINT16_C(val) | 351 | typedef unsigned char uint8_t; |
| 323 | #define zig_as_i16(val) INT16_C(val) | 352 | typedef signed char int8_t; |
| 324 | #define zig_as_u32(val) UINT32_C(val) | 353 | #define INT8_C(c) c |
| 325 | #define zig_as_i32(val) INT32_C(val) | 354 | #define UINT8_C(c) c##U |
| 326 | #define zig_as_u64(val) UINT64_C(val) | 355 | #elif SHRT_MIN == ~0x7F && SHRT_MAX == 0x7F && USHRT_MAX == 0xFF |
| 327 | #define zig_as_i64(val) INT64_C(val) | 356 | typedef unsigned short uint8_t; |
| 328 | 357 | typedef signed short int8_t; | |
| 329 | #define zig_minInt_u8 zig_as_u8(0) | 358 | #define INT8_C(c) c |
| 330 | #define zig_maxInt_u8 UINT8_MAX | 359 | #define UINT8_C(c) c##U |
| 360 | #elif INT_MIN == ~0x7F && INT_MAX == 0x7F && UINT_MAX == 0xFF | ||
| 361 | typedef unsigned int uint8_t; | ||
| 362 | typedef signed int int8_t; | ||
| 363 | #define INT8_C(c) c | ||
| 364 | #define UINT8_C(c) c##U | ||
| 365 | #elif LONG_MIN == ~0x7F && LONG_MAX == 0x7F && ULONG_MAX == 0xFF | ||
| 366 | typedef unsigned long uint8_t; | ||
| 367 | typedef signed long int8_t; | ||
| 368 | #define INT8_C(c) c##L | ||
| 369 | #define UINT8_C(c) c##LU | ||
| 370 | #elif LLONG_MIN == ~0x7F && LLONG_MAX == 0x7F && ULLONG_MAX == 0xFF | ||
| 371 | typedef unsigned long long uint8_t; | ||
| 372 | typedef signed long long int8_t; | ||
| 373 | #define INT8_C(c) c##LL | ||
| 374 | #define UINT8_C(c) c##LLU | ||
| 375 | #endif | ||
| 376 | #define INT8_MIN (~INT8_C(0x7F)) | ||
| 377 | #define INT8_MAX ( INT8_C(0x7F)) | ||
| 378 | #define UINT8_MAX ( INT8_C(0xFF)) | ||
| 379 | |||
| 380 | #if SCHAR_MIN == ~0x7FFF && SCHAR_MAX == 0x7FFF && UCHAR_MAX == 0xFFFF | ||
| 381 | typedef unsigned char uint16_t; | ||
| 382 | typedef signed char int16_t; | ||
| 383 | #define INT16_C(c) c | ||
| 384 | #define UINT16_C(c) c##U | ||
| 385 | #elif SHRT_MIN == ~0x7FFF && SHRT_MAX == 0x7FFF && USHRT_MAX == 0xFFFF | ||
| 386 | typedef unsigned short uint16_t; | ||
| 387 | typedef signed short int16_t; | ||
| 388 | #define INT16_C(c) c | ||
| 389 | #define UINT16_C(c) c##U | ||
| 390 | #elif INT_MIN == ~0x7FFF && INT_MAX == 0x7FFF && UINT_MAX == 0xFFFF | ||
| 391 | typedef unsigned int uint16_t; | ||
| 392 | typedef signed int int16_t; | ||
| 393 | #define INT16_C(c) c | ||
| 394 | #define UINT16_C(c) c##U | ||
| 395 | #elif LONG_MIN == ~0x7FFF && LONG_MAX == 0x7FFF && ULONG_MAX == 0xFFFF | ||
| 396 | typedef unsigned long uint16_t; | ||
| 397 | typedef signed long int16_t; | ||
| 398 | #define INT16_C(c) c##L | ||
| 399 | #define UINT16_C(c) c##LU | ||
| 400 | #elif LLONG_MIN == ~0x7FFF && LLONG_MAX == 0x7FFF && ULLONG_MAX == 0xFFFF | ||
| 401 | typedef unsigned long long uint16_t; | ||
| 402 | typedef signed long long int16_t; | ||
| 403 | #define INT16_C(c) c##LL | ||
| 404 | #define UINT16_C(c) c##LLU | ||
| 405 | #endif | ||
| 406 | #define INT16_MIN (~INT16_C(0x7FFF)) | ||
| 407 | #define INT16_MAX ( INT16_C(0x7FFF)) | ||
| 408 | #define UINT16_MAX ( INT16_C(0xFFFF)) | ||
| 409 | |||
| 410 | #if SCHAR_MIN == ~0x7FFFFFFF && SCHAR_MAX == 0x7FFFFFFF && UCHAR_MAX == 0xFFFFFFFF | ||
| 411 | typedef unsigned char uint32_t; | ||
| 412 | typedef signed char int32_t; | ||
| 413 | #define INT32_C(c) c | ||
| 414 | #define UINT32_C(c) c##U | ||
| 415 | #elif SHRT_MIN == ~0x7FFFFFFF && SHRT_MAX == 0x7FFFFFFF && USHRT_MAX == 0xFFFFFFFF | ||
| 416 | typedef unsigned short uint32_t; | ||
| 417 | typedef signed short int32_t; | ||
| 418 | #define INT32_C(c) c | ||
| 419 | #define UINT32_C(c) c##U | ||
| 420 | #elif INT_MIN == ~0x7FFFFFFF && INT_MAX == 0x7FFFFFFF && UINT_MAX == 0xFFFFFFFF | ||
| 421 | typedef unsigned int uint32_t; | ||
| 422 | typedef signed int int32_t; | ||
| 423 | #define INT32_C(c) c | ||
| 424 | #define UINT32_C(c) c##U | ||
| 425 | #elif LONG_MIN == ~0x7FFFFFFF && LONG_MAX == 0x7FFFFFFF && ULONG_MAX == 0xFFFFFFFF | ||
| 426 | typedef unsigned long uint32_t; | ||
| 427 | typedef signed long int32_t; | ||
| 428 | #define INT32_C(c) c##L | ||
| 429 | #define UINT32_C(c) c##LU | ||
| 430 | #elif LLONG_MIN == ~0x7FFFFFFF && LLONG_MAX == 0x7FFFFFFF && ULLONG_MAX == 0xFFFFFFFF | ||
| 431 | typedef unsigned long long uint32_t; | ||
| 432 | typedef signed long long int32_t; | ||
| 433 | #define INT32_C(c) c##LL | ||
| 434 | #define UINT32_C(c) c##LLU | ||
| 435 | #endif | ||
| 436 | #define INT32_MIN (~INT32_C(0x7FFFFFFF)) | ||
| 437 | #define INT32_MAX ( INT32_C(0x7FFFFFFF)) | ||
| 438 | #define UINT32_MAX ( INT32_C(0xFFFFFFFF)) | ||
| 439 | |||
| 440 | #if SCHAR_MIN == ~0x7FFFFFFFFFFFFFFF && SCHAR_MAX == 0x7FFFFFFFFFFFFFFF && UCHAR_MAX == 0xFFFFFFFFFFFFFFFF | ||
| 441 | typedef unsigned char uint64_t; | ||
| 442 | typedef signed char int64_t; | ||
| 443 | #define INT64_C(c) c | ||
| 444 | #define UINT64_C(c) c##U | ||
| 445 | #elif SHRT_MIN == ~0x7FFFFFFFFFFFFFFF && SHRT_MAX == 0x7FFFFFFFFFFFFFFF && USHRT_MAX == 0xFFFFFFFFFFFFFFFF | ||
| 446 | typedef unsigned short uint64_t; | ||
| 447 | typedef signed short int64_t; | ||
| 448 | #define INT64_C(c) c | ||
| 449 | #define UINT64_C(c) c##U | ||
| 450 | #elif INT_MIN == ~0x7FFFFFFFFFFFFFFF && INT_MAX == 0x7FFFFFFFFFFFFFFF && UINT_MAX == 0xFFFFFFFFFFFFFFFF | ||
| 451 | typedef unsigned int uint64_t; | ||
| 452 | typedef signed int int64_t; | ||
| 453 | #define INT64_C(c) c | ||
| 454 | #define UINT64_C(c) c##U | ||
| 455 | #elif LONG_MIN == ~0x7FFFFFFFFFFFFFFF && LONG_MAX == 0x7FFFFFFFFFFFFFFF && ULONG_MAX == 0xFFFFFFFFFFFFFFFF | ||
| 456 | typedef unsigned long uint64_t; | ||
| 457 | typedef signed long int64_t; | ||
| 458 | #define INT64_C(c) c##L | ||
| 459 | #define UINT64_C(c) c##LU | ||
| 460 | #elif LLONG_MIN == ~0x7FFFFFFFFFFFFFFF && LLONG_MAX == 0x7FFFFFFFFFFFFFFF && ULLONG_MAX == 0xFFFFFFFFFFFFFFFF | ||
| 461 | typedef unsigned long long uint64_t; | ||
| 462 | typedef signed long long int64_t; | ||
| 463 | #define INT64_C(c) c##LL | ||
| 464 | #define UINT64_C(c) c##LLU | ||
| 465 | #endif | ||
| 466 | #define INT64_MIN (~INT64_C(0x7FFFFFFFFFFFFFFF)) | ||
| 467 | #define INT64_MAX ( INT64_C(0x7FFFFFFFFFFFFFFF)) | ||
| 468 | #define UINT64_MAX ( INT64_C(0xFFFFFFFFFFFFFFFF)) | ||
| 469 | |||
| 470 | typedef size_t uintptr_t; | ||
| 471 | typedef ptrdiff_t intptr_t; | ||
| 472 | |||
| 473 | #endif | ||
| 474 | |||
| 331 | #define zig_minInt_i8 INT8_MIN | 475 | #define zig_minInt_i8 INT8_MIN |
| 332 | #define zig_maxInt_i8 INT8_MAX | 476 | #define zig_maxInt_i8 INT8_MAX |
| 333 | #define zig_minInt_u16 zig_as_u16(0) | 477 | #define zig_minInt_u8 UINT8_C(0) |
| 334 | #define zig_maxInt_u16 UINT16_MAX | 478 | #define zig_maxInt_u8 UINT8_MAX |
| 335 | #define zig_minInt_i16 INT16_MIN | 479 | #define zig_minInt_i16 INT16_MIN |
| 336 | #define zig_maxInt_i16 INT16_MAX | 480 | #define zig_maxInt_i16 INT16_MAX |
| 337 | #define zig_minInt_u32 zig_as_u32(0) | 481 | #define zig_minInt_u16 UINT16_C(0) |
| 338 | #define zig_maxInt_u32 UINT32_MAX | 482 | #define zig_maxInt_u16 UINT16_MAX |
| 339 | #define zig_minInt_i32 INT32_MIN | 483 | #define zig_minInt_i32 INT32_MIN |
| 340 | #define zig_maxInt_i32 INT32_MAX | 484 | #define zig_maxInt_i32 INT32_MAX |
| 341 | #define zig_minInt_u64 zig_as_u64(0) | 485 | #define zig_minInt_u32 UINT32_C(0) |
| 342 | #define zig_maxInt_u64 UINT64_MAX | 486 | #define zig_maxInt_u32 UINT32_MAX |
| 343 | #define zig_minInt_i64 INT64_MIN | 487 | #define zig_minInt_i64 INT64_MIN |
| 344 | #define zig_maxInt_i64 INT64_MAX | 488 | #define zig_maxInt_i64 INT64_MAX |
| 489 | #define zig_minInt_u64 UINT64_C(0) | ||
| 490 | #define zig_maxInt_u64 UINT64_MAX | ||
| 345 | 491 | ||
| 346 | #define zig_compiler_rt_abbrev_u32 si | 492 | #define zig_intLimit(s, w, limit, bits) zig_shr_##s##w(zig_##limit##Int_##s##w, w - (bits)) |
| 347 | #define zig_compiler_rt_abbrev_i32 si | 493 | #define zig_minInt_i(w, bits) zig_intLimit(i, w, min, bits) |
| 348 | #define zig_compiler_rt_abbrev_u64 di | 494 | #define zig_maxInt_i(w, bits) zig_intLimit(i, w, max, bits) |
| 349 | #define zig_compiler_rt_abbrev_i64 di | 495 | #define zig_minInt_u(w, bits) zig_intLimit(u, w, min, bits) |
| 350 | #define zig_compiler_rt_abbrev_u128 ti | 496 | #define zig_maxInt_u(w, bits) zig_intLimit(u, w, max, bits) |
| 351 | #define zig_compiler_rt_abbrev_i128 ti | ||
| 352 | #define zig_compiler_rt_abbrev_f16 hf | ||
| 353 | #define zig_compiler_rt_abbrev_f32 sf | ||
| 354 | #define zig_compiler_rt_abbrev_f64 df | ||
| 355 | #define zig_compiler_rt_abbrev_f80 xf | ||
| 356 | #define zig_compiler_rt_abbrev_f128 tf | ||
| 357 | |||
| 358 | zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, zig_usize); | ||
| 359 | zig_extern void *memset (void *, int, zig_usize); | ||
| 360 | |||
| 361 | /* ==================== 8/16/32/64-bit Integer Routines ===================== */ | ||
| 362 | |||
| 363 | #define zig_maxInt(Type, bits) zig_shr_##Type(zig_maxInt_##Type, (zig_bitSizeOf(zig_##Type) - bits)) | ||
| 364 | #define zig_expand_maxInt(Type, bits) zig_maxInt(Type, bits) | ||
| 365 | #define zig_minInt(Type, bits) zig_not_##Type(zig_maxInt(Type, bits), bits) | ||
| 366 | #define zig_expand_minInt(Type, bits) zig_minInt(Type, bits) | ||
| 367 | 497 | ||
| 368 | #define zig_int_operator(Type, RhsType, operation, operator) \ | 498 | #define zig_int_operator(Type, RhsType, operation, operator) \ |
| 369 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##RhsType rhs) { \ | 499 | static inline Type zig_##operation(Type lhs, RhsType rhs) { \ |
| 370 | return lhs operator rhs; \ | 500 | return lhs operator rhs; \ |
| 371 | } | 501 | } |
| 372 | #define zig_int_basic_operator(Type, operation, operator) \ | 502 | #define zig_int_basic_operator(Type, operation, operator) \ |
| 373 | zig_int_operator(Type, Type, operation, operator) | 503 | zig_int_operator(Type, Type, operation, operator) |
| 374 | #define zig_int_shift_operator(Type, operation, operator) \ | 504 | #define zig_int_shift_operator(Type, operation, operator) \ |
| 375 | zig_int_operator(Type, u8, operation, operator) | 505 | zig_int_operator(Type, uint8_t, operation, operator) |
| 376 | #define zig_int_helpers(w) \ | 506 | #define zig_int_helpers(w) \ |
| 377 | zig_int_basic_operator(u##w, and, &) \ | 507 | zig_int_basic_operator(uint##w##_t, and_u##w, &) \ |
| 378 | zig_int_basic_operator(i##w, and, &) \ | 508 | zig_int_basic_operator( int##w##_t, and_i##w, &) \ |
| 379 | zig_int_basic_operator(u##w, or, |) \ | 509 | zig_int_basic_operator(uint##w##_t, or_u##w, |) \ |
| 380 | zig_int_basic_operator(i##w, or, |) \ | 510 | zig_int_basic_operator( int##w##_t, or_i##w, |) \ |
| 381 | zig_int_basic_operator(u##w, xor, ^) \ | 511 | zig_int_basic_operator(uint##w##_t, xor_u##w, ^) \ |
| 382 | zig_int_basic_operator(i##w, xor, ^) \ | 512 | zig_int_basic_operator( int##w##_t, xor_i##w, ^) \ |
| 383 | zig_int_shift_operator(u##w, shl, <<) \ | 513 | zig_int_shift_operator(uint##w##_t, shl_u##w, <<) \ |
| 384 | zig_int_shift_operator(i##w, shl, <<) \ | 514 | zig_int_shift_operator( int##w##_t, shl_i##w, <<) \ |
| 385 | zig_int_shift_operator(u##w, shr, >>) \ | 515 | zig_int_shift_operator(uint##w##_t, shr_u##w, >>) \ |
| 386 | \ | 516 | \ |
| 387 | static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \ | 517 | static inline int##w##_t zig_shr_i##w(int##w##_t lhs, uint8_t rhs) { \ |
| 388 | zig_i##w sign_mask = lhs < zig_as_i##w(0) ? -zig_as_i##w(1) : zig_as_i##w(0); \ | 518 | int##w##_t sign_mask = lhs < INT##w##_C(0) ? -INT##w##_C(1) : INT##w##_C(0); \ |
| 389 | return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ | 519 | return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ |
| 390 | } \ | 520 | } \ |
| 391 | \ | 521 | \ |
| 392 | static inline zig_u##w zig_not_u##w(zig_u##w val, zig_u8 bits) { \ | 522 | static inline uint##w##_t zig_not_u##w(uint##w##_t val, uint8_t bits) { \ |
| 393 | return val ^ zig_maxInt(u##w, bits); \ | 523 | return val ^ zig_maxInt_u(w, bits); \ |
| 394 | } \ | 524 | } \ |
| 395 | \ | 525 | \ |
| 396 | static inline zig_i##w zig_not_i##w(zig_i##w val, zig_u8 bits) { \ | 526 | static inline int##w##_t zig_not_i##w(int##w##_t val, uint8_t bits) { \ |
| 397 | (void)bits; \ | 527 | (void)bits; \ |
| 398 | return ~val; \ | 528 | return ~val; \ |
| 399 | } \ | 529 | } \ |
| 400 | \ | 530 | \ |
| 401 | static inline zig_u##w zig_wrap_u##w(zig_u##w val, zig_u8 bits) { \ | 531 | static inline uint##w##_t zig_wrap_u##w(uint##w##_t val, uint8_t bits) { \ |
| 402 | return val & zig_maxInt(u##w, bits); \ | 532 | return val & zig_maxInt_u(w, bits); \ |
| 403 | } \ | 533 | } \ |
| 404 | \ | 534 | \ |
| 405 | static inline zig_i##w zig_wrap_i##w(zig_i##w val, zig_u8 bits) { \ | 535 | static inline int##w##_t zig_wrap_i##w(int##w##_t val, uint8_t bits) { \ |
| 406 | return (val & zig_as_u##w(1) << (bits - zig_as_u8(1))) != 0 \ | 536 | return (val & UINT##w##_C(1) << (bits - UINT8_C(1))) != 0 \ |
| 407 | ? val | zig_minInt(i##w, bits) : val & zig_maxInt(i##w, bits); \ | 537 | ? val | zig_minInt_i(w, bits) : val & zig_maxInt_i(w, bits); \ |
| 408 | } \ | 538 | } \ |
| 409 | \ | 539 | \ |
| 410 | zig_int_basic_operator(u##w, div_floor, /) \ | 540 | zig_int_basic_operator(uint##w##_t, div_floor_u##w, /) \ |
| 411 | \ | 541 | \ |
| 412 | static inline zig_i##w zig_div_floor_i##w(zig_i##w lhs, zig_i##w rhs) { \ | 542 | static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \ |
| 413 | return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < zig_as_i##w(0)); \ | 543 | return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < INT##w##_C(0)); \ |
| 414 | } \ | 544 | } \ |
| 415 | \ | 545 | \ |
| 416 | zig_int_basic_operator(u##w, mod, %) \ | 546 | zig_int_basic_operator(uint##w##_t, mod_u##w, %) \ |
| 417 | \ | 547 | \ |
| 418 | static inline zig_i##w zig_mod_i##w(zig_i##w lhs, zig_i##w rhs) { \ | 548 | static inline int##w##_t zig_mod_i##w(int##w##_t lhs, int##w##_t rhs) { \ |
| 419 | zig_i##w rem = lhs % rhs; \ | 549 | int##w##_t rem = lhs % rhs; \ |
| 420 | return rem + (((lhs ^ rhs) & rem) < zig_as_i##w(0) ? rhs : zig_as_i##w(0)); \ | 550 | return rem + (((lhs ^ rhs) & rem) < INT##w##_C(0) ? rhs : INT##w##_C(0)); \ |
| 421 | } \ | 551 | } \ |
| 422 | \ | 552 | \ |
| 423 | static inline zig_u##w zig_shlw_u##w(zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ | 553 | static inline uint##w##_t zig_shlw_u##w(uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ |
| 424 | return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \ | 554 | return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \ |
| 425 | } \ | 555 | } \ |
| 426 | \ | 556 | \ |
| 427 | static inline zig_i##w zig_shlw_i##w(zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ | 557 | static inline int##w##_t zig_shlw_i##w(int##w##_t lhs, uint8_t rhs, uint8_t bits) { \ |
| 428 | return zig_wrap_i##w((zig_i##w)zig_shl_u##w((zig_u##w)lhs, (zig_u##w)rhs), bits); \ | 558 | return zig_wrap_i##w((int##w##_t)zig_shl_u##w((uint##w##_t)lhs, (uint##w##_t)rhs), bits); \ |
| 429 | } \ | 559 | } \ |
| 430 | \ | 560 | \ |
| 431 | static inline zig_u##w zig_addw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 561 | static inline uint##w##_t zig_addw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 432 | return zig_wrap_u##w(lhs + rhs, bits); \ | 562 | return zig_wrap_u##w(lhs + rhs, bits); \ |
| 433 | } \ | 563 | } \ |
| 434 | \ | 564 | \ |
| 435 | static inline zig_i##w zig_addw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 565 | static inline int##w##_t zig_addw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 436 | return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs + (zig_u##w)rhs), bits); \ | 566 | return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs + (uint##w##_t)rhs), bits); \ |
| 437 | } \ | 567 | } \ |
| 438 | \ | 568 | \ |
| 439 | static inline zig_u##w zig_subw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 569 | static inline uint##w##_t zig_subw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 440 | return zig_wrap_u##w(lhs - rhs, bits); \ | 570 | return zig_wrap_u##w(lhs - rhs, bits); \ |
| 441 | } \ | 571 | } \ |
| 442 | \ | 572 | \ |
| 443 | static inline zig_i##w zig_subw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 573 | static inline int##w##_t zig_subw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 444 | return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs - (zig_u##w)rhs), bits); \ | 574 | return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs - (uint##w##_t)rhs), bits); \ |
| 445 | } \ | 575 | } \ |
| 446 | \ | 576 | \ |
| 447 | static inline zig_u##w zig_mulw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 577 | static inline uint##w##_t zig_mulw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 448 | return zig_wrap_u##w(lhs * rhs, bits); \ | 578 | return zig_wrap_u##w(lhs * rhs, bits); \ |
| 449 | } \ | 579 | } \ |
| 450 | \ | 580 | \ |
| 451 | static inline zig_i##w zig_mulw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 581 | static inline int##w##_t zig_mulw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 452 | return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs * (zig_u##w)rhs), bits); \ | 582 | return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs * (uint##w##_t)rhs), bits); \ |
| 453 | } | 583 | } |
| 454 | zig_int_helpers(8) | 584 | zig_int_helpers(8) |
| 455 | zig_int_helpers(16) | 585 | zig_int_helpers(16) |
| 456 | zig_int_helpers(32) | 586 | zig_int_helpers(32) |
| 457 | zig_int_helpers(64) | 587 | zig_int_helpers(64) |
| 458 | 588 | ||
| 459 | static inline bool zig_addo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { | 589 | static inline bool zig_addo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { |
| 460 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 590 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 461 | zig_u32 full_res; | 591 | uint32_t full_res; |
| 462 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 592 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 463 | *res = zig_wrap_u32(full_res, bits); | 593 | *res = zig_wrap_u32(full_res, bits); |
| 464 | return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); | 594 | return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); |
| 465 | #else | 595 | #else |
| 466 | *res = zig_addw_u32(lhs, rhs, bits); | 596 | *res = zig_addw_u32(lhs, rhs, bits); |
| 467 | return *res < lhs; | 597 | return *res < lhs; |
| 468 | #endif | 598 | #endif |
| 469 | } | 599 | } |
| 470 | 600 | ||
| 471 | static inline void zig_vaddo_u32(zig_u8 *ov, zig_u32 *res, int n, | 601 | static inline void zig_vaddo_u32(uint8_t *ov, uint32_t *res, int n, |
| 472 | const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) | 602 | const uint32_t *lhs, const uint32_t *rhs, uint8_t bits) |
| 473 | { | 603 | { |
| 474 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits); | 604 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits); |
| 475 | } | 605 | } |
| 476 | 606 | ||
| 477 | zig_extern zig_i32 __addosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); | 607 | zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow); |
| 478 | static inline bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { | 608 | static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { |
| 479 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 609 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 480 | zig_i32 full_res; | 610 | int32_t full_res; |
| 481 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 611 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 482 | #else | 612 | #else |
| 483 | zig_c_int overflow_int; | 613 | int overflow_int; |
| 484 | zig_i32 full_res = __addosi4(lhs, rhs, &overflow_int); | 614 | int32_t full_res = __addosi4(lhs, rhs, &overflow_int); |
| 485 | bool overflow = overflow_int != 0; | 615 | bool overflow = overflow_int != 0; |
| 486 | #endif | 616 | #endif |
| 487 | *res = zig_wrap_i32(full_res, bits); | 617 | *res = zig_wrap_i32(full_res, bits); |
| 488 | return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); | 618 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); |
| 489 | } | 619 | } |
| 490 | 620 | ||
| 491 | static inline void zig_vaddo_i32(zig_u8 *ov, zig_i32 *res, int n, | 621 | static inline void zig_vaddo_i32(uint8_t *ov, int32_t *res, int n, |
| 492 | const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) | 622 | const int32_t *lhs, const int32_t *rhs, uint8_t bits) |
| 493 | { | 623 | { |
| 494 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits); | 624 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits); |
| 495 | } | 625 | } |
| 496 | 626 | ||
| 497 | static inline bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { | 627 | static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { |
| 498 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 628 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 499 | zig_u64 full_res; | 629 | uint64_t full_res; |
| 500 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 630 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 501 | *res = zig_wrap_u64(full_res, bits); | 631 | *res = zig_wrap_u64(full_res, bits); |
| 502 | return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); | 632 | return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); |
| 503 | #else | 633 | #else |
| 504 | *res = zig_addw_u64(lhs, rhs, bits); | 634 | *res = zig_addw_u64(lhs, rhs, bits); |
| 505 | return *res < lhs; | 635 | return *res < lhs; |
| 506 | #endif | 636 | #endif |
| 507 | } | 637 | } |
| 508 | 638 | ||
| 509 | static inline void zig_vaddo_u64(zig_u8 *ov, zig_u64 *res, int n, | 639 | static inline void zig_vaddo_u64(uint8_t *ov, uint64_t *res, int n, |
| 510 | const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) | 640 | const uint64_t *lhs, const uint64_t *rhs, uint8_t bits) |
| 511 | { | 641 | { |
| 512 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits); | 642 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits); |
| 513 | } | 643 | } |
| 514 | 644 | ||
| 515 | zig_extern zig_i64 __addodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); | 645 | zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); |
| 516 | static inline bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { | 646 | static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { |
| 517 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 647 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 518 | zig_i64 full_res; | 648 | int64_t full_res; |
| 519 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 649 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 520 | #else | 650 | #else |
| 521 | zig_c_int overflow_int; | 651 | int overflow_int; |
| 522 | zig_i64 full_res = __addodi4(lhs, rhs, &overflow_int); | 652 | int64_t full_res = __addodi4(lhs, rhs, &overflow_int); |
| 523 | bool overflow = overflow_int != 0; | 653 | bool overflow = overflow_int != 0; |
| 524 | #endif | 654 | #endif |
| 525 | *res = zig_wrap_i64(full_res, bits); | 655 | *res = zig_wrap_i64(full_res, bits); |
| 526 | return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); | 656 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); |
| 527 | } | 657 | } |
| 528 | 658 | ||
| 529 | static inline void zig_vaddo_i64(zig_u8 *ov, zig_i64 *res, int n, | 659 | static inline void zig_vaddo_i64(uint8_t *ov, int64_t *res, int n, |
| 530 | const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) | 660 | const int64_t *lhs, const int64_t *rhs, uint8_t bits) |
| 531 | { | 661 | { |
| 532 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits); | 662 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits); |
| 533 | } | 663 | } |
| 534 | 664 | ||
| 535 | static inline bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { | 665 | static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { |
| 536 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 666 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 537 | zig_u8 full_res; | 667 | uint8_t full_res; |
| 538 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 668 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 539 | *res = zig_wrap_u8(full_res, bits); | 669 | *res = zig_wrap_u8(full_res, bits); |
| 540 | return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); | 670 | return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); |
| 541 | #else | 671 | #else |
| 542 | zig_u32 full_res; | 672 | uint32_t full_res; |
| 543 | bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); | 673 | bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); |
| 544 | *res = (zig_u8)full_res; | 674 | *res = (uint8_t)full_res; |
| 545 | return overflow; | 675 | return overflow; |
| 546 | #endif | 676 | #endif |
| 547 | } | 677 | } |
| 548 | 678 | ||
| 549 | static inline void zig_vaddo_u8(zig_u8 *ov, zig_u8 *res, int n, | 679 | static inline void zig_vaddo_u8(uint8_t *ov, uint8_t *res, int n, |
| 550 | const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) | 680 | const uint8_t *lhs, const uint8_t *rhs, uint8_t bits) |
| 551 | { | 681 | { |
| 552 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits); | 682 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits); |
| 553 | } | 683 | } |
| 554 | 684 | ||
| 555 | static inline bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { | 685 | static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { |
| 556 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 686 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 557 | zig_i8 full_res; | 687 | int8_t full_res; |
| 558 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 688 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 559 | *res = zig_wrap_i8(full_res, bits); | 689 | *res = zig_wrap_i8(full_res, bits); |
| 560 | return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); | 690 | return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); |
| 561 | #else | 691 | #else |
| 562 | zig_i32 full_res; | 692 | int32_t full_res; |
| 563 | bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); | 693 | bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); |
| 564 | *res = (zig_i8)full_res; | 694 | *res = (int8_t)full_res; |
| 565 | return overflow; | 695 | return overflow; |
| 566 | #endif | 696 | #endif |
| 567 | } | 697 | } |
| 568 | 698 | ||
| 569 | static inline void zig_vaddo_i8(zig_u8 *ov, zig_i8 *res, int n, | 699 | static inline void zig_vaddo_i8(uint8_t *ov, int8_t *res, int n, |
| 570 | const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) | 700 | const int8_t *lhs, const int8_t *rhs, uint8_t bits) |
| 571 | { | 701 | { |
| 572 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits); | 702 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits); |
| 573 | } | 703 | } |
| 574 | 704 | ||
| 575 | static inline bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { | 705 | static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { |
| 576 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 706 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 577 | zig_u16 full_res; | 707 | uint16_t full_res; |
| 578 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 708 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 579 | *res = zig_wrap_u16(full_res, bits); | 709 | *res = zig_wrap_u16(full_res, bits); |
| 580 | return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); | 710 | return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); |
| 581 | #else | 711 | #else |
| 582 | zig_u32 full_res; | 712 | uint32_t full_res; |
| 583 | bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); | 713 | bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); |
| 584 | *res = (zig_u16)full_res; | 714 | *res = (uint16_t)full_res; |
| 585 | return overflow; | 715 | return overflow; |
| 586 | #endif | 716 | #endif |
| 587 | } | 717 | } |
| 588 | 718 | ||
| 589 | static inline void zig_vaddo_u16(zig_u8 *ov, zig_u16 *res, int n, | 719 | static inline void zig_vaddo_u16(uint8_t *ov, uint16_t *res, int n, |
| 590 | const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) | 720 | const uint16_t *lhs, const uint16_t *rhs, uint8_t bits) |
| 591 | { | 721 | { |
| 592 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits); | 722 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits); |
| 593 | } | 723 | } |
| 594 | 724 | ||
| 595 | static inline bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { | 725 | static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { |
| 596 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | 726 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) |
| 597 | zig_i16 full_res; | 727 | int16_t full_res; |
| 598 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 728 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 599 | *res = zig_wrap_i16(full_res, bits); | 729 | *res = zig_wrap_i16(full_res, bits); |
| 600 | return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); | 730 | return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); |
| 601 | #else | 731 | #else |
| 602 | zig_i32 full_res; | 732 | int32_t full_res; |
| 603 | bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); | 733 | bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); |
| 604 | *res = (zig_i16)full_res; | 734 | *res = (int16_t)full_res; |
| 605 | return overflow; | 735 | return overflow; |
| 606 | #endif | 736 | #endif |
| 607 | } | 737 | } |
| 608 | 738 | ||
| 609 | static inline void zig_vaddo_i16(zig_u8 *ov, zig_i16 *res, int n, | 739 | static inline void zig_vaddo_i16(uint8_t *ov, int16_t *res, int n, |
| 610 | const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) | 740 | const int16_t *lhs, const int16_t *rhs, uint8_t bits) |
| 611 | { | 741 | { |
| 612 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits); | 742 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits); |
| 613 | } | 743 | } |
| 614 | 744 | ||
| 615 | static inline bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { | 745 | static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { |
| 616 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 746 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 617 | zig_u32 full_res; | 747 | uint32_t full_res; |
| 618 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 748 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 619 | *res = zig_wrap_u32(full_res, bits); | 749 | *res = zig_wrap_u32(full_res, bits); |
| 620 | return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); | 750 | return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); |
| 621 | #else | 751 | #else |
| 622 | *res = zig_subw_u32(lhs, rhs, bits); | 752 | *res = zig_subw_u32(lhs, rhs, bits); |
| 623 | return *res > lhs; | 753 | return *res > lhs; |
| 624 | #endif | 754 | #endif |
| 625 | } | 755 | } |
| 626 | 756 | ||
| 627 | static inline void zig_vsubo_u32(zig_u8 *ov, zig_u32 *res, int n, | 757 | static inline void zig_vsubo_u32(uint8_t *ov, uint32_t *res, int n, |
| 628 | const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) | 758 | const uint32_t *lhs, const uint32_t *rhs, uint8_t bits) |
| 629 | { | 759 | { |
| 630 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits); | 760 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits); |
| 631 | } | 761 | } |
| 632 | 762 | ||
| 633 | zig_extern zig_i32 __subosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); | 763 | zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); |
| 634 | static inline bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { | 764 | static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { |
| 635 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 765 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 636 | zig_i32 full_res; | 766 | int32_t full_res; |
| 637 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 767 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 638 | #else | 768 | #else |
| 639 | zig_c_int overflow_int; | 769 | int overflow_int; |
| 640 | zig_i32 full_res = __subosi4(lhs, rhs, &overflow_int); | 770 | int32_t full_res = __subosi4(lhs, rhs, &overflow_int); |
| 641 | bool overflow = overflow_int != 0; | 771 | bool overflow = overflow_int != 0; |
| 642 | #endif | 772 | #endif |
| 643 | *res = zig_wrap_i32(full_res, bits); | 773 | *res = zig_wrap_i32(full_res, bits); |
| 644 | return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); | 774 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); |
| 645 | } | 775 | } |
| 646 | 776 | ||
| 647 | static inline void zig_vsubo_i32(zig_u8 *ov, zig_i32 *res, int n, | 777 | static inline void zig_vsubo_i32(uint8_t *ov, int32_t *res, int n, |
| 648 | const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) | 778 | const int32_t *lhs, const int32_t *rhs, uint8_t bits) |
| 649 | { | 779 | { |
| 650 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits); | 780 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits); |
| 651 | } | 781 | } |
| 652 | 782 | ||
| 653 | static inline bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { | 783 | static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { |
| 654 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 784 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 655 | zig_u64 full_res; | 785 | uint64_t full_res; |
| 656 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 786 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 657 | *res = zig_wrap_u64(full_res, bits); | 787 | *res = zig_wrap_u64(full_res, bits); |
| 658 | return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); | 788 | return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); |
| 659 | #else | 789 | #else |
| 660 | *res = zig_subw_u64(lhs, rhs, bits); | 790 | *res = zig_subw_u64(lhs, rhs, bits); |
| 661 | return *res > lhs; | 791 | return *res > lhs; |
| 662 | #endif | 792 | #endif |
| 663 | } | 793 | } |
| 664 | 794 | ||
| 665 | static inline void zig_vsubo_u64(zig_u8 *ov, zig_u64 *res, int n, | 795 | static inline void zig_vsubo_u64(uint8_t *ov, uint64_t *res, int n, |
| 666 | const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) | 796 | const uint64_t *lhs, const uint64_t *rhs, uint8_t bits) |
| 667 | { | 797 | { |
| 668 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits); | 798 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits); |
| 669 | } | 799 | } |
| 670 | 800 | ||
| 671 | zig_extern zig_i64 __subodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); | 801 | zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); |
| 672 | static inline bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { | 802 | static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { |
| 673 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 803 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 674 | zig_i64 full_res; | 804 | int64_t full_res; |
| 675 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 805 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 676 | #else | 806 | #else |
| 677 | zig_c_int overflow_int; | 807 | int overflow_int; |
| 678 | zig_i64 full_res = __subodi4(lhs, rhs, &overflow_int); | 808 | int64_t full_res = __subodi4(lhs, rhs, &overflow_int); |
| 679 | bool overflow = overflow_int != 0; | 809 | bool overflow = overflow_int != 0; |
| 680 | #endif | 810 | #endif |
| 681 | *res = zig_wrap_i64(full_res, bits); | 811 | *res = zig_wrap_i64(full_res, bits); |
| 682 | return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); | 812 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); |
| 683 | } | 813 | } |
| 684 | 814 | ||
| 685 | static inline void zig_vsubo_i64(zig_u8 *ov, zig_i64 *res, int n, | 815 | static inline void zig_vsubo_i64(uint8_t *ov, int64_t *res, int n, |
| 686 | const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) | 816 | const int64_t *lhs, const int64_t *rhs, uint8_t bits) |
| 687 | { | 817 | { |
| 688 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits); | 818 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits); |
| 689 | } | 819 | } |
| 690 | 820 | ||
| 691 | static inline bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { | 821 | static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { |
| 692 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 822 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 693 | zig_u8 full_res; | 823 | uint8_t full_res; |
| 694 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 824 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 695 | *res = zig_wrap_u8(full_res, bits); | 825 | *res = zig_wrap_u8(full_res, bits); |
| 696 | return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); | 826 | return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); |
| 697 | #else | 827 | #else |
| 698 | zig_u32 full_res; | 828 | uint32_t full_res; |
| 699 | bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); | 829 | bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); |
| 700 | *res = (zig_u8)full_res; | 830 | *res = (uint8_t)full_res; |
| 701 | return overflow; | 831 | return overflow; |
| 702 | #endif | 832 | #endif |
| 703 | } | 833 | } |
| 704 | 834 | ||
| 705 | static inline void zig_vsubo_u8(zig_u8 *ov, zig_u8 *res, int n, | 835 | static inline void zig_vsubo_u8(uint8_t *ov, uint8_t *res, int n, |
| 706 | const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) | 836 | const uint8_t *lhs, const uint8_t *rhs, uint8_t bits) |
| 707 | { | 837 | { |
| 708 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits); | 838 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits); |
| 709 | } | 839 | } |
| 710 | 840 | ||
| 711 | static inline bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { | 841 | static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { |
| 712 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 842 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 713 | zig_i8 full_res; | 843 | int8_t full_res; |
| 714 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 844 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 715 | *res = zig_wrap_i8(full_res, bits); | 845 | *res = zig_wrap_i8(full_res, bits); |
| 716 | return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); | 846 | return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); |
| 717 | #else | 847 | #else |
| 718 | zig_i32 full_res; | 848 | int32_t full_res; |
| 719 | bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); | 849 | bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); |
| 720 | *res = (zig_i8)full_res; | 850 | *res = (int8_t)full_res; |
| 721 | return overflow; | 851 | return overflow; |
| 722 | #endif | 852 | #endif |
| 723 | } | 853 | } |
| 724 | 854 | ||
| 725 | static inline void zig_vsubo_i8(zig_u8 *ov, zig_i8 *res, int n, | 855 | static inline void zig_vsubo_i8(uint8_t *ov, int8_t *res, int n, |
| 726 | const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) | 856 | const int8_t *lhs, const int8_t *rhs, uint8_t bits) |
| 727 | { | 857 | { |
| 728 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits); | 858 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits); |
| 729 | } | 859 | } |
| 730 | 860 | ||
| 731 | 861 | ||
| 732 | static inline bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { | 862 | static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { |
| 733 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 863 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 734 | zig_u16 full_res; | 864 | uint16_t full_res; |
| 735 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 865 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 736 | *res = zig_wrap_u16(full_res, bits); | 866 | *res = zig_wrap_u16(full_res, bits); |
| 737 | return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); | 867 | return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); |
| 738 | #else | 868 | #else |
| 739 | zig_u32 full_res; | 869 | uint32_t full_res; |
| 740 | bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); | 870 | bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); |
| 741 | *res = (zig_u16)full_res; | 871 | *res = (uint16_t)full_res; |
| 742 | return overflow; | 872 | return overflow; |
| 743 | #endif | 873 | #endif |
| 744 | } | 874 | } |
| 745 | 875 | ||
| 746 | static inline void zig_vsubo_u16(zig_u8 *ov, zig_u16 *res, int n, | 876 | static inline void zig_vsubo_u16(uint8_t *ov, uint16_t *res, int n, |
| 747 | const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) | 877 | const uint16_t *lhs, const uint16_t *rhs, uint8_t bits) |
| 748 | { | 878 | { |
| 749 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits); | 879 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits); |
| 750 | } | 880 | } |
| 751 | 881 | ||
| 752 | 882 | ||
| 753 | static inline bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { | 883 | static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { |
| 754 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | 884 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) |
| 755 | zig_i16 full_res; | 885 | int16_t full_res; |
| 756 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 886 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 757 | *res = zig_wrap_i16(full_res, bits); | 887 | *res = zig_wrap_i16(full_res, bits); |
| 758 | return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); | 888 | return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); |
| 759 | #else | 889 | #else |
| 760 | zig_i32 full_res; | 890 | int32_t full_res; |
| 761 | bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); | 891 | bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); |
| 762 | *res = (zig_i16)full_res; | 892 | *res = (int16_t)full_res; |
| 763 | return overflow; | 893 | return overflow; |
| 764 | #endif | 894 | #endif |
| 765 | } | 895 | } |
| 766 | 896 | ||
| 767 | static inline void zig_vsubo_i16(zig_u8 *ov, zig_i16 *res, int n, | 897 | static inline void zig_vsubo_i16(uint8_t *ov, int16_t *res, int n, |
| 768 | const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) | 898 | const int16_t *lhs, const int16_t *rhs, uint8_t bits) |
| 769 | { | 899 | { |
| 770 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits); | 900 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits); |
| 771 | } | 901 | } |
| 772 | 902 | ||
| 773 | static inline bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { | 903 | static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { |
| 774 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 904 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 775 | zig_u32 full_res; | 905 | uint32_t full_res; |
| 776 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 906 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 777 | *res = zig_wrap_u32(full_res, bits); | 907 | *res = zig_wrap_u32(full_res, bits); |
| 778 | return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); | 908 | return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); |
| 779 | #else | 909 | #else |
| 780 | *res = zig_mulw_u32(lhs, rhs, bits); | 910 | *res = zig_mulw_u32(lhs, rhs, bits); |
| 781 | return rhs != zig_as_u32(0) && lhs > zig_maxInt(u32, bits) / rhs; | 911 | return rhs != UINT32_C(0) && lhs > zig_maxInt_u(32, bits) / rhs; |
| 782 | #endif | 912 | #endif |
| 783 | } | 913 | } |
| 784 | 914 | ||
| 785 | static inline void zig_vmulo_u32(zig_u8 *ov, zig_u32 *res, int n, | 915 | static inline void zig_vmulo_u32(uint8_t *ov, uint32_t *res, int n, |
| 786 | const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) | 916 | const uint32_t *lhs, const uint32_t *rhs, uint8_t bits) |
| 787 | { | 917 | { |
| 788 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits); | 918 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits); |
| 789 | } | 919 | } |
| 790 | 920 | ||
| 791 | zig_extern zig_i32 __mulosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); | 921 | zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow); |
| 792 | static inline bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { | 922 | static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { |
| 793 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 923 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 794 | zig_i32 full_res; | 924 | int32_t full_res; |
| 795 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 925 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 796 | #else | 926 | #else |
| 797 | zig_c_int overflow_int; | 927 | int overflow_int; |
| 798 | zig_i32 full_res = __mulosi4(lhs, rhs, &overflow_int); | 928 | int32_t full_res = __mulosi4(lhs, rhs, &overflow_int); |
| 799 | bool overflow = overflow_int != 0; | 929 | bool overflow = overflow_int != 0; |
| 800 | #endif | 930 | #endif |
| 801 | *res = zig_wrap_i32(full_res, bits); | 931 | *res = zig_wrap_i32(full_res, bits); |
| 802 | return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); | 932 | return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); |
| 803 | } | 933 | } |
| 804 | 934 | ||
| 805 | static inline void zig_vmulo_i32(zig_u8 *ov, zig_i32 *res, int n, | 935 | static inline void zig_vmulo_i32(uint8_t *ov, int32_t *res, int n, |
| 806 | const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) | 936 | const int32_t *lhs, const int32_t *rhs, uint8_t bits) |
| 807 | { | 937 | { |
| 808 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits); | 938 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits); |
| 809 | } | 939 | } |
| 810 | 940 | ||
| 811 | static inline bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { | 941 | static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { |
| 812 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 942 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 813 | zig_u64 full_res; | 943 | uint64_t full_res; |
| 814 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 944 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 815 | *res = zig_wrap_u64(full_res, bits); | 945 | *res = zig_wrap_u64(full_res, bits); |
| 816 | return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); | 946 | return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); |
| 817 | #else | 947 | #else |
| 818 | *res = zig_mulw_u64(lhs, rhs, bits); | 948 | *res = zig_mulw_u64(lhs, rhs, bits); |
| 819 | return rhs != zig_as_u64(0) && lhs > zig_maxInt(u64, bits) / rhs; | 949 | return rhs != UINT64_C(0) && lhs > zig_maxInt_u(64, bits) / rhs; |
| 820 | #endif | 950 | #endif |
| 821 | } | 951 | } |
| 822 | 952 | ||
| 823 | static inline void zig_vmulo_u64(zig_u8 *ov, zig_u64 *res, int n, | 953 | static inline void zig_vmulo_u64(uint8_t *ov, uint64_t *res, int n, |
| 824 | const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) | 954 | const uint64_t *lhs, const uint64_t *rhs, uint8_t bits) |
| 825 | { | 955 | { |
| 826 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits); | 956 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits); |
| 827 | } | 957 | } |
| 828 | 958 | ||
| 829 | zig_extern zig_i64 __mulodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); | 959 | zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); |
| 830 | static inline bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { | 960 | static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { |
| 831 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 961 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 832 | zig_i64 full_res; | 962 | int64_t full_res; |
| 833 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 963 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 834 | #else | 964 | #else |
| 835 | zig_c_int overflow_int; | 965 | int overflow_int; |
| 836 | zig_i64 full_res = __mulodi4(lhs, rhs, &overflow_int); | 966 | int64_t full_res = __mulodi4(lhs, rhs, &overflow_int); |
| 837 | bool overflow = overflow_int != 0; | 967 | bool overflow = overflow_int != 0; |
| 838 | #endif | 968 | #endif |
| 839 | *res = zig_wrap_i64(full_res, bits); | 969 | *res = zig_wrap_i64(full_res, bits); |
| 840 | return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); | 970 | return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); |
| 841 | } | 971 | } |
| 842 | 972 | ||
| 843 | static inline void zig_vmulo_i64(zig_u8 *ov, zig_i64 *res, int n, | 973 | static inline void zig_vmulo_i64(uint8_t *ov, int64_t *res, int n, |
| 844 | const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) | 974 | const int64_t *lhs, const int64_t *rhs, uint8_t bits) |
| 845 | { | 975 | { |
| 846 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits); | 976 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits); |
| 847 | } | 977 | } |
| 848 | 978 | ||
| 849 | static inline bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { | 979 | static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { |
| 850 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 980 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 851 | zig_u8 full_res; | 981 | uint8_t full_res; |
| 852 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 982 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 853 | *res = zig_wrap_u8(full_res, bits); | 983 | *res = zig_wrap_u8(full_res, bits); |
| 854 | return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); | 984 | return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); |
| 855 | #else | 985 | #else |
| 856 | zig_u32 full_res; | 986 | uint32_t full_res; |
| 857 | bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); | 987 | bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); |
| 858 | *res = (zig_u8)full_res; | 988 | *res = (uint8_t)full_res; |
| 859 | return overflow; | 989 | return overflow; |
| 860 | #endif | 990 | #endif |
| 861 | } | 991 | } |
| 862 | 992 | ||
| 863 | static inline void zig_vmulo_u8(zig_u8 *ov, zig_u8 *res, int n, | 993 | static inline void zig_vmulo_u8(uint8_t *ov, uint8_t *res, int n, |
| 864 | const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) | 994 | const uint8_t *lhs, const uint8_t *rhs, uint8_t bits) |
| 865 | { | 995 | { |
| 866 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits); | 996 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits); |
| 867 | } | 997 | } |
| 868 | 998 | ||
| 869 | static inline bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { | 999 | static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { |
| 870 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 1000 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 871 | zig_i8 full_res; | 1001 | int8_t full_res; |
| 872 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 1002 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 873 | *res = zig_wrap_i8(full_res, bits); | 1003 | *res = zig_wrap_i8(full_res, bits); |
| 874 | return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); | 1004 | return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); |
| 875 | #else | 1005 | #else |
| 876 | zig_i32 full_res; | 1006 | int32_t full_res; |
| 877 | bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); | 1007 | bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); |
| 878 | *res = (zig_i8)full_res; | 1008 | *res = (int8_t)full_res; |
| 879 | return overflow; | 1009 | return overflow; |
| 880 | #endif | 1010 | #endif |
| 881 | } | 1011 | } |
| 882 | 1012 | ||
| 883 | static inline void zig_vmulo_i8(zig_u8 *ov, zig_i8 *res, int n, | 1013 | static inline void zig_vmulo_i8(uint8_t *ov, int8_t *res, int n, |
| 884 | const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) | 1014 | const int8_t *lhs, const int8_t *rhs, uint8_t bits) |
| 885 | { | 1015 | { |
| 886 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits); | 1016 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits); |
| 887 | } | 1017 | } |
| 888 | 1018 | ||
| 889 | static inline bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { | 1019 | static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { |
| 890 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 1020 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 891 | zig_u16 full_res; | 1021 | uint16_t full_res; |
| 892 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 1022 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 893 | *res = zig_wrap_u16(full_res, bits); | 1023 | *res = zig_wrap_u16(full_res, bits); |
| 894 | return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); | 1024 | return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); |
| 895 | #else | 1025 | #else |
| 896 | zig_u32 full_res; | 1026 | uint32_t full_res; |
| 897 | bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); | 1027 | bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); |
| 898 | *res = (zig_u16)full_res; | 1028 | *res = (uint16_t)full_res; |
| 899 | return overflow; | 1029 | return overflow; |
| 900 | #endif | 1030 | #endif |
| 901 | } | 1031 | } |
| 902 | 1032 | ||
| 903 | static inline void zig_vmulo_u16(zig_u8 *ov, zig_u16 *res, int n, | 1033 | static inline void zig_vmulo_u16(uint8_t *ov, uint16_t *res, int n, |
| 904 | const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) | 1034 | const uint16_t *lhs, const uint16_t *rhs, uint8_t bits) |
| 905 | { | 1035 | { |
| 906 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits); | 1036 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits); |
| 907 | } | 1037 | } |
| 908 | 1038 | ||
| 909 | static inline bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { | 1039 | static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { |
| 910 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | 1040 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) |
| 911 | zig_i16 full_res; | 1041 | int16_t full_res; |
| 912 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 1042 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 913 | *res = zig_wrap_i16(full_res, bits); | 1043 | *res = zig_wrap_i16(full_res, bits); |
| 914 | return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); | 1044 | return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); |
| 915 | #else | 1045 | #else |
| 916 | zig_i32 full_res; | 1046 | int32_t full_res; |
| 917 | bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); | 1047 | bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); |
| 918 | *res = (zig_i16)full_res; | 1048 | *res = (int16_t)full_res; |
| 919 | return overflow; | 1049 | return overflow; |
| 920 | #endif | 1050 | #endif |
| 921 | } | 1051 | } |
| 922 | 1052 | ||
| 923 | static inline void zig_vmulo_i16(zig_u8 *ov, zig_i16 *res, int n, | 1053 | static inline void zig_vmulo_i16(uint8_t *ov, int16_t *res, int n, |
| 924 | const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) | 1054 | const int16_t *lhs, const int16_t *rhs, uint8_t bits) |
| 925 | { | 1055 | { |
| 926 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits); | 1056 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits); |
| 927 | } | 1057 | } |
| 928 | 1058 | ||
| 929 | #define zig_int_builtins(w) \ | 1059 | #define zig_int_builtins(w) \ |
| 930 | static inline bool zig_shlo_u##w(zig_u##w *res, zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ | 1060 | static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ |
| 931 | *res = zig_shlw_u##w(lhs, rhs, bits); \ | 1061 | *res = zig_shlw_u##w(lhs, rhs, bits); \ |
| 932 | return lhs > zig_maxInt(u##w, bits) >> rhs; \ | 1062 | return lhs > zig_maxInt_u(w, bits) >> rhs; \ |
| 933 | } \ | 1063 | } \ |
| 934 | \ | 1064 | \ |
| 935 | static inline bool zig_shlo_i##w(zig_i##w *res, zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ | 1065 | static inline bool zig_shlo_i##w(int##w##_t *res, int##w##_t lhs, uint8_t rhs, uint8_t bits) { \ |
| 936 | *res = zig_shlw_i##w(lhs, rhs, bits); \ | 1066 | *res = zig_shlw_i##w(lhs, rhs, bits); \ |
| 937 | zig_i##w mask = (zig_i##w)(zig_maxInt_u##w << (bits - rhs - 1)); \ | 1067 | int##w##_t mask = (int##w##_t)(UINT##w##_MAX << (bits - rhs - 1)); \ |
| 938 | return (lhs & mask) != zig_as_i##w(0) && (lhs & mask) != mask; \ | 1068 | return (lhs & mask) != INT##w##_C(0) && (lhs & mask) != mask; \ |
| 939 | } \ | 1069 | } \ |
| 940 | \ | 1070 | \ |
| 941 | static inline zig_u##w zig_shls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 1071 | static inline uint##w##_t zig_shls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 942 | zig_u##w res; \ | 1072 | uint##w##_t res; \ |
| 943 | if (rhs >= bits) return lhs != zig_as_u##w(0) ? zig_maxInt(u##w, bits) : lhs; \ | 1073 | if (rhs >= bits) return lhs != UINT##w##_C(0) ? zig_maxInt_u(w, bits) : lhs; \ |
| 944 | return zig_shlo_u##w(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u##w, bits) : res; \ | 1074 | return zig_shlo_u##w(&res, lhs, (uint8_t)rhs, bits) ? zig_maxInt_u(w, bits) : res; \ |
| 945 | } \ | 1075 | } \ |
| 946 | \ | 1076 | \ |
| 947 | static inline zig_i##w zig_shls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 1077 | static inline int##w##_t zig_shls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 948 | zig_i##w res; \ | 1078 | int##w##_t res; \ |
| 949 | if ((zig_u##w)rhs < (zig_u##w)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \ | 1079 | if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, (uint8_t)rhs, bits)) return res; \ |
| 950 | return lhs < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | 1080 | return lhs < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ |
| 951 | } \ | 1081 | } \ |
| 952 | \ | 1082 | \ |
| 953 | static inline zig_u##w zig_adds_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 1083 | static inline uint##w##_t zig_adds_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 954 | zig_u##w res; \ | 1084 | uint##w##_t res; \ |
| 955 | return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ | 1085 | return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \ |
| 956 | } \ | 1086 | } \ |
| 957 | \ | 1087 | \ |
| 958 | static inline zig_i##w zig_adds_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 1088 | static inline int##w##_t zig_adds_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 959 | zig_i##w res; \ | 1089 | int##w##_t res; \ |
| 960 | if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \ | 1090 | if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \ |
| 961 | return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | 1091 | return res >= INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ |
| 962 | } \ | 1092 | } \ |
| 963 | \ | 1093 | \ |
| 964 | static inline zig_u##w zig_subs_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 1094 | static inline uint##w##_t zig_subs_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 965 | zig_u##w res; \ | 1095 | uint##w##_t res; \ |
| 966 | return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt(u##w, bits) : res; \ | 1096 | return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt_u(w, bits) : res; \ |
| 967 | } \ | 1097 | } \ |
| 968 | \ | 1098 | \ |
| 969 | static inline zig_i##w zig_subs_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 1099 | static inline int##w##_t zig_subs_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 970 | zig_i##w res; \ | 1100 | int##w##_t res; \ |
| 971 | if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \ | 1101 | if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \ |
| 972 | return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | 1102 | return res >= INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ |
| 973 | } \ | 1103 | } \ |
| 974 | \ | 1104 | \ |
| 975 | static inline zig_u##w zig_muls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | 1105 | static inline uint##w##_t zig_muls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ |
| 976 | zig_u##w res; \ | 1106 | uint##w##_t res; \ |
| 977 | return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ | 1107 | return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \ |
| 978 | } \ | 1108 | } \ |
| 979 | \ | 1109 | \ |
| 980 | static inline zig_i##w zig_muls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | 1110 | static inline int##w##_t zig_muls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ |
| 981 | zig_i##w res; \ | 1111 | int##w##_t res; \ |
| 982 | if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \ | 1112 | if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \ |
| 983 | return (lhs ^ rhs) < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | 1113 | return (lhs ^ rhs) < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ |
| 984 | } | 1114 | } |
| 985 | zig_int_builtins(8) | 1115 | zig_int_builtins(8) |
| 986 | zig_int_builtins(16) | 1116 | zig_int_builtins(16) |
| ... | @@ -988,89 +1118,89 @@ zig_int_builtins(32) | ... | @@ -988,89 +1118,89 @@ zig_int_builtins(32) |
| 988 | zig_int_builtins(64) | 1118 | zig_int_builtins(64) |
| 989 | 1119 | ||
| 990 | #define zig_builtin8(name, val) __builtin_##name(val) | 1120 | #define zig_builtin8(name, val) __builtin_##name(val) |
| 991 | typedef zig_c_uint zig_Builtin8; | 1121 | typedef unsigned int zig_Builtin8; |
| 992 | 1122 | ||
| 993 | #define zig_builtin16(name, val) __builtin_##name(val) | 1123 | #define zig_builtin16(name, val) __builtin_##name(val) |
| 994 | typedef zig_c_uint zig_Builtin16; | 1124 | typedef unsigned int zig_Builtin16; |
| 995 | 1125 | ||
| 996 | #if INT_MIN <= INT32_MIN | 1126 | #if INT_MIN <= INT32_MIN |
| 997 | #define zig_builtin32(name, val) __builtin_##name(val) | 1127 | #define zig_builtin32(name, val) __builtin_##name(val) |
| 998 | typedef zig_c_uint zig_Builtin32; | 1128 | typedef unsigned int zig_Builtin32; |
| 999 | #elif LONG_MIN <= INT32_MIN | 1129 | #elif LONG_MIN <= INT32_MIN |
| 1000 | #define zig_builtin32(name, val) __builtin_##name##l(val) | 1130 | #define zig_builtin32(name, val) __builtin_##name##l(val) |
| 1001 | typedef zig_c_ulong zig_Builtin32; | 1131 | typedef unsigned long zig_Builtin32; |
| 1002 | #endif | 1132 | #endif |
| 1003 | 1133 | ||
| 1004 | #if INT_MIN <= INT64_MIN | 1134 | #if INT_MIN <= INT64_MIN |
| 1005 | #define zig_builtin64(name, val) __builtin_##name(val) | 1135 | #define zig_builtin64(name, val) __builtin_##name(val) |
| 1006 | typedef zig_c_uint zig_Builtin64; | 1136 | typedef unsigned int zig_Builtin64; |
| 1007 | #elif LONG_MIN <= INT64_MIN | 1137 | #elif LONG_MIN <= INT64_MIN |
| 1008 | #define zig_builtin64(name, val) __builtin_##name##l(val) | 1138 | #define zig_builtin64(name, val) __builtin_##name##l(val) |
| 1009 | typedef zig_c_ulong zig_Builtin64; | 1139 | typedef unsigned long zig_Builtin64; |
| 1010 | #elif LLONG_MIN <= INT64_MIN | 1140 | #elif LLONG_MIN <= INT64_MIN |
| 1011 | #define zig_builtin64(name, val) __builtin_##name##ll(val) | 1141 | #define zig_builtin64(name, val) __builtin_##name##ll(val) |
| 1012 | typedef zig_c_ulonglong zig_Builtin64; | 1142 | typedef unsigned long long zig_Builtin64; |
| 1013 | #endif | 1143 | #endif |
| 1014 | 1144 | ||
| 1015 | static inline zig_u8 zig_byte_swap_u8(zig_u8 val, zig_u8 bits) { | 1145 | static inline uint8_t zig_byte_swap_u8(uint8_t val, uint8_t bits) { |
| 1016 | return zig_wrap_u8(val >> (8 - bits), bits); | 1146 | return zig_wrap_u8(val >> (8 - bits), bits); |
| 1017 | } | 1147 | } |
| 1018 | 1148 | ||
| 1019 | static inline zig_i8 zig_byte_swap_i8(zig_i8 val, zig_u8 bits) { | 1149 | static inline int8_t zig_byte_swap_i8(int8_t val, uint8_t bits) { |
| 1020 | return zig_wrap_i8((zig_i8)zig_byte_swap_u8((zig_u8)val, bits), bits); | 1150 | return zig_wrap_i8((int8_t)zig_byte_swap_u8((uint8_t)val, bits), bits); |
| 1021 | } | 1151 | } |
| 1022 | 1152 | ||
| 1023 | static inline zig_u16 zig_byte_swap_u16(zig_u16 val, zig_u8 bits) { | 1153 | static inline uint16_t zig_byte_swap_u16(uint16_t val, uint8_t bits) { |
| 1024 | zig_u16 full_res; | 1154 | uint16_t full_res; |
| 1025 | #if zig_has_builtin(bswap16) || defined(zig_gnuc) | 1155 | #if zig_has_builtin(bswap16) || defined(zig_gnuc) |
| 1026 | full_res = __builtin_bswap16(val); | 1156 | full_res = __builtin_bswap16(val); |
| 1027 | #else | 1157 | #else |
| 1028 | full_res = (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 0), 8) << 8 | | 1158 | full_res = (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 0), 8) << 8 | |
| 1029 | (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 8), 8) >> 0; | 1159 | (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 8), 8) >> 0; |
| 1030 | #endif | 1160 | #endif |
| 1031 | return zig_wrap_u16(full_res >> (16 - bits), bits); | 1161 | return zig_wrap_u16(full_res >> (16 - bits), bits); |
| 1032 | } | 1162 | } |
| 1033 | 1163 | ||
| 1034 | static inline zig_i16 zig_byte_swap_i16(zig_i16 val, zig_u8 bits) { | 1164 | static inline int16_t zig_byte_swap_i16(int16_t val, uint8_t bits) { |
| 1035 | return zig_wrap_i16((zig_i16)zig_byte_swap_u16((zig_u16)val, bits), bits); | 1165 | return zig_wrap_i16((int16_t)zig_byte_swap_u16((uint16_t)val, bits), bits); |
| 1036 | } | 1166 | } |
| 1037 | 1167 | ||
| 1038 | static inline zig_u32 zig_byte_swap_u32(zig_u32 val, zig_u8 bits) { | 1168 | static inline uint32_t zig_byte_swap_u32(uint32_t val, uint8_t bits) { |
| 1039 | zig_u32 full_res; | 1169 | uint32_t full_res; |
| 1040 | #if zig_has_builtin(bswap32) || defined(zig_gnuc) | 1170 | #if zig_has_builtin(bswap32) || defined(zig_gnuc) |
| 1041 | full_res = __builtin_bswap32(val); | 1171 | full_res = __builtin_bswap32(val); |
| 1042 | #else | 1172 | #else |
| 1043 | full_res = (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 0), 16) << 16 | | 1173 | full_res = (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 0), 16) << 16 | |
| 1044 | (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 16), 16) >> 0; | 1174 | (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 16), 16) >> 0; |
| 1045 | #endif | 1175 | #endif |
| 1046 | return zig_wrap_u32(full_res >> (32 - bits), bits); | 1176 | return zig_wrap_u32(full_res >> (32 - bits), bits); |
| 1047 | } | 1177 | } |
| 1048 | 1178 | ||
| 1049 | static inline zig_i32 zig_byte_swap_i32(zig_i32 val, zig_u8 bits) { | 1179 | static inline int32_t zig_byte_swap_i32(int32_t val, uint8_t bits) { |
| 1050 | return zig_wrap_i32((zig_i32)zig_byte_swap_u32((zig_u32)val, bits), bits); | 1180 | return zig_wrap_i32((int32_t)zig_byte_swap_u32((uint32_t)val, bits), bits); |
| 1051 | } | 1181 | } |
| 1052 | 1182 | ||
| 1053 | static inline zig_u64 zig_byte_swap_u64(zig_u64 val, zig_u8 bits) { | 1183 | static inline uint64_t zig_byte_swap_u64(uint64_t val, uint8_t bits) { |
| 1054 | zig_u64 full_res; | 1184 | uint64_t full_res; |
| 1055 | #if zig_has_builtin(bswap64) || defined(zig_gnuc) | 1185 | #if zig_has_builtin(bswap64) || defined(zig_gnuc) |
| 1056 | full_res = __builtin_bswap64(val); | 1186 | full_res = __builtin_bswap64(val); |
| 1057 | #else | 1187 | #else |
| 1058 | full_res = (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 0), 32) << 32 | | 1188 | full_res = (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 0), 32) << 32 | |
| 1059 | (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 32), 32) >> 0; | 1189 | (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 32), 32) >> 0; |
| 1060 | #endif | 1190 | #endif |
| 1061 | return zig_wrap_u64(full_res >> (64 - bits), bits); | 1191 | return zig_wrap_u64(full_res >> (64 - bits), bits); |
| 1062 | } | 1192 | } |
| 1063 | 1193 | ||
| 1064 | static inline zig_i64 zig_byte_swap_i64(zig_i64 val, zig_u8 bits) { | 1194 | static inline int64_t zig_byte_swap_i64(int64_t val, uint8_t bits) { |
| 1065 | return zig_wrap_i64((zig_i64)zig_byte_swap_u64((zig_u64)val, bits), bits); | 1195 | return zig_wrap_i64((int64_t)zig_byte_swap_u64((uint64_t)val, bits), bits); |
| 1066 | } | 1196 | } |
| 1067 | 1197 | ||
| 1068 | static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { | 1198 | static inline uint8_t zig_bit_reverse_u8(uint8_t val, uint8_t bits) { |
| 1069 | zig_u8 full_res; | 1199 | uint8_t full_res; |
| 1070 | #if zig_has_builtin(bitreverse8) | 1200 | #if zig_has_builtin(bitreverse8) |
| 1071 | full_res = __builtin_bitreverse8(val); | 1201 | full_res = __builtin_bitreverse8(val); |
| 1072 | #else | 1202 | #else |
| 1073 | static zig_u8 const lut[0x10] = { | 1203 | static uint8_t const lut[0x10] = { |
| 1074 | 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, | 1204 | 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, |
| 1075 | 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf | 1205 | 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf |
| 1076 | }; | 1206 | }; |
| ... | @@ -1079,62 +1209,62 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { | ... | @@ -1079,62 +1209,62 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { |
| 1079 | return zig_wrap_u8(full_res >> (8 - bits), bits); | 1209 | return zig_wrap_u8(full_res >> (8 - bits), bits); |
| 1080 | } | 1210 | } |
| 1081 | 1211 | ||
| 1082 | static inline zig_i8 zig_bit_reverse_i8(zig_i8 val, zig_u8 bits) { | 1212 | static inline int8_t zig_bit_reverse_i8(int8_t val, uint8_t bits) { |
| 1083 | return zig_wrap_i8((zig_i8)zig_bit_reverse_u8((zig_u8)val, bits), bits); | 1213 | return zig_wrap_i8((int8_t)zig_bit_reverse_u8((uint8_t)val, bits), bits); |
| 1084 | } | 1214 | } |
| 1085 | 1215 | ||
| 1086 | static inline zig_u16 zig_bit_reverse_u16(zig_u16 val, zig_u8 bits) { | 1216 | static inline uint16_t zig_bit_reverse_u16(uint16_t val, uint8_t bits) { |
| 1087 | zig_u16 full_res; | 1217 | uint16_t full_res; |
| 1088 | #if zig_has_builtin(bitreverse16) | 1218 | #if zig_has_builtin(bitreverse16) |
| 1089 | full_res = __builtin_bitreverse16(val); | 1219 | full_res = __builtin_bitreverse16(val); |
| 1090 | #else | 1220 | #else |
| 1091 | full_res = (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 0), 8) << 8 | | 1221 | full_res = (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 0), 8) << 8 | |
| 1092 | (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 8), 8) >> 0; | 1222 | (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 8), 8) >> 0; |
| 1093 | #endif | 1223 | #endif |
| 1094 | return zig_wrap_u16(full_res >> (16 - bits), bits); | 1224 | return zig_wrap_u16(full_res >> (16 - bits), bits); |
| 1095 | } | 1225 | } |
| 1096 | 1226 | ||
| 1097 | static inline zig_i16 zig_bit_reverse_i16(zig_i16 val, zig_u8 bits) { | 1227 | static inline int16_t zig_bit_reverse_i16(int16_t val, uint8_t bits) { |
| 1098 | return zig_wrap_i16((zig_i16)zig_bit_reverse_u16((zig_u16)val, bits), bits); | 1228 | return zig_wrap_i16((int16_t)zig_bit_reverse_u16((uint16_t)val, bits), bits); |
| 1099 | } | 1229 | } |
| 1100 | 1230 | ||
| 1101 | static inline zig_u32 zig_bit_reverse_u32(zig_u32 val, zig_u8 bits) { | 1231 | static inline uint32_t zig_bit_reverse_u32(uint32_t val, uint8_t bits) { |
| 1102 | zig_u32 full_res; | 1232 | uint32_t full_res; |
| 1103 | #if zig_has_builtin(bitreverse32) | 1233 | #if zig_has_builtin(bitreverse32) |
| 1104 | full_res = __builtin_bitreverse32(val); | 1234 | full_res = __builtin_bitreverse32(val); |
| 1105 | #else | 1235 | #else |
| 1106 | full_res = (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 0), 16) << 16 | | 1236 | full_res = (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 0), 16) << 16 | |
| 1107 | (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 16), 16) >> 0; | 1237 | (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 16), 16) >> 0; |
| 1108 | #endif | 1238 | #endif |
| 1109 | return zig_wrap_u32(full_res >> (32 - bits), bits); | 1239 | return zig_wrap_u32(full_res >> (32 - bits), bits); |
| 1110 | } | 1240 | } |
| 1111 | 1241 | ||
| 1112 | static inline zig_i32 zig_bit_reverse_i32(zig_i32 val, zig_u8 bits) { | 1242 | static inline int32_t zig_bit_reverse_i32(int32_t val, uint8_t bits) { |
| 1113 | return zig_wrap_i32((zig_i32)zig_bit_reverse_u32((zig_u32)val, bits), bits); | 1243 | return zig_wrap_i32((int32_t)zig_bit_reverse_u32((uint32_t)val, bits), bits); |
| 1114 | } | 1244 | } |
| 1115 | 1245 | ||
| 1116 | static inline zig_u64 zig_bit_reverse_u64(zig_u64 val, zig_u8 bits) { | 1246 | static inline uint64_t zig_bit_reverse_u64(uint64_t val, uint8_t bits) { |
| 1117 | zig_u64 full_res; | 1247 | uint64_t full_res; |
| 1118 | #if zig_has_builtin(bitreverse64) | 1248 | #if zig_has_builtin(bitreverse64) |
| 1119 | full_res = __builtin_bitreverse64(val); | 1249 | full_res = __builtin_bitreverse64(val); |
| 1120 | #else | 1250 | #else |
| 1121 | full_res = (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 0), 32) << 32 | | 1251 | full_res = (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 0), 32) << 32 | |
| 1122 | (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 32), 32) >> 0; | 1252 | (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 32), 32) >> 0; |
| 1123 | #endif | 1253 | #endif |
| 1124 | return zig_wrap_u64(full_res >> (64 - bits), bits); | 1254 | return zig_wrap_u64(full_res >> (64 - bits), bits); |
| 1125 | } | 1255 | } |
| 1126 | 1256 | ||
| 1127 | static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { | 1257 | static inline int64_t zig_bit_reverse_i64(int64_t val, uint8_t bits) { |
| 1128 | return zig_wrap_i64((zig_i64)zig_bit_reverse_u64((zig_u64)val, bits), bits); | 1258 | return zig_wrap_i64((int64_t)zig_bit_reverse_u64((uint64_t)val, bits), bits); |
| 1129 | } | 1259 | } |
| 1130 | 1260 | ||
| 1131 | #define zig_builtin_popcount_common(w) \ | 1261 | #define zig_builtin_popcount_common(w) \ |
| 1132 | static inline zig_u8 zig_popcount_i##w(zig_i##w val, zig_u8 bits) { \ | 1262 | static inline uint8_t zig_popcount_i##w(int##w##_t val, uint8_t bits) { \ |
| 1133 | return zig_popcount_u##w((zig_u##w)val, bits); \ | 1263 | return zig_popcount_u##w((uint##w##_t)val, bits); \ |
| 1134 | } | 1264 | } |
| 1135 | #if zig_has_builtin(popcount) || defined(zig_gnuc) | 1265 | #if zig_has_builtin(popcount) || defined(zig_gnuc) |
| 1136 | #define zig_builtin_popcount(w) \ | 1266 | #define zig_builtin_popcount(w) \ |
| 1137 | static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ | 1267 | static inline uint8_t zig_popcount_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1138 | (void)bits; \ | 1268 | (void)bits; \ |
| 1139 | return zig_builtin##w(popcount, val); \ | 1269 | return zig_builtin##w(popcount, val); \ |
| 1140 | } \ | 1270 | } \ |
| ... | @@ -1142,12 +1272,12 @@ static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { | ... | @@ -1142,12 +1272,12 @@ static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { |
| 1142 | zig_builtin_popcount_common(w) | 1272 | zig_builtin_popcount_common(w) |
| 1143 | #else | 1273 | #else |
| 1144 | #define zig_builtin_popcount(w) \ | 1274 | #define zig_builtin_popcount(w) \ |
| 1145 | static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ | 1275 | static inline uint8_t zig_popcount_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1146 | (void)bits; \ | 1276 | (void)bits; \ |
| 1147 | zig_u##w temp = val - ((val >> 1) & (zig_maxInt_u##w / 3)); \ | 1277 | uint##w##_t temp = val - ((val >> 1) & (UINT##w##_MAX / 3)); \ |
| 1148 | temp = (temp & (zig_maxInt_u##w / 5)) + ((temp >> 2) & (zig_maxInt_u##w / 5)); \ | 1278 | temp = (temp & (UINT##w##_MAX / 5)) + ((temp >> 2) & (UINT##w##_MAX / 5)); \ |
| 1149 | temp = (temp + (temp >> 4)) & (zig_maxInt_u##w / 17); \ | 1279 | temp = (temp + (temp >> 4)) & (UINT##w##_MAX / 17); \ |
| 1150 | return temp * (zig_maxInt_u##w / 255) >> (w - 8); \ | 1280 | return temp * (UINT##w##_MAX / 255) >> (w - 8); \ |
| 1151 | } \ | 1281 | } \ |
| 1152 | \ | 1282 | \ |
| 1153 | zig_builtin_popcount_common(w) | 1283 | zig_builtin_popcount_common(w) |
| ... | @@ -1158,12 +1288,12 @@ zig_builtin_popcount(32) | ... | @@ -1158,12 +1288,12 @@ zig_builtin_popcount(32) |
| 1158 | zig_builtin_popcount(64) | 1288 | zig_builtin_popcount(64) |
| 1159 | 1289 | ||
| 1160 | #define zig_builtin_ctz_common(w) \ | 1290 | #define zig_builtin_ctz_common(w) \ |
| 1161 | static inline zig_u8 zig_ctz_i##w(zig_i##w val, zig_u8 bits) { \ | 1291 | static inline uint8_t zig_ctz_i##w(int##w##_t val, uint8_t bits) { \ |
| 1162 | return zig_ctz_u##w((zig_u##w)val, bits); \ | 1292 | return zig_ctz_u##w((uint##w##_t)val, bits); \ |
| 1163 | } | 1293 | } |
| 1164 | #if zig_has_builtin(ctz) || defined(zig_gnuc) | 1294 | #if zig_has_builtin(ctz) || defined(zig_gnuc) |
| 1165 | #define zig_builtin_ctz(w) \ | 1295 | #define zig_builtin_ctz(w) \ |
| 1166 | static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ | 1296 | static inline uint8_t zig_ctz_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1167 | if (val == 0) return bits; \ | 1297 | if (val == 0) return bits; \ |
| 1168 | return zig_builtin##w(ctz, val); \ | 1298 | return zig_builtin##w(ctz, val); \ |
| 1169 | } \ | 1299 | } \ |
| ... | @@ -1171,7 +1301,7 @@ zig_builtin_popcount(64) | ... | @@ -1171,7 +1301,7 @@ zig_builtin_popcount(64) |
| 1171 | zig_builtin_ctz_common(w) | 1301 | zig_builtin_ctz_common(w) |
| 1172 | #else | 1302 | #else |
| 1173 | #define zig_builtin_ctz(w) \ | 1303 | #define zig_builtin_ctz(w) \ |
| 1174 | static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ | 1304 | static inline uint8_t zig_ctz_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1175 | return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \ | 1305 | return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \ |
| 1176 | } \ | 1306 | } \ |
| 1177 | \ | 1307 | \ |
| ... | @@ -1183,12 +1313,12 @@ zig_builtin_ctz(32) | ... | @@ -1183,12 +1313,12 @@ zig_builtin_ctz(32) |
| 1183 | zig_builtin_ctz(64) | 1313 | zig_builtin_ctz(64) |
| 1184 | 1314 | ||
| 1185 | #define zig_builtin_clz_common(w) \ | 1315 | #define zig_builtin_clz_common(w) \ |
| 1186 | static inline zig_u8 zig_clz_i##w(zig_i##w val, zig_u8 bits) { \ | 1316 | static inline uint8_t zig_clz_i##w(int##w##_t val, uint8_t bits) { \ |
| 1187 | return zig_clz_u##w((zig_u##w)val, bits); \ | 1317 | return zig_clz_u##w((uint##w##_t)val, bits); \ |
| 1188 | } | 1318 | } |
| 1189 | #if zig_has_builtin(clz) || defined(zig_gnuc) | 1319 | #if zig_has_builtin(clz) || defined(zig_gnuc) |
| 1190 | #define zig_builtin_clz(w) \ | 1320 | #define zig_builtin_clz(w) \ |
| 1191 | static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ | 1321 | static inline uint8_t zig_clz_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1192 | if (val == 0) return bits; \ | 1322 | if (val == 0) return bits; \ |
| 1193 | return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \ | 1323 | return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \ |
| 1194 | } \ | 1324 | } \ |
| ... | @@ -1196,7 +1326,7 @@ zig_builtin_ctz(64) | ... | @@ -1196,7 +1326,7 @@ zig_builtin_ctz(64) |
| 1196 | zig_builtin_clz_common(w) | 1326 | zig_builtin_clz_common(w) |
| 1197 | #else | 1327 | #else |
| 1198 | #define zig_builtin_clz(w) \ | 1328 | #define zig_builtin_clz(w) \ |
| 1199 | static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ | 1329 | static inline uint8_t zig_clz_u##w(uint##w##_t val, uint8_t bits) { \ |
| 1200 | return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \ | 1330 | return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \ |
| 1201 | } \ | 1331 | } \ |
| 1202 | \ | 1332 | \ |
| ... | @@ -1207,7 +1337,7 @@ zig_builtin_clz(16) | ... | @@ -1207,7 +1337,7 @@ zig_builtin_clz(16) |
| 1207 | zig_builtin_clz(32) | 1337 | zig_builtin_clz(32) |
| 1208 | zig_builtin_clz(64) | 1338 | zig_builtin_clz(64) |
| 1209 | 1339 | ||
| 1210 | /* ======================== 128-bit Integer Routines ======================== */ | 1340 | /* ======================== 128-bit Integer Support ========================= */ |
| 1211 | 1341 | ||
| 1212 | #if !defined(zig_has_int128) | 1342 | #if !defined(zig_has_int128) |
| 1213 | # if defined(__SIZEOF_INT128__) | 1343 | # if defined(__SIZEOF_INT128__) |
| ... | @@ -1222,18 +1352,18 @@ zig_builtin_clz(64) | ... | @@ -1222,18 +1352,18 @@ zig_builtin_clz(64) |
| 1222 | typedef unsigned __int128 zig_u128; | 1352 | typedef unsigned __int128 zig_u128; |
| 1223 | typedef signed __int128 zig_i128; | 1353 | typedef signed __int128 zig_i128; |
| 1224 | 1354 | ||
| 1225 | #define zig_as_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) | 1355 | #define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) |
| 1226 | #define zig_as_i128(hi, lo) ((zig_i128)zig_as_u128(hi, lo)) | 1356 | #define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo)) |
| 1227 | #define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) | 1357 | #define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo) |
| 1228 | #define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) | 1358 | #define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo) |
| 1229 | #define zig_hi_u128(val) ((zig_u64)((val) >> 64)) | 1359 | #define zig_hi_u128(val) ((uint64_t)((val) >> 64)) |
| 1230 | #define zig_lo_u128(val) ((zig_u64)((val) >> 0)) | 1360 | #define zig_lo_u128(val) ((uint64_t)((val) >> 0)) |
| 1231 | #define zig_hi_i128(val) ((zig_i64)((val) >> 64)) | 1361 | #define zig_hi_i128(val) (( int64_t)((val) >> 64)) |
| 1232 | #define zig_lo_i128(val) ((zig_u64)((val) >> 0)) | 1362 | #define zig_lo_i128(val) ((uint64_t)((val) >> 0)) |
| 1233 | #define zig_bitcast_u128(val) ((zig_u128)(val)) | 1363 | #define zig_bitcast_u128(val) ((zig_u128)(val)) |
| 1234 | #define zig_bitcast_i128(val) ((zig_i128)(val)) | 1364 | #define zig_bitcast_i128(val) ((zig_i128)(val)) |
| 1235 | #define zig_cmp_int128(Type) \ | 1365 | #define zig_cmp_int128(Type) \ |
| 1236 | static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 1366 | static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 1237 | return (lhs > rhs) - (lhs < rhs); \ | 1367 | return (lhs > rhs) - (lhs < rhs); \ |
| 1238 | } | 1368 | } |
| 1239 | #define zig_bit_int128(Type, operation, operator) \ | 1369 | #define zig_bit_int128(Type, operation, operator) \ |
| ... | @@ -1244,31 +1374,31 @@ typedef signed __int128 zig_i128; | ... | @@ -1244,31 +1374,31 @@ typedef signed __int128 zig_i128; |
| 1244 | #else /* zig_has_int128 */ | 1374 | #else /* zig_has_int128 */ |
| 1245 | 1375 | ||
| 1246 | #if __LITTLE_ENDIAN__ || _MSC_VER | 1376 | #if __LITTLE_ENDIAN__ || _MSC_VER |
| 1247 | typedef struct { zig_align(16) zig_u64 lo; zig_u64 hi; } zig_u128; | 1377 | typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128; |
| 1248 | typedef struct { zig_align(16) zig_u64 lo; zig_i64 hi; } zig_i128; | 1378 | typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128; |
| 1249 | #else | 1379 | #else |
| 1250 | typedef struct { zig_align(16) zig_u64 hi; zig_u64 lo; } zig_u128; | 1380 | typedef struct { zig_align(16) uint64_t hi; uint64_t lo; } zig_u128; |
| 1251 | typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; | 1381 | typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128; |
| 1252 | #endif | 1382 | #endif |
| 1253 | 1383 | ||
| 1254 | #define zig_as_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) | 1384 | #define zig_make_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) |
| 1255 | #define zig_as_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) | 1385 | #define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) |
| 1256 | 1386 | ||
| 1257 | #if _MSC_VER | 1387 | #if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */ |
| 1258 | #define zig_as_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } | 1388 | #define zig_make_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } |
| 1259 | #define zig_as_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } | 1389 | #define zig_make_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } |
| 1260 | #else | 1390 | #else /* But non-MSVC doesn't like the unprotected commas */ |
| 1261 | #define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) | 1391 | #define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo) |
| 1262 | #define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) | 1392 | #define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo) |
| 1263 | #endif | 1393 | #endif |
| 1264 | #define zig_hi_u128(val) ((val).hi) | 1394 | #define zig_hi_u128(val) ((val).hi) |
| 1265 | #define zig_lo_u128(val) ((val).lo) | 1395 | #define zig_lo_u128(val) ((val).lo) |
| 1266 | #define zig_hi_i128(val) ((val).hi) | 1396 | #define zig_hi_i128(val) ((val).hi) |
| 1267 | #define zig_lo_i128(val) ((val).lo) | 1397 | #define zig_lo_i128(val) ((val).lo) |
| 1268 | #define zig_bitcast_u128(val) zig_as_u128((zig_u64)(val).hi, (val).lo) | 1398 | #define zig_bitcast_u128(val) zig_make_u128((uint64_t)(val).hi, (val).lo) |
| 1269 | #define zig_bitcast_i128(val) zig_as_i128((zig_i64)(val).hi, (val).lo) | 1399 | #define zig_bitcast_i128(val) zig_make_i128(( int64_t)(val).hi, (val).lo) |
| 1270 | #define zig_cmp_int128(Type) \ | 1400 | #define zig_cmp_int128(Type) \ |
| 1271 | static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 1401 | static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 1272 | return (lhs.hi == rhs.hi) \ | 1402 | return (lhs.hi == rhs.hi) \ |
| 1273 | ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \ | 1403 | ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \ |
| 1274 | : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \ | 1404 | : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \ |
| ... | @@ -1280,10 +1410,10 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; | ... | @@ -1280,10 +1410,10 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; |
| 1280 | 1410 | ||
| 1281 | #endif /* zig_has_int128 */ | 1411 | #endif /* zig_has_int128 */ |
| 1282 | 1412 | ||
| 1283 | #define zig_minInt_u128 zig_as_u128(zig_minInt_u64, zig_minInt_u64) | 1413 | #define zig_minInt_u128 zig_make_u128(zig_minInt_u64, zig_minInt_u64) |
| 1284 | #define zig_maxInt_u128 zig_as_u128(zig_maxInt_u64, zig_maxInt_u64) | 1414 | #define zig_maxInt_u128 zig_make_u128(zig_maxInt_u64, zig_maxInt_u64) |
| 1285 | #define zig_minInt_i128 zig_as_i128(zig_minInt_i64, zig_minInt_u64) | 1415 | #define zig_minInt_i128 zig_make_i128(zig_minInt_i64, zig_minInt_u64) |
| 1286 | #define zig_maxInt_i128 zig_as_i128(zig_maxInt_i64, zig_maxInt_u64) | 1416 | #define zig_maxInt_i128 zig_make_i128(zig_maxInt_i64, zig_maxInt_u64) |
| 1287 | 1417 | ||
| 1288 | zig_cmp_int128(u128) | 1418 | zig_cmp_int128(u128) |
| 1289 | zig_cmp_int128(i128) | 1419 | zig_cmp_int128(i128) |
| ... | @@ -1297,28 +1427,33 @@ zig_bit_int128(i128, or, |) | ... | @@ -1297,28 +1427,33 @@ zig_bit_int128(i128, or, |) |
| 1297 | zig_bit_int128(u128, xor, ^) | 1427 | zig_bit_int128(u128, xor, ^) |
| 1298 | zig_bit_int128(i128, xor, ^) | 1428 | zig_bit_int128(i128, xor, ^) |
| 1299 | 1429 | ||
| 1300 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs); | 1430 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs); |
| 1301 | 1431 | ||
| 1302 | #if zig_has_int128 | 1432 | #if zig_has_int128 |
| 1303 | 1433 | ||
| 1304 | static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { | 1434 | static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) { |
| 1305 | return val ^ zig_maxInt(u128, bits); | 1435 | return val ^ zig_maxInt_u(128, bits); |
| 1306 | } | 1436 | } |
| 1307 | 1437 | ||
| 1308 | static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { | 1438 | static inline zig_i128 zig_not_i128(zig_i128 val, uint8_t bits) { |
| 1309 | (void)bits; | 1439 | (void)bits; |
| 1310 | return ~val; | 1440 | return ~val; |
| 1311 | } | 1441 | } |
| 1312 | 1442 | ||
| 1313 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { | 1443 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) { |
| 1314 | return lhs >> rhs; | 1444 | return lhs >> rhs; |
| 1315 | } | 1445 | } |
| 1316 | 1446 | ||
| 1317 | static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { | 1447 | static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) { |
| 1318 | return lhs << rhs; | 1448 | return lhs << rhs; |
| 1319 | } | 1449 | } |
| 1320 | 1450 | ||
| 1321 | static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { | 1451 | static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) { |
| 1452 | zig_i128 sign_mask = lhs < zig_make_i128(0, 0) ? -zig_make_i128(0, 1) : zig_make_i128(0, 0); | ||
| 1453 | return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) { | ||
| 1322 | return lhs << rhs; | 1457 | return lhs << rhs; |
| 1323 | } | 1458 | } |
| 1324 | 1459 | ||
| ... | @@ -1363,40 +1498,46 @@ static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { | ... | @@ -1363,40 +1498,46 @@ static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1363 | } | 1498 | } |
| 1364 | 1499 | ||
| 1365 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { | 1500 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1366 | return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_as_i128(0, 0)); | 1501 | return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_make_i128(0, 0)); |
| 1367 | } | 1502 | } |
| 1368 | 1503 | ||
| 1369 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { | 1504 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1370 | zig_i128 rem = zig_rem_i128(lhs, rhs); | 1505 | zig_i128 rem = zig_rem_i128(lhs, rhs); |
| 1371 | return rem + (((lhs ^ rhs) & rem) < zig_as_i128(0, 0) ? rhs : zig_as_i128(0, 0)); | 1506 | return rem + (((lhs ^ rhs) & rem) < zig_make_i128(0, 0) ? rhs : zig_make_i128(0, 0)); |
| 1372 | } | 1507 | } |
| 1373 | 1508 | ||
| 1374 | #else /* zig_has_int128 */ | 1509 | #else /* zig_has_int128 */ |
| 1375 | 1510 | ||
| 1376 | static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { | 1511 | static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) { |
| 1377 | return (zig_u128){ .hi = zig_not_u64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; | 1512 | return (zig_u128){ .hi = zig_not_u64(val.hi, bits - UINT8_C(64)), .lo = zig_not_u64(val.lo, UINT8_C(64)) }; |
| 1378 | } | 1513 | } |
| 1379 | 1514 | ||
| 1380 | static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { | 1515 | static inline zig_i128 zig_not_i128(zig_i128 val, uint8_t bits) { |
| 1381 | return (zig_i128){ .hi = zig_not_i64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; | 1516 | return (zig_i128){ .hi = zig_not_i64(val.hi, bits - UINT8_C(64)), .lo = zig_not_u64(val.lo, UINT8_C(64)) }; |
| 1382 | } | 1517 | } |
| 1383 | 1518 | ||
| 1384 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { | 1519 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) { |
| 1385 | if (rhs == zig_as_u8(0)) return lhs; | 1520 | if (rhs == UINT8_C(0)) return lhs; |
| 1386 | if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - zig_as_u8(64)) }; | 1521 | if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - UINT8_C(64)) }; |
| 1387 | return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (zig_as_u8(64) - rhs) | lhs.lo >> rhs }; | 1522 | return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (UINT8_C(64) - rhs) | lhs.lo >> rhs }; |
| 1388 | } | 1523 | } |
| 1389 | 1524 | ||
| 1390 | static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { | 1525 | static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) { |
| 1391 | if (rhs == zig_as_u8(0)) return lhs; | 1526 | if (rhs == UINT8_C(0)) return lhs; |
| 1392 | if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; | 1527 | if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 }; |
| 1393 | return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; | 1528 | return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs }; |
| 1394 | } | 1529 | } |
| 1395 | 1530 | ||
| 1396 | static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { | 1531 | static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) { |
| 1397 | if (rhs == zig_as_u8(0)) return lhs; | 1532 | if (rhs == UINT8_C(0)) return lhs; |
| 1398 | if (rhs >= zig_as_u8(64)) return (zig_i128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; | 1533 | if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = zig_shr_i64(lhs.hi, 63), .lo = zig_shr_i64(lhs.hi, (rhs - UINT8_C(64))) }; |
| 1399 | return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; | 1534 | return (zig_i128){ .hi = zig_shr_i64(lhs.hi, rhs), .lo = lhs.lo >> rhs | (uint64_t)lhs.hi << (UINT8_C(64) - rhs) }; |
| 1535 | } | ||
| 1536 | |||
| 1537 | static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) { | ||
| 1538 | if (rhs == UINT8_C(0)) return lhs; | ||
| 1539 | if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 }; | ||
| 1540 | return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs }; | ||
| 1400 | } | 1541 | } |
| 1401 | 1542 | ||
| 1402 | static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { | 1543 | static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { |
| ... | @@ -1424,14 +1565,14 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { | ... | @@ -1424,14 +1565,14 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1424 | } | 1565 | } |
| 1425 | 1566 | ||
| 1426 | zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs); | 1567 | zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs); |
| 1427 | static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1428 | return zig_bitcast_u128(__multi3(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs))); | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { | 1568 | static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1432 | return __multi3(lhs, rhs); | 1569 | return __multi3(lhs, rhs); |
| 1433 | } | 1570 | } |
| 1434 | 1571 | ||
| 1572 | static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1573 | return zig_bitcast_u128(zig_mul_i128(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs))); | ||
| 1574 | } | ||
| 1575 | |||
| 1435 | zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); | 1576 | zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); |
| 1436 | static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { | 1577 | static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { |
| 1437 | return __udivti3(lhs, rhs); | 1578 | return __udivti3(lhs, rhs); |
| ... | @@ -1454,11 +1595,11 @@ static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { | ... | @@ -1454,11 +1595,11 @@ static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1454 | 1595 | ||
| 1455 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { | 1596 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1456 | zig_i128 rem = zig_rem_i128(lhs, rhs); | 1597 | zig_i128 rem = zig_rem_i128(lhs, rhs); |
| 1457 | return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < zig_as_i64(0) ? rhs : zig_as_i128(0, 0))); | 1598 | return zig_add_i128(rem, ((lhs.hi ^ rhs.hi) & rem.hi) < INT64_C(0) ? rhs : zig_make_i128(0, 0)); |
| 1458 | } | 1599 | } |
| 1459 | 1600 | ||
| 1460 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { | 1601 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1461 | return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_as_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_as_i128(0, 0)) < zig_as_i32(0))); | 1602 | return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_make_i128(0, 0)) < INT32_C(0))); |
| 1462 | } | 1603 | } |
| 1463 | 1604 | ||
| 1464 | #endif /* zig_has_int128 */ | 1605 | #endif /* zig_has_int128 */ |
| ... | @@ -1471,323 +1612,294 @@ static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) { | ... | @@ -1471,323 +1612,294 @@ static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) { |
| 1471 | } | 1612 | } |
| 1472 | 1613 | ||
| 1473 | static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) { | 1614 | static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) { |
| 1474 | return zig_cmp_u128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; | 1615 | return zig_cmp_u128(lhs, rhs) < INT32_C(0) ? lhs : rhs; |
| 1475 | } | 1616 | } |
| 1476 | 1617 | ||
| 1477 | static inline zig_i128 zig_min_i128(zig_i128 lhs, zig_i128 rhs) { | 1618 | static inline zig_i128 zig_min_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1478 | return zig_cmp_i128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; | 1619 | return zig_cmp_i128(lhs, rhs) < INT32_C(0) ? lhs : rhs; |
| 1479 | } | 1620 | } |
| 1480 | 1621 | ||
| 1481 | static inline zig_u128 zig_max_u128(zig_u128 lhs, zig_u128 rhs) { | 1622 | static inline zig_u128 zig_max_u128(zig_u128 lhs, zig_u128 rhs) { |
| 1482 | return zig_cmp_u128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; | 1623 | return zig_cmp_u128(lhs, rhs) > INT32_C(0) ? lhs : rhs; |
| 1483 | } | 1624 | } |
| 1484 | 1625 | ||
| 1485 | static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) { | 1626 | static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) { |
| 1486 | return zig_cmp_i128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; | 1627 | return zig_cmp_i128(lhs, rhs) > INT32_C(0) ? lhs : rhs; |
| 1487 | } | ||
| 1488 | |||
| 1489 | static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) { | ||
| 1490 | zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_sub_i128(zig_as_i128(0, 0), zig_as_i128(0, 1)) : zig_as_i128(0, 0); | ||
| 1491 | return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask); | ||
| 1492 | } | 1628 | } |
| 1493 | 1629 | ||
| 1494 | static inline zig_u128 zig_wrap_u128(zig_u128 val, zig_u8 bits) { | 1630 | static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) { |
| 1495 | return zig_and_u128(val, zig_maxInt(u128, bits)); | 1631 | return zig_and_u128(val, zig_maxInt_u(128, bits)); |
| 1496 | } | 1632 | } |
| 1497 | 1633 | ||
| 1498 | static inline zig_i128 zig_wrap_i128(zig_i128 val, zig_u8 bits) { | 1634 | static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t bits) { |
| 1499 | return zig_as_i128(zig_wrap_i64(zig_hi_i128(val), bits - zig_as_u8(64)), zig_lo_i128(val)); | 1635 | return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val)); |
| 1500 | } | 1636 | } |
| 1501 | 1637 | ||
| 1502 | static inline zig_u128 zig_shlw_u128(zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { | 1638 | static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) { |
| 1503 | return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits); | 1639 | return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits); |
| 1504 | } | 1640 | } |
| 1505 | 1641 | ||
| 1506 | static inline zig_i128 zig_shlw_i128(zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { | 1642 | static inline zig_i128 zig_shlw_i128(zig_i128 lhs, uint8_t rhs, uint8_t bits) { |
| 1507 | return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits); | 1643 | return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits); |
| 1508 | } | 1644 | } |
| 1509 | 1645 | ||
| 1510 | static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1646 | static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1511 | return zig_wrap_u128(zig_add_u128(lhs, rhs), bits); | 1647 | return zig_wrap_u128(zig_add_u128(lhs, rhs), bits); |
| 1512 | } | 1648 | } |
| 1513 | 1649 | ||
| 1514 | static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1650 | static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1515 | return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); | 1651 | return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); |
| 1516 | } | 1652 | } |
| 1517 | 1653 | ||
| 1518 | static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1654 | static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1519 | return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits); | 1655 | return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits); |
| 1520 | } | 1656 | } |
| 1521 | 1657 | ||
| 1522 | static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1658 | static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1523 | return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); | 1659 | return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); |
| 1524 | } | 1660 | } |
| 1525 | 1661 | ||
| 1526 | static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1662 | static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1527 | return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); | 1663 | return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); |
| 1528 | } | 1664 | } |
| 1529 | 1665 | ||
| 1530 | static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1666 | static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1531 | return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); | 1667 | return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); |
| 1532 | } | 1668 | } |
| 1533 | 1669 | ||
| 1534 | #if zig_has_int128 | 1670 | #if zig_has_int128 |
| 1535 | 1671 | ||
| 1536 | static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1672 | static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1537 | #if zig_has_builtin(add_overflow) | 1673 | #if zig_has_builtin(add_overflow) |
| 1538 | zig_u128 full_res; | 1674 | zig_u128 full_res; |
| 1539 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 1675 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 1540 | *res = zig_wrap_u128(full_res, bits); | 1676 | *res = zig_wrap_u128(full_res, bits); |
| 1541 | return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); | 1677 | return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); |
| 1542 | #else | 1678 | #else |
| 1543 | *res = zig_addw_u128(lhs, rhs, bits); | 1679 | *res = zig_addw_u128(lhs, rhs, bits); |
| 1544 | return *res < lhs; | 1680 | return *res < lhs; |
| 1545 | #endif | 1681 | #endif |
| 1546 | } | 1682 | } |
| 1547 | 1683 | ||
| 1548 | zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1684 | zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, int *overflow); |
| 1549 | static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1685 | static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1550 | #if zig_has_builtin(add_overflow) | 1686 | #if zig_has_builtin(add_overflow) |
| 1551 | zig_i128 full_res; | 1687 | zig_i128 full_res; |
| 1552 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | 1688 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); |
| 1553 | #else | 1689 | #else |
| 1554 | zig_c_int overflow_int; | 1690 | int overflow_int; |
| 1555 | zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); | 1691 | zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); |
| 1556 | bool overflow = overflow_int != 0; | 1692 | bool overflow = overflow_int != 0; |
| 1557 | #endif | 1693 | #endif |
| 1558 | *res = zig_wrap_i128(full_res, bits); | 1694 | *res = zig_wrap_i128(full_res, bits); |
| 1559 | return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); | 1695 | return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); |
| 1560 | } | 1696 | } |
| 1561 | 1697 | ||
| 1562 | static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1698 | static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1563 | #if zig_has_builtin(sub_overflow) | 1699 | #if zig_has_builtin(sub_overflow) |
| 1564 | zig_u128 full_res; | 1700 | zig_u128 full_res; |
| 1565 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 1701 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 1566 | *res = zig_wrap_u128(full_res, bits); | 1702 | *res = zig_wrap_u128(full_res, bits); |
| 1567 | return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); | 1703 | return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); |
| 1568 | #else | 1704 | #else |
| 1569 | *res = zig_subw_u128(lhs, rhs, bits); | 1705 | *res = zig_subw_u128(lhs, rhs, bits); |
| 1570 | return *res > lhs; | 1706 | return *res > lhs; |
| 1571 | #endif | 1707 | #endif |
| 1572 | } | 1708 | } |
| 1573 | 1709 | ||
| 1574 | zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1710 | zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, int *overflow); |
| 1575 | static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1711 | static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1576 | #if zig_has_builtin(sub_overflow) | 1712 | #if zig_has_builtin(sub_overflow) |
| 1577 | zig_i128 full_res; | 1713 | zig_i128 full_res; |
| 1578 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | 1714 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); |
| 1579 | #else | 1715 | #else |
| 1580 | zig_c_int overflow_int; | 1716 | int overflow_int; |
| 1581 | zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); | 1717 | zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); |
| 1582 | bool overflow = overflow_int != 0; | 1718 | bool overflow = overflow_int != 0; |
| 1583 | #endif | 1719 | #endif |
| 1584 | *res = zig_wrap_i128(full_res, bits); | 1720 | *res = zig_wrap_i128(full_res, bits); |
| 1585 | return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); | 1721 | return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); |
| 1586 | } | 1722 | } |
| 1587 | 1723 | ||
| 1588 | static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1724 | static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1589 | #if zig_has_builtin(mul_overflow) | 1725 | #if zig_has_builtin(mul_overflow) |
| 1590 | zig_u128 full_res; | 1726 | zig_u128 full_res; |
| 1591 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 1727 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 1592 | *res = zig_wrap_u128(full_res, bits); | 1728 | *res = zig_wrap_u128(full_res, bits); |
| 1593 | return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); | 1729 | return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); |
| 1594 | #else | 1730 | #else |
| 1595 | *res = zig_mulw_u128(lhs, rhs, bits); | 1731 | *res = zig_mulw_u128(lhs, rhs, bits); |
| 1596 | return rhs != zig_as_u128(0, 0) && lhs > zig_maxInt(u128, bits) / rhs; | 1732 | return rhs != zig_make_u128(0, 0) && lhs > zig_maxInt_u(128, bits) / rhs; |
| 1597 | #endif | 1733 | #endif |
| 1598 | } | 1734 | } |
| 1599 | 1735 | ||
| 1600 | zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1736 | zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow); |
| 1601 | static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1737 | static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1602 | #if zig_has_builtin(mul_overflow) | 1738 | #if zig_has_builtin(mul_overflow) |
| 1603 | zig_i128 full_res; | 1739 | zig_i128 full_res; |
| 1604 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | 1740 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); |
| 1605 | #else | 1741 | #else |
| 1606 | zig_c_int overflow_int; | 1742 | int overflow_int; |
| 1607 | zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); | 1743 | zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); |
| 1608 | bool overflow = overflow_int != 0; | 1744 | bool overflow = overflow_int != 0; |
| 1609 | #endif | 1745 | #endif |
| 1610 | *res = zig_wrap_i128(full_res, bits); | 1746 | *res = zig_wrap_i128(full_res, bits); |
| 1611 | return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); | 1747 | return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); |
| 1612 | } | 1748 | } |
| 1613 | 1749 | ||
| 1614 | #else /* zig_has_int128 */ | 1750 | #else /* zig_has_int128 */ |
| 1615 | 1751 | ||
| 1616 | static inline bool zig_overflow_u128(bool overflow, zig_u128 full_res, zig_u8 bits) { | 1752 | static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1617 | return overflow || | 1753 | uint64_t hi; |
| 1618 | zig_cmp_u128(full_res, zig_minInt(u128, bits)) < zig_as_i32(0) || | 1754 | bool overflow = zig_addo_u64(&hi, lhs.hi, rhs.hi, bits - 64); |
| 1619 | zig_cmp_u128(full_res, zig_maxInt(u128, bits)) > zig_as_i32(0); | 1755 | return overflow ^ zig_addo_u64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); |
| 1620 | } | 1756 | } |
| 1621 | 1757 | ||
| 1622 | static inline bool zig_overflow_i128(bool overflow, zig_i128 full_res, zig_u8 bits) { | 1758 | static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1623 | return overflow || | 1759 | int64_t hi; |
| 1624 | zig_cmp_i128(full_res, zig_minInt(i128, bits)) < zig_as_i32(0) || | 1760 | bool overflow = zig_addo_i64(&hi, lhs.hi, rhs.hi, bits - 64); |
| 1625 | zig_cmp_i128(full_res, zig_maxInt(i128, bits)) > zig_as_i32(0); | 1761 | return overflow ^ zig_addo_i64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); |
| 1626 | } | 1762 | } |
| 1627 | 1763 | ||
| 1628 | static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1764 | static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1629 | zig_u128 full_res; | 1765 | uint64_t hi; |
| 1630 | bool overflow = | 1766 | bool overflow = zig_subo_u64(&hi, lhs.hi, rhs.hi, bits - 64); |
| 1631 | zig_addo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | | 1767 | return overflow ^ zig_subo_u64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); |
| 1632 | zig_addo_u64(&full_res.hi, full_res.hi, zig_addo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); | ||
| 1633 | *res = zig_wrap_u128(full_res, bits); | ||
| 1634 | return zig_overflow_u128(overflow, full_res, bits); | ||
| 1635 | } | 1768 | } |
| 1636 | 1769 | ||
| 1637 | zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1770 | static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1638 | static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1771 | int64_t hi; |
| 1639 | zig_c_int overflow_int; | 1772 | bool overflow = zig_subo_i64(&hi, lhs.hi, rhs.hi, bits - 64); |
| 1640 | zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); | 1773 | return overflow ^ zig_subo_i64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); |
| 1641 | *res = zig_wrap_i128(full_res, bits); | ||
| 1642 | return zig_overflow_i128(overflow_int, full_res, bits); | ||
| 1643 | } | 1774 | } |
| 1644 | 1775 | ||
| 1645 | static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1776 | static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1646 | zig_u128 full_res; | ||
| 1647 | bool overflow = | ||
| 1648 | zig_subo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | | ||
| 1649 | zig_subo_u64(&full_res.hi, full_res.hi, zig_subo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); | ||
| 1650 | *res = zig_wrap_u128(full_res, bits); | ||
| 1651 | return zig_overflow_u128(overflow, full_res, bits); | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | ||
| 1655 | static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1656 | zig_c_int overflow_int; | ||
| 1657 | zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); | ||
| 1658 | *res = zig_wrap_i128(full_res, bits); | ||
| 1659 | return zig_overflow_i128(overflow_int, full_res, bits); | ||
| 1660 | } | ||
| 1661 | |||
| 1662 | static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1663 | *res = zig_mulw_u128(lhs, rhs, bits); | 1777 | *res = zig_mulw_u128(lhs, rhs, bits); |
| 1664 | return zig_cmp_u128(*res, zig_as_u128(0, 0)) != zig_as_i32(0) && | 1778 | return zig_cmp_u128(*res, zig_make_u128(0, 0)) != INT32_C(0) && |
| 1665 | zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); | 1779 | zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt_u(128, bits), rhs)) > INT32_C(0); |
| 1666 | } | 1780 | } |
| 1667 | 1781 | ||
| 1668 | zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | 1782 | zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow); |
| 1669 | static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1783 | static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1670 | zig_c_int overflow_int; | 1784 | int overflow_int; |
| 1671 | zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); | 1785 | zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); |
| 1786 | bool overflow = overflow_int != 0 || | ||
| 1787 | zig_cmp_i128(full_res, zig_minInt_i(128, bits)) < INT32_C(0) || | ||
| 1788 | zig_cmp_i128(full_res, zig_maxInt_i(128, bits)) > INT32_C(0); | ||
| 1672 | *res = zig_wrap_i128(full_res, bits); | 1789 | *res = zig_wrap_i128(full_res, bits); |
| 1673 | return zig_overflow_i128(overflow_int, full_res, bits); | 1790 | return overflow; |
| 1674 | } | 1791 | } |
| 1675 | 1792 | ||
| 1676 | #endif /* zig_has_int128 */ | 1793 | #endif /* zig_has_int128 */ |
| 1677 | 1794 | ||
| 1678 | static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { | 1795 | static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, uint8_t rhs, uint8_t bits) { |
| 1679 | *res = zig_shlw_u128(lhs, rhs, bits); | 1796 | *res = zig_shlw_u128(lhs, rhs, bits); |
| 1680 | return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); | 1797 | return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt_u(128, bits), rhs)) > INT32_C(0); |
| 1681 | } | 1798 | } |
| 1682 | 1799 | ||
| 1683 | static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { | 1800 | static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, uint8_t rhs, uint8_t bits) { |
| 1684 | *res = zig_shlw_i128(lhs, rhs, bits); | 1801 | *res = zig_shlw_i128(lhs, rhs, bits); |
| 1685 | zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - zig_as_u8(1))); | 1802 | zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - UINT8_C(1))); |
| 1686 | return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != zig_as_i32(0) && | 1803 | return zig_cmp_i128(zig_and_i128(lhs, mask), zig_make_i128(0, 0)) != INT32_C(0) && |
| 1687 | zig_cmp_i128(zig_and_i128(lhs, mask), mask) != zig_as_i32(0); | 1804 | zig_cmp_i128(zig_and_i128(lhs, mask), mask) != INT32_C(0); |
| 1688 | } | 1805 | } |
| 1689 | 1806 | ||
| 1690 | static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1807 | static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1691 | zig_u128 res; | 1808 | zig_u128 res; |
| 1692 | if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= zig_as_i32(0)) | 1809 | if (zig_cmp_u128(rhs, zig_make_u128(0, bits)) >= INT32_C(0)) |
| 1693 | return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != zig_as_i32(0) ? zig_maxInt(u128, bits) : lhs; | 1810 | return zig_cmp_u128(lhs, zig_make_u128(0, 0)) != INT32_C(0) ? zig_maxInt_u(128, bits) : lhs; |
| 1694 | 1811 | return zig_shlo_u128(&res, lhs, (uint8_t)zig_lo_u128(rhs), bits) ? zig_maxInt_u(128, bits) : res; | |
| 1695 | #if zig_has_int128 | ||
| 1696 | return zig_shlo_u128(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u128, bits) : res; | ||
| 1697 | #else | ||
| 1698 | return zig_shlo_u128(&res, lhs, (zig_u8)rhs.lo, bits) ? zig_maxInt(u128, bits) : res; | ||
| 1699 | #endif | ||
| 1700 | } | 1812 | } |
| 1701 | 1813 | ||
| 1702 | static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1814 | static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1703 | zig_i128 res; | 1815 | zig_i128 res; |
| 1704 | if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_as_u128(0, bits)) < zig_as_i32(0) && !zig_shlo_i128(&res, lhs, zig_lo_i128(rhs), bits)) return res; | 1816 | if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, (uint8_t)zig_lo_i128(rhs), bits)) return res; |
| 1705 | return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | 1817 | return zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); |
| 1706 | } | 1818 | } |
| 1707 | 1819 | ||
| 1708 | static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1820 | static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1709 | zig_u128 res; | 1821 | zig_u128 res; |
| 1710 | return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; | 1822 | return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt_u(128, bits) : res; |
| 1711 | } | 1823 | } |
| 1712 | 1824 | ||
| 1713 | static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1825 | static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1714 | zig_i128 res; | 1826 | zig_i128 res; |
| 1715 | if (!zig_addo_i128(&res, lhs, rhs, bits)) return res; | 1827 | if (!zig_addo_i128(&res, lhs, rhs, bits)) return res; |
| 1716 | return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | 1828 | return zig_cmp_i128(res, zig_make_i128(0, 0)) >= INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); |
| 1717 | } | 1829 | } |
| 1718 | 1830 | ||
| 1719 | static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1831 | static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1720 | zig_u128 res; | 1832 | zig_u128 res; |
| 1721 | return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt(u128, bits) : res; | 1833 | return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt_u(128, bits) : res; |
| 1722 | } | 1834 | } |
| 1723 | 1835 | ||
| 1724 | static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1836 | static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1725 | zig_i128 res; | 1837 | zig_i128 res; |
| 1726 | if (!zig_subo_i128(&res, lhs, rhs, bits)) return res; | 1838 | if (!zig_subo_i128(&res, lhs, rhs, bits)) return res; |
| 1727 | return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | 1839 | return zig_cmp_i128(res, zig_make_i128(0, 0)) >= INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); |
| 1728 | } | 1840 | } |
| 1729 | 1841 | ||
| 1730 | static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | 1842 | static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { |
| 1731 | zig_u128 res; | 1843 | zig_u128 res; |
| 1732 | return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; | 1844 | return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt_u(128, bits) : res; |
| 1733 | } | 1845 | } |
| 1734 | 1846 | ||
| 1735 | static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | 1847 | static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { |
| 1736 | zig_i128 res; | 1848 | zig_i128 res; |
| 1737 | if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res; | 1849 | if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res; |
| 1738 | return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | 1850 | return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); |
| 1739 | } | 1851 | } |
| 1740 | 1852 | ||
| 1741 | static inline zig_u8 zig_clz_u128(zig_u128 val, zig_u8 bits) { | 1853 | static inline uint8_t zig_clz_u128(zig_u128 val, uint8_t bits) { |
| 1742 | if (bits <= zig_as_u8(64)) return zig_clz_u64(zig_lo_u128(val), bits); | 1854 | if (bits <= UINT8_C(64)) return zig_clz_u64(zig_lo_u128(val), bits); |
| 1743 | if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - zig_as_u8(64)); | 1855 | if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - UINT8_C(64)); |
| 1744 | return zig_clz_u64(zig_lo_u128(val), zig_as_u8(64)) + (bits - zig_as_u8(64)); | 1856 | return zig_clz_u64(zig_lo_u128(val), UINT8_C(64)) + (bits - UINT8_C(64)); |
| 1745 | } | 1857 | } |
| 1746 | 1858 | ||
| 1747 | static inline zig_u8 zig_clz_i128(zig_i128 val, zig_u8 bits) { | 1859 | static inline uint8_t zig_clz_i128(zig_i128 val, uint8_t bits) { |
| 1748 | return zig_clz_u128(zig_bitcast_u128(val), bits); | 1860 | return zig_clz_u128(zig_bitcast_u128(val), bits); |
| 1749 | } | 1861 | } |
| 1750 | 1862 | ||
| 1751 | static inline zig_u8 zig_ctz_u128(zig_u128 val, zig_u8 bits) { | 1863 | static inline uint8_t zig_ctz_u128(zig_u128 val, uint8_t bits) { |
| 1752 | if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), zig_as_u8(64)); | 1864 | if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), UINT8_C(64)); |
| 1753 | return zig_ctz_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + zig_as_u8(64); | 1865 | return zig_ctz_u64(zig_hi_u128(val), bits - UINT8_C(64)) + UINT8_C(64); |
| 1754 | } | 1866 | } |
| 1755 | 1867 | ||
| 1756 | static inline zig_u8 zig_ctz_i128(zig_i128 val, zig_u8 bits) { | 1868 | static inline uint8_t zig_ctz_i128(zig_i128 val, uint8_t bits) { |
| 1757 | return zig_ctz_u128(zig_bitcast_u128(val), bits); | 1869 | return zig_ctz_u128(zig_bitcast_u128(val), bits); |
| 1758 | } | 1870 | } |
| 1759 | 1871 | ||
| 1760 | static inline zig_u8 zig_popcount_u128(zig_u128 val, zig_u8 bits) { | 1872 | static inline uint8_t zig_popcount_u128(zig_u128 val, uint8_t bits) { |
| 1761 | return zig_popcount_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + | 1873 | return zig_popcount_u64(zig_hi_u128(val), bits - UINT8_C(64)) + |
| 1762 | zig_popcount_u64(zig_lo_u128(val), zig_as_u8(64)); | 1874 | zig_popcount_u64(zig_lo_u128(val), UINT8_C(64)); |
| 1763 | } | 1875 | } |
| 1764 | 1876 | ||
| 1765 | static inline zig_u8 zig_popcount_i128(zig_i128 val, zig_u8 bits) { | 1877 | static inline uint8_t zig_popcount_i128(zig_i128 val, uint8_t bits) { |
| 1766 | return zig_popcount_u128(zig_bitcast_u128(val), bits); | 1878 | return zig_popcount_u128(zig_bitcast_u128(val), bits); |
| 1767 | } | 1879 | } |
| 1768 | 1880 | ||
| 1769 | static inline zig_u128 zig_byte_swap_u128(zig_u128 val, zig_u8 bits) { | 1881 | static inline zig_u128 zig_byte_swap_u128(zig_u128 val, uint8_t bits) { |
| 1770 | zig_u128 full_res; | 1882 | zig_u128 full_res; |
| 1771 | #if zig_has_builtin(bswap128) | 1883 | #if zig_has_builtin(bswap128) |
| 1772 | full_res = __builtin_bswap128(val); | 1884 | full_res = __builtin_bswap128(val); |
| 1773 | #else | 1885 | #else |
| 1774 | full_res = zig_as_u128(zig_byte_swap_u64(zig_lo_u128(val), zig_as_u8(64)), | 1886 | full_res = zig_make_u128(zig_byte_swap_u64(zig_lo_u128(val), UINT8_C(64)), |
| 1775 | zig_byte_swap_u64(zig_hi_u128(val), zig_as_u8(64))); | 1887 | zig_byte_swap_u64(zig_hi_u128(val), UINT8_C(64))); |
| 1776 | #endif | 1888 | #endif |
| 1777 | return zig_shr_u128(full_res, zig_as_u8(128) - bits); | 1889 | return zig_shr_u128(full_res, UINT8_C(128) - bits); |
| 1778 | } | 1890 | } |
| 1779 | 1891 | ||
| 1780 | static inline zig_i128 zig_byte_swap_i128(zig_i128 val, zig_u8 bits) { | 1892 | static inline zig_i128 zig_byte_swap_i128(zig_i128 val, uint8_t bits) { |
| 1781 | return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits)); | 1893 | return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits)); |
| 1782 | } | 1894 | } |
| 1783 | 1895 | ||
| 1784 | static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, zig_u8 bits) { | 1896 | static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, uint8_t bits) { |
| 1785 | return zig_shr_u128(zig_as_u128(zig_bit_reverse_u64(zig_lo_u128(val), zig_as_u8(64)), | 1897 | return zig_shr_u128(zig_make_u128(zig_bit_reverse_u64(zig_lo_u128(val), UINT8_C(64)), |
| 1786 | zig_bit_reverse_u64(zig_hi_u128(val), zig_as_u8(64))), | 1898 | zig_bit_reverse_u64(zig_hi_u128(val), UINT8_C(64))), |
| 1787 | zig_as_u8(128) - bits); | 1899 | UINT8_C(128) - bits); |
| 1788 | } | 1900 | } |
| 1789 | 1901 | ||
| 1790 | static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { | 1902 | static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { |
| 1791 | return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); | 1903 | return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); |
| 1792 | } | 1904 | } |
| 1793 | 1905 | ||
| ... | @@ -1810,85 +1922,87 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { | ... | @@ -1810,85 +1922,87 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { |
| 1810 | 1922 | ||
| 1811 | #if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc) | 1923 | #if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc) |
| 1812 | #define zig_has_float_builtins 1 | 1924 | #define zig_has_float_builtins 1 |
| 1813 | #define zig_as_special_f16(sign, name, arg, repr) sign zig_as_f16(__builtin_##name, )(arg) | 1925 | #define zig_make_special_f16(sign, name, arg, repr) sign zig_make_f16(__builtin_##name, )(arg) |
| 1814 | #define zig_as_special_f32(sign, name, arg, repr) sign zig_as_f32(__builtin_##name, )(arg) | 1926 | #define zig_make_special_f32(sign, name, arg, repr) sign zig_make_f32(__builtin_##name, )(arg) |
| 1815 | #define zig_as_special_f64(sign, name, arg, repr) sign zig_as_f64(__builtin_##name, )(arg) | 1927 | #define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg) |
| 1816 | #define zig_as_special_f80(sign, name, arg, repr) sign zig_as_f80(__builtin_##name, )(arg) | 1928 | #define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg) |
| 1817 | #define zig_as_special_f128(sign, name, arg, repr) sign zig_as_f128(__builtin_##name, )(arg) | 1929 | #define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg) |
| 1818 | #define zig_as_special_c_longdouble(sign, name, arg, repr) sign zig_as_c_longdouble(__builtin_##name, )(arg) | 1930 | #define zig_make_special_c_longdouble(sign, name, arg, repr) sign zig_make_c_longdouble(__builtin_##name, )(arg) |
| 1819 | #else | 1931 | #else |
| 1820 | #define zig_has_float_builtins 0 | 1932 | #define zig_has_float_builtins 0 |
| 1821 | #define zig_as_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) | 1933 | #define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) |
| 1822 | #define zig_as_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) | 1934 | #define zig_make_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) |
| 1823 | #define zig_as_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) | 1935 | #define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) |
| 1824 | #define zig_as_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) | 1936 | #define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) |
| 1825 | #define zig_as_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) | 1937 | #define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) |
| 1826 | #define zig_as_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr) | 1938 | #define zig_make_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr) |
| 1827 | #endif | 1939 | #endif |
| 1828 | 1940 | ||
| 1829 | #define zig_has_f16 1 | 1941 | #define zig_has_f16 1 |
| 1830 | #define zig_bitSizeOf_f16 16 | 1942 | #define zig_bitSizeOf_f16 16 |
| 1831 | #define zig_libc_name_f16(name) __##name##h | 1943 | #define zig_libc_name_f16(name) __##name##h |
| 1832 | #define zig_as_special_constant_f16(sign, name, arg, repr) zig_as_special_f16(sign, name, arg, repr) | 1944 | #define zig_make_special_constant_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr) |
| 1833 | #if FLT_MANT_DIG == 11 | 1945 | #if FLT_MANT_DIG == 11 |
| 1834 | typedef float zig_f16; | 1946 | typedef float zig_f16; |
| 1835 | #define zig_as_f16(fp, repr) fp##f | 1947 | #define zig_make_f16(fp, repr) fp##f |
| 1836 | #elif DBL_MANT_DIG == 11 | 1948 | #elif DBL_MANT_DIG == 11 |
| 1837 | typedef double zig_f16; | 1949 | typedef double zig_f16; |
| 1838 | #define zig_as_f16(fp, repr) fp | 1950 | #define zig_make_f16(fp, repr) fp |
| 1839 | #elif LDBL_MANT_DIG == 11 | 1951 | #elif LDBL_MANT_DIG == 11 |
| 1840 | #define zig_bitSizeOf_c_longdouble 16 | 1952 | #define zig_bitSizeOf_c_longdouble 16 |
| 1953 | typedef uint16_t zig_repr_c_longdouble; | ||
| 1841 | typedef long double zig_f16; | 1954 | typedef long double zig_f16; |
| 1842 | #define zig_as_f16(fp, repr) fp##l | 1955 | #define zig_make_f16(fp, repr) fp##l |
| 1843 | #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) | 1956 | #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) |
| 1844 | typedef _Float16 zig_f16; | 1957 | typedef _Float16 zig_f16; |
| 1845 | #define zig_as_f16(fp, repr) fp##f16 | 1958 | #define zig_make_f16(fp, repr) fp##f16 |
| 1846 | #elif defined(__SIZEOF_FP16__) | 1959 | #elif defined(__SIZEOF_FP16__) |
| 1847 | typedef __fp16 zig_f16; | 1960 | typedef __fp16 zig_f16; |
| 1848 | #define zig_as_f16(fp, repr) fp##f16 | 1961 | #define zig_make_f16(fp, repr) fp##f16 |
| 1849 | #else | 1962 | #else |
| 1850 | #undef zig_has_f16 | 1963 | #undef zig_has_f16 |
| 1851 | #define zig_has_f16 0 | 1964 | #define zig_has_f16 0 |
| 1852 | #define zig_repr_f16 i16 | 1965 | #define zig_bitSizeOf_repr_f16 16 |
| 1853 | typedef zig_i16 zig_f16; | 1966 | typedef int16_t zig_f16; |
| 1854 | #define zig_as_f16(fp, repr) repr | 1967 | #define zig_make_f16(fp, repr) repr |
| 1855 | #undef zig_as_special_f16 | 1968 | #undef zig_make_special_f16 |
| 1856 | #define zig_as_special_f16(sign, name, arg, repr) repr | 1969 | #define zig_make_special_f16(sign, name, arg, repr) repr |
| 1857 | #undef zig_as_special_constant_f16 | 1970 | #undef zig_make_special_constant_f16 |
| 1858 | #define zig_as_special_constant_f16(sign, name, arg, repr) repr | 1971 | #define zig_make_special_constant_f16(sign, name, arg, repr) repr |
| 1859 | #endif | 1972 | #endif |
| 1860 | 1973 | ||
| 1861 | #define zig_has_f32 1 | 1974 | #define zig_has_f32 1 |
| 1862 | #define zig_bitSizeOf_f32 32 | 1975 | #define zig_bitSizeOf_f32 32 |
| 1863 | #define zig_libc_name_f32(name) name##f | 1976 | #define zig_libc_name_f32(name) name##f |
| 1864 | #if _MSC_VER | 1977 | #if _MSC_VER |
| 1865 | #define zig_as_special_constant_f32(sign, name, arg, repr) sign zig_as_f32(zig_msvc_flt_##name, ) | 1978 | #define zig_make_special_constant_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, ) |
| 1866 | #else | 1979 | #else |
| 1867 | #define zig_as_special_constant_f32(sign, name, arg, repr) zig_as_special_f32(sign, name, arg, repr) | 1980 | #define zig_make_special_constant_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr) |
| 1868 | #endif | 1981 | #endif |
| 1869 | #if FLT_MANT_DIG == 24 | 1982 | #if FLT_MANT_DIG == 24 |
| 1870 | typedef float zig_f32; | 1983 | typedef float zig_f32; |
| 1871 | #define zig_as_f32(fp, repr) fp##f | 1984 | #define zig_make_f32(fp, repr) fp##f |
| 1872 | #elif DBL_MANT_DIG == 24 | 1985 | #elif DBL_MANT_DIG == 24 |
| 1873 | typedef double zig_f32; | 1986 | typedef double zig_f32; |
| 1874 | #define zig_as_f32(fp, repr) fp | 1987 | #define zig_make_f32(fp, repr) fp |
| 1875 | #elif LDBL_MANT_DIG == 24 | 1988 | #elif LDBL_MANT_DIG == 24 |
| 1876 | #define zig_bitSizeOf_c_longdouble 32 | 1989 | #define zig_bitSizeOf_c_longdouble 32 |
| 1990 | typedef uint32_t zig_repr_c_longdouble; | ||
| 1877 | typedef long double zig_f32; | 1991 | typedef long double zig_f32; |
| 1878 | #define zig_as_f32(fp, repr) fp##l | 1992 | #define zig_make_f32(fp, repr) fp##l |
| 1879 | #elif FLT32_MANT_DIG == 24 | 1993 | #elif FLT32_MANT_DIG == 24 |
| 1880 | typedef _Float32 zig_f32; | 1994 | typedef _Float32 zig_f32; |
| 1881 | #define zig_as_f32(fp, repr) fp##f32 | 1995 | #define zig_make_f32(fp, repr) fp##f32 |
| 1882 | #else | 1996 | #else |
| 1883 | #undef zig_has_f32 | 1997 | #undef zig_has_f32 |
| 1884 | #define zig_has_f32 0 | 1998 | #define zig_has_f32 0 |
| 1885 | #define zig_repr_f32 i32 | 1999 | #define zig_bitSizeOf_repr_f32 32 |
| 1886 | typedef zig_i32 zig_f32; | 2000 | typedef int32_t zig_f32; |
| 1887 | #define zig_as_f32(fp, repr) repr | 2001 | #define zig_make_f32(fp, repr) repr |
| 1888 | #undef zig_as_special_f32 | 2002 | #undef zig_make_special_f32 |
| 1889 | #define zig_as_special_f32(sign, name, arg, repr) repr | 2003 | #define zig_make_special_f32(sign, name, arg, repr) repr |
| 1890 | #undef zig_as_special_constant_f32 | 2004 | #undef zig_make_special_constant_f32 |
| 1891 | #define zig_as_special_constant_f32(sign, name, arg, repr) repr | 2005 | #define zig_make_special_constant_f32(sign, name, arg, repr) repr |
| 1892 | #endif | 2006 | #endif |
| 1893 | 2007 | ||
| 1894 | #define zig_has_f64 1 | 2008 | #define zig_has_f64 1 |
| ... | @@ -1897,109 +2011,113 @@ typedef zig_i32 zig_f32; | ... | @@ -1897,109 +2011,113 @@ typedef zig_i32 zig_f32; |
| 1897 | #if _MSC_VER | 2011 | #if _MSC_VER |
| 1898 | #ifdef ZIG_TARGET_ABI_MSVC | 2012 | #ifdef ZIG_TARGET_ABI_MSVC |
| 1899 | #define zig_bitSizeOf_c_longdouble 64 | 2013 | #define zig_bitSizeOf_c_longdouble 64 |
| 2014 | typedef uint64_t zig_repr_c_longdouble; | ||
| 1900 | #endif | 2015 | #endif |
| 1901 | #define zig_as_special_constant_f64(sign, name, arg, repr) sign zig_as_f64(zig_msvc_flt_##name, ) | 2016 | #define zig_make_special_constant_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) |
| 1902 | #else /* _MSC_VER */ | 2017 | #else /* _MSC_VER */ |
| 1903 | #define zig_as_special_constant_f64(sign, name, arg, repr) zig_as_special_f64(sign, name, arg, repr) | 2018 | #define zig_make_special_constant_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr) |
| 1904 | #endif /* _MSC_VER */ | 2019 | #endif /* _MSC_VER */ |
| 1905 | #if FLT_MANT_DIG == 53 | 2020 | #if FLT_MANT_DIG == 53 |
| 1906 | typedef float zig_f64; | 2021 | typedef float zig_f64; |
| 1907 | #define zig_as_f64(fp, repr) fp##f | 2022 | #define zig_make_f64(fp, repr) fp##f |
| 1908 | #elif DBL_MANT_DIG == 53 | 2023 | #elif DBL_MANT_DIG == 53 |
| 1909 | typedef double zig_f64; | 2024 | typedef double zig_f64; |
| 1910 | #define zig_as_f64(fp, repr) fp | 2025 | #define zig_make_f64(fp, repr) fp |
| 1911 | #elif LDBL_MANT_DIG == 53 | 2026 | #elif LDBL_MANT_DIG == 53 |
| 1912 | #define zig_bitSizeOf_c_longdouble 64 | 2027 | #define zig_bitSizeOf_c_longdouble 64 |
| 2028 | typedef uint64_t zig_repr_c_longdouble; | ||
| 1913 | typedef long double zig_f64; | 2029 | typedef long double zig_f64; |
| 1914 | #define zig_as_f64(fp, repr) fp##l | 2030 | #define zig_make_f64(fp, repr) fp##l |
| 1915 | #elif FLT64_MANT_DIG == 53 | 2031 | #elif FLT64_MANT_DIG == 53 |
| 1916 | typedef _Float64 zig_f64; | 2032 | typedef _Float64 zig_f64; |
| 1917 | #define zig_as_f64(fp, repr) fp##f64 | 2033 | #define zig_make_f64(fp, repr) fp##f64 |
| 1918 | #elif FLT32X_MANT_DIG == 53 | 2034 | #elif FLT32X_MANT_DIG == 53 |
| 1919 | typedef _Float32x zig_f64; | 2035 | typedef _Float32x zig_f64; |
| 1920 | #define zig_as_f64(fp, repr) fp##f32x | 2036 | #define zig_make_f64(fp, repr) fp##f32x |
| 1921 | #else | 2037 | #else |
| 1922 | #undef zig_has_f64 | 2038 | #undef zig_has_f64 |
| 1923 | #define zig_has_f64 0 | 2039 | #define zig_has_f64 0 |
| 1924 | #define zig_repr_f64 i64 | 2040 | #define zig_bitSizeOf_repr_f64 64 |
| 1925 | typedef zig_i64 zig_f64; | 2041 | typedef int64_t zig_f64; |
| 1926 | #define zig_as_f64(fp, repr) repr | 2042 | #define zig_make_f64(fp, repr) repr |
| 1927 | #undef zig_as_special_f64 | 2043 | #undef zig_make_special_f64 |
| 1928 | #define zig_as_special_f64(sign, name, arg, repr) repr | 2044 | #define zig_make_special_f64(sign, name, arg, repr) repr |
| 1929 | #undef zig_as_special_constant_f64 | 2045 | #undef zig_make_special_constant_f64 |
| 1930 | #define zig_as_special_constant_f64(sign, name, arg, repr) repr | 2046 | #define zig_make_special_constant_f64(sign, name, arg, repr) repr |
| 1931 | #endif | 2047 | #endif |
| 1932 | 2048 | ||
| 1933 | #define zig_has_f80 1 | 2049 | #define zig_has_f80 1 |
| 1934 | #define zig_bitSizeOf_f80 80 | 2050 | #define zig_bitSizeOf_f80 80 |
| 1935 | #define zig_libc_name_f80(name) __##name##x | 2051 | #define zig_libc_name_f80(name) __##name##x |
| 1936 | #define zig_as_special_constant_f80(sign, name, arg, repr) zig_as_special_f80(sign, name, arg, repr) | 2052 | #define zig_make_special_constant_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr) |
| 1937 | #if FLT_MANT_DIG == 64 | 2053 | #if FLT_MANT_DIG == 64 |
| 1938 | typedef float zig_f80; | 2054 | typedef float zig_f80; |
| 1939 | #define zig_as_f80(fp, repr) fp##f | 2055 | #define zig_make_f80(fp, repr) fp##f |
| 1940 | #elif DBL_MANT_DIG == 64 | 2056 | #elif DBL_MANT_DIG == 64 |
| 1941 | typedef double zig_f80; | 2057 | typedef double zig_f80; |
| 1942 | #define zig_as_f80(fp, repr) fp | 2058 | #define zig_make_f80(fp, repr) fp |
| 1943 | #elif LDBL_MANT_DIG == 64 | 2059 | #elif LDBL_MANT_DIG == 64 |
| 1944 | #define zig_bitSizeOf_c_longdouble 80 | 2060 | #define zig_bitSizeOf_c_longdouble 80 |
| 2061 | typedef zig_u128 zig_repr_c_longdouble; | ||
| 1945 | typedef long double zig_f80; | 2062 | typedef long double zig_f80; |
| 1946 | #define zig_as_f80(fp, repr) fp##l | 2063 | #define zig_make_f80(fp, repr) fp##l |
| 1947 | #elif FLT80_MANT_DIG == 64 | 2064 | #elif FLT80_MANT_DIG == 64 |
| 1948 | typedef _Float80 zig_f80; | 2065 | typedef _Float80 zig_f80; |
| 1949 | #define zig_as_f80(fp, repr) fp##f80 | 2066 | #define zig_make_f80(fp, repr) fp##f80 |
| 1950 | #elif FLT64X_MANT_DIG == 64 | 2067 | #elif FLT64X_MANT_DIG == 64 |
| 1951 | typedef _Float64x zig_f80; | 2068 | typedef _Float64x zig_f80; |
| 1952 | #define zig_as_f80(fp, repr) fp##f64x | 2069 | #define zig_make_f80(fp, repr) fp##f64x |
| 1953 | #elif defined(__SIZEOF_FLOAT80__) | 2070 | #elif defined(__SIZEOF_FLOAT80__) |
| 1954 | typedef __float80 zig_f80; | 2071 | typedef __float80 zig_f80; |
| 1955 | #define zig_as_f80(fp, repr) fp##l | 2072 | #define zig_make_f80(fp, repr) fp##l |
| 1956 | #else | 2073 | #else |
| 1957 | #undef zig_has_f80 | 2074 | #undef zig_has_f80 |
| 1958 | #define zig_has_f80 0 | 2075 | #define zig_has_f80 0 |
| 1959 | #define zig_repr_f80 i128 | 2076 | #define zig_bitSizeOf_repr_f80 128 |
| 1960 | typedef zig_i128 zig_f80; | 2077 | typedef zig_i128 zig_f80; |
| 1961 | #define zig_as_f80(fp, repr) repr | 2078 | #define zig_make_f80(fp, repr) repr |
| 1962 | #undef zig_as_special_f80 | 2079 | #undef zig_make_special_f80 |
| 1963 | #define zig_as_special_f80(sign, name, arg, repr) repr | 2080 | #define zig_make_special_f80(sign, name, arg, repr) repr |
| 1964 | #undef zig_as_special_constant_f80 | 2081 | #undef zig_make_special_constant_f80 |
| 1965 | #define zig_as_special_constant_f80(sign, name, arg, repr) repr | 2082 | #define zig_make_special_constant_f80(sign, name, arg, repr) repr |
| 1966 | #endif | 2083 | #endif |
| 1967 | 2084 | ||
| 1968 | #define zig_has_f128 1 | 2085 | #define zig_has_f128 1 |
| 1969 | #define zig_bitSizeOf_f128 128 | 2086 | #define zig_bitSizeOf_f128 128 |
| 1970 | #define zig_libc_name_f128(name) name##q | 2087 | #define zig_libc_name_f128(name) name##q |
| 1971 | #define zig_as_special_constant_f128(sign, name, arg, repr) zig_as_special_f128(sign, name, arg, repr) | 2088 | #define zig_make_special_constant_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr) |
| 1972 | #if FLT_MANT_DIG == 113 | 2089 | #if FLT_MANT_DIG == 113 |
| 1973 | typedef float zig_f128; | 2090 | typedef float zig_f128; |
| 1974 | #define zig_as_f128(fp, repr) fp##f | 2091 | #define zig_make_f128(fp, repr) fp##f |
| 1975 | #elif DBL_MANT_DIG == 113 | 2092 | #elif DBL_MANT_DIG == 113 |
| 1976 | typedef double zig_f128; | 2093 | typedef double zig_f128; |
| 1977 | #define zig_as_f128(fp, repr) fp | 2094 | #define zig_make_f128(fp, repr) fp |
| 1978 | #elif LDBL_MANT_DIG == 113 | 2095 | #elif LDBL_MANT_DIG == 113 |
| 1979 | #define zig_bitSizeOf_c_longdouble 128 | 2096 | #define zig_bitSizeOf_c_longdouble 128 |
| 2097 | typedef zig_u128 zig_repr_c_longdouble; | ||
| 1980 | typedef long double zig_f128; | 2098 | typedef long double zig_f128; |
| 1981 | #define zig_as_f128(fp, repr) fp##l | 2099 | #define zig_make_f128(fp, repr) fp##l |
| 1982 | #elif FLT128_MANT_DIG == 113 | 2100 | #elif FLT128_MANT_DIG == 113 |
| 1983 | typedef _Float128 zig_f128; | 2101 | typedef _Float128 zig_f128; |
| 1984 | #define zig_as_f128(fp, repr) fp##f128 | 2102 | #define zig_make_f128(fp, repr) fp##f128 |
| 1985 | #elif FLT64X_MANT_DIG == 113 | 2103 | #elif FLT64X_MANT_DIG == 113 |
| 1986 | typedef _Float64x zig_f128; | 2104 | typedef _Float64x zig_f128; |
| 1987 | #define zig_as_f128(fp, repr) fp##f64x | 2105 | #define zig_make_f128(fp, repr) fp##f64x |
| 1988 | #elif defined(__SIZEOF_FLOAT128__) | 2106 | #elif defined(__SIZEOF_FLOAT128__) |
| 1989 | typedef __float128 zig_f128; | 2107 | typedef __float128 zig_f128; |
| 1990 | #define zig_as_f128(fp, repr) fp##q | 2108 | #define zig_make_f128(fp, repr) fp##q |
| 1991 | #undef zig_as_special_f128 | 2109 | #undef zig_make_special_f128 |
| 1992 | #define zig_as_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg) | 2110 | #define zig_make_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg) |
| 1993 | #else | 2111 | #else |
| 1994 | #undef zig_has_f128 | 2112 | #undef zig_has_f128 |
| 1995 | #define zig_has_f128 0 | 2113 | #define zig_has_f128 0 |
| 1996 | #define zig_repr_f128 i128 | 2114 | #define zig_bitSizeOf_repr_f128 128 |
| 1997 | typedef zig_i128 zig_f128; | 2115 | typedef zig_i128 zig_f128; |
| 1998 | #define zig_as_f128(fp, repr) repr | 2116 | #define zig_make_f128(fp, repr) repr |
| 1999 | #undef zig_as_special_f128 | 2117 | #undef zig_make_special_f128 |
| 2000 | #define zig_as_special_f128(sign, name, arg, repr) repr | 2118 | #define zig_make_special_f128(sign, name, arg, repr) repr |
| 2001 | #undef zig_as_special_constant_f128 | 2119 | #undef zig_make_special_constant_f128 |
| 2002 | #define zig_as_special_constant_f128(sign, name, arg, repr) repr | 2120 | #define zig_make_special_constant_f128(sign, name, arg, repr) repr |
| 2003 | #endif | 2121 | #endif |
| 2004 | 2122 | ||
| 2005 | #define zig_has_c_longdouble 1 | 2123 | #define zig_has_c_longdouble 1 |
| ... | @@ -2010,17 +2128,18 @@ typedef zig_i128 zig_f128; | ... | @@ -2010,17 +2128,18 @@ typedef zig_i128 zig_f128; |
| 2010 | #define zig_libc_name_c_longdouble(name) name##l | 2128 | #define zig_libc_name_c_longdouble(name) name##l |
| 2011 | #endif | 2129 | #endif |
| 2012 | 2130 | ||
| 2013 | #define zig_as_special_constant_c_longdouble(sign, name, arg, repr) zig_as_special_c_longdouble(sign, name, arg, repr) | 2131 | #define zig_make_special_constant_c_longdouble(sign, name, arg, repr) zig_make_special_c_longdouble(sign, name, arg, repr) |
| 2014 | #ifdef zig_bitSizeOf_c_longdouble | 2132 | #ifdef zig_bitSizeOf_c_longdouble |
| 2015 | 2133 | ||
| 2016 | #ifdef ZIG_TARGET_ABI_MSVC | 2134 | #ifdef ZIG_TARGET_ABI_MSVC |
| 2017 | typedef double zig_c_longdouble; | ||
| 2018 | #undef zig_bitSizeOf_c_longdouble | 2135 | #undef zig_bitSizeOf_c_longdouble |
| 2019 | #define zig_bitSizeOf_c_longdouble 64 | 2136 | #define zig_bitSizeOf_c_longdouble 64 |
| 2020 | #define zig_as_c_longdouble(fp, repr) fp | 2137 | typedef uint64_t zig_repr_c_longdouble; |
| 2138 | typedef zig_f64 zig_c_longdouble; | ||
| 2139 | #define zig_make_c_longdouble(fp, repr) fp | ||
| 2021 | #else | 2140 | #else |
| 2022 | typedef long double zig_c_longdouble; | 2141 | typedef long double zig_c_longdouble; |
| 2023 | #define zig_as_c_longdouble(fp, repr) fp##l | 2142 | #define zig_make_c_longdouble(fp, repr) fp##l |
| 2024 | #endif | 2143 | #endif |
| 2025 | 2144 | ||
| 2026 | #else /* zig_bitSizeOf_c_longdouble */ | 2145 | #else /* zig_bitSizeOf_c_longdouble */ |
| ... | @@ -2028,34 +2147,32 @@ typedef long double zig_c_longdouble; | ... | @@ -2028,34 +2147,32 @@ typedef long double zig_c_longdouble; |
| 2028 | #undef zig_has_c_longdouble | 2147 | #undef zig_has_c_longdouble |
| 2029 | #define zig_has_c_longdouble 0 | 2148 | #define zig_has_c_longdouble 0 |
| 2030 | #define zig_bitSizeOf_c_longdouble 80 | 2149 | #define zig_bitSizeOf_c_longdouble 80 |
| 2150 | typedef zig_u128 zig_repr_c_longdouble; | ||
| 2031 | #define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80 | 2151 | #define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80 |
| 2032 | #define zig_repr_c_longdouble i128 | 2152 | #define zig_bitSizeOf_repr_c_longdouble 128 |
| 2033 | typedef zig_i128 zig_c_longdouble; | 2153 | typedef zig_i128 zig_c_longdouble; |
| 2034 | #define zig_as_c_longdouble(fp, repr) repr | 2154 | #define zig_make_c_longdouble(fp, repr) repr |
| 2035 | #undef zig_as_special_c_longdouble | 2155 | #undef zig_make_special_c_longdouble |
| 2036 | #define zig_as_special_c_longdouble(sign, name, arg, repr) repr | 2156 | #define zig_make_special_c_longdouble(sign, name, arg, repr) repr |
| 2037 | #undef zig_as_special_constant_c_longdouble | 2157 | #undef zig_make_special_constant_c_longdouble |
| 2038 | #define zig_as_special_constant_c_longdouble(sign, name, arg, repr) repr | 2158 | #define zig_make_special_constant_c_longdouble(sign, name, arg, repr) repr |
| 2039 | 2159 | ||
| 2040 | #endif /* zig_bitSizeOf_c_longdouble */ | 2160 | #endif /* zig_bitSizeOf_c_longdouble */ |
| 2041 | 2161 | ||
| 2042 | #if !zig_has_float_builtins | 2162 | #if !zig_has_float_builtins |
| 2043 | #define zig_float_from_repr(Type, ReprType) \ | 2163 | #define zig_float_from_repr(Type, ReprType) \ |
| 2044 | static inline zig_##Type zig_float_from_repr_##Type(zig_##ReprType repr) { \ | 2164 | static inline zig_##Type zig_float_from_repr_##Type(ReprType repr) { \ |
| 2045 | return *((zig_##Type*)&repr); \ | 2165 | zig_##Type result; \ |
| 2166 | memcpy(&result, &repr, sizeof(result)); \ | ||
| 2167 | return result; \ | ||
| 2046 | } | 2168 | } |
| 2047 | 2169 | ||
| 2048 | zig_float_from_repr(f16, u16) | 2170 | zig_float_from_repr(f16, uint16_t) |
| 2049 | zig_float_from_repr(f32, u32) | 2171 | zig_float_from_repr(f32, uint32_t) |
| 2050 | zig_float_from_repr(f64, u64) | 2172 | zig_float_from_repr(f64, uint64_t) |
| 2051 | zig_float_from_repr(f80, u128) | 2173 | zig_float_from_repr(f80, zig_u128) |
| 2052 | zig_float_from_repr(f128, u128) | 2174 | zig_float_from_repr(f128, zig_u128) |
| 2053 | #if zig_bitSizeOf_c_longdouble == 80 | 2175 | zig_float_from_repr(c_longdouble, zig_repr_c_longdouble) |
| 2054 | zig_float_from_repr(c_longdouble, u128) | ||
| 2055 | #else | ||
| 2056 | #define zig_expand_float_from_repr(Type, ReprType) zig_float_from_repr(Type, ReprType) | ||
| 2057 | zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_longdouble)) | ||
| 2058 | #endif | ||
| 2059 | #endif | 2176 | #endif |
| 2060 | 2177 | ||
| 2061 | #define zig_cast_f16 (zig_f16) | 2178 | #define zig_cast_f16 (zig_f16) |
| ... | @@ -2073,32 +2190,35 @@ zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_lo | ... | @@ -2073,32 +2190,35 @@ zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_lo |
| 2073 | #endif | 2190 | #endif |
| 2074 | 2191 | ||
| 2075 | #define zig_convert_builtin(ResType, operation, ArgType, version) \ | 2192 | #define zig_convert_builtin(ResType, operation, ArgType, version) \ |
| 2076 | zig_extern zig_##ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ | 2193 | zig_extern ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ |
| 2077 | zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(zig_##ArgType); | 2194 | zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(ArgType); |
| 2078 | zig_convert_builtin(f16, trunc, f32, 2) | 2195 | zig_convert_builtin(zig_f16, trunc, zig_f32, 2) |
| 2079 | zig_convert_builtin(f16, trunc, f64, 2) | 2196 | zig_convert_builtin(zig_f16, trunc, zig_f64, 2) |
| 2080 | zig_convert_builtin(f16, trunc, f80, 2) | 2197 | zig_convert_builtin(zig_f16, trunc, zig_f80, 2) |
| 2081 | zig_convert_builtin(f16, trunc, f128, 2) | 2198 | zig_convert_builtin(zig_f16, trunc, zig_f128, 2) |
| 2082 | zig_convert_builtin(f32, extend, f16, 2) | 2199 | zig_convert_builtin(zig_f32, extend, zig_f16, 2) |
| 2083 | zig_convert_builtin(f32, trunc, f64, 2) | 2200 | zig_convert_builtin(zig_f32, trunc, zig_f64, 2) |
| 2084 | zig_convert_builtin(f32, trunc, f80, 2) | 2201 | zig_convert_builtin(zig_f32, trunc, zig_f80, 2) |
| 2085 | zig_convert_builtin(f32, trunc, f128, 2) | 2202 | zig_convert_builtin(zig_f32, trunc, zig_f128, 2) |
| 2086 | zig_convert_builtin(f64, extend, f16, 2) | 2203 | zig_convert_builtin(zig_f64, extend, zig_f16, 2) |
| 2087 | zig_convert_builtin(f64, extend, f32, 2) | 2204 | zig_convert_builtin(zig_f64, extend, zig_f32, 2) |
| 2088 | zig_convert_builtin(f64, trunc, f80, 2) | 2205 | zig_convert_builtin(zig_f64, trunc, zig_f80, 2) |
| 2089 | zig_convert_builtin(f64, trunc, f128, 2) | 2206 | zig_convert_builtin(zig_f64, trunc, zig_f128, 2) |
| 2090 | zig_convert_builtin(f80, extend, f16, 2) | 2207 | zig_convert_builtin(zig_f80, extend, zig_f16, 2) |
| 2091 | zig_convert_builtin(f80, extend, f32, 2) | 2208 | zig_convert_builtin(zig_f80, extend, zig_f32, 2) |
| 2092 | zig_convert_builtin(f80, extend, f64, 2) | 2209 | zig_convert_builtin(zig_f80, extend, zig_f64, 2) |
| 2093 | zig_convert_builtin(f80, trunc, f128, 2) | 2210 | zig_convert_builtin(zig_f80, trunc, zig_f128, 2) |
| 2094 | zig_convert_builtin(f128, extend, f16, 2) | 2211 | zig_convert_builtin(zig_f128, extend, zig_f16, 2) |
| 2095 | zig_convert_builtin(f128, extend, f32, 2) | 2212 | zig_convert_builtin(zig_f128, extend, zig_f32, 2) |
| 2096 | zig_convert_builtin(f128, extend, f64, 2) | 2213 | zig_convert_builtin(zig_f128, extend, zig_f64, 2) |
| 2097 | zig_convert_builtin(f128, extend, f80, 2) | 2214 | zig_convert_builtin(zig_f128, extend, zig_f80, 2) |
| 2098 | 2215 | ||
| 2099 | #define zig_float_negate_builtin_0(Type) \ | 2216 | #define zig_float_negate_builtin_0(Type) \ |
| 2100 | static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ | 2217 | static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ |
| 2101 | return zig_expand_concat(zig_xor_, zig_repr_##Type)(arg, zig_expand_minInt(zig_repr_##Type, zig_bitSizeOf_##Type)); \ | 2218 | return zig_expand_concat(zig_xor_i, zig_bitSizeOf_repr_##Type)( \ |
| 2219 | arg, \ | ||
| 2220 | zig_minInt_i(zig_bitSizeOf_repr_##Type, zig_bitSizeOf_##Type) \ | ||
| 2221 | ); \ | ||
| 2102 | } | 2222 | } |
| 2103 | #define zig_float_negate_builtin_1(Type) \ | 2223 | #define zig_float_negate_builtin_1(Type) \ |
| 2104 | static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ | 2224 | static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ |
| ... | @@ -2106,28 +2226,28 @@ zig_convert_builtin(f128, extend, f80, 2) | ... | @@ -2106,28 +2226,28 @@ zig_convert_builtin(f128, extend, f80, 2) |
| 2106 | } | 2226 | } |
| 2107 | 2227 | ||
| 2108 | #define zig_float_less_builtin_0(Type, operation) \ | 2228 | #define zig_float_less_builtin_0(Type, operation) \ |
| 2109 | zig_extern zig_i32 zig_expand_concat(zig_expand_concat(__##operation, \ | 2229 | zig_extern int32_t zig_expand_concat(zig_expand_concat(__##operation, \ |
| 2110 | zig_compiler_rt_abbrev_##Type), 2)(zig_##Type, zig_##Type); \ | 2230 | zig_compiler_rt_abbrev_zig_##Type), 2)(zig_##Type, zig_##Type); \ |
| 2111 | static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 2231 | static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 2112 | return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 2)(lhs, rhs); \ | 2232 | return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 2)(lhs, rhs); \ |
| 2113 | } | 2233 | } |
| 2114 | #define zig_float_less_builtin_1(Type, operation) \ | 2234 | #define zig_float_less_builtin_1(Type, operation) \ |
| 2115 | static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 2235 | static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 2116 | return (!(lhs <= rhs) - (lhs < rhs)); \ | 2236 | return (!(lhs <= rhs) - (lhs < rhs)); \ |
| 2117 | } | 2237 | } |
| 2118 | 2238 | ||
| 2119 | #define zig_float_greater_builtin_0(Type, operation) \ | 2239 | #define zig_float_greater_builtin_0(Type, operation) \ |
| 2120 | zig_float_less_builtin_0(Type, operation) | 2240 | zig_float_less_builtin_0(Type, operation) |
| 2121 | #define zig_float_greater_builtin_1(Type, operation) \ | 2241 | #define zig_float_greater_builtin_1(Type, operation) \ |
| 2122 | static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 2242 | static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 2123 | return ((lhs > rhs) - !(lhs >= rhs)); \ | 2243 | return ((lhs > rhs) - !(lhs >= rhs)); \ |
| 2124 | } | 2244 | } |
| 2125 | 2245 | ||
| 2126 | #define zig_float_binary_builtin_0(Type, operation, operator) \ | 2246 | #define zig_float_binary_builtin_0(Type, operation, operator) \ |
| 2127 | zig_extern zig_##Type zig_expand_concat(zig_expand_concat(__##operation, \ | 2247 | zig_extern zig_##Type zig_expand_concat(zig_expand_concat(__##operation, \ |
| 2128 | zig_compiler_rt_abbrev_##Type), 3)(zig_##Type, zig_##Type); \ | 2248 | zig_compiler_rt_abbrev_zig_##Type), 3)(zig_##Type, zig_##Type); \ |
| 2129 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 2249 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| 2130 | return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 3)(lhs, rhs); \ | 2250 | return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 3)(lhs, rhs); \ |
| 2131 | } | 2251 | } |
| 2132 | #define zig_float_binary_builtin_1(Type, operation, operator) \ | 2252 | #define zig_float_binary_builtin_1(Type, operation, operator) \ |
| 2133 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | 2253 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ |
| ... | @@ -2135,18 +2255,18 @@ zig_convert_builtin(f128, extend, f80, 2) | ... | @@ -2135,18 +2255,18 @@ zig_convert_builtin(f128, extend, f80, 2) |
| 2135 | } | 2255 | } |
| 2136 | 2256 | ||
| 2137 | #define zig_float_builtins(Type) \ | 2257 | #define zig_float_builtins(Type) \ |
| 2138 | zig_convert_builtin(i32, fix, Type, ) \ | 2258 | zig_convert_builtin( int32_t, fix, zig_##Type, ) \ |
| 2139 | zig_convert_builtin(u32, fixuns, Type, ) \ | 2259 | zig_convert_builtin(uint32_t, fixuns, zig_##Type, ) \ |
| 2140 | zig_convert_builtin(i64, fix, Type, ) \ | 2260 | zig_convert_builtin( int64_t, fix, zig_##Type, ) \ |
| 2141 | zig_convert_builtin(u64, fixuns, Type, ) \ | 2261 | zig_convert_builtin(uint64_t, fixuns, zig_##Type, ) \ |
| 2142 | zig_convert_builtin(i128, fix, Type, ) \ | 2262 | zig_convert_builtin(zig_i128, fix, zig_##Type, ) \ |
| 2143 | zig_convert_builtin(u128, fixuns, Type, ) \ | 2263 | zig_convert_builtin(zig_u128, fixuns, zig_##Type, ) \ |
| 2144 | zig_convert_builtin(Type, float, i32, ) \ | 2264 | zig_convert_builtin(zig_##Type, float, int32_t, ) \ |
| 2145 | zig_convert_builtin(Type, floatun, u32, ) \ | 2265 | zig_convert_builtin(zig_##Type, floatun, uint32_t, ) \ |
| 2146 | zig_convert_builtin(Type, float, i64, ) \ | 2266 | zig_convert_builtin(zig_##Type, float, int64_t, ) \ |
| 2147 | zig_convert_builtin(Type, floatun, u64, ) \ | 2267 | zig_convert_builtin(zig_##Type, floatun, uint64_t, ) \ |
| 2148 | zig_convert_builtin(Type, float, i128, ) \ | 2268 | zig_convert_builtin(zig_##Type, float, zig_i128, ) \ |
| 2149 | zig_convert_builtin(Type, floatun, u128, ) \ | 2269 | zig_convert_builtin(zig_##Type, floatun, zig_u128, ) \ |
| 2150 | zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \ | 2270 | zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \ |
| 2151 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \ | 2271 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \ |
| 2152 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \ | 2272 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \ |
| ... | @@ -2200,149 +2320,157 @@ zig_float_builtins(c_longdouble) | ... | @@ -2200,149 +2320,157 @@ zig_float_builtins(c_longdouble) |
| 2200 | 2320 | ||
| 2201 | // TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 | 2321 | // TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 |
| 2202 | 2322 | ||
| 2203 | #define zig_msvc_atomics(Type, suffix) \ | 2323 | #define zig_msvc_atomics(ZigType, Type, suffix) \ |
| 2204 | static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ | 2324 | static inline bool zig_msvc_cmpxchg_##ZigType(Type volatile* obj, Type* expected, Type desired) { \ |
| 2205 | zig_##Type comparand = *expected; \ | 2325 | Type comparand = *expected; \ |
| 2206 | zig_##Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \ | 2326 | Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \ |
| 2207 | bool exchanged = initial == comparand; \ | 2327 | bool exchanged = initial == comparand; \ |
| 2208 | if (!exchanged) { \ | 2328 | if (!exchanged) { \ |
| 2209 | *expected = initial; \ | 2329 | *expected = initial; \ |
| 2210 | } \ | 2330 | } \ |
| 2211 | return exchanged; \ | 2331 | return exchanged; \ |
| 2212 | } \ | 2332 | } \ |
| 2213 | static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2333 | static inline Type zig_msvc_atomicrmw_xchg_##ZigType(Type volatile* obj, Type value) { \ |
| 2214 | return _InterlockedExchange##suffix(obj, value); \ | 2334 | return _InterlockedExchange##suffix(obj, value); \ |
| 2215 | } \ | 2335 | } \ |
| 2216 | static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2336 | static inline Type zig_msvc_atomicrmw_add_##ZigType(Type volatile* obj, Type value) { \ |
| 2217 | return _InterlockedExchangeAdd##suffix(obj, value); \ | 2337 | return _InterlockedExchangeAdd##suffix(obj, value); \ |
| 2218 | } \ | 2338 | } \ |
| 2219 | static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2339 | static inline Type zig_msvc_atomicrmw_sub_##ZigType(Type volatile* obj, Type value) { \ |
| 2220 | bool success = false; \ | 2340 | bool success = false; \ |
| 2221 | zig_##Type new; \ | 2341 | Type new; \ |
| 2222 | zig_##Type prev; \ | 2342 | Type prev; \ |
| 2223 | while (!success) { \ | 2343 | while (!success) { \ |
| 2224 | prev = *obj; \ | 2344 | prev = *obj; \ |
| 2225 | new = prev - value; \ | 2345 | new = prev - value; \ |
| 2226 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | 2346 | success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ |
| 2227 | } \ | 2347 | } \ |
| 2228 | return prev; \ | 2348 | return prev; \ |
| 2229 | } \ | 2349 | } \ |
| 2230 | static inline zig_##Type zig_msvc_atomicrmw_or_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2350 | static inline Type zig_msvc_atomicrmw_or_##ZigType(Type volatile* obj, Type value) { \ |
| 2231 | return _InterlockedOr##suffix(obj, value); \ | 2351 | return _InterlockedOr##suffix(obj, value); \ |
| 2232 | } \ | 2352 | } \ |
| 2233 | static inline zig_##Type zig_msvc_atomicrmw_xor_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2353 | static inline Type zig_msvc_atomicrmw_xor_##ZigType(Type volatile* obj, Type value) { \ |
| 2234 | return _InterlockedXor##suffix(obj, value); \ | 2354 | return _InterlockedXor##suffix(obj, value); \ |
| 2235 | } \ | 2355 | } \ |
| 2236 | static inline zig_##Type zig_msvc_atomicrmw_and_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2356 | static inline Type zig_msvc_atomicrmw_and_##ZigType(Type volatile* obj, Type value) { \ |
| 2237 | return _InterlockedAnd##suffix(obj, value); \ | 2357 | return _InterlockedAnd##suffix(obj, value); \ |
| 2238 | } \ | 2358 | } \ |
| 2239 | static inline zig_##Type zig_msvc_atomicrmw_nand_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2359 | static inline Type zig_msvc_atomicrmw_nand_##ZigType(Type volatile* obj, Type value) { \ |
| 2240 | bool success = false; \ | 2360 | bool success = false; \ |
| 2241 | zig_##Type new; \ | 2361 | Type new; \ |
| 2242 | zig_##Type prev; \ | 2362 | Type prev; \ |
| 2243 | while (!success) { \ | 2363 | while (!success) { \ |
| 2244 | prev = *obj; \ | 2364 | prev = *obj; \ |
| 2245 | new = ~(prev & value); \ | 2365 | new = ~(prev & value); \ |
| 2246 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | 2366 | success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ |
| 2247 | } \ | 2367 | } \ |
| 2248 | return prev; \ | 2368 | return prev; \ |
| 2249 | } \ | 2369 | } \ |
| 2250 | static inline zig_##Type zig_msvc_atomicrmw_min_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2370 | static inline Type zig_msvc_atomicrmw_min_##ZigType(Type volatile* obj, Type value) { \ |
| 2251 | bool success = false; \ | 2371 | bool success = false; \ |
| 2252 | zig_##Type new; \ | 2372 | Type new; \ |
| 2253 | zig_##Type prev; \ | 2373 | Type prev; \ |
| 2254 | while (!success) { \ | 2374 | while (!success) { \ |
| 2255 | prev = *obj; \ | 2375 | prev = *obj; \ |
| 2256 | new = value < prev ? value : prev; \ | 2376 | new = value < prev ? value : prev; \ |
| 2257 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | 2377 | success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ |
| 2258 | } \ | 2378 | } \ |
| 2259 | return prev; \ | 2379 | return prev; \ |
| 2260 | } \ | 2380 | } \ |
| 2261 | static inline zig_##Type zig_msvc_atomicrmw_max_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2381 | static inline Type zig_msvc_atomicrmw_max_##ZigType(Type volatile* obj, Type value) { \ |
| 2262 | bool success = false; \ | 2382 | bool success = false; \ |
| 2263 | zig_##Type new; \ | 2383 | Type new; \ |
| 2264 | zig_##Type prev; \ | 2384 | Type prev; \ |
| 2265 | while (!success) { \ | 2385 | while (!success) { \ |
| 2266 | prev = *obj; \ | 2386 | prev = *obj; \ |
| 2267 | new = value > prev ? value : prev; \ | 2387 | new = value > prev ? value : prev; \ |
| 2268 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | 2388 | success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ |
| 2269 | } \ | 2389 | } \ |
| 2270 | return prev; \ | 2390 | return prev; \ |
| 2271 | } \ | 2391 | } \ |
| 2272 | static inline void zig_msvc_atomic_store_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2392 | static inline void zig_msvc_atomic_store_##ZigType(Type volatile* obj, Type value) { \ |
| 2273 | _InterlockedExchange##suffix(obj, value); \ | 2393 | _InterlockedExchange##suffix(obj, value); \ |
| 2274 | } \ | 2394 | } \ |
| 2275 | static inline zig_##Type zig_msvc_atomic_load_##Type(zig_##Type volatile* obj) { \ | 2395 | static inline Type zig_msvc_atomic_load_##ZigType(Type volatile* obj) { \ |
| 2276 | return _InterlockedOr##suffix(obj, 0); \ | 2396 | return _InterlockedOr##suffix(obj, 0); \ |
| 2277 | } | 2397 | } |
| 2278 | 2398 | ||
| 2279 | zig_msvc_atomics(u8, 8) | 2399 | zig_msvc_atomics( u8, uint8_t, 8) |
| 2280 | zig_msvc_atomics(i8, 8) | 2400 | zig_msvc_atomics( i8, int8_t, 8) |
| 2281 | zig_msvc_atomics(u16, 16) | 2401 | zig_msvc_atomics(u16, uint16_t, 16) |
| 2282 | zig_msvc_atomics(i16, 16) | 2402 | zig_msvc_atomics(i16, int16_t, 16) |
| 2283 | zig_msvc_atomics(u32, ) | 2403 | zig_msvc_atomics(u32, uint32_t, ) |
| 2284 | zig_msvc_atomics(i32, ) | 2404 | zig_msvc_atomics(i32, int32_t, ) |
| 2285 | 2405 | ||
| 2286 | #if _M_X64 | 2406 | #if _M_X64 |
| 2287 | zig_msvc_atomics(u64, 64) | 2407 | zig_msvc_atomics(u64, uint64_t, 64) |
| 2288 | zig_msvc_atomics(i64, 64) | 2408 | zig_msvc_atomics(i64, int64_t, 64) |
| 2289 | #endif | 2409 | #endif |
| 2290 | 2410 | ||
| 2291 | #define zig_msvc_flt_atomics(Type, ReprType, suffix) \ | 2411 | #define zig_msvc_flt_atomics(Type, ReprType, suffix) \ |
| 2292 | static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ | 2412 | static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ |
| 2293 | zig_##ReprType comparand = *((zig_##ReprType*)expected); \ | 2413 | ReprType exchange; \ |
| 2294 | zig_##ReprType initial = _InterlockedCompareExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&desired), comparand); \ | 2414 | ReprType comparand; \ |
| 2295 | bool exchanged = initial == comparand; \ | 2415 | ReprType initial; \ |
| 2296 | if (!exchanged) { \ | 2416 | bool success; \ |
| 2297 | *expected = *((zig_##Type*)&initial); \ | 2417 | memcpy(&comparand, expected, sizeof(comparand)); \ |
| 2298 | } \ | 2418 | memcpy(&exchange, &desired, sizeof(exchange)); \ |
| 2299 | return exchanged; \ | 2419 | initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, exchange, comparand); \ |
| 2420 | success = initial == comparand; \ | ||
| 2421 | if (!success) memcpy(expected, &initial, sizeof(*expected)); \ | ||
| 2422 | return success; \ | ||
| 2300 | } \ | 2423 | } \ |
| 2301 | static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2424 | static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ |
| 2302 | zig_##ReprType initial = _InterlockedExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&value)); \ | 2425 | ReprType repr; \ |
| 2303 | return *((zig_##Type*)&initial); \ | 2426 | ReprType initial; \ |
| 2427 | zig_##Type result; \ | ||
| 2428 | memcpy(&repr, &value, sizeof(repr)); \ | ||
| 2429 | initial = _InterlockedExchange##suffix((ReprType volatile*)obj, repr); \ | ||
| 2430 | memcpy(&result, &initial, sizeof(result)); \ | ||
| 2431 | return result; \ | ||
| 2304 | } \ | 2432 | } \ |
| 2305 | static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2433 | static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ |
| 2306 | bool success = false; \ | 2434 | ReprType repr; \ |
| 2307 | zig_##ReprType new; \ | 2435 | zig_##Type expected; \ |
| 2308 | zig_##Type prev; \ | 2436 | zig_##Type desired; \ |
| 2309 | while (!success) { \ | 2437 | repr = *(ReprType volatile*)obj; \ |
| 2310 | prev = *obj; \ | 2438 | memcpy(&expected, &repr, sizeof(expected)); \ |
| 2311 | new = prev + value; \ | 2439 | do { \ |
| 2312 | success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ | 2440 | desired = expected + value; \ |
| 2313 | } \ | 2441 | } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ |
| 2314 | return prev; \ | 2442 | return expected; \ |
| 2315 | } \ | 2443 | } \ |
| 2316 | static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | 2444 | static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ |
| 2317 | bool success = false; \ | 2445 | ReprType repr; \ |
| 2318 | zig_##ReprType new; \ | 2446 | zig_##Type expected; \ |
| 2319 | zig_##Type prev; \ | 2447 | zig_##Type desired; \ |
| 2320 | while (!success) { \ | 2448 | repr = *(ReprType volatile*)obj; \ |
| 2321 | prev = *obj; \ | 2449 | memcpy(&expected, &repr, sizeof(expected)); \ |
| 2322 | new = prev - value; \ | 2450 | do { \ |
| 2323 | success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ | 2451 | desired = expected - value; \ |
| 2324 | } \ | 2452 | } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ |
| 2325 | return prev; \ | 2453 | return expected; \ |
| 2326 | } | 2454 | } |
| 2327 | 2455 | ||
| 2328 | zig_msvc_flt_atomics(f32, u32, ) | 2456 | zig_msvc_flt_atomics(f32, uint32_t, ) |
| 2329 | #if _M_X64 | 2457 | #if _M_X64 |
| 2330 | zig_msvc_flt_atomics(f64, u64, 64) | 2458 | zig_msvc_flt_atomics(f64, uint64_t, 64) |
| 2331 | #endif | 2459 | #endif |
| 2332 | 2460 | ||
| 2333 | #if _M_IX86 | 2461 | #if _M_IX86 |
| 2334 | static inline void zig_msvc_atomic_barrier() { | 2462 | static inline void zig_msvc_atomic_barrier() { |
| 2335 | zig_i32 barrier; | 2463 | int32_t barrier; |
| 2336 | __asm { | 2464 | __asm { |
| 2337 | xchg barrier, eax | 2465 | xchg barrier, eax |
| 2338 | } | 2466 | } |
| 2339 | } | 2467 | } |
| 2340 | 2468 | ||
| 2341 | static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, zig_u32* arg) { | 2469 | static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, void* arg) { |
| 2342 | return _InterlockedExchangePointer(obj, arg); | 2470 | return _InterlockedExchangePointer(obj, arg); |
| 2343 | } | 2471 | } |
| 2344 | 2472 | ||
| 2345 | static inline void zig_msvc_atomic_store_p32(void** obj, zig_u32* arg) { | 2473 | static inline void zig_msvc_atomic_store_p32(void** obj, void* arg) { |
| 2346 | _InterlockedExchangePointer(obj, arg); | 2474 | _InterlockedExchangePointer(obj, arg); |
| 2347 | } | 2475 | } |
| 2348 | 2476 | ||
| ... | @@ -2360,11 +2488,11 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir | ... | @@ -2360,11 +2488,11 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir |
| 2360 | return exchanged; | 2488 | return exchanged; |
| 2361 | } | 2489 | } |
| 2362 | #else /* _M_IX86 */ | 2490 | #else /* _M_IX86 */ |
| 2363 | static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, zig_u64* arg) { | 2491 | static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, void* arg) { |
| 2364 | return _InterlockedExchangePointer(obj, arg); | 2492 | return _InterlockedExchangePointer(obj, arg); |
| 2365 | } | 2493 | } |
| 2366 | 2494 | ||
| 2367 | static inline void zig_msvc_atomic_store_p64(void** obj, zig_u64* arg) { | 2495 | static inline void zig_msvc_atomic_store_p64(void** obj, void* arg) { |
| 2368 | _InterlockedExchangePointer(obj, arg); | 2496 | _InterlockedExchangePointer(obj, arg); |
| 2369 | } | 2497 | } |
| 2370 | 2498 | ||
| ... | @@ -2383,11 +2511,11 @@ static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desir | ... | @@ -2383,11 +2511,11 @@ static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desir |
| 2383 | } | 2511 | } |
| 2384 | 2512 | ||
| 2385 | static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) { | 2513 | static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) { |
| 2386 | return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_i64*)expected); | 2514 | return _InterlockedCompareExchange128((int64_t volatile*)obj, desired.hi, desired.lo, (int64_t*)expected); |
| 2387 | } | 2515 | } |
| 2388 | 2516 | ||
| 2389 | static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) { | 2517 | static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) { |
| 2390 | return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_u64*)expected); | 2518 | return _InterlockedCompareExchange128((int64_t volatile*)obj, desired.hi, desired.lo, (uint64_t*)expected); |
| 2391 | } | 2519 | } |
| 2392 | 2520 | ||
| 2393 | #define zig_msvc_atomics_128xchg(Type) \ | 2521 | #define zig_msvc_atomics_128xchg(Type) \ |
| ... | @@ -2429,7 +2557,7 @@ zig_msvc_atomics_128op(u128, max) | ... | @@ -2429,7 +2557,7 @@ zig_msvc_atomics_128op(u128, max) |
| 2429 | 2557 | ||
| 2430 | #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ | 2558 | #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ |
| 2431 | 2559 | ||
| 2432 | /* ========================= Special Case Intrinsics ========================= */ | 2560 | /* ======================== Special Case Intrinsics ========================= */ |
| 2433 | 2561 | ||
| 2434 | #if (_MSC_VER && _M_X64) || defined(__x86_64__) | 2562 | #if (_MSC_VER && _M_X64) || defined(__x86_64__) |
| 2435 | 2563 | ||
| ... | @@ -2459,8 +2587,8 @@ static inline void* zig_x86_windows_teb(void) { | ... | @@ -2459,8 +2587,8 @@ static inline void* zig_x86_windows_teb(void) { |
| 2459 | 2587 | ||
| 2460 | #if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__) | 2588 | #if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__) |
| 2461 | 2589 | ||
| 2462 | static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, zig_u32* ebx, zig_u32* ecx, zig_u32* edx) { | 2590 | static inline void zig_x86_cpuid(uint32_t leaf_id, uint32_t subid, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) { |
| 2463 | zig_u32 cpu_info[4]; | 2591 | uint32_t cpu_info[4]; |
| 2464 | #if _MSC_VER | 2592 | #if _MSC_VER |
| 2465 | __cpuidex(cpu_info, leaf_id, subid); | 2593 | __cpuidex(cpu_info, leaf_id, subid); |
| 2466 | #else | 2594 | #else |
| ... | @@ -2472,12 +2600,12 @@ static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, z | ... | @@ -2472,12 +2600,12 @@ static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, z |
| 2472 | *edx = cpu_info[3]; | 2600 | *edx = cpu_info[3]; |
| 2473 | } | 2601 | } |
| 2474 | 2602 | ||
| 2475 | static inline zig_u32 zig_x86_get_xcr0(void) { | 2603 | static inline uint32_t zig_x86_get_xcr0(void) { |
| 2476 | #if _MSC_VER | 2604 | #if _MSC_VER |
| 2477 | return (zig_u32)_xgetbv(0); | 2605 | return (uint32_t)_xgetbv(0); |
| 2478 | #else | 2606 | #else |
| 2479 | zig_u32 eax; | 2607 | uint32_t eax; |
| 2480 | zig_u32 edx; | 2608 | uint32_t edx; |
| 2481 | __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); | 2609 | __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); |
| 2482 | return eax; | 2610 | return eax; |
| 2483 | #endif | 2611 | #endif |
src/Autodoc.zig+2-10| ... | @@ -860,17 +860,9 @@ fn walkInstruction( | ... | @@ -860,17 +860,9 @@ fn walkInstruction( |
| 860 | const str_tok = data[inst_index].str_tok; | 860 | const str_tok = data[inst_index].str_tok; |
| 861 | var path = str_tok.get(file.zir); | 861 | var path = str_tok.get(file.zir); |
| 862 | 862 | ||
| 863 | const maybe_other_package: ?*Package = blk: { | ||
| 864 | if (self.module.main_pkg_is_std and std.mem.eql(u8, path, "std")) { | ||
| 865 | path = "std"; | ||
| 866 | break :blk self.module.main_pkg; | ||
| 867 | } else { | ||
| 868 | break :blk file.pkg.table.get(path); | ||
| 869 | } | ||
| 870 | }; | ||
| 871 | // importFile cannot error out since all files | 863 | // importFile cannot error out since all files |
| 872 | // are already loaded at this point | 864 | // are already loaded at this point |
| 873 | if (maybe_other_package) |other_package| { | 865 | if (file.pkg.table.get(path)) |other_package| { |
| 874 | const result = try self.packages.getOrPut(self.arena, other_package); | 866 | const result = try self.packages.getOrPut(self.arena, other_package); |
| 875 | 867 | ||
| 876 | // Immediately add this package to the import table of our | 868 | // Immediately add this package to the import table of our |
| ... | @@ -3629,7 +3621,7 @@ fn tryResolveRefPath( | ... | @@ -3629,7 +3621,7 @@ fn tryResolveRefPath( |
| 3629 | } | 3621 | } |
| 3630 | 3622 | ||
| 3631 | if (self.pending_ref_paths.get(&path[path.len - 1])) |waiter_list| { | 3623 | if (self.pending_ref_paths.get(&path[path.len - 1])) |waiter_list| { |
| 3632 | // It's important to de-register oureslves as pending before | 3624 | // It's important to de-register ourselves as pending before |
| 3633 | // attempting to resolve any other decl. | 3625 | // attempting to resolve any other decl. |
| 3634 | _ = self.pending_ref_paths.remove(&path[path.len - 1]); | 3626 | _ = self.pending_ref_paths.remove(&path[path.len - 1]); |
| 3635 | 3627 |
src/Compilation.zig+145-91| ... | @@ -1596,36 +1596,53 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1596,36 +1596,53 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1596 | 1596 | ||
| 1597 | const builtin_pkg = try Package.createWithDir( | 1597 | const builtin_pkg = try Package.createWithDir( |
| 1598 | gpa, | 1598 | gpa, |
| 1599 | "builtin", | ||
| 1600 | zig_cache_artifact_directory, | 1599 | zig_cache_artifact_directory, |
| 1601 | null, | 1600 | null, |
| 1602 | "builtin.zig", | 1601 | "builtin.zig", |
| 1603 | ); | 1602 | ); |
| 1604 | errdefer builtin_pkg.destroy(gpa); | 1603 | errdefer builtin_pkg.destroy(gpa); |
| 1605 | 1604 | ||
| 1606 | const std_pkg = try Package.createWithDir( | 1605 | // When you're testing std, the main module is std. In that case, we'll just set the std |
| 1607 | gpa, | 1606 | // module to the main one, since avoiding the errors caused by duplicating it is more |
| 1608 | "std", | 1607 | // effort than it's worth. |
| 1609 | options.zig_lib_directory, | 1608 | const main_pkg_is_std = m: { |
| 1610 | "std", | 1609 | const std_path = try std.fs.path.resolve(arena, &[_][]const u8{ |
| 1611 | "std.zig", | 1610 | options.zig_lib_directory.path orelse ".", |
| 1612 | ); | 1611 | "std", |
| 1613 | errdefer std_pkg.destroy(gpa); | 1612 | "std.zig", |
| 1613 | }); | ||
| 1614 | defer arena.free(std_path); | ||
| 1615 | const main_path = try std.fs.path.resolve(arena, &[_][]const u8{ | ||
| 1616 | main_pkg.root_src_directory.path orelse ".", | ||
| 1617 | main_pkg.root_src_path, | ||
| 1618 | }); | ||
| 1619 | defer arena.free(main_path); | ||
| 1620 | break :m mem.eql(u8, main_path, std_path); | ||
| 1621 | }; | ||
| 1622 | |||
| 1623 | const std_pkg = if (main_pkg_is_std) | ||
| 1624 | main_pkg | ||
| 1625 | else | ||
| 1626 | try Package.createWithDir( | ||
| 1627 | gpa, | ||
| 1628 | options.zig_lib_directory, | ||
| 1629 | "std", | ||
| 1630 | "std.zig", | ||
| 1631 | ); | ||
| 1632 | |||
| 1633 | errdefer if (!main_pkg_is_std) std_pkg.destroy(gpa); | ||
| 1614 | 1634 | ||
| 1615 | const root_pkg = if (options.is_test) root_pkg: { | 1635 | const root_pkg = if (options.is_test) root_pkg: { |
| 1616 | // TODO: we currently have two packages named 'root' here, which is weird. This | ||
| 1617 | // should be changed as part of the resolution of #12201 | ||
| 1618 | const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: { | 1636 | const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: { |
| 1619 | const test_dir = std.fs.path.dirname(test_runner); | 1637 | const test_dir = std.fs.path.dirname(test_runner); |
| 1620 | const basename = std.fs.path.basename(test_runner); | 1638 | const basename = std.fs.path.basename(test_runner); |
| 1621 | const pkg = try Package.create(gpa, "root", test_dir, basename); | 1639 | const pkg = try Package.create(gpa, test_dir, basename); |
| 1622 | 1640 | ||
| 1623 | // copy package table from main_pkg to root_pkg | 1641 | // copy package table from main_pkg to root_pkg |
| 1624 | pkg.table = try main_pkg.table.clone(gpa); | 1642 | pkg.table = try main_pkg.table.clone(gpa); |
| 1625 | break :test_pkg pkg; | 1643 | break :test_pkg pkg; |
| 1626 | } else try Package.createWithDir( | 1644 | } else try Package.createWithDir( |
| 1627 | gpa, | 1645 | gpa, |
| 1628 | "root", | ||
| 1629 | options.zig_lib_directory, | 1646 | options.zig_lib_directory, |
| 1630 | null, | 1647 | null, |
| 1631 | "test_runner.zig", | 1648 | "test_runner.zig", |
| ... | @@ -1639,7 +1656,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1639,7 +1656,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1639 | const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: { | 1656 | const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: { |
| 1640 | break :compiler_rt_pkg try Package.createWithDir( | 1657 | break :compiler_rt_pkg try Package.createWithDir( |
| 1641 | gpa, | 1658 | gpa, |
| 1642 | "compiler_rt", | ||
| 1643 | options.zig_lib_directory, | 1659 | options.zig_lib_directory, |
| 1644 | null, | 1660 | null, |
| 1645 | "compiler_rt.zig", | 1661 | "compiler_rt.zig", |
| ... | @@ -1647,28 +1663,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1647,28 +1663,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1647 | } else null; | 1663 | } else null; |
| 1648 | errdefer if (compiler_rt_pkg) |p| p.destroy(gpa); | 1664 | errdefer if (compiler_rt_pkg) |p| p.destroy(gpa); |
| 1649 | 1665 | ||
| 1650 | try main_pkg.addAndAdopt(gpa, builtin_pkg); | 1666 | try main_pkg.add(gpa, "builtin", builtin_pkg); |
| 1651 | try main_pkg.add(gpa, root_pkg); | 1667 | try main_pkg.add(gpa, "root", root_pkg); |
| 1652 | try main_pkg.addAndAdopt(gpa, std_pkg); | 1668 | try main_pkg.add(gpa, "std", std_pkg); |
| 1653 | 1669 | ||
| 1654 | if (compiler_rt_pkg) |p| { | 1670 | if (compiler_rt_pkg) |p| { |
| 1655 | try main_pkg.addAndAdopt(gpa, p); | 1671 | try main_pkg.add(gpa, "compiler_rt", p); |
| 1656 | } | 1672 | } |
| 1657 | 1673 | ||
| 1658 | const main_pkg_is_std = m: { | ||
| 1659 | const std_path = try std.fs.path.resolve(arena, &[_][]const u8{ | ||
| 1660 | std_pkg.root_src_directory.path orelse ".", | ||
| 1661 | std_pkg.root_src_path, | ||
| 1662 | }); | ||
| 1663 | defer arena.free(std_path); | ||
| 1664 | const main_path = try std.fs.path.resolve(arena, &[_][]const u8{ | ||
| 1665 | main_pkg.root_src_directory.path orelse ".", | ||
| 1666 | main_pkg.root_src_path, | ||
| 1667 | }); | ||
| 1668 | defer arena.free(main_path); | ||
| 1669 | break :m mem.eql(u8, main_path, std_path); | ||
| 1670 | }; | ||
| 1671 | |||
| 1672 | // Pre-open the directory handles for cached ZIR code so that it does not need | 1674 | // Pre-open the directory handles for cached ZIR code so that it does not need |
| 1673 | // to redundantly happen for each AstGen operation. | 1675 | // to redundantly happen for each AstGen operation. |
| 1674 | const zir_sub_dir = "z"; | 1676 | const zir_sub_dir = "z"; |
| ... | @@ -1705,7 +1707,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1705,7 +1707,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1705 | .gpa = gpa, | 1707 | .gpa = gpa, |
| 1706 | .comp = comp, | 1708 | .comp = comp, |
| 1707 | .main_pkg = main_pkg, | 1709 | .main_pkg = main_pkg, |
| 1708 | .main_pkg_is_std = main_pkg_is_std, | ||
| 1709 | .root_pkg = root_pkg, | 1710 | .root_pkg = root_pkg, |
| 1710 | .zig_cache_artifact_directory = zig_cache_artifact_directory, | 1711 | .zig_cache_artifact_directory = zig_cache_artifact_directory, |
| 1711 | .global_zir_cache = global_zir_cache, | 1712 | .global_zir_cache = global_zir_cache, |
| ... | @@ -2772,6 +2773,111 @@ fn emitOthers(comp: *Compilation) void { | ... | @@ -2772,6 +2773,111 @@ fn emitOthers(comp: *Compilation) void { |
| 2772 | } | 2773 | } |
| 2773 | } | 2774 | } |
| 2774 | 2775 | ||
| 2776 | fn reportMultiModuleErrors(mod: *Module) !void { | ||
| 2777 | // Some cases can give you a whole bunch of multi-module errors, which it's not helpful to | ||
| 2778 | // print all of, so we'll cap the number of these to emit. | ||
| 2779 | var num_errors: u32 = 0; | ||
| 2780 | const max_errors = 5; | ||
| 2781 | // Attach the "some omitted" note to the final error message | ||
| 2782 | var last_err: ?*Module.ErrorMsg = null; | ||
| 2783 | |||
| 2784 | for (mod.import_table.values()) |file| { | ||
| 2785 | if (!file.multi_pkg) continue; | ||
| 2786 | |||
| 2787 | num_errors += 1; | ||
| 2788 | if (num_errors > max_errors) continue; | ||
| 2789 | |||
| 2790 | const err = err_blk: { | ||
| 2791 | // Like with errors, let's cap the number of notes to prevent a huge error spew. | ||
| 2792 | const max_notes = 5; | ||
| 2793 | const omitted = file.references.items.len -| max_notes; | ||
| 2794 | const num_notes = file.references.items.len - omitted; | ||
| 2795 | |||
| 2796 | const notes = try mod.gpa.alloc(Module.ErrorMsg, if (omitted > 0) num_notes + 1 else num_notes); | ||
| 2797 | errdefer mod.gpa.free(notes); | ||
| 2798 | |||
| 2799 | for (notes[0..num_notes], file.references.items[0..num_notes], 0..) |*note, ref, i| { | ||
| 2800 | errdefer for (notes[0..i]) |*n| n.deinit(mod.gpa); | ||
| 2801 | note.* = switch (ref) { | ||
| 2802 | .import => |loc| blk: { | ||
| 2803 | const name = try loc.file_scope.pkg.getName(mod.gpa, mod.*); | ||
| 2804 | defer mod.gpa.free(name); | ||
| 2805 | break :blk try Module.ErrorMsg.init( | ||
| 2806 | mod.gpa, | ||
| 2807 | loc, | ||
| 2808 | "imported from module {s}", | ||
| 2809 | .{name}, | ||
| 2810 | ); | ||
| 2811 | }, | ||
| 2812 | .root => |pkg| blk: { | ||
| 2813 | const name = try pkg.getName(mod.gpa, mod.*); | ||
| 2814 | defer mod.gpa.free(name); | ||
| 2815 | break :blk try Module.ErrorMsg.init( | ||
| 2816 | mod.gpa, | ||
| 2817 | .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, | ||
| 2818 | "root of module {s}", | ||
| 2819 | .{name}, | ||
| 2820 | ); | ||
| 2821 | }, | ||
| 2822 | }; | ||
| 2823 | } | ||
| 2824 | errdefer for (notes[0..num_notes]) |*n| n.deinit(mod.gpa); | ||
| 2825 | |||
| 2826 | if (omitted > 0) { | ||
| 2827 | notes[num_notes] = try Module.ErrorMsg.init( | ||
| 2828 | mod.gpa, | ||
| 2829 | .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, | ||
| 2830 | "{} more references omitted", | ||
| 2831 | .{omitted}, | ||
| 2832 | ); | ||
| 2833 | } | ||
| 2834 | errdefer if (omitted > 0) notes[num_notes].deinit(mod.gpa); | ||
| 2835 | |||
| 2836 | const err = try Module.ErrorMsg.create( | ||
| 2837 | mod.gpa, | ||
| 2838 | .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, | ||
| 2839 | "file exists in multiple modules", | ||
| 2840 | .{}, | ||
| 2841 | ); | ||
| 2842 | err.notes = notes; | ||
| 2843 | break :err_blk err; | ||
| 2844 | }; | ||
| 2845 | errdefer err.destroy(mod.gpa); | ||
| 2846 | try mod.failed_files.putNoClobber(mod.gpa, file, err); | ||
| 2847 | last_err = err; | ||
| 2848 | } | ||
| 2849 | |||
| 2850 | // If we omitted any errors, add a note saying that | ||
| 2851 | if (num_errors > max_errors) { | ||
| 2852 | const err = last_err.?; | ||
| 2853 | |||
| 2854 | // There isn't really any meaningful place to put this note, so just attach it to the | ||
| 2855 | // last failed file | ||
| 2856 | var note = try Module.ErrorMsg.init( | ||
| 2857 | mod.gpa, | ||
| 2858 | err.src_loc, | ||
| 2859 | "{} more errors omitted", | ||
| 2860 | .{num_errors - max_errors}, | ||
| 2861 | ); | ||
| 2862 | errdefer note.deinit(mod.gpa); | ||
| 2863 | |||
| 2864 | const i = err.notes.len; | ||
| 2865 | err.notes = try mod.gpa.realloc(err.notes, i + 1); | ||
| 2866 | err.notes[i] = note; | ||
| 2867 | } | ||
| 2868 | |||
| 2869 | // Now that we've reported the errors, we need to deal with | ||
| 2870 | // dependencies. Any file referenced by a multi_pkg file should also be | ||
| 2871 | // marked multi_pkg and have its status set to astgen_failure, as it's | ||
| 2872 | // ambiguous which package they should be analyzed as a part of. We need | ||
| 2873 | // to add this flag after reporting the errors however, as otherwise | ||
| 2874 | // we'd get an error for every single downstream file, which wouldn't be | ||
| 2875 | // very useful. | ||
| 2876 | for (mod.import_table.values()) |file| { | ||
| 2877 | if (file.multi_pkg) file.recursiveMarkMultiPkg(mod); | ||
| 2878 | } | ||
| 2879 | } | ||
| 2880 | |||
| 2775 | /// Having the file open for writing is problematic as far as executing the | 2881 | /// Having the file open for writing is problematic as far as executing the |
| 2776 | /// binary is concerned. This will remove the write flag, or close the file, | 2882 | /// binary is concerned. This will remove the write flag, or close the file, |
| 2777 | /// or whatever is needed so that it can be executed. | 2883 | /// or whatever is needed so that it can be executed. |
| ... | @@ -3098,54 +3204,7 @@ pub fn performAllTheWork( | ... | @@ -3098,54 +3204,7 @@ pub fn performAllTheWork( |
| 3098 | } | 3204 | } |
| 3099 | 3205 | ||
| 3100 | if (comp.bin_file.options.module) |mod| { | 3206 | if (comp.bin_file.options.module) |mod| { |
| 3101 | for (mod.import_table.values()) |file| { | 3207 | try reportMultiModuleErrors(mod); |
| 3102 | if (!file.multi_pkg) continue; | ||
| 3103 | const err = err_blk: { | ||
| 3104 | const notes = try mod.gpa.alloc(Module.ErrorMsg, file.references.items.len); | ||
| 3105 | errdefer mod.gpa.free(notes); | ||
| 3106 | |||
| 3107 | for (notes, 0..) |*note, i| { | ||
| 3108 | errdefer for (notes[0..i]) |*n| n.deinit(mod.gpa); | ||
| 3109 | note.* = switch (file.references.items[i]) { | ||
| 3110 | .import => |loc| try Module.ErrorMsg.init( | ||
| 3111 | mod.gpa, | ||
| 3112 | loc, | ||
| 3113 | "imported from package {s}", | ||
| 3114 | .{loc.file_scope.pkg.name}, | ||
| 3115 | ), | ||
| 3116 | .root => |pkg| try Module.ErrorMsg.init( | ||
| 3117 | mod.gpa, | ||
| 3118 | .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, | ||
| 3119 | "root of package {s}", | ||
| 3120 | .{pkg.name}, | ||
| 3121 | ), | ||
| 3122 | }; | ||
| 3123 | } | ||
| 3124 | errdefer for (notes) |*n| n.deinit(mod.gpa); | ||
| 3125 | |||
| 3126 | const err = try Module.ErrorMsg.create( | ||
| 3127 | mod.gpa, | ||
| 3128 | .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, | ||
| 3129 | "file exists in multiple packages", | ||
| 3130 | .{}, | ||
| 3131 | ); | ||
| 3132 | err.notes = notes; | ||
| 3133 | break :err_blk err; | ||
| 3134 | }; | ||
| 3135 | errdefer err.destroy(mod.gpa); | ||
| 3136 | try mod.failed_files.putNoClobber(mod.gpa, file, err); | ||
| 3137 | } | ||
| 3138 | |||
| 3139 | // Now that we've reported the errors, we need to deal with | ||
| 3140 | // dependencies. Any file referenced by a multi_pkg file should also be | ||
| 3141 | // marked multi_pkg and have its status set to astgen_failure, as it's | ||
| 3142 | // ambiguous which package they should be analyzed as a part of. We need | ||
| 3143 | // to add this flag after reporting the errors however, as otherwise | ||
| 3144 | // we'd get an error for every single downstream file, which wouldn't be | ||
| 3145 | // very useful. | ||
| 3146 | for (mod.import_table.values()) |file| { | ||
| 3147 | if (file.multi_pkg) file.recursiveMarkMultiPkg(mod); | ||
| 3148 | } | ||
| 3149 | } | 3208 | } |
| 3150 | 3209 | ||
| 3151 | { | 3210 | { |
| ... | @@ -3266,24 +3325,20 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3266,24 +3325,20 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3266 | const decl_emit_h = emit_h.declPtr(decl_index); | 3325 | const decl_emit_h = emit_h.declPtr(decl_index); |
| 3267 | const fwd_decl = &decl_emit_h.fwd_decl; | 3326 | const fwd_decl = &decl_emit_h.fwd_decl; |
| 3268 | fwd_decl.shrinkRetainingCapacity(0); | 3327 | fwd_decl.shrinkRetainingCapacity(0); |
| 3269 | var typedefs_arena = std.heap.ArenaAllocator.init(gpa); | 3328 | var ctypes_arena = std.heap.ArenaAllocator.init(gpa); |
| 3270 | defer typedefs_arena.deinit(); | 3329 | defer ctypes_arena.deinit(); |
| 3271 | 3330 | ||
| 3272 | var dg: c_codegen.DeclGen = .{ | 3331 | var dg: c_codegen.DeclGen = .{ |
| 3273 | .gpa = gpa, | 3332 | .gpa = gpa, |
| 3274 | .module = module, | 3333 | .module = module, |
| 3275 | .error_msg = null, | 3334 | .error_msg = null, |
| 3276 | .decl_index = decl_index, | 3335 | .decl_index = decl_index.toOptional(), |
| 3277 | .decl = decl, | 3336 | .decl = decl, |
| 3278 | .fwd_decl = fwd_decl.toManaged(gpa), | 3337 | .fwd_decl = fwd_decl.toManaged(gpa), |
| 3279 | .typedefs = c_codegen.TypedefMap.initContext(gpa, .{ .mod = module }), | 3338 | .ctypes = .{}, |
| 3280 | .typedefs_arena = typedefs_arena.allocator(), | ||
| 3281 | }; | 3339 | }; |
| 3282 | defer { | 3340 | defer { |
| 3283 | for (dg.typedefs.values()) |typedef| { | 3341 | dg.ctypes.deinit(gpa); |
| 3284 | module.gpa.free(typedef.rendered); | ||
| 3285 | } | ||
| 3286 | dg.typedefs.deinit(); | ||
| 3287 | dg.fwd_decl.deinit(); | 3342 | dg.fwd_decl.deinit(); |
| 3288 | } | 3343 | } |
| 3289 | 3344 | ||
| ... | @@ -5415,7 +5470,6 @@ fn buildOutputFromZig( | ... | @@ -5415,7 +5470,6 @@ fn buildOutputFromZig( |
| 5415 | var main_pkg: Package = .{ | 5470 | var main_pkg: Package = .{ |
| 5416 | .root_src_directory = comp.zig_lib_directory, | 5471 | .root_src_directory = comp.zig_lib_directory, |
| 5417 | .root_src_path = src_basename, | 5472 | .root_src_path = src_basename, |
| 5418 | .name = "root", | ||
| 5419 | }; | 5473 | }; |
| 5420 | defer main_pkg.deinitTable(comp.gpa); | 5474 | defer main_pkg.deinitTable(comp.gpa); |
| 5421 | const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; | 5475 | const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; |
src/Module.zig+46-23| ... | @@ -144,10 +144,6 @@ stage1_flags: packed struct { | ... | @@ -144,10 +144,6 @@ stage1_flags: packed struct { |
| 144 | } = .{}, | 144 | } = .{}, |
| 145 | 145 | ||
| 146 | job_queued_update_builtin_zig: bool = true, | 146 | job_queued_update_builtin_zig: bool = true, |
| 147 | /// This makes it so that we can run `zig test` on the standard library. | ||
| 148 | /// Otherwise, the logic for scanning test decls skips all of them because | ||
| 149 | /// `main_pkg != std_pkg`. | ||
| 150 | main_pkg_is_std: bool, | ||
| 151 | 147 | ||
| 152 | compile_log_text: ArrayListUnmanaged(u8) = .{}, | 148 | compile_log_text: ArrayListUnmanaged(u8) = .{}, |
| 153 | 149 | ||
| ... | @@ -1950,7 +1946,7 @@ pub const File = struct { | ... | @@ -1950,7 +1946,7 @@ pub const File = struct { |
| 1950 | prev_zir: ?*Zir = null, | 1946 | prev_zir: ?*Zir = null, |
| 1951 | 1947 | ||
| 1952 | /// A single reference to a file. | 1948 | /// A single reference to a file. |
| 1953 | const Reference = union(enum) { | 1949 | pub const Reference = union(enum) { |
| 1954 | /// The file is imported directly (i.e. not as a package) with @import. | 1950 | /// The file is imported directly (i.e. not as a package) with @import. |
| 1955 | import: SrcLoc, | 1951 | import: SrcLoc, |
| 1956 | /// The file is the root of a package. | 1952 | /// The file is the root of a package. |
| ... | @@ -2113,7 +2109,27 @@ pub const File = struct { | ... | @@ -2113,7 +2109,27 @@ pub const File = struct { |
| 2113 | 2109 | ||
| 2114 | /// Add a reference to this file during AstGen. | 2110 | /// Add a reference to this file during AstGen. |
| 2115 | pub fn addReference(file: *File, mod: Module, ref: Reference) !void { | 2111 | pub fn addReference(file: *File, mod: Module, ref: Reference) !void { |
| 2116 | try file.references.append(mod.gpa, ref); | 2112 | // Don't add the same module root twice. Note that since we always add module roots at the |
| 2113 | // front of the references array (see below), this loop is actually O(1) on valid code. | ||
| 2114 | if (ref == .root) { | ||
| 2115 | for (file.references.items) |other| { | ||
| 2116 | switch (other) { | ||
| 2117 | .root => |r| if (ref.root == r) return, | ||
| 2118 | else => break, // reached the end of the "is-root" references | ||
| 2119 | } | ||
| 2120 | } | ||
| 2121 | } | ||
| 2122 | |||
| 2123 | switch (ref) { | ||
| 2124 | // We put root references at the front of the list both to make the above loop fast and | ||
| 2125 | // to make multi-module errors more helpful (since "root-of" notes are generally more | ||
| 2126 | // informative than "imported-from" notes). This path is hit very rarely, so the speed | ||
| 2127 | // of the insert operation doesn't matter too much. | ||
| 2128 | .root => try file.references.insert(mod.gpa, 0, ref), | ||
| 2129 | |||
| 2130 | // Other references we'll just put at the end. | ||
| 2131 | else => try file.references.append(mod.gpa, ref), | ||
| 2132 | } | ||
| 2117 | 2133 | ||
| 2118 | const pkg = switch (ref) { | 2134 | const pkg = switch (ref) { |
| 2119 | .import => |loc| loc.file_scope.pkg, | 2135 | .import => |loc| loc.file_scope.pkg, |
| ... | @@ -2128,7 +2144,10 @@ pub const File = struct { | ... | @@ -2128,7 +2144,10 @@ pub const File = struct { |
| 2128 | file.multi_pkg = true; | 2144 | file.multi_pkg = true; |
| 2129 | file.status = .astgen_failure; | 2145 | file.status = .astgen_failure; |
| 2130 | 2146 | ||
| 2131 | std.debug.assert(file.zir_loaded); | 2147 | // We can only mark children as failed if the ZIR is loaded, which may not |
| 2148 | // be the case if there were other astgen failures in this file | ||
| 2149 | if (!file.zir_loaded) return; | ||
| 2150 | |||
| 2132 | const imports_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)]; | 2151 | const imports_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)]; |
| 2133 | if (imports_index == 0) return; | 2152 | if (imports_index == 0) return; |
| 2134 | const extra = file.zir.extraData(Zir.Inst.Imports, imports_index); | 2153 | const extra = file.zir.extraData(Zir.Inst.Imports, imports_index); |
| ... | @@ -3323,10 +3342,19 @@ pub fn deinit(mod: *Module) void { | ... | @@ -3323,10 +3342,19 @@ pub fn deinit(mod: *Module) void { |
| 3323 | // The callsite of `Compilation.create` owns the `main_pkg`, however | 3342 | // The callsite of `Compilation.create` owns the `main_pkg`, however |
| 3324 | // Module owns the builtin and std packages that it adds. | 3343 | // Module owns the builtin and std packages that it adds. |
| 3325 | if (mod.main_pkg.table.fetchRemove("builtin")) |kv| { | 3344 | if (mod.main_pkg.table.fetchRemove("builtin")) |kv| { |
| 3345 | gpa.free(kv.key); | ||
| 3326 | kv.value.destroy(gpa); | 3346 | kv.value.destroy(gpa); |
| 3327 | } | 3347 | } |
| 3328 | if (mod.main_pkg.table.fetchRemove("std")) |kv| { | 3348 | if (mod.main_pkg.table.fetchRemove("std")) |kv| { |
| 3329 | kv.value.destroy(gpa); | 3349 | gpa.free(kv.key); |
| 3350 | // It's possible for main_pkg to be std when running 'zig test'! In this case, we must not | ||
| 3351 | // destroy it, since it would lead to a double-free. | ||
| 3352 | if (kv.value != mod.main_pkg) { | ||
| 3353 | kv.value.destroy(gpa); | ||
| 3354 | } | ||
| 3355 | } | ||
| 3356 | if (mod.main_pkg.table.fetchRemove("root")) |kv| { | ||
| 3357 | gpa.free(kv.key); | ||
| 3330 | } | 3358 | } |
| 3331 | if (mod.root_pkg != mod.main_pkg) { | 3359 | if (mod.root_pkg != mod.main_pkg) { |
| 3332 | mod.root_pkg.destroy(gpa); | 3360 | mod.root_pkg.destroy(gpa); |
| ... | @@ -4808,11 +4836,14 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult { | ... | @@ -4808,11 +4836,14 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult { |
| 4808 | 4836 | ||
| 4809 | const gop = try mod.import_table.getOrPut(gpa, resolved_path); | 4837 | const gop = try mod.import_table.getOrPut(gpa, resolved_path); |
| 4810 | errdefer _ = mod.import_table.pop(); | 4838 | errdefer _ = mod.import_table.pop(); |
| 4811 | if (gop.found_existing) return ImportFileResult{ | 4839 | if (gop.found_existing) { |
| 4812 | .file = gop.value_ptr.*, | 4840 | try gop.value_ptr.*.addReference(mod.*, .{ .root = pkg }); |
| 4813 | .is_new = false, | 4841 | return ImportFileResult{ |
| 4814 | .is_pkg = true, | 4842 | .file = gop.value_ptr.*, |
| 4815 | }; | 4843 | .is_new = false, |
| 4844 | .is_pkg = true, | ||
| 4845 | }; | ||
| 4846 | } | ||
| 4816 | 4847 | ||
| 4817 | const sub_file_path = try gpa.dupe(u8, pkg.root_src_path); | 4848 | const sub_file_path = try gpa.dupe(u8, pkg.root_src_path); |
| 4818 | errdefer gpa.free(sub_file_path); | 4849 | errdefer gpa.free(sub_file_path); |
| ... | @@ -5208,22 +5239,14 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err | ... | @@ -5208,22 +5239,14 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err |
| 5208 | // test decl with no name. Skip the part where we check against | 5239 | // test decl with no name. Skip the part where we check against |
| 5209 | // the test name filter. | 5240 | // the test name filter. |
| 5210 | if (!comp.bin_file.options.is_test) break :blk false; | 5241 | if (!comp.bin_file.options.is_test) break :blk false; |
| 5211 | if (decl_pkg != mod.main_pkg) { | 5242 | if (decl_pkg != mod.main_pkg) break :blk false; |
| 5212 | if (!mod.main_pkg_is_std) break :blk false; | ||
| 5213 | const std_pkg = mod.main_pkg.table.get("std").?; | ||
| 5214 | if (std_pkg != decl_pkg) break :blk false; | ||
| 5215 | } | ||
| 5216 | try mod.test_functions.put(gpa, new_decl_index, {}); | 5243 | try mod.test_functions.put(gpa, new_decl_index, {}); |
| 5217 | break :blk true; | 5244 | break :blk true; |
| 5218 | }, | 5245 | }, |
| 5219 | else => blk: { | 5246 | else => blk: { |
| 5220 | if (!is_named_test) break :blk false; | 5247 | if (!is_named_test) break :blk false; |
| 5221 | if (!comp.bin_file.options.is_test) break :blk false; | 5248 | if (!comp.bin_file.options.is_test) break :blk false; |
| 5222 | if (decl_pkg != mod.main_pkg) { | 5249 | if (decl_pkg != mod.main_pkg) break :blk false; |
| 5223 | if (!mod.main_pkg_is_std) break :blk false; | ||
| 5224 | const std_pkg = mod.main_pkg.table.get("std").?; | ||
| 5225 | if (std_pkg != decl_pkg) break :blk false; | ||
| 5226 | } | ||
| 5227 | if (comp.test_filter) |test_filter| { | 5250 | if (comp.test_filter) |test_filter| { |
| 5228 | if (mem.indexOf(u8, decl_name, test_filter) == null) { | 5251 | if (mem.indexOf(u8, decl_name, test_filter) == null) { |
| 5229 | break :blk false; | 5252 | break :blk false; |
src/Package.zig+127-35| ... | @@ -22,17 +22,16 @@ pub const Table = std.StringHashMapUnmanaged(*Package); | ... | @@ -22,17 +22,16 @@ pub const Table = std.StringHashMapUnmanaged(*Package); |
| 22 | root_src_directory: Compilation.Directory, | 22 | root_src_directory: Compilation.Directory, |
| 23 | /// Relative to `root_src_directory`. May contain path separators. | 23 | /// Relative to `root_src_directory`. May contain path separators. |
| 24 | root_src_path: []const u8, | 24 | root_src_path: []const u8, |
| 25 | /// The dependency table of this module. Shared dependencies such as 'std', 'builtin', and 'root' | ||
| 26 | /// are not specified in every dependency table, but instead only in the table of `main_pkg`. | ||
| 27 | /// `Module.importFile` is responsible for detecting these names and using the correct package. | ||
| 25 | table: Table = .{}, | 28 | table: Table = .{}, |
| 26 | parent: ?*Package = null, | ||
| 27 | /// Whether to free `root_src_directory` on `destroy`. | 29 | /// Whether to free `root_src_directory` on `destroy`. |
| 28 | root_src_directory_owned: bool = false, | 30 | root_src_directory_owned: bool = false, |
| 29 | /// This information can be recovered from 'table', but it's more convenient to store on the package. | ||
| 30 | name: []const u8, | ||
| 31 | 31 | ||
| 32 | /// Allocate a Package. No references to the slices passed are kept. | 32 | /// Allocate a Package. No references to the slices passed are kept. |
| 33 | pub fn create( | 33 | pub fn create( |
| 34 | gpa: Allocator, | 34 | gpa: Allocator, |
| 35 | name: []const u8, | ||
| 36 | /// Null indicates the current working directory | 35 | /// Null indicates the current working directory |
| 37 | root_src_dir_path: ?[]const u8, | 36 | root_src_dir_path: ?[]const u8, |
| 38 | /// Relative to root_src_dir_path | 37 | /// Relative to root_src_dir_path |
| ... | @@ -47,9 +46,6 @@ pub fn create( | ... | @@ -47,9 +46,6 @@ pub fn create( |
| 47 | const owned_src_path = try gpa.dupe(u8, root_src_path); | 46 | const owned_src_path = try gpa.dupe(u8, root_src_path); |
| 48 | errdefer gpa.free(owned_src_path); | 47 | errdefer gpa.free(owned_src_path); |
| 49 | 48 | ||
| 50 | const owned_name = try gpa.dupe(u8, name); | ||
| 51 | errdefer gpa.free(owned_name); | ||
| 52 | |||
| 53 | ptr.* = .{ | 49 | ptr.* = .{ |
| 54 | .root_src_directory = .{ | 50 | .root_src_directory = .{ |
| 55 | .path = owned_dir_path, | 51 | .path = owned_dir_path, |
| ... | @@ -57,7 +53,6 @@ pub fn create( | ... | @@ -57,7 +53,6 @@ pub fn create( |
| 57 | }, | 53 | }, |
| 58 | .root_src_path = owned_src_path, | 54 | .root_src_path = owned_src_path, |
| 59 | .root_src_directory_owned = true, | 55 | .root_src_directory_owned = true, |
| 60 | .name = owned_name, | ||
| 61 | }; | 56 | }; |
| 62 | 57 | ||
| 63 | return ptr; | 58 | return ptr; |
| ... | @@ -65,7 +60,6 @@ pub fn create( | ... | @@ -65,7 +60,6 @@ pub fn create( |
| 65 | 60 | ||
| 66 | pub fn createWithDir( | 61 | pub fn createWithDir( |
| 67 | gpa: Allocator, | 62 | gpa: Allocator, |
| 68 | name: []const u8, | ||
| 69 | directory: Compilation.Directory, | 63 | directory: Compilation.Directory, |
| 70 | /// Relative to `directory`. If null, means `directory` is the root src dir | 64 | /// Relative to `directory`. If null, means `directory` is the root src dir |
| 71 | /// and is owned externally. | 65 | /// and is owned externally. |
| ... | @@ -79,9 +73,6 @@ pub fn createWithDir( | ... | @@ -79,9 +73,6 @@ pub fn createWithDir( |
| 79 | const owned_src_path = try gpa.dupe(u8, root_src_path); | 73 | const owned_src_path = try gpa.dupe(u8, root_src_path); |
| 80 | errdefer gpa.free(owned_src_path); | 74 | errdefer gpa.free(owned_src_path); |
| 81 | 75 | ||
| 82 | const owned_name = try gpa.dupe(u8, name); | ||
| 83 | errdefer gpa.free(owned_name); | ||
| 84 | |||
| 85 | if (root_src_dir_path) |p| { | 76 | if (root_src_dir_path) |p| { |
| 86 | const owned_dir_path = try directory.join(gpa, &[1][]const u8{p}); | 77 | const owned_dir_path = try directory.join(gpa, &[1][]const u8{p}); |
| 87 | errdefer gpa.free(owned_dir_path); | 78 | errdefer gpa.free(owned_dir_path); |
| ... | @@ -93,14 +84,12 @@ pub fn createWithDir( | ... | @@ -93,14 +84,12 @@ pub fn createWithDir( |
| 93 | }, | 84 | }, |
| 94 | .root_src_directory_owned = true, | 85 | .root_src_directory_owned = true, |
| 95 | .root_src_path = owned_src_path, | 86 | .root_src_path = owned_src_path, |
| 96 | .name = owned_name, | ||
| 97 | }; | 87 | }; |
| 98 | } else { | 88 | } else { |
| 99 | ptr.* = .{ | 89 | ptr.* = .{ |
| 100 | .root_src_directory = directory, | 90 | .root_src_directory = directory, |
| 101 | .root_src_directory_owned = false, | 91 | .root_src_directory_owned = false, |
| 102 | .root_src_path = owned_src_path, | 92 | .root_src_path = owned_src_path, |
| 103 | .name = owned_name, | ||
| 104 | }; | 93 | }; |
| 105 | } | 94 | } |
| 106 | return ptr; | 95 | return ptr; |
| ... | @@ -110,7 +99,6 @@ pub fn createWithDir( | ... | @@ -110,7 +99,6 @@ pub fn createWithDir( |
| 110 | /// inside its table; the caller is responsible for calling destroy() on them. | 99 | /// inside its table; the caller is responsible for calling destroy() on them. |
| 111 | pub fn destroy(pkg: *Package, gpa: Allocator) void { | 100 | pub fn destroy(pkg: *Package, gpa: Allocator) void { |
| 112 | gpa.free(pkg.root_src_path); | 101 | gpa.free(pkg.root_src_path); |
| 113 | gpa.free(pkg.name); | ||
| 114 | 102 | ||
| 115 | if (pkg.root_src_directory_owned) { | 103 | if (pkg.root_src_directory_owned) { |
| 116 | // If root_src_directory.path is null then the handle is the cwd() | 104 | // If root_src_directory.path is null then the handle is the cwd() |
| ... | @@ -130,15 +118,97 @@ pub fn deinitTable(pkg: *Package, gpa: Allocator) void { | ... | @@ -130,15 +118,97 @@ pub fn deinitTable(pkg: *Package, gpa: Allocator) void { |
| 130 | pkg.table.deinit(gpa); | 118 | pkg.table.deinit(gpa); |
| 131 | } | 119 | } |
| 132 | 120 | ||
| 133 | pub fn add(pkg: *Package, gpa: Allocator, package: *Package) !void { | 121 | pub fn add(pkg: *Package, gpa: Allocator, name: []const u8, package: *Package) !void { |
| 134 | try pkg.table.ensureUnusedCapacity(gpa, 1); | 122 | try pkg.table.ensureUnusedCapacity(gpa, 1); |
| 135 | pkg.table.putAssumeCapacityNoClobber(package.name, package); | 123 | const name_dupe = try gpa.dupe(u8, name); |
| 124 | pkg.table.putAssumeCapacityNoClobber(name_dupe, package); | ||
| 136 | } | 125 | } |
| 137 | 126 | ||
| 138 | pub fn addAndAdopt(parent: *Package, gpa: Allocator, child: *Package) !void { | 127 | /// Compute a readable name for the package. The returned name should be freed from gpa. This |
| 139 | assert(child.parent == null); // make up your mind, who is the parent?? | 128 | /// function is very slow, as it traverses the whole package hierarchy to find a path to this |
| 140 | child.parent = parent; | 129 | /// package. It should only be used for error output. |
| 141 | return parent.add(gpa, child); | 130 | pub fn getName(target: *const Package, gpa: Allocator, mod: Module) ![]const u8 { |
| 131 | // we'll do a breadth-first search from the root module to try and find a short name for this | ||
| 132 | // module, using a TailQueue of module/parent pairs. note that the "parent" there is just the | ||
| 133 | // first-found shortest path - a module may be children of arbitrarily many other modules. | ||
| 134 | // also, this path may vary between executions due to hashmap iteration order, but that doesn't | ||
| 135 | // matter too much. | ||
| 136 | var node_arena = std.heap.ArenaAllocator.init(gpa); | ||
| 137 | defer node_arena.deinit(); | ||
| 138 | const Parented = struct { | ||
| 139 | parent: ?*const @This(), | ||
| 140 | mod: *const Package, | ||
| 141 | }; | ||
| 142 | const Queue = std.TailQueue(Parented); | ||
| 143 | var to_check: Queue = .{}; | ||
| 144 | |||
| 145 | { | ||
| 146 | const new = try node_arena.allocator().create(Queue.Node); | ||
| 147 | new.* = .{ .data = .{ .parent = null, .mod = mod.root_pkg } }; | ||
| 148 | to_check.prepend(new); | ||
| 149 | } | ||
| 150 | |||
| 151 | if (mod.main_pkg != mod.root_pkg) { | ||
| 152 | const new = try node_arena.allocator().create(Queue.Node); | ||
| 153 | // TODO: once #12201 is resolved, we may want a way of indicating a different name for this | ||
| 154 | new.* = .{ .data = .{ .parent = null, .mod = mod.main_pkg } }; | ||
| 155 | to_check.prepend(new); | ||
| 156 | } | ||
| 157 | |||
| 158 | // set of modules we've already checked to prevent loops | ||
| 159 | var checked = std.AutoHashMap(*const Package, void).init(gpa); | ||
| 160 | defer checked.deinit(); | ||
| 161 | |||
| 162 | const linked = while (to_check.pop()) |node| { | ||
| 163 | const check = &node.data; | ||
| 164 | |||
| 165 | if (checked.contains(check.mod)) continue; | ||
| 166 | try checked.put(check.mod, {}); | ||
| 167 | |||
| 168 | if (check.mod == target) break check; | ||
| 169 | |||
| 170 | var it = check.mod.table.iterator(); | ||
| 171 | while (it.next()) |kv| { | ||
| 172 | var new = try node_arena.allocator().create(Queue.Node); | ||
| 173 | new.* = .{ .data = .{ | ||
| 174 | .parent = check, | ||
| 175 | .mod = kv.value_ptr.*, | ||
| 176 | } }; | ||
| 177 | to_check.prepend(new); | ||
| 178 | } | ||
| 179 | } else { | ||
| 180 | // this can happen for e.g. @cImport packages | ||
| 181 | return gpa.dupe(u8, "<unnamed>"); | ||
| 182 | }; | ||
| 183 | |||
| 184 | // we found a path to the module! unfortunately, we can only traverse *up* it, so we have to put | ||
| 185 | // all the names into a buffer so we can then print them in order. | ||
| 186 | var names = std.ArrayList([]const u8).init(gpa); | ||
| 187 | defer names.deinit(); | ||
| 188 | |||
| 189 | var cur: *const Parented = linked; | ||
| 190 | while (cur.parent) |parent| : (cur = parent) { | ||
| 191 | // find cur's name in parent | ||
| 192 | var it = parent.mod.table.iterator(); | ||
| 193 | const name = while (it.next()) |kv| { | ||
| 194 | if (kv.value_ptr.* == cur.mod) { | ||
| 195 | break kv.key_ptr.*; | ||
| 196 | } | ||
| 197 | } else unreachable; | ||
| 198 | try names.append(name); | ||
| 199 | } | ||
| 200 | |||
| 201 | // finally, print the names into a buffer! | ||
| 202 | var buf = std.ArrayList(u8).init(gpa); | ||
| 203 | defer buf.deinit(); | ||
| 204 | try buf.writer().writeAll("root"); | ||
| 205 | var i: usize = names.items.len; | ||
| 206 | while (i > 0) { | ||
| 207 | i -= 1; | ||
| 208 | try buf.writer().print(".{s}", .{names.items[i]}); | ||
| 209 | } | ||
| 210 | |||
| 211 | return buf.toOwnedSlice(); | ||
| 142 | } | 212 | } |
| 143 | 213 | ||
| 144 | pub const build_zig_basename = "build.zig"; | 214 | pub const build_zig_basename = "build.zig"; |
| ... | @@ -236,7 +306,7 @@ pub fn fetchAndAddDependencies( | ... | @@ -236,7 +306,7 @@ pub fn fetchAndAddDependencies( |
| 236 | color, | 306 | color, |
| 237 | ); | 307 | ); |
| 238 | 308 | ||
| 239 | try addAndAdopt(pkg, gpa, sub_pkg); | 309 | try add(pkg, gpa, fqn, sub_pkg); |
| 240 | 310 | ||
| 241 | try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{ | 311 | try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{ |
| 242 | std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn), | 312 | std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn), |
| ... | @@ -248,7 +318,6 @@ pub fn fetchAndAddDependencies( | ... | @@ -248,7 +318,6 @@ pub fn fetchAndAddDependencies( |
| 248 | 318 | ||
| 249 | pub fn createFilePkg( | 319 | pub fn createFilePkg( |
| 250 | gpa: Allocator, | 320 | gpa: Allocator, |
| 251 | name: []const u8, | ||
| 252 | cache_directory: Compilation.Directory, | 321 | cache_directory: Compilation.Directory, |
| 253 | basename: []const u8, | 322 | basename: []const u8, |
| 254 | contents: []const u8, | 323 | contents: []const u8, |
| ... | @@ -269,7 +338,7 @@ pub fn createFilePkg( | ... | @@ -269,7 +338,7 @@ pub fn createFilePkg( |
| 269 | const o_dir_sub_path = "o" ++ fs.path.sep_str ++ hex_digest; | 338 | const o_dir_sub_path = "o" ++ fs.path.sep_str ++ hex_digest; |
| 270 | try renameTmpIntoCache(cache_directory.handle, tmp_dir_sub_path, o_dir_sub_path); | 339 | try renameTmpIntoCache(cache_directory.handle, tmp_dir_sub_path, o_dir_sub_path); |
| 271 | 340 | ||
| 272 | return createWithDir(gpa, name, cache_directory, o_dir_sub_path, basename); | 341 | return createWithDir(gpa, cache_directory, o_dir_sub_path, basename); |
| 273 | } | 342 | } |
| 274 | 343 | ||
| 275 | const Report = struct { | 344 | const Report = struct { |
| ... | @@ -363,9 +432,6 @@ fn fetchAndUnpack( | ... | @@ -363,9 +432,6 @@ fn fetchAndUnpack( |
| 363 | const owned_src_path = try gpa.dupe(u8, build_zig_basename); | 432 | const owned_src_path = try gpa.dupe(u8, build_zig_basename); |
| 364 | errdefer gpa.free(owned_src_path); | 433 | errdefer gpa.free(owned_src_path); |
| 365 | 434 | ||
| 366 | const owned_name = try gpa.dupe(u8, fqn); | ||
| 367 | errdefer gpa.free(owned_name); | ||
| 368 | |||
| 369 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); | 435 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); |
| 370 | errdefer gpa.free(build_root); | 436 | errdefer gpa.free(build_root); |
| 371 | 437 | ||
| ... | @@ -380,7 +446,6 @@ fn fetchAndUnpack( | ... | @@ -380,7 +446,6 @@ fn fetchAndUnpack( |
| 380 | }, | 446 | }, |
| 381 | .root_src_directory_owned = true, | 447 | .root_src_directory_owned = true, |
| 382 | .root_src_path = owned_src_path, | 448 | .root_src_path = owned_src_path, |
| 383 | .name = owned_name, | ||
| 384 | }; | 449 | }; |
| 385 | 450 | ||
| 386 | return ptr; | 451 | return ptr; |
| ... | @@ -428,6 +493,11 @@ fn fetchAndUnpack( | ... | @@ -428,6 +493,11 @@ fn fetchAndUnpack( |
| 428 | // apply those rules directly to the filesystem right here. This ensures that files | 493 | // apply those rules directly to the filesystem right here. This ensures that files |
| 429 | // not protected by the hash are not present on the file system. | 494 | // not protected by the hash are not present on the file system. |
| 430 | 495 | ||
| 496 | // TODO: raise an error for files that have illegal paths on some operating systems. | ||
| 497 | // For example, on Linux a path with a backslash should raise an error here. | ||
| 498 | // Of course, if the ignore rules above omit the file from the package, then everything | ||
| 499 | // is fine and no error should be raised. | ||
| 500 | |||
| 431 | break :a try computePackageHash(thread_pool, .{ .dir = tmp_directory.handle }); | 501 | break :a try computePackageHash(thread_pool, .{ .dir = tmp_directory.handle }); |
| 432 | }; | 502 | }; |
| 433 | 503 | ||
| ... | @@ -455,7 +525,7 @@ fn fetchAndUnpack( | ... | @@ -455,7 +525,7 @@ fn fetchAndUnpack( |
| 455 | std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root), | 525 | std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root), |
| 456 | }); | 526 | }); |
| 457 | 527 | ||
| 458 | return createWithDir(gpa, fqn, global_cache_directory, pkg_dir_sub_path, build_zig_basename); | 528 | return createWithDir(gpa, global_cache_directory, pkg_dir_sub_path, build_zig_basename); |
| 459 | } | 529 | } |
| 460 | 530 | ||
| 461 | fn unpackTarball( | 531 | fn unpackTarball( |
| ... | @@ -481,7 +551,8 @@ fn unpackTarball( | ... | @@ -481,7 +551,8 @@ fn unpackTarball( |
| 481 | } | 551 | } |
| 482 | 552 | ||
| 483 | const HashedFile = struct { | 553 | const HashedFile = struct { |
| 484 | path: []const u8, | 554 | fs_path: []const u8, |
| 555 | normalized_path: []const u8, | ||
| 485 | hash: [Manifest.Hash.digest_length]u8, | 556 | hash: [Manifest.Hash.digest_length]u8, |
| 486 | failure: Error!void, | 557 | failure: Error!void, |
| 487 | 558 | ||
| ... | @@ -489,7 +560,7 @@ const HashedFile = struct { | ... | @@ -489,7 +560,7 @@ const HashedFile = struct { |
| 489 | 560 | ||
| 490 | fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool { | 561 | fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool { |
| 491 | _ = context; | 562 | _ = context; |
| 492 | return mem.lessThan(u8, lhs.path, rhs.path); | 563 | return mem.lessThan(u8, lhs.normalized_path, rhs.normalized_path); |
| 493 | } | 564 | } |
| 494 | }; | 565 | }; |
| 495 | 566 | ||
| ... | @@ -525,8 +596,10 @@ fn computePackageHash( | ... | @@ -525,8 +596,10 @@ fn computePackageHash( |
| 525 | else => return error.IllegalFileTypeInPackage, | 596 | else => return error.IllegalFileTypeInPackage, |
| 526 | } | 597 | } |
| 527 | const hashed_file = try arena.create(HashedFile); | 598 | const hashed_file = try arena.create(HashedFile); |
| 599 | const fs_path = try arena.dupe(u8, entry.path); | ||
| 528 | hashed_file.* = .{ | 600 | hashed_file.* = .{ |
| 529 | .path = try arena.dupe(u8, entry.path), | 601 | .fs_path = fs_path, |
| 602 | .normalized_path = try normalizePath(arena, fs_path), | ||
| 530 | .hash = undefined, // to be populated by the worker | 603 | .hash = undefined, // to be populated by the worker |
| 531 | .failure = undefined, // to be populated by the worker | 604 | .failure = undefined, // to be populated by the worker |
| 532 | }; | 605 | }; |
| ... | @@ -544,7 +617,7 @@ fn computePackageHash( | ... | @@ -544,7 +617,7 @@ fn computePackageHash( |
| 544 | for (all_files.items) |hashed_file| { | 617 | for (all_files.items) |hashed_file| { |
| 545 | hashed_file.failure catch |err| { | 618 | hashed_file.failure catch |err| { |
| 546 | any_failures = true; | 619 | any_failures = true; |
| 547 | std.log.err("unable to hash '{s}': {s}", .{ hashed_file.path, @errorName(err) }); | 620 | std.log.err("unable to hash '{s}': {s}", .{ hashed_file.fs_path, @errorName(err) }); |
| 548 | }; | 621 | }; |
| 549 | hasher.update(&hashed_file.hash); | 622 | hasher.update(&hashed_file.hash); |
| 550 | } | 623 | } |
| ... | @@ -552,6 +625,24 @@ fn computePackageHash( | ... | @@ -552,6 +625,24 @@ fn computePackageHash( |
| 552 | return hasher.finalResult(); | 625 | return hasher.finalResult(); |
| 553 | } | 626 | } |
| 554 | 627 | ||
| 628 | /// Make a file system path identical independently of operating system path inconsistencies. | ||
| 629 | /// This converts backslashes into forward slashes. | ||
| 630 | fn normalizePath(arena: Allocator, fs_path: []const u8) ![]const u8 { | ||
| 631 | const canonical_sep = '/'; | ||
| 632 | |||
| 633 | if (fs.path.sep == canonical_sep) | ||
| 634 | return fs_path; | ||
| 635 | |||
| 636 | const normalized = try arena.dupe(u8, fs_path); | ||
| 637 | for (normalized) |*byte| { | ||
| 638 | switch (byte.*) { | ||
| 639 | fs.path.sep => byte.* = canonical_sep, | ||
| 640 | else => continue, | ||
| 641 | } | ||
| 642 | } | ||
| 643 | return normalized; | ||
| 644 | } | ||
| 645 | |||
| 555 | fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { | 646 | fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { |
| 556 | defer wg.finish(); | 647 | defer wg.finish(); |
| 557 | hashed_file.failure = hashFileFallible(dir, hashed_file); | 648 | hashed_file.failure = hashFileFallible(dir, hashed_file); |
| ... | @@ -559,9 +650,10 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { | ... | @@ -559,9 +650,10 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { |
| 559 | 650 | ||
| 560 | fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { | 651 | fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { |
| 561 | var buf: [8000]u8 = undefined; | 652 | var buf: [8000]u8 = undefined; |
| 562 | var file = try dir.openFile(hashed_file.path, .{}); | 653 | var file = try dir.openFile(hashed_file.fs_path, .{}); |
| 654 | defer file.close(); | ||
| 563 | var hasher = Manifest.Hash.init(.{}); | 655 | var hasher = Manifest.Hash.init(.{}); |
| 564 | hasher.update(hashed_file.path); | 656 | hasher.update(hashed_file.normalized_path); |
| 565 | hasher.update(&.{ 0, @boolToInt(try isExecutable(file)) }); | 657 | hasher.update(&.{ 0, @boolToInt(try isExecutable(file)) }); |
| 566 | while (true) { | 658 | while (true) { |
| 567 | const bytes_read = try file.read(&buf); | 659 | const bytes_read = try file.read(&buf); |
src/Sema.zig+67-44| ... | @@ -2529,7 +2529,7 @@ fn coerceResultPtr( | ... | @@ -2529,7 +2529,7 @@ fn coerceResultPtr( |
| 2529 | _ = try block.addBinOp(.store, new_ptr, null_inst); | 2529 | _ = try block.addBinOp(.store, new_ptr, null_inst); |
| 2530 | return Air.Inst.Ref.void_value; | 2530 | return Air.Inst.Ref.void_value; |
| 2531 | } | 2531 | } |
| 2532 | return sema.bitCast(block, ptr_ty, new_ptr, src); | 2532 | return sema.bitCast(block, ptr_ty, new_ptr, src, null); |
| 2533 | } | 2533 | } |
| 2534 | 2534 | ||
| 2535 | const trash_inst = trash_block.instructions.pop(); | 2535 | const trash_inst = trash_block.instructions.pop(); |
| ... | @@ -2545,7 +2545,7 @@ fn coerceResultPtr( | ... | @@ -2545,7 +2545,7 @@ fn coerceResultPtr( |
| 2545 | if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| { | 2545 | if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| { |
| 2546 | new_ptr = try sema.addConstant(ptr_operand_ty, ptr_val); | 2546 | new_ptr = try sema.addConstant(ptr_operand_ty, ptr_val); |
| 2547 | } else { | 2547 | } else { |
| 2548 | new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src); | 2548 | new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src, null); |
| 2549 | } | 2549 | } |
| 2550 | }, | 2550 | }, |
| 2551 | .wrap_optional => { | 2551 | .wrap_optional => { |
| ... | @@ -5311,7 +5311,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -5311,7 +5311,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr |
| 5311 | } | 5311 | } |
| 5312 | const c_import_pkg = Package.create( | 5312 | const c_import_pkg = Package.create( |
| 5313 | sema.gpa, | 5313 | sema.gpa, |
| 5314 | "c_import", // TODO: should we make this unique? | ||
| 5315 | null, | 5314 | null, |
| 5316 | c_import_res.out_zig_path, | 5315 | c_import_res.out_zig_path, |
| 5317 | ) catch |err| switch (err) { | 5316 | ) catch |err| switch (err) { |
| ... | @@ -9655,7 +9654,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -9655,7 +9654,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 9655 | .Vector, | 9654 | .Vector, |
| 9656 | => {}, | 9655 | => {}, |
| 9657 | } | 9656 | } |
| 9658 | return sema.bitCast(block, dest_ty, operand, operand_src); | 9657 | return sema.bitCast(block, dest_ty, operand, inst_data.src(), operand_src); |
| 9659 | } | 9658 | } |
| 9660 | 9659 | ||
| 9661 | fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 9660 | fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -9888,7 +9887,7 @@ fn zirSwitchCapture( | ... | @@ -9888,7 +9887,7 @@ fn zirSwitchCapture( |
| 9888 | 9887 | ||
| 9889 | switch (operand_ty.zigTypeTag()) { | 9888 | switch (operand_ty.zigTypeTag()) { |
| 9890 | .ErrorSet => if (block.switch_else_err_ty) |some| { | 9889 | .ErrorSet => if (block.switch_else_err_ty) |some| { |
| 9891 | return sema.bitCast(block, some, operand, operand_src); | 9890 | return sema.bitCast(block, some, operand, operand_src, null); |
| 9892 | } else { | 9891 | } else { |
| 9893 | try block.addUnreachable(false); | 9892 | try block.addUnreachable(false); |
| 9894 | return Air.Inst.Ref.unreachable_value; | 9893 | return Air.Inst.Ref.unreachable_value; |
| ... | @@ -9988,14 +9987,14 @@ fn zirSwitchCapture( | ... | @@ -9988,14 +9987,14 @@ fn zirSwitchCapture( |
| 9988 | Module.ErrorSet.sortNames(&names); | 9987 | Module.ErrorSet.sortNames(&names); |
| 9989 | const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); | 9988 | const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); |
| 9990 | 9989 | ||
| 9991 | return sema.bitCast(block, else_error_ty, operand, operand_src); | 9990 | return sema.bitCast(block, else_error_ty, operand, operand_src, null); |
| 9992 | } else { | 9991 | } else { |
| 9993 | const item_ref = try sema.resolveInst(items[0]); | 9992 | const item_ref = try sema.resolveInst(items[0]); |
| 9994 | // Previous switch validation ensured this will succeed | 9993 | // Previous switch validation ensured this will succeed |
| 9995 | const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable; | 9994 | const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable; |
| 9996 | 9995 | ||
| 9997 | const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?); | 9996 | const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?); |
| 9998 | return sema.bitCast(block, item_ty, operand, operand_src); | 9997 | return sema.bitCast(block, item_ty, operand, operand_src, null); |
| 9999 | } | 9998 | } |
| 10000 | }, | 9999 | }, |
| 10001 | else => { | 10000 | else => { |
| ... | @@ -11793,8 +11792,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. | ... | @@ -11793,8 +11792,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 11793 | return sema.fail(block, operand_src, "import of file outside package path: '{s}'", .{operand}); | 11792 | return sema.fail(block, operand_src, "import of file outside package path: '{s}'", .{operand}); |
| 11794 | }, | 11793 | }, |
| 11795 | error.PackageNotFound => { | 11794 | error.PackageNotFound => { |
| 11796 | const cur_pkg = block.getFileScope().pkg; | 11795 | const name = try block.getFileScope().pkg.getName(sema.gpa, mod.*); |
| 11797 | return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, cur_pkg.name }); | 11796 | defer sema.gpa.free(name); |
| 11797 | return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, name }); | ||
| 11798 | }, | 11798 | }, |
| 11799 | else => { | 11799 | else => { |
| 11800 | // TODO: these errors are file system errors; make sure an update() will | 11800 | // TODO: these errors are file system errors; make sure an update() will |
| ... | @@ -19953,7 +19953,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A | ... | @@ -19953,7 +19953,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 19953 | } else is_aligned; | 19953 | } else is_aligned; |
| 19954 | try sema.addSafetyCheck(block, ok, .incorrect_alignment); | 19954 | try sema.addSafetyCheck(block, ok, .incorrect_alignment); |
| 19955 | } | 19955 | } |
| 19956 | return sema.bitCast(block, dest_ty, ptr, ptr_src); | 19956 | return sema.bitCast(block, dest_ty, ptr, ptr_src, null); |
| 19957 | } | 19957 | } |
| 19958 | 19958 | ||
| 19959 | fn zirBitCount( | 19959 | fn zirBitCount( |
| ... | @@ -21482,24 +21482,32 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -21482,24 +21482,32 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr |
| 21482 | const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | 21482 | const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 21483 | const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | 21483 | const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 21484 | 21484 | ||
| 21485 | const struct_ty = try sema.resolveType(block, ty_src, extra.parent_type); | 21485 | const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type); |
| 21486 | const field_name = try sema.resolveConstString(block, name_src, extra.field_name, "field name must be comptime-known"); | 21486 | const field_name = try sema.resolveConstString(block, name_src, extra.field_name, "field name must be comptime-known"); |
| 21487 | const field_ptr = try sema.resolveInst(extra.field_ptr); | 21487 | const field_ptr = try sema.resolveInst(extra.field_ptr); |
| 21488 | const field_ptr_ty = sema.typeOf(field_ptr); | 21488 | const field_ptr_ty = sema.typeOf(field_ptr); |
| 21489 | 21489 | ||
| 21490 | if (struct_ty.zigTypeTag() != .Struct) { | 21490 | if (parent_ty.zigTypeTag() != .Struct and parent_ty.zigTypeTag() != .Union) { |
| 21491 | return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty.fmt(sema.mod)}); | 21491 | return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)}); |
| 21492 | } | 21492 | } |
| 21493 | try sema.resolveTypeLayout(struct_ty); | 21493 | try sema.resolveTypeLayout(parent_ty); |
| 21494 | 21494 | ||
| 21495 | const field_index = if (struct_ty.isTuple()) blk: { | 21495 | const field_index = switch (parent_ty.zigTypeTag()) { |
| 21496 | if (mem.eql(u8, field_name, "len")) { | 21496 | .Struct => blk: { |
| 21497 | return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); | 21497 | if (parent_ty.isTuple()) { |
| 21498 | } | 21498 | if (mem.eql(u8, field_name, "len")) { |
| 21499 | break :blk try sema.tupleFieldIndex(block, struct_ty, field_name, name_src); | 21499 | return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); |
| 21500 | } else try sema.structFieldIndex(block, struct_ty, field_name, name_src); | 21500 | } |
| 21501 | break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src); | ||
| 21502 | } else { | ||
| 21503 | break :blk try sema.structFieldIndex(block, parent_ty, field_name, name_src); | ||
| 21504 | } | ||
| 21505 | }, | ||
| 21506 | .Union => try sema.unionFieldIndex(block, parent_ty, field_name, name_src), | ||
| 21507 | else => unreachable, | ||
| 21508 | }; | ||
| 21501 | 21509 | ||
| 21502 | if (struct_ty.structFieldIsComptime(field_index)) { | 21510 | if (parent_ty.zigTypeTag() == .Struct and parent_ty.structFieldIsComptime(field_index)) { |
| 21503 | return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{}); | 21511 | return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{}); |
| 21504 | } | 21512 | } |
| 21505 | 21513 | ||
| ... | @@ -21507,23 +21515,29 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -21507,23 +21515,29 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr |
| 21507 | const field_ptr_ty_info = field_ptr_ty.ptrInfo().data; | 21515 | const field_ptr_ty_info = field_ptr_ty.ptrInfo().data; |
| 21508 | 21516 | ||
| 21509 | var ptr_ty_data: Type.Payload.Pointer.Data = .{ | 21517 | var ptr_ty_data: Type.Payload.Pointer.Data = .{ |
| 21510 | .pointee_type = struct_ty.structFieldType(field_index), | 21518 | .pointee_type = parent_ty.structFieldType(field_index), |
| 21511 | .mutable = field_ptr_ty_info.mutable, | 21519 | .mutable = field_ptr_ty_info.mutable, |
| 21512 | .@"addrspace" = field_ptr_ty_info.@"addrspace", | 21520 | .@"addrspace" = field_ptr_ty_info.@"addrspace", |
| 21513 | }; | 21521 | }; |
| 21514 | 21522 | ||
| 21515 | if (struct_ty.containerLayout() == .Packed) { | 21523 | if (parent_ty.containerLayout() == .Packed) { |
| 21516 | return sema.fail(block, src, "TODO handle packed structs with @fieldParentPtr", .{}); | 21524 | return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{}); |
| 21517 | } else { | 21525 | } else { |
| 21518 | ptr_ty_data.@"align" = if (struct_ty.castTag(.@"struct")) |struct_obj| b: { | 21526 | ptr_ty_data.@"align" = blk: { |
| 21519 | break :b struct_obj.data.fields.values()[field_index].abi_align; | 21527 | if (parent_ty.castTag(.@"struct")) |struct_obj| { |
| 21520 | } else 0; | 21528 | break :blk struct_obj.data.fields.values()[field_index].abi_align; |
| 21529 | } else if (parent_ty.cast(Type.Payload.Union)) |union_obj| { | ||
| 21530 | break :blk union_obj.data.fields.values()[field_index].abi_align; | ||
| 21531 | } else { | ||
| 21532 | break :blk 0; | ||
| 21533 | } | ||
| 21534 | }; | ||
| 21521 | } | 21535 | } |
| 21522 | 21536 | ||
| 21523 | const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); | 21537 | const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); |
| 21524 | const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src); | 21538 | const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src); |
| 21525 | 21539 | ||
| 21526 | ptr_ty_data.pointee_type = struct_ty; | 21540 | ptr_ty_data.pointee_type = parent_ty; |
| 21527 | const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); | 21541 | const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); |
| 21528 | 21542 | ||
| 21529 | if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| { | 21543 | if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| { |
| ... | @@ -21540,11 +21554,11 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -21540,11 +21554,11 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr |
| 21540 | field_name, | 21554 | field_name, |
| 21541 | field_index, | 21555 | field_index, |
| 21542 | payload.data.field_index, | 21556 | payload.data.field_index, |
| 21543 | struct_ty.fmt(sema.mod), | 21557 | parent_ty.fmt(sema.mod), |
| 21544 | }, | 21558 | }, |
| 21545 | ); | 21559 | ); |
| 21546 | errdefer msg.destroy(sema.gpa); | 21560 | errdefer msg.destroy(sema.gpa); |
| 21547 | try sema.addDeclaredHereNote(msg, struct_ty); | 21561 | try sema.addDeclaredHereNote(msg, parent_ty); |
| 21548 | break :msg msg; | 21562 | break :msg msg; |
| 21549 | }; | 21563 | }; |
| 21550 | return sema.failWithOwnedErrorMsg(msg); | 21564 | return sema.failWithOwnedErrorMsg(msg); |
| ... | @@ -24141,8 +24155,9 @@ fn unionFieldVal( | ... | @@ -24141,8 +24155,9 @@ fn unionFieldVal( |
| 24141 | return sema.addConstant(field.ty, tag_and_val.val); | 24155 | return sema.addConstant(field.ty, tag_and_val.val); |
| 24142 | } else { | 24156 | } else { |
| 24143 | const old_ty = union_ty.unionFieldType(tag_and_val.tag, sema.mod); | 24157 | const old_ty = union_ty.unionFieldType(tag_and_val.tag, sema.mod); |
| 24144 | const new_val = try sema.bitCastVal(block, src, tag_and_val.val, old_ty, field.ty, 0); | 24158 | if (try sema.bitCastVal(block, src, tag_and_val.val, old_ty, field.ty, 0)) |new_val| { |
| 24145 | return sema.addConstant(field.ty, new_val); | 24159 | return sema.addConstant(field.ty, new_val); |
| 24160 | } | ||
| 24146 | } | 24161 | } |
| 24147 | }, | 24162 | }, |
| 24148 | } | 24163 | } |
| ... | @@ -26514,8 +26529,12 @@ fn storePtrVal( | ... | @@ -26514,8 +26529,12 @@ fn storePtrVal( |
| 26514 | const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(target)); | 26529 | const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(target)); |
| 26515 | const buffer = try sema.gpa.alloc(u8, abi_size); | 26530 | const buffer = try sema.gpa.alloc(u8, abi_size); |
| 26516 | defer sema.gpa.free(buffer); | 26531 | defer sema.gpa.free(buffer); |
| 26517 | reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, sema.mod, buffer); | 26532 | reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, sema.mod, buffer) catch |err| switch (err) { |
| 26518 | operand_val.writeToMemory(operand_ty, sema.mod, buffer[reinterpret.byte_offset..]); | 26533 | error.ReinterpretDeclRef => unreachable, |
| 26534 | }; | ||
| 26535 | operand_val.writeToMemory(operand_ty, sema.mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) { | ||
| 26536 | error.ReinterpretDeclRef => unreachable, | ||
| 26537 | }; | ||
| 26519 | 26538 | ||
| 26520 | const arena = mut_kit.beginArena(sema.mod); | 26539 | const arena = mut_kit.beginArena(sema.mod); |
| 26521 | defer mut_kit.finishArena(sema.mod); | 26540 | defer mut_kit.finishArena(sema.mod); |
| ... | @@ -27398,6 +27417,7 @@ fn bitCast( | ... | @@ -27398,6 +27417,7 @@ fn bitCast( |
| 27398 | dest_ty_unresolved: Type, | 27417 | dest_ty_unresolved: Type, |
| 27399 | inst: Air.Inst.Ref, | 27418 | inst: Air.Inst.Ref, |
| 27400 | inst_src: LazySrcLoc, | 27419 | inst_src: LazySrcLoc, |
| 27420 | operand_src: ?LazySrcLoc, | ||
| 27401 | ) CompileError!Air.Inst.Ref { | 27421 | ) CompileError!Air.Inst.Ref { |
| 27402 | const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); | 27422 | const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); |
| 27403 | try sema.resolveTypeLayout(dest_ty); | 27423 | try sema.resolveTypeLayout(dest_ty); |
| ... | @@ -27419,10 +27439,11 @@ fn bitCast( | ... | @@ -27419,10 +27439,11 @@ fn bitCast( |
| 27419 | } | 27439 | } |
| 27420 | 27440 | ||
| 27421 | if (try sema.resolveMaybeUndefVal(inst)) |val| { | 27441 | if (try sema.resolveMaybeUndefVal(inst)) |val| { |
| 27422 | const result_val = try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0); | 27442 | if (try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0)) |result_val| { |
| 27423 | return sema.addConstant(dest_ty, result_val); | 27443 | return sema.addConstant(dest_ty, result_val); |
| 27444 | } | ||
| 27424 | } | 27445 | } |
| 27425 | try sema.requireRuntimeBlock(block, inst_src, null); | 27446 | try sema.requireRuntimeBlock(block, inst_src, operand_src); |
| 27426 | return block.addBitCast(dest_ty, inst); | 27447 | return block.addBitCast(dest_ty, inst); |
| 27427 | } | 27448 | } |
| 27428 | 27449 | ||
| ... | @@ -27434,7 +27455,7 @@ fn bitCastVal( | ... | @@ -27434,7 +27455,7 @@ fn bitCastVal( |
| 27434 | old_ty: Type, | 27455 | old_ty: Type, |
| 27435 | new_ty: Type, | 27456 | new_ty: Type, |
| 27436 | buffer_offset: usize, | 27457 | buffer_offset: usize, |
| 27437 | ) !Value { | 27458 | ) !?Value { |
| 27438 | const target = sema.mod.getTarget(); | 27459 | const target = sema.mod.getTarget(); |
| 27439 | if (old_ty.eql(new_ty, sema.mod)) return val; | 27460 | if (old_ty.eql(new_ty, sema.mod)) return val; |
| 27440 | 27461 | ||
| ... | @@ -27443,8 +27464,10 @@ fn bitCastVal( | ... | @@ -27443,8 +27464,10 @@ fn bitCastVal( |
| 27443 | const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target)); | 27464 | const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target)); |
| 27444 | const buffer = try sema.gpa.alloc(u8, abi_size); | 27465 | const buffer = try sema.gpa.alloc(u8, abi_size); |
| 27445 | defer sema.gpa.free(buffer); | 27466 | defer sema.gpa.free(buffer); |
| 27446 | val.writeToMemory(old_ty, sema.mod, buffer); | 27467 | val.writeToMemory(old_ty, sema.mod, buffer) catch |err| switch (err) { |
| 27447 | return Value.readFromMemory(new_ty, sema.mod, buffer[buffer_offset..], sema.arena); | 27468 | error.ReinterpretDeclRef => return null, |
| 27469 | }; | ||
| 27470 | return try Value.readFromMemory(new_ty, sema.mod, buffer[buffer_offset..], sema.arena); | ||
| 27448 | } | 27471 | } |
| 27449 | 27472 | ||
| 27450 | fn coerceArrayPtrToSlice( | 27473 | fn coerceArrayPtrToSlice( |
| ... | @@ -27551,7 +27574,7 @@ fn coerceCompatiblePtrs( | ... | @@ -27551,7 +27574,7 @@ fn coerceCompatiblePtrs( |
| 27551 | } else is_non_zero; | 27574 | } else is_non_zero; |
| 27552 | try sema.addSafetyCheck(block, ok, .cast_to_null); | 27575 | try sema.addSafetyCheck(block, ok, .cast_to_null); |
| 27553 | } | 27576 | } |
| 27554 | return sema.bitCast(block, dest_ty, inst, inst_src); | 27577 | return sema.bitCast(block, dest_ty, inst, inst_src, null); |
| 27555 | } | 27578 | } |
| 27556 | 27579 | ||
| 27557 | fn coerceEnumToUnion( | 27580 | fn coerceEnumToUnion( |
| ... | @@ -28291,7 +28314,7 @@ fn analyzeRef( | ... | @@ -28291,7 +28314,7 @@ fn analyzeRef( |
| 28291 | try sema.storePtr(block, src, alloc, operand); | 28314 | try sema.storePtr(block, src, alloc, operand); |
| 28292 | 28315 | ||
| 28293 | // TODO: Replace with sema.coerce when that supports adding pointer constness. | 28316 | // TODO: Replace with sema.coerce when that supports adding pointer constness. |
| 28294 | return sema.bitCast(block, ptr_type, alloc, src); | 28317 | return sema.bitCast(block, ptr_type, alloc, src, null); |
| 28295 | } | 28318 | } |
| 28296 | 28319 | ||
| 28297 | fn analyzeLoad( | 28320 | fn analyzeLoad( |
| ... | @@ -32327,11 +32350,11 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value | ... | @@ -32327,11 +32350,11 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value |
| 32327 | 32350 | ||
| 32328 | // Try the smaller bit-cast first, since that's more efficient than using the larger `parent` | 32351 | // Try the smaller bit-cast first, since that's more efficient than using the larger `parent` |
| 32329 | if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(tv.ty)) | 32352 | if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(tv.ty)) |
| 32330 | return DerefResult{ .val = try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0) }; | 32353 | return DerefResult{ .val = (try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0)) orelse return .runtime_load }; |
| 32331 | 32354 | ||
| 32332 | // If that fails, try to bit-cast from the largest parent value with a well-defined layout | 32355 | // If that fails, try to bit-cast from the largest parent value with a well-defined layout |
| 32333 | if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent.tv.ty)) | 32356 | if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent.tv.ty)) |
| 32334 | return DerefResult{ .val = try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset) }; | 32357 | return DerefResult{ .val = (try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset)) orelse return .runtime_load }; |
| 32335 | 32358 | ||
| 32336 | if (deref.ty_without_well_defined_layout) |bad_ty| { | 32359 | if (deref.ty_without_well_defined_layout) |bad_ty| { |
| 32337 | // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem | 32360 | // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem |
src/arch/aarch64/CodeGen.zig+19-2| ... | @@ -3958,7 +3958,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -3958,7 +3958,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3958 | 3958 | ||
| 3959 | switch (value) { | 3959 | switch (value) { |
| 3960 | .dead => unreachable, | 3960 | .dead => unreachable, |
| 3961 | .undef => unreachable, | 3961 | .undef => { |
| 3962 | try self.genSetReg(value_ty, addr_reg, value); | ||
| 3963 | }, | ||
| 3962 | .register => |value_reg| { | 3964 | .register => |value_reg| { |
| 3963 | log.debug("store: register {} to {}", .{ value_reg, addr_reg }); | 3965 | log.debug("store: register {} to {}", .{ value_reg, addr_reg }); |
| 3964 | try self.genStrRegister(value_reg, addr_reg, value_ty); | 3966 | try self.genStrRegister(value_reg, addr_reg, value_ty); |
| ... | @@ -5870,7 +5872,22 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5870,7 +5872,22 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 5870 | 5872 | ||
| 5871 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { | 5873 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 5872 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 5874 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 5873 | const result = try self.resolveInst(ty_op.operand); | 5875 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 5876 | const operand = try self.resolveInst(ty_op.operand); | ||
| 5877 | if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; | ||
| 5878 | |||
| 5879 | const operand_lock = switch (operand) { | ||
| 5880 | .register => |reg| self.register_manager.lockReg(reg), | ||
| 5881 | .register_with_overflow => |rwo| self.register_manager.lockReg(rwo.reg), | ||
| 5882 | else => null, | ||
| 5883 | }; | ||
| 5884 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); | ||
| 5885 | |||
| 5886 | const dest_ty = self.air.typeOfIndex(inst); | ||
| 5887 | const dest = try self.allocRegOrMem(dest_ty, true, inst); | ||
| 5888 | try self.setRegOrMem(dest_ty, dest, operand); | ||
| 5889 | break :result dest; | ||
| 5890 | }; | ||
| 5874 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 5891 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 5875 | } | 5892 | } |
| 5876 | 5893 |
src/arch/arm/CodeGen.zig+26-2| ... | @@ -2765,7 +2765,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2765,7 +2765,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2765 | 2765 | ||
| 2766 | switch (value) { | 2766 | switch (value) { |
| 2767 | .dead => unreachable, | 2767 | .dead => unreachable, |
| 2768 | .undef => unreachable, | 2768 | .undef => { |
| 2769 | try self.genSetReg(value_ty, addr_reg, value); | ||
| 2770 | }, | ||
| 2769 | .register => |value_reg| { | 2771 | .register => |value_reg| { |
| 2770 | try self.genStrRegister(value_reg, addr_reg, value_ty); | 2772 | try self.genStrRegister(value_reg, addr_reg, value_ty); |
| 2771 | }, | 2773 | }, |
| ... | @@ -2971,6 +2973,11 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2971,6 +2973,11 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2971 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2973 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2972 | const field_ptr = try self.resolveInst(extra.field_ptr); | 2974 | const field_ptr = try self.resolveInst(extra.field_ptr); |
| 2973 | const struct_ty = self.air.getRefType(ty_pl.ty).childType(); | 2975 | const struct_ty = self.air.getRefType(ty_pl.ty).childType(); |
| 2976 | |||
| 2977 | if (struct_ty.zigTypeTag() == .Union) { | ||
| 2978 | return self.fail("TODO implement @fieldParentPtr codegen for unions", .{}); | ||
| 2979 | } | ||
| 2980 | |||
| 2974 | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(extra.field_index, self.target.*)); | 2981 | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(extra.field_index, self.target.*)); |
| 2975 | switch (field_ptr) { | 2982 | switch (field_ptr) { |
| 2976 | .ptr_stack_offset => |off| { | 2983 | .ptr_stack_offset => |off| { |
| ... | @@ -5816,7 +5823,24 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5816,7 +5823,24 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 5816 | 5823 | ||
| 5817 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { | 5824 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 5818 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 5825 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 5819 | const result = try self.resolveInst(ty_op.operand); | 5826 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 5827 | const operand = try self.resolveInst(ty_op.operand); | ||
| 5828 | if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; | ||
| 5829 | |||
| 5830 | const operand_lock = switch (operand) { | ||
| 5831 | .register, | ||
| 5832 | .register_c_flag, | ||
| 5833 | .register_v_flag, | ||
| 5834 | => |reg| self.register_manager.lockReg(reg), | ||
| 5835 | else => null, | ||
| 5836 | }; | ||
| 5837 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); | ||
| 5838 | |||
| 5839 | const dest_ty = self.air.typeOfIndex(inst); | ||
| 5840 | const dest = try self.allocRegOrMem(dest_ty, true, inst); | ||
| 5841 | try self.setRegOrMem(dest_ty, dest, operand); | ||
| 5842 | break :result dest; | ||
| 5843 | }; | ||
| 5820 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 5844 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 5821 | } | 5845 | } |
| 5822 | 5846 |
src/arch/riscv64/CodeGen.zig+14-1| ... | @@ -2338,7 +2338,20 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2338,7 +2338,20 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 2338 | 2338 | ||
| 2339 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { | 2339 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 2340 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2340 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2341 | const result = try self.resolveInst(ty_op.operand); | 2341 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2342 | const operand = try self.resolveInst(ty_op.operand); | ||
| 2343 | if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; | ||
| 2344 | |||
| 2345 | const operand_lock = switch (operand) { | ||
| 2346 | .register => |reg| self.register_manager.lockReg(reg), | ||
| 2347 | else => null, | ||
| 2348 | }; | ||
| 2349 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); | ||
| 2350 | |||
| 2351 | const dest = try self.allocRegOrMem(inst, true); | ||
| 2352 | try self.setRegOrMem(self.air.typeOfIndex(inst), dest, operand); | ||
| 2353 | break :result dest; | ||
| 2354 | }; | ||
| 2342 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 2355 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2343 | } | 2356 | } |
| 2344 | 2357 |
src/arch/sparc64/CodeGen.zig+15-1| ... | @@ -1091,7 +1091,21 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void | ... | @@ -1091,7 +1091,21 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void |
| 1091 | 1091 | ||
| 1092 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { | 1092 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1093 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1093 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1094 | const result = try self.resolveInst(ty_op.operand); | 1094 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1095 | const operand = try self.resolveInst(ty_op.operand); | ||
| 1096 | if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; | ||
| 1097 | |||
| 1098 | const operand_lock = switch (operand) { | ||
| 1099 | .register => |reg| self.register_manager.lockReg(reg), | ||
| 1100 | .register_with_overflow => |rwo| self.register_manager.lockReg(rwo.reg), | ||
| 1101 | else => null, | ||
| 1102 | }; | ||
| 1103 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); | ||
| 1104 | |||
| 1105 | const dest = try self.allocRegOrMem(inst, true); | ||
| 1106 | try self.setRegOrMem(self.air.typeOfIndex(inst), dest, operand); | ||
| 1107 | break :result dest; | ||
| 1108 | }; | ||
| 1095 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1109 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1096 | } | 1110 | } |
| 1097 | 1111 |
src/arch/wasm/CodeGen.zig+4-4| ... | @@ -2896,7 +2896,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { | ... | @@ -2896,7 +2896,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { |
| 2896 | const struct_obj = ty.castTag(.@"struct").?.data; | 2896 | const struct_obj = ty.castTag(.@"struct").?.data; |
| 2897 | assert(struct_obj.layout == .Packed); | 2897 | assert(struct_obj.layout == .Packed); |
| 2898 | var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer | 2898 | var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer |
| 2899 | val.writeToPackedMemory(ty, func.bin_file.base.options.module.?, &buf, 0); | 2899 | val.writeToPackedMemory(ty, func.bin_file.base.options.module.?, &buf, 0) catch unreachable; |
| 2900 | var payload: Value.Payload.U64 = .{ | 2900 | var payload: Value.Payload.U64 = .{ |
| 2901 | .base = .{ .tag = .int_u64 }, | 2901 | .base = .{ .tag = .int_u64 }, |
| 2902 | .data = std.mem.readIntLittle(u64, &buf), | 2902 | .data = std.mem.readIntLittle(u64, &buf), |
| ... | @@ -2907,7 +2907,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { | ... | @@ -2907,7 +2907,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { |
| 2907 | .Vector => { | 2907 | .Vector => { |
| 2908 | assert(determineSimdStoreStrategy(ty, target) == .direct); | 2908 | assert(determineSimdStoreStrategy(ty, target) == .direct); |
| 2909 | var buf: [16]u8 = undefined; | 2909 | var buf: [16]u8 = undefined; |
| 2910 | val.writeToMemory(ty, func.bin_file.base.options.module.?, &buf); | 2910 | val.writeToMemory(ty, func.bin_file.base.options.module.?, &buf) catch unreachable; |
| 2911 | return func.storeSimdImmd(buf); | 2911 | return func.storeSimdImmd(buf); |
| 2912 | }, | 2912 | }, |
| 2913 | else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}), | 2913 | else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}), |
| ... | @@ -4944,8 +4944,8 @@ fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4944,8 +4944,8 @@ fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4944 | if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.field_ptr}); | 4944 | if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.field_ptr}); |
| 4945 | 4945 | ||
| 4946 | const field_ptr = try func.resolveInst(extra.field_ptr); | 4946 | const field_ptr = try func.resolveInst(extra.field_ptr); |
| 4947 | const struct_ty = func.air.getRefType(ty_pl.ty).childType(); | 4947 | const parent_ty = func.air.getRefType(ty_pl.ty).childType(); |
| 4948 | const field_offset = struct_ty.structFieldOffset(extra.field_index, func.target); | 4948 | const field_offset = parent_ty.structFieldOffset(extra.field_index, func.target); |
| 4949 | 4949 | ||
| 4950 | const result = if (field_offset != 0) result: { | 4950 | const result = if (field_offset != 0) result: { |
| 4951 | const base = try func.buildPointerOffset(field_ptr, 0, .new); | 4951 | const base = try func.buildPointerOffset(field_ptr, 0, .new); |
src/arch/x86_64/CodeGen.zig+57-10| ... | @@ -920,9 +920,6 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { | ... | @@ -920,9 +920,6 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 920 | const mod = self.bin_file.options.module.?; | 920 | const mod = self.bin_file.options.module.?; |
| 921 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); | 921 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); |
| 922 | }; | 922 | }; |
| 923 | const abi_align = elem_ty.abiAlignment(self.target.*); | ||
| 924 | if (abi_align > self.stack_align) | ||
| 925 | self.stack_align = abi_align; | ||
| 926 | 923 | ||
| 927 | if (reg_ok) { | 924 | if (reg_ok) { |
| 928 | switch (elem_ty.zigTypeTag()) { | 925 | switch (elem_ty.zigTypeTag()) { |
| ... | @@ -951,6 +948,10 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { | ... | @@ -951,6 +948,10 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 951 | }, | 948 | }, |
| 952 | } | 949 | } |
| 953 | } | 950 | } |
| 951 | |||
| 952 | const abi_align = elem_ty.abiAlignment(self.target.*); | ||
| 953 | if (abi_align > self.stack_align) | ||
| 954 | self.stack_align = abi_align; | ||
| 954 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); | 955 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); |
| 955 | return MCValue{ .stack_offset = @intCast(i32, stack_offset) }; | 956 | return MCValue{ .stack_offset = @intCast(i32, stack_offset) }; |
| 956 | } | 957 | } |
| ... | @@ -990,7 +991,7 @@ fn revertState(self: *Self, state: State) void { | ... | @@ -990,7 +991,7 @@ fn revertState(self: *Self, state: State) void { |
| 990 | 991 | ||
| 991 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { | 992 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 992 | const stack_mcv = try self.allocRegOrMem(inst, false); | 993 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 993 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); | 994 | log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 994 | const reg_mcv = self.getResolvedInstValue(inst); | 995 | const reg_mcv = self.getResolvedInstValue(inst); |
| 995 | switch (reg_mcv) { | 996 | switch (reg_mcv) { |
| 996 | .register => |other| { | 997 | .register => |other| { |
| ... | @@ -1016,7 +1017,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void { | ... | @@ -1016,7 +1017,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void { |
| 1016 | }; | 1017 | }; |
| 1017 | 1018 | ||
| 1018 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); | 1019 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| 1019 | log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); | 1020 | log.debug("spilling %{d} to mcv {any}", .{ inst_to_save, new_mcv }); |
| 1020 | 1021 | ||
| 1021 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 1022 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1022 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); | 1023 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); |
| ... | @@ -2114,6 +2115,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2114,6 +2115,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 2114 | }; | 2115 | }; |
| 2115 | break :result dst_mcv; | 2116 | break :result dst_mcv; |
| 2116 | }; | 2117 | }; |
| 2118 | log.debug("airSliceLen(%{d}): {}", .{ inst, result }); | ||
| 2117 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 2119 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2118 | } | 2120 | } |
| 2119 | 2121 | ||
| ... | @@ -2641,6 +2643,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -2641,6 +2643,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2641 | fn airLoad(self: *Self, inst: Air.Inst.Index) !void { | 2643 | fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 2642 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2644 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2643 | const elem_ty = self.air.typeOfIndex(inst); | 2645 | const elem_ty = self.air.typeOfIndex(inst); |
| 2646 | const elem_size = elem_ty.abiSize(self.target.*); | ||
| 2644 | const result: MCValue = result: { | 2647 | const result: MCValue = result: { |
| 2645 | if (!elem_ty.hasRuntimeBitsIgnoreComptime()) | 2648 | if (!elem_ty.hasRuntimeBitsIgnoreComptime()) |
| 2646 | break :result MCValue.none; | 2649 | break :result MCValue.none; |
| ... | @@ -2651,13 +2654,14 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2651,13 +2654,14 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 2651 | break :result MCValue.dead; | 2654 | break :result MCValue.dead; |
| 2652 | 2655 | ||
| 2653 | const dst_mcv: MCValue = blk: { | 2656 | const dst_mcv: MCValue = blk: { |
| 2654 | if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) { | 2657 | if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { |
| 2655 | // The MCValue that holds the pointer can be re-used as the value. | 2658 | // The MCValue that holds the pointer can be re-used as the value. |
| 2656 | break :blk ptr; | 2659 | break :blk ptr; |
| 2657 | } else { | 2660 | } else { |
| 2658 | break :blk try self.allocRegOrMem(inst, true); | 2661 | break :blk try self.allocRegOrMem(inst, true); |
| 2659 | } | 2662 | } |
| 2660 | }; | 2663 | }; |
| 2664 | log.debug("airLoad(%{d}): {} <- {}", .{ inst, dst_mcv, ptr }); | ||
| 2661 | try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); | 2665 | try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); |
| 2662 | break :result dst_mcv; | 2666 | break :result dst_mcv; |
| 2663 | }; | 2667 | }; |
| ... | @@ -2728,10 +2732,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2728,10 +2732,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2728 | 2732 | ||
| 2729 | switch (value) { | 2733 | switch (value) { |
| 2730 | .none => unreachable, | 2734 | .none => unreachable, |
| 2731 | .undef => unreachable, | ||
| 2732 | .dead => unreachable, | 2735 | .dead => unreachable, |
| 2733 | .unreach => unreachable, | 2736 | .unreach => unreachable, |
| 2734 | .eflags => unreachable, | 2737 | .eflags => unreachable, |
| 2738 | .undef => { | ||
| 2739 | try self.genSetReg(value_ty, reg, value); | ||
| 2740 | }, | ||
| 2735 | .immediate => |imm| { | 2741 | .immediate => |imm| { |
| 2736 | switch (abi_size) { | 2742 | switch (abi_size) { |
| 2737 | 1, 2, 4 => { | 2743 | 1, 2, 4 => { |
| ... | @@ -2773,6 +2779,30 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2773,6 +2779,30 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2773 | .register => |src_reg| { | 2779 | .register => |src_reg| { |
| 2774 | try self.genInlineMemcpyRegisterRegister(value_ty, reg, src_reg, 0); | 2780 | try self.genInlineMemcpyRegisterRegister(value_ty, reg, src_reg, 0); |
| 2775 | }, | 2781 | }, |
| 2782 | .register_overflow => |ro| { | ||
| 2783 | const ro_reg_lock = self.register_manager.lockReg(ro.reg); | ||
| 2784 | defer if (ro_reg_lock) |lock| self.register_manager.unlockReg(lock); | ||
| 2785 | |||
| 2786 | const wrapped_ty = value_ty.structFieldType(0); | ||
| 2787 | try self.genInlineMemcpyRegisterRegister(wrapped_ty, reg, ro.reg, 0); | ||
| 2788 | |||
| 2789 | const overflow_bit_ty = value_ty.structFieldType(1); | ||
| 2790 | const overflow_bit_offset = value_ty.structFieldOffset(1, self.target.*); | ||
| 2791 | const tmp_reg = try self.register_manager.allocReg(null, gp); | ||
| 2792 | _ = try self.addInst(.{ | ||
| 2793 | .tag = .cond_set_byte, | ||
| 2794 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 2795 | .reg1 = tmp_reg.to8(), | ||
| 2796 | }), | ||
| 2797 | .data = .{ .cc = ro.eflags }, | ||
| 2798 | }); | ||
| 2799 | try self.genInlineMemcpyRegisterRegister( | ||
| 2800 | overflow_bit_ty, | ||
| 2801 | reg, | ||
| 2802 | tmp_reg, | ||
| 2803 | -@intCast(i32, overflow_bit_offset), | ||
| 2804 | ); | ||
| 2805 | }, | ||
| 2776 | .linker_load, | 2806 | .linker_load, |
| 2777 | .memory, | 2807 | .memory, |
| 2778 | .stack_offset, | 2808 | .stack_offset, |
| ... | @@ -2787,8 +2817,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2787,8 +2817,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2787 | .dest_stack_base = reg.to64(), | 2817 | .dest_stack_base = reg.to64(), |
| 2788 | }); | 2818 | }); |
| 2789 | }, | 2819 | }, |
| 2790 | else => |other| { | 2820 | .ptr_stack_offset => { |
| 2791 | return self.fail("TODO implement set pointee with {}", .{other}); | 2821 | const tmp_reg = try self.copyToTmpRegister(value_ty, value); |
| 2822 | return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); | ||
| 2792 | }, | 2823 | }, |
| 2793 | } | 2824 | } |
| 2794 | }, | 2825 | }, |
| ... | @@ -2902,6 +2933,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2902,6 +2933,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 2902 | const ptr_ty = self.air.typeOf(bin_op.lhs); | 2933 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2903 | const value = try self.resolveInst(bin_op.rhs); | 2934 | const value = try self.resolveInst(bin_op.rhs); |
| 2904 | const value_ty = self.air.typeOf(bin_op.rhs); | 2935 | const value_ty = self.air.typeOf(bin_op.rhs); |
| 2936 | log.debug("airStore(%{d}): {} <- {}", .{ inst, ptr, value }); | ||
| 2905 | try self.store(ptr, value, ptr_ty, value_ty); | 2937 | try self.store(ptr, value, ptr_ty, value_ty); |
| 2906 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | 2938 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2907 | } | 2939 | } |
| ... | @@ -6321,7 +6353,22 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6321,7 +6353,22 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 6321 | 6353 | ||
| 6322 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { | 6354 | fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 6323 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 6355 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 6324 | const result = try self.resolveInst(ty_op.operand); | 6356 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 6357 | const operand = try self.resolveInst(ty_op.operand); | ||
| 6358 | if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; | ||
| 6359 | |||
| 6360 | const operand_lock = switch (operand) { | ||
| 6361 | .register => |reg| self.register_manager.lockReg(reg), | ||
| 6362 | .register_overflow => |ro| self.register_manager.lockReg(ro.reg), | ||
| 6363 | else => null, | ||
| 6364 | }; | ||
| 6365 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); | ||
| 6366 | |||
| 6367 | const dest = try self.allocRegOrMem(inst, true); | ||
| 6368 | try self.setRegOrMem(self.air.typeOfIndex(inst), dest, operand); | ||
| 6369 | break :result dest; | ||
| 6370 | }; | ||
| 6371 | log.debug("airBitCast(%{d}): {}", .{ inst, result }); | ||
| 6325 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 6372 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 6326 | } | 6373 | } |
| 6327 | 6374 |
src/codegen.zig+1-1| ... | @@ -527,7 +527,7 @@ pub fn generateSymbol( | ... | @@ -527,7 +527,7 @@ pub fn generateSymbol( |
| 527 | .fail => |em| return Result{ .fail = em }, | 527 | .fail => |em| return Result{ .fail = em }, |
| 528 | } | 528 | } |
| 529 | } else { | 529 | } else { |
| 530 | field_val.writeToPackedMemory(field_ty, mod, code.items[current_pos..], bits); | 530 | field_val.writeToPackedMemory(field_ty, mod, code.items[current_pos..], bits) catch unreachable; |
| 531 | } | 531 | } |
| 532 | bits += @intCast(u16, field_ty.bitSize(target)); | 532 | bits += @intCast(u16, field_ty.bitSize(target)); |
| 533 | } | 533 | } |
src/codegen/c.zig+1617-1689| ... | @@ -23,12 +23,14 @@ const libcFloatSuffix = target_util.libcFloatSuffix; | ... | @@ -23,12 +23,14 @@ const libcFloatSuffix = target_util.libcFloatSuffix; |
| 23 | const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; | 23 | const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; |
| 24 | const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; | 24 | const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; |
| 25 | 25 | ||
| 26 | const Mutability = enum { Const, ConstArgument, Mut }; | ||
| 27 | const BigIntLimb = std.math.big.Limb; | 26 | const BigIntLimb = std.math.big.Limb; |
| 28 | const BigInt = std.math.big.int; | 27 | const BigInt = std.math.big.int; |
| 29 | 28 | ||
| 29 | pub const CType = @import("c/type.zig").CType; | ||
| 30 | |||
| 30 | pub const CValue = union(enum) { | 31 | pub const CValue = union(enum) { |
| 31 | none: void, | 32 | none: void, |
| 33 | new_local: LocalIndex, | ||
| 32 | local: LocalIndex, | 34 | local: LocalIndex, |
| 33 | /// Address of a local. | 35 | /// Address of a local. |
| 34 | local_ref: LocalIndex, | 36 | local_ref: LocalIndex, |
| ... | @@ -36,6 +38,8 @@ pub const CValue = union(enum) { | ... | @@ -36,6 +38,8 @@ pub const CValue = union(enum) { |
| 36 | constant: Air.Inst.Ref, | 38 | constant: Air.Inst.Ref, |
| 37 | /// Index into the parameters | 39 | /// Index into the parameters |
| 38 | arg: usize, | 40 | arg: usize, |
| 41 | /// The array field of a parameter | ||
| 42 | arg_array: usize, | ||
| 39 | /// Index into a tuple's fields | 43 | /// Index into a tuple's fields |
| 40 | field: usize, | 44 | field: usize, |
| 41 | /// By-value | 45 | /// By-value |
| ... | @@ -45,6 +49,8 @@ pub const CValue = union(enum) { | ... | @@ -45,6 +49,8 @@ pub const CValue = union(enum) { |
| 45 | undef: Type, | 49 | undef: Type, |
| 46 | /// Render the slice as an identifier (using fmtIdent) | 50 | /// Render the slice as an identifier (using fmtIdent) |
| 47 | identifier: []const u8, | 51 | identifier: []const u8, |
| 52 | /// Render the slice as an payload.identifier (using fmtIdent) | ||
| 53 | payload_identifier: []const u8, | ||
| 48 | /// Render these bytes literally. | 54 | /// Render these bytes literally. |
| 49 | /// TODO make this a [*:0]const u8 to save memory | 55 | /// TODO make this a [*:0]const u8 to save memory |
| 50 | bytes: []const u8, | 56 | bytes: []const u8, |
| ... | @@ -55,37 +61,43 @@ const BlockData = struct { | ... | @@ -55,37 +61,43 @@ const BlockData = struct { |
| 55 | result: CValue, | 61 | result: CValue, |
| 56 | }; | 62 | }; |
| 57 | 63 | ||
| 58 | const TypedefKind = enum { | ||
| 59 | Forward, | ||
| 60 | Complete, | ||
| 61 | }; | ||
| 62 | |||
| 63 | pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue); | 64 | pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue); |
| 64 | pub const TypedefMap = std.ArrayHashMap( | 65 | |
| 65 | Type, | 66 | pub const LazyFnKey = union(enum) { |
| 66 | struct { name: []const u8, rendered: []u8 }, | 67 | tag_name: Decl.Index, |
| 67 | Type.HashContext32, | 68 | never_tail: Decl.Index, |
| 68 | true, | 69 | never_inline: Decl.Index, |
| 69 | ); | 70 | }; |
| 71 | pub const LazyFnValue = struct { | ||
| 72 | fn_name: []const u8, | ||
| 73 | data: Data, | ||
| 74 | |||
| 75 | pub const Data = union { | ||
| 76 | tag_name: Type, | ||
| 77 | never_tail: void, | ||
| 78 | never_inline: void, | ||
| 79 | }; | ||
| 80 | }; | ||
| 81 | pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue); | ||
| 70 | 82 | ||
| 71 | const LoopDepth = u16; | 83 | const LoopDepth = u16; |
| 72 | const Local = struct { | 84 | const Local = struct { |
| 73 | ty: Type, | 85 | cty_idx: CType.Index, |
| 74 | alignment: u32, | ||
| 75 | /// How many loops the last definition was nested in. | 86 | /// How many loops the last definition was nested in. |
| 76 | loop_depth: LoopDepth, | 87 | loop_depth: LoopDepth, |
| 88 | alignas: CType.AlignAs, | ||
| 89 | |||
| 90 | pub fn getType(local: Local) LocalType { | ||
| 91 | return .{ .cty_idx = local.cty_idx, .alignas = local.alignas }; | ||
| 92 | } | ||
| 77 | }; | 93 | }; |
| 78 | 94 | ||
| 79 | const LocalIndex = u16; | 95 | const LocalIndex = u16; |
| 80 | const LocalsList = std.ArrayListUnmanaged(LocalIndex); | 96 | const LocalType = struct { cty_idx: CType.Index, alignas: CType.AlignAs }; |
| 81 | const LocalsMap = std.ArrayHashMapUnmanaged(Type, LocalsList, Type.HashContext32, true); | 97 | const LocalsList = std.AutoArrayHashMapUnmanaged(LocalIndex, void); |
| 98 | const LocalsMap = std.AutoArrayHashMapUnmanaged(LocalType, LocalsList); | ||
| 82 | const LocalsStack = std.ArrayListUnmanaged(LocalsMap); | 99 | const LocalsStack = std.ArrayListUnmanaged(LocalsMap); |
| 83 | 100 | ||
| 84 | const FormatTypeAsCIdentContext = struct { | ||
| 85 | ty: Type, | ||
| 86 | mod: *Module, | ||
| 87 | }; | ||
| 88 | |||
| 89 | const ValueRenderLocation = enum { | 101 | const ValueRenderLocation = enum { |
| 90 | FunctionArgument, | 102 | FunctionArgument, |
| 91 | Initializer, | 103 | Initializer, |
| ... | @@ -106,26 +118,6 @@ const BuiltinInfo = enum { | ... | @@ -106,26 +118,6 @@ const BuiltinInfo = enum { |
| 106 | Bits, | 118 | Bits, |
| 107 | }; | 119 | }; |
| 108 | 120 | ||
| 109 | fn formatTypeAsCIdentifier( | ||
| 110 | data: FormatTypeAsCIdentContext, | ||
| 111 | comptime fmt: []const u8, | ||
| 112 | options: std.fmt.FormatOptions, | ||
| 113 | writer: anytype, | ||
| 114 | ) !void { | ||
| 115 | var stack = std.heap.stackFallback(128, data.mod.gpa); | ||
| 116 | const allocator = stack.get(); | ||
| 117 | const str = std.fmt.allocPrint(allocator, "{}", .{data.ty.fmt(data.mod)}) catch ""; | ||
| 118 | defer allocator.free(str); | ||
| 119 | return formatIdent(str, fmt, options, writer); | ||
| 120 | } | ||
| 121 | |||
| 122 | pub fn typeToCIdentifier(ty: Type, mod: *Module) std.fmt.Formatter(formatTypeAsCIdentifier) { | ||
| 123 | return .{ .data = .{ | ||
| 124 | .ty = ty, | ||
| 125 | .mod = mod, | ||
| 126 | } }; | ||
| 127 | } | ||
| 128 | |||
| 129 | const reserved_idents = std.ComptimeStringMap(void, .{ | 121 | const reserved_idents = std.ComptimeStringMap(void, .{ |
| 130 | // C language | 122 | // C language |
| 131 | .{ "alignas", { | 123 | .{ "alignas", { |
| ... | @@ -224,6 +216,15 @@ const reserved_idents = std.ComptimeStringMap(void, .{ | ... | @@ -224,6 +216,15 @@ const reserved_idents = std.ComptimeStringMap(void, .{ |
| 224 | .{ "volatile", {} }, | 216 | .{ "volatile", {} }, |
| 225 | .{ "while ", {} }, | 217 | .{ "while ", {} }, |
| 226 | 218 | ||
| 219 | // stdarg.h | ||
| 220 | .{ "va_start", {} }, | ||
| 221 | .{ "va_arg", {} }, | ||
| 222 | .{ "va_end", {} }, | ||
| 223 | .{ "va_copy", {} }, | ||
| 224 | |||
| 225 | // stddef.h | ||
| 226 | .{ "offsetof", {} }, | ||
| 227 | |||
| 227 | // windows.h | 228 | // windows.h |
| 228 | .{ "max", {} }, | 229 | .{ "max", {} }, |
| 229 | .{ "min", {} }, | 230 | .{ "min", {} }, |
| ... | @@ -281,6 +282,7 @@ pub const Function = struct { | ... | @@ -281,6 +282,7 @@ pub const Function = struct { |
| 281 | next_arg_index: usize = 0, | 282 | next_arg_index: usize = 0, |
| 282 | next_block_index: usize = 0, | 283 | next_block_index: usize = 0, |
| 283 | object: Object, | 284 | object: Object, |
| 285 | lazy_fns: LazyFnMap, | ||
| 284 | func: *Module.Fn, | 286 | func: *Module.Fn, |
| 285 | /// All the locals, to be emitted at the top of the function. | 287 | /// All the locals, to be emitted at the top of the function. |
| 286 | locals: std.ArrayListUnmanaged(Local) = .{}, | 288 | locals: std.ArrayListUnmanaged(Local) = .{}, |
| ... | @@ -299,10 +301,6 @@ pub const Function = struct { | ... | @@ -299,10 +301,6 @@ pub const Function = struct { |
| 299 | /// Needed for memory used by the keys of free_locals_stack entries. | 301 | /// Needed for memory used by the keys of free_locals_stack entries. |
| 300 | arena: std.heap.ArenaAllocator, | 302 | arena: std.heap.ArenaAllocator, |
| 301 | 303 | ||
| 302 | fn tyHashCtx(f: Function) Type.HashContext32 { | ||
| 303 | return .{ .mod = f.object.dg.module }; | ||
| 304 | } | ||
| 305 | |||
| 306 | fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue { | 304 | fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue { |
| 307 | const gop = try f.value_map.getOrPut(inst); | 305 | const gop = try f.value_map.getOrPut(inst); |
| 308 | if (gop.found_existing) return gop.value_ptr.*; | 306 | if (gop.found_existing) return gop.value_ptr.*; |
| ... | @@ -310,19 +308,19 @@ pub const Function = struct { | ... | @@ -310,19 +308,19 @@ pub const Function = struct { |
| 310 | const val = f.air.value(inst).?; | 308 | const val = f.air.value(inst).?; |
| 311 | const ty = f.air.typeOf(inst); | 309 | const ty = f.air.typeOf(inst); |
| 312 | 310 | ||
| 313 | const result = if (lowersToArray(ty, f.object.dg.module.getTarget())) result: { | 311 | const result: CValue = if (lowersToArray(ty, f.object.dg.module.getTarget())) result: { |
| 314 | const writer = f.object.code_header.writer(); | 312 | const writer = f.object.code_header.writer(); |
| 315 | const alignment = 0; | 313 | const alignment = 0; |
| 316 | const decl_c_value = try f.allocLocalValue(ty, alignment); | 314 | const decl_c_value = try f.allocLocalValue(ty, alignment); |
| 317 | const gpa = f.object.dg.gpa; | 315 | const gpa = f.object.dg.gpa; |
| 318 | try f.allocs.put(gpa, decl_c_value.local, true); | 316 | try f.allocs.put(gpa, decl_c_value.new_local, true); |
| 319 | try writer.writeAll("static "); | 317 | try writer.writeAll("static "); |
| 320 | try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const, alignment, .Complete); | 318 | try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, Const, alignment, .complete); |
| 321 | try writer.writeAll(" = "); | 319 | try writer.writeAll(" = "); |
| 322 | try f.object.dg.renderValue(writer, ty, val, .StaticInitializer); | 320 | try f.object.dg.renderValue(writer, ty, val, .StaticInitializer); |
| 323 | try writer.writeAll(";\n "); | 321 | try writer.writeAll(";\n "); |
| 324 | break :result decl_c_value; | 322 | break :result decl_c_value; |
| 325 | } else CValue{ .constant = inst }; | 323 | } else .{ .constant = inst }; |
| 326 | 324 | ||
| 327 | gop.value_ptr.* = result; | 325 | gop.value_ptr.* = result; |
| 328 | return result; | 326 | return result; |
| ... | @@ -342,32 +340,32 @@ pub const Function = struct { | ... | @@ -342,32 +340,32 @@ pub const Function = struct { |
| 342 | /// Skips the reuse logic. | 340 | /// Skips the reuse logic. |
| 343 | fn allocLocalValue(f: *Function, ty: Type, alignment: u32) !CValue { | 341 | fn allocLocalValue(f: *Function, ty: Type, alignment: u32) !CValue { |
| 344 | const gpa = f.object.dg.gpa; | 342 | const gpa = f.object.dg.gpa; |
| 343 | const target = f.object.dg.module.getTarget(); | ||
| 345 | try f.locals.append(gpa, .{ | 344 | try f.locals.append(gpa, .{ |
| 346 | .ty = ty, | 345 | .cty_idx = try f.typeToIndex(ty, .complete), |
| 347 | .alignment = alignment, | ||
| 348 | .loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1), | 346 | .loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1), |
| 347 | .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)), | ||
| 349 | }); | 348 | }); |
| 350 | return CValue{ .local = @intCast(LocalIndex, f.locals.items.len - 1) }; | 349 | return .{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) }; |
| 351 | } | 350 | } |
| 352 | 351 | ||
| 353 | fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue { | 352 | fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue { |
| 354 | const result = try f.allocAlignedLocal(ty, .Mut, 0); | 353 | const result = try f.allocAlignedLocal(ty, .{}, 0); |
| 355 | log.debug("%{d}: allocating t{d}", .{ inst, result.local }); | 354 | log.debug("%{d}: allocating t{d}", .{ inst, result.new_local }); |
| 356 | return result; | 355 | return result; |
| 357 | } | 356 | } |
| 358 | 357 | ||
| 359 | /// Only allocates the local; does not print anything. | 358 | /// Only allocates the local; does not print anything. |
| 360 | fn allocAlignedLocal(f: *Function, ty: Type, mutability: Mutability, alignment: u32) !CValue { | 359 | fn allocAlignedLocal(f: *Function, ty: Type, _: CQualifiers, alignment: u32) !CValue { |
| 361 | _ = mutability; | 360 | const target = f.object.dg.module.getTarget(); |
| 362 | 361 | if (f.getFreeLocals().getPtr(.{ | |
| 363 | if (f.getFreeLocals().getPtrContext(ty, f.tyHashCtx())) |locals_list| { | 362 | .cty_idx = try f.typeToIndex(ty, .complete), |
| 364 | for (locals_list.items, 0..) |local_index, i| { | 363 | .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)), |
| 365 | const local = &f.locals.items[local_index]; | 364 | })) |locals_list| { |
| 366 | if (local.alignment >= alignment) { | 365 | if (locals_list.popOrNull()) |local_entry| { |
| 367 | local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); | 366 | const local = &f.locals.items[local_entry.key]; |
| 368 | _ = locals_list.swapRemove(i); | 367 | local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); |
| 369 | return CValue{ .local = local_index }; | 368 | return .{ .new_local = local_entry.key }; |
| 370 | } | ||
| 371 | } | 369 | } |
| 372 | } | 370 | } |
| 373 | 371 | ||
| ... | @@ -430,12 +428,20 @@ pub const Function = struct { | ... | @@ -430,12 +428,20 @@ pub const Function = struct { |
| 430 | return f.object.dg.fail(format, args); | 428 | return f.object.dg.fail(format, args); |
| 431 | } | 429 | } |
| 432 | 430 | ||
| 433 | fn renderType(f: *Function, w: anytype, t: Type) !void { | 431 | fn indexToCType(f: *Function, idx: CType.Index) CType { |
| 434 | return f.object.dg.renderType(w, t, .Complete); | 432 | return f.object.dg.indexToCType(idx); |
| 435 | } | 433 | } |
| 436 | 434 | ||
| 437 | fn renderTypecast(f: *Function, w: anytype, t: Type) !void { | 435 | fn typeToIndex(f: *Function, ty: Type, kind: CType.Kind) !CType.Index { |
| 438 | return f.object.dg.renderTypecast(w, t); | 436 | return f.object.dg.typeToIndex(ty, kind); |
| 437 | } | ||
| 438 | |||
| 439 | fn typeToCType(f: *Function, ty: Type, kind: CType.Kind) !CType { | ||
| 440 | return f.object.dg.typeToCType(ty, kind); | ||
| 441 | } | ||
| 442 | |||
| 443 | fn renderType(f: *Function, w: anytype, t: Type) !void { | ||
| 444 | return f.object.dg.renderType(w, t); | ||
| 439 | } | 445 | } |
| 440 | 446 | ||
| 441 | fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, src_ty: Type, location: ValueRenderLocation) !void { | 447 | fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, src_ty: Type, location: ValueRenderLocation) !void { |
| ... | @@ -446,7 +452,39 @@ pub const Function = struct { | ... | @@ -446,7 +452,39 @@ pub const Function = struct { |
| 446 | return f.object.dg.fmtIntLiteral(ty, val); | 452 | return f.object.dg.fmtIntLiteral(ty, val); |
| 447 | } | 453 | } |
| 448 | 454 | ||
| 449 | pub fn deinit(f: *Function, gpa: mem.Allocator) void { | 455 | fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 { |
| 456 | const gpa = f.object.dg.gpa; | ||
| 457 | const gop = try f.lazy_fns.getOrPut(gpa, key); | ||
| 458 | if (!gop.found_existing) { | ||
| 459 | errdefer _ = f.lazy_fns.pop(); | ||
| 460 | |||
| 461 | var promoted = f.object.dg.ctypes.promote(gpa); | ||
| 462 | defer f.object.dg.ctypes.demote(promoted); | ||
| 463 | const arena = promoted.arena.allocator(); | ||
| 464 | |||
| 465 | gop.value_ptr.* = .{ | ||
| 466 | .fn_name = switch (key) { | ||
| 467 | .tag_name, | ||
| 468 | .never_tail, | ||
| 469 | .never_inline, | ||
| 470 | => |owner_decl| try std.fmt.allocPrint(arena, "zig_{s}_{}__{d}", .{ | ||
| 471 | @tagName(key), | ||
| 472 | fmtIdent(mem.span(f.object.dg.module.declPtr(owner_decl).name)), | ||
| 473 | @enumToInt(owner_decl), | ||
| 474 | }), | ||
| 475 | }, | ||
| 476 | .data = switch (key) { | ||
| 477 | .tag_name => .{ .tag_name = try data.tag_name.copy(arena) }, | ||
| 478 | .never_tail => .{ .never_tail = data.never_tail }, | ||
| 479 | .never_inline => .{ .never_inline = data.never_inline }, | ||
| 480 | }, | ||
| 481 | }; | ||
| 482 | } | ||
| 483 | return gop.value_ptr.fn_name; | ||
| 484 | } | ||
| 485 | |||
| 486 | pub fn deinit(f: *Function) void { | ||
| 487 | const gpa = f.object.dg.gpa; | ||
| 450 | f.allocs.deinit(gpa); | 488 | f.allocs.deinit(gpa); |
| 451 | f.locals.deinit(gpa); | 489 | f.locals.deinit(gpa); |
| 452 | for (f.free_locals_stack.items) |*free_locals| { | 490 | for (f.free_locals_stack.items) |*free_locals| { |
| ... | @@ -455,11 +493,9 @@ pub const Function = struct { | ... | @@ -455,11 +493,9 @@ pub const Function = struct { |
| 455 | f.free_locals_stack.deinit(gpa); | 493 | f.free_locals_stack.deinit(gpa); |
| 456 | f.blocks.deinit(gpa); | 494 | f.blocks.deinit(gpa); |
| 457 | f.value_map.deinit(); | 495 | f.value_map.deinit(); |
| 496 | f.lazy_fns.deinit(gpa); | ||
| 458 | f.object.code.deinit(); | 497 | f.object.code.deinit(); |
| 459 | for (f.object.dg.typedefs.values()) |typedef| { | 498 | f.object.dg.ctypes.deinit(gpa); |
| 460 | gpa.free(typedef.rendered); | ||
| 461 | } | ||
| 462 | f.object.dg.typedefs.deinit(); | ||
| 463 | f.object.dg.fwd_decl.deinit(); | 499 | f.object.dg.fwd_decl.deinit(); |
| 464 | f.arena.deinit(); | 500 | f.arena.deinit(); |
| 465 | } | 501 | } |
| ... | @@ -483,30 +519,20 @@ pub const Object = struct { | ... | @@ -483,30 +519,20 @@ pub const Object = struct { |
| 483 | pub const DeclGen = struct { | 519 | pub const DeclGen = struct { |
| 484 | gpa: std.mem.Allocator, | 520 | gpa: std.mem.Allocator, |
| 485 | module: *Module, | 521 | module: *Module, |
| 486 | decl: *Decl, | 522 | decl: ?*Decl, |
| 487 | decl_index: Decl.Index, | 523 | decl_index: Decl.OptionalIndex, |
| 488 | fwd_decl: std.ArrayList(u8), | 524 | fwd_decl: std.ArrayList(u8), |
| 489 | error_msg: ?*Module.ErrorMsg, | 525 | error_msg: ?*Module.ErrorMsg, |
| 490 | /// The key of this map is Type which has references to typedefs_arena. | 526 | ctypes: CType.Store, |
| 491 | typedefs: TypedefMap, | ||
| 492 | typedefs_arena: std.mem.Allocator, | ||
| 493 | 527 | ||
| 494 | fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { | 528 | fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { |
| 495 | @setCold(true); | 529 | @setCold(true); |
| 496 | const src = LazySrcLoc.nodeOffset(0); | 530 | const src = LazySrcLoc.nodeOffset(0); |
| 497 | const src_loc = src.toSrcLoc(dg.decl); | 531 | const src_loc = src.toSrcLoc(dg.decl.?); |
| 498 | dg.error_msg = try Module.ErrorMsg.create(dg.gpa, src_loc, format, args); | 532 | dg.error_msg = try Module.ErrorMsg.create(dg.gpa, src_loc, format, args); |
| 499 | return error.AnalysisFail; | 533 | return error.AnalysisFail; |
| 500 | } | 534 | } |
| 501 | 535 | ||
| 502 | fn getTypedefName(dg: *DeclGen, t: Type) ?[]const u8 { | ||
| 503 | if (dg.typedefs.get(t)) |typedef| { | ||
| 504 | return typedef.name; | ||
| 505 | } else { | ||
| 506 | return null; | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | fn renderDeclValue( | 536 | fn renderDeclValue( |
| 511 | dg: *DeclGen, | 537 | dg: *DeclGen, |
| 512 | writer: anytype, | 538 | writer: anytype, |
| ... | @@ -520,7 +546,7 @@ pub const DeclGen = struct { | ... | @@ -520,7 +546,7 @@ pub const DeclGen = struct { |
| 520 | 546 | ||
| 521 | // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. | 547 | // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. |
| 522 | if (ty.isPtrAtRuntime() and !decl.ty.isFnOrHasRuntimeBits()) { | 548 | if (ty.isPtrAtRuntime() and !decl.ty.isFnOrHasRuntimeBits()) { |
| 523 | return dg.writeCValue(writer, CValue{ .undef = ty }); | 549 | return dg.writeCValue(writer, .{ .undef = ty }); |
| 524 | } | 550 | } |
| 525 | 551 | ||
| 526 | // Chase function values in order to be able to reference the original function. | 552 | // Chase function values in order to be able to reference the original function. |
| ... | @@ -534,7 +560,7 @@ pub const DeclGen = struct { | ... | @@ -534,7 +560,7 @@ pub const DeclGen = struct { |
| 534 | try writer.writeByte('{'); | 560 | try writer.writeByte('{'); |
| 535 | } else { | 561 | } else { |
| 536 | try writer.writeByte('('); | 562 | try writer.writeByte('('); |
| 537 | try dg.renderTypecast(writer, ty); | 563 | try dg.renderType(writer, ty); |
| 538 | try writer.writeAll("){ .ptr = "); | 564 | try writer.writeAll("){ .ptr = "); |
| 539 | } | 565 | } |
| 540 | 566 | ||
| ... | @@ -561,7 +587,7 @@ pub const DeclGen = struct { | ... | @@ -561,7 +587,7 @@ pub const DeclGen = struct { |
| 561 | const need_typecast = if (ty.castPtrToFn()) |_| false else !ty.eql(decl.ty, dg.module); | 587 | const need_typecast = if (ty.castPtrToFn()) |_| false else !ty.eql(decl.ty, dg.module); |
| 562 | if (need_typecast) { | 588 | if (need_typecast) { |
| 563 | try writer.writeAll("(("); | 589 | try writer.writeAll("(("); |
| 564 | try dg.renderTypecast(writer, ty); | 590 | try dg.renderType(writer, ty); |
| 565 | try writer.writeByte(')'); | 591 | try writer.writeByte(')'); |
| 566 | } | 592 | } |
| 567 | try writer.writeByte('&'); | 593 | try writer.writeByte('&'); |
| ... | @@ -576,7 +602,7 @@ pub const DeclGen = struct { | ... | @@ -576,7 +602,7 @@ pub const DeclGen = struct { |
| 576 | fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type, location: ValueRenderLocation) error{ OutOfMemory, AnalysisFail }!void { | 602 | fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type, location: ValueRenderLocation) error{ OutOfMemory, AnalysisFail }!void { |
| 577 | if (!ptr_ty.isSlice()) { | 603 | if (!ptr_ty.isSlice()) { |
| 578 | try writer.writeByte('('); | 604 | try writer.writeByte('('); |
| 579 | try dg.renderTypecast(writer, ptr_ty); | 605 | try dg.renderType(writer, ptr_ty); |
| 580 | try writer.writeByte(')'); | 606 | try writer.writeByte(')'); |
| 581 | } | 607 | } |
| 582 | switch (ptr_val.tag()) { | 608 | switch (ptr_val.tag()) { |
| ... | @@ -591,90 +617,71 @@ pub const DeclGen = struct { | ... | @@ -591,90 +617,71 @@ pub const DeclGen = struct { |
| 591 | try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl_index, location); | 617 | try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl_index, location); |
| 592 | }, | 618 | }, |
| 593 | .field_ptr => { | 619 | .field_ptr => { |
| 594 | const ptr_info = ptr_ty.ptrInfo(); | 620 | const target = dg.module.getTarget(); |
| 595 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; | 621 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; |
| 596 | const container_ty = field_ptr.container_ty; | ||
| 597 | const index = field_ptr.field_index; | ||
| 598 | |||
| 599 | var container_ptr_ty_pl: Type.Payload.ElemType = .{ | ||
| 600 | .base = .{ .tag = .c_mut_pointer }, | ||
| 601 | .data = field_ptr.container_ty, | ||
| 602 | }; | ||
| 603 | const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); | ||
| 604 | |||
| 605 | const FieldInfo = struct { name: []const u8, ty: Type }; | ||
| 606 | const field_info: FieldInfo = switch (container_ty.zigTypeTag()) { | ||
| 607 | .Struct => switch (container_ty.containerLayout()) { | ||
| 608 | .Auto, .Extern => FieldInfo{ | ||
| 609 | .name = container_ty.structFields().keys()[index], | ||
| 610 | .ty = container_ty.structFields().values()[index].ty, | ||
| 611 | }, | ||
| 612 | .Packed => if (ptr_info.data.host_size == 0) { | ||
| 613 | const target = dg.module.getTarget(); | ||
| 614 | |||
| 615 | const byte_offset = container_ty.packedStructFieldByteOffset(index, target); | ||
| 616 | var byte_offset_pl = Value.Payload.U64{ | ||
| 617 | .base = .{ .tag = .int_u64 }, | ||
| 618 | .data = byte_offset, | ||
| 619 | }; | ||
| 620 | const byte_offset_val = Value.initPayload(&byte_offset_pl.base); | ||
| 621 | |||
| 622 | var u8_ptr_pl = ptr_info; | ||
| 623 | u8_ptr_pl.data.pointee_type = Type.u8; | ||
| 624 | const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); | ||
| 625 | |||
| 626 | try writer.writeAll("&(("); | ||
| 627 | try dg.renderTypecast(writer, u8_ptr_ty); | ||
| 628 | try writer.writeByte(')'); | ||
| 629 | try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty, location); | ||
| 630 | return writer.print(")[{}]", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)}); | ||
| 631 | } else { | ||
| 632 | var host_pl = Type.Payload.Bits{ | ||
| 633 | .base = .{ .tag = .int_unsigned }, | ||
| 634 | .data = ptr_info.data.host_size * 8, | ||
| 635 | }; | ||
| 636 | const host_ty = Type.initPayload(&host_pl.base); | ||
| 637 | 622 | ||
| 638 | try writer.writeByte('('); | 623 | // Ensure complete type definition is visible before accessing fields. |
| 639 | try dg.renderTypecast(writer, ptr_ty); | 624 | _ = try dg.typeToIndex(field_ptr.container_ty, .complete); |
| 640 | try writer.writeByte(')'); | 625 | |
| 641 | return dg.renderParentPtr(writer, field_ptr.container_ptr, host_ty, location); | 626 | var container_ptr_pl = ptr_ty.ptrInfo(); |
| 642 | }, | 627 | container_ptr_pl.data.pointee_type = field_ptr.container_ty; |
| 643 | }, | 628 | const container_ptr_ty = Type.initPayload(&container_ptr_pl.base); |
| 644 | .Union => switch (container_ty.containerLayout()) { | 629 | |
| 645 | .Auto, .Extern => FieldInfo{ | 630 | switch (fieldLocation( |
| 646 | .name = container_ty.unionFields().keys()[index], | 631 | field_ptr.container_ty, |
| 647 | .ty = container_ty.unionFields().values()[index].ty, | 632 | ptr_ty, |
| 648 | }, | 633 | @intCast(u32, field_ptr.field_index), |
| 649 | .Packed => { | 634 | target, |
| 650 | return dg.renderParentPtr(writer, field_ptr.container_ptr, ptr_ty, location); | 635 | )) { |
| 651 | }, | 636 | .begin => try dg.renderParentPtr( |
| 637 | writer, | ||
| 638 | field_ptr.container_ptr, | ||
| 639 | container_ptr_ty, | ||
| 640 | location, | ||
| 641 | ), | ||
| 642 | .field => |field| { | ||
| 643 | try writer.writeAll("&("); | ||
| 644 | try dg.renderParentPtr( | ||
| 645 | writer, | ||
| 646 | field_ptr.container_ptr, | ||
| 647 | container_ptr_ty, | ||
| 648 | location, | ||
| 649 | ); | ||
| 650 | try writer.writeAll(")->"); | ||
| 651 | try dg.writeCValue(writer, field); | ||
| 652 | }, | 652 | }, |
| 653 | .Pointer => field_info: { | 653 | .byte_offset => |byte_offset| { |
| 654 | assert(container_ty.isSlice()); | 654 | var u8_ptr_pl = ptr_ty.ptrInfo(); |
| 655 | break :field_info switch (index) { | 655 | u8_ptr_pl.data.pointee_type = Type.u8; |
| 656 | 0 => FieldInfo{ .name = "ptr", .ty = container_ty.childType() }, | 656 | const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); |
| 657 | 1 => FieldInfo{ .name = "len", .ty = Type.usize }, | 657 | |
| 658 | else => unreachable, | 658 | var byte_offset_pl = Value.Payload.U64{ |
| 659 | .base = .{ .tag = .int_u64 }, | ||
| 660 | .data = byte_offset, | ||
| 659 | }; | 661 | }; |
| 660 | }, | 662 | const byte_offset_val = Value.initPayload(&byte_offset_pl.base); |
| 661 | else => unreachable, | ||
| 662 | }; | ||
| 663 | 663 | ||
| 664 | if (field_info.ty.hasRuntimeBitsIgnoreComptime()) { | 664 | try writer.writeAll("(("); |
| 665 | // Ensure complete type definition is visible before accessing fields. | 665 | try dg.renderType(writer, u8_ptr_ty); |
| 666 | try dg.renderType(std.io.null_writer, field_ptr.container_ty, .Complete); | 666 | try writer.writeByte(')'); |
| 667 | 667 | try dg.renderParentPtr( | |
| 668 | try writer.writeAll("&("); | 668 | writer, |
| 669 | try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty, location); | 669 | field_ptr.container_ptr, |
| 670 | try writer.writeAll(")->"); | 670 | container_ptr_ty, |
| 671 | switch (field_ptr.container_ty.tag()) { | 671 | location, |
| 672 | .union_tagged, .union_safety_tagged => try writer.writeAll("payload."), | 672 | ); |
| 673 | else => {}, | 673 | try writer.print(" + {})", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)}); |
| 674 | } | 674 | }, |
| 675 | try writer.print("{ }", .{fmtIdent(field_info.name)}); | 675 | .end => { |
| 676 | } else { | 676 | try writer.writeAll("(("); |
| 677 | try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty, location); | 677 | try dg.renderParentPtr( |
| 678 | writer, | ||
| 679 | field_ptr.container_ptr, | ||
| 680 | container_ptr_ty, | ||
| 681 | location, | ||
| 682 | ); | ||
| 683 | try writer.print(") + {})", .{try dg.fmtIntLiteral(Type.usize, Value.one)}); | ||
| 684 | }, | ||
| 678 | } | 685 | } |
| 679 | }, | 686 | }, |
| 680 | .elem_ptr => { | 687 | .elem_ptr => { |
| ... | @@ -698,7 +705,7 @@ pub const DeclGen = struct { | ... | @@ -698,7 +705,7 @@ pub const DeclGen = struct { |
| 698 | const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); | 705 | const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); |
| 699 | 706 | ||
| 700 | // Ensure complete type definition is visible before accessing fields. | 707 | // Ensure complete type definition is visible before accessing fields. |
| 701 | try dg.renderType(std.io.null_writer, payload_ptr.container_ty, .Complete); | 708 | _ = try dg.typeToIndex(payload_ptr.container_ty, .complete); |
| 702 | 709 | ||
| 703 | try writer.writeAll("&("); | 710 | try writer.writeAll("&("); |
| 704 | try dg.renderParentPtr(writer, payload_ptr.container_ptr, container_ptr_ty, location); | 711 | try dg.renderParentPtr(writer, payload_ptr.container_ptr, container_ptr_ty, location); |
| ... | @@ -747,7 +754,7 @@ pub const DeclGen = struct { | ... | @@ -747,7 +754,7 @@ pub const DeclGen = struct { |
| 747 | 754 | ||
| 748 | try writer.writeAll("zig_cast_"); | 755 | try writer.writeAll("zig_cast_"); |
| 749 | try dg.renderTypeForBuiltinFnName(writer, ty); | 756 | try dg.renderTypeForBuiltinFnName(writer, ty); |
| 750 | try writer.writeAll(" zig_as_"); | 757 | try writer.writeAll(" zig_make_"); |
| 751 | try dg.renderTypeForBuiltinFnName(writer, ty); | 758 | try dg.renderTypeForBuiltinFnName(writer, ty); |
| 752 | try writer.writeByte('('); | 759 | try writer.writeByte('('); |
| 753 | switch (bits) { | 760 | switch (bits) { |
| ... | @@ -765,18 +772,18 @@ pub const DeclGen = struct { | ... | @@ -765,18 +772,18 @@ pub const DeclGen = struct { |
| 765 | .Pointer => if (ty.isSlice()) { | 772 | .Pointer => if (ty.isSlice()) { |
| 766 | if (!location.isInitializer()) { | 773 | if (!location.isInitializer()) { |
| 767 | try writer.writeByte('('); | 774 | try writer.writeByte('('); |
| 768 | try dg.renderTypecast(writer, ty); | 775 | try dg.renderType(writer, ty); |
| 769 | try writer.writeByte(')'); | 776 | try writer.writeByte(')'); |
| 770 | } | 777 | } |
| 771 | 778 | ||
| 772 | try writer.writeAll("{("); | 779 | try writer.writeAll("{("); |
| 773 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 780 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 774 | const ptr_ty = ty.slicePtrFieldType(&buf); | 781 | const ptr_ty = ty.slicePtrFieldType(&buf); |
| 775 | try dg.renderTypecast(writer, ptr_ty); | 782 | try dg.renderType(writer, ptr_ty); |
| 776 | return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val)}); | 783 | return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val)}); |
| 777 | } else { | 784 | } else { |
| 778 | try writer.writeAll("(("); | 785 | try writer.writeAll("(("); |
| 779 | try dg.renderTypecast(writer, ty); | 786 | try dg.renderType(writer, ty); |
| 780 | return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); | 787 | return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); |
| 781 | }, | 788 | }, |
| 782 | .Optional => { | 789 | .Optional => { |
| ... | @@ -793,7 +800,7 @@ pub const DeclGen = struct { | ... | @@ -793,7 +800,7 @@ pub const DeclGen = struct { |
| 793 | 800 | ||
| 794 | if (!location.isInitializer()) { | 801 | if (!location.isInitializer()) { |
| 795 | try writer.writeByte('('); | 802 | try writer.writeByte('('); |
| 796 | try dg.renderTypecast(writer, ty); | 803 | try dg.renderType(writer, ty); |
| 797 | try writer.writeByte(')'); | 804 | try writer.writeByte(')'); |
| 798 | } | 805 | } |
| 799 | 806 | ||
| ... | @@ -807,7 +814,7 @@ pub const DeclGen = struct { | ... | @@ -807,7 +814,7 @@ pub const DeclGen = struct { |
| 807 | .Auto, .Extern => { | 814 | .Auto, .Extern => { |
| 808 | if (!location.isInitializer()) { | 815 | if (!location.isInitializer()) { |
| 809 | try writer.writeByte('('); | 816 | try writer.writeByte('('); |
| 810 | try dg.renderTypecast(writer, ty); | 817 | try dg.renderType(writer, ty); |
| 811 | try writer.writeByte(')'); | 818 | try writer.writeByte(')'); |
| 812 | } | 819 | } |
| 813 | 820 | ||
| ... | @@ -821,7 +828,7 @@ pub const DeclGen = struct { | ... | @@ -821,7 +828,7 @@ pub const DeclGen = struct { |
| 821 | 828 | ||
| 822 | empty = false; | 829 | empty = false; |
| 823 | } | 830 | } |
| 824 | if (empty) try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); | 831 | |
| 825 | return writer.writeByte('}'); | 832 | return writer.writeByte('}'); |
| 826 | }, | 833 | }, |
| 827 | .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef)}), | 834 | .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef)}), |
| ... | @@ -829,7 +836,7 @@ pub const DeclGen = struct { | ... | @@ -829,7 +836,7 @@ pub const DeclGen = struct { |
| 829 | .Union => { | 836 | .Union => { |
| 830 | if (!location.isInitializer()) { | 837 | if (!location.isInitializer()) { |
| 831 | try writer.writeByte('('); | 838 | try writer.writeByte('('); |
| 832 | try dg.renderTypecast(writer, ty); | 839 | try dg.renderType(writer, ty); |
| 833 | try writer.writeByte(')'); | 840 | try writer.writeByte(')'); |
| 834 | } | 841 | } |
| 835 | 842 | ||
| ... | @@ -854,7 +861,7 @@ pub const DeclGen = struct { | ... | @@ -854,7 +861,7 @@ pub const DeclGen = struct { |
| 854 | .ErrorUnion => { | 861 | .ErrorUnion => { |
| 855 | if (!location.isInitializer()) { | 862 | if (!location.isInitializer()) { |
| 856 | try writer.writeByte('('); | 863 | try writer.writeByte('('); |
| 857 | try dg.renderTypecast(writer, ty); | 864 | try dg.renderType(writer, ty); |
| 858 | try writer.writeByte(')'); | 865 | try writer.writeByte(')'); |
| 859 | } | 866 | } |
| 860 | 867 | ||
| ... | @@ -867,7 +874,7 @@ pub const DeclGen = struct { | ... | @@ -867,7 +874,7 @@ pub const DeclGen = struct { |
| 867 | .Array, .Vector => { | 874 | .Array, .Vector => { |
| 868 | if (!location.isInitializer()) { | 875 | if (!location.isInitializer()) { |
| 869 | try writer.writeByte('('); | 876 | try writer.writeByte('('); |
| 870 | try dg.renderTypecast(writer, ty); | 877 | try dg.renderType(writer, ty); |
| 871 | try writer.writeByte(')'); | 878 | try writer.writeByte(')'); |
| 872 | } | 879 | } |
| 873 | 880 | ||
| ... | @@ -876,14 +883,14 @@ pub const DeclGen = struct { | ... | @@ -876,14 +883,14 @@ pub const DeclGen = struct { |
| 876 | var literal = stringLiteral(writer); | 883 | var literal = stringLiteral(writer); |
| 877 | try literal.start(); | 884 | try literal.start(); |
| 878 | const c_len = ty.arrayLenIncludingSentinel(); | 885 | const c_len = ty.arrayLenIncludingSentinel(); |
| 879 | var index: usize = 0; | 886 | var index: u64 = 0; |
| 880 | while (index < c_len) : (index += 1) | 887 | while (index < c_len) : (index += 1) |
| 881 | try literal.writeChar(0xaa); | 888 | try literal.writeChar(0xaa); |
| 882 | return literal.end(); | 889 | return literal.end(); |
| 883 | } else { | 890 | } else { |
| 884 | try writer.writeByte('{'); | 891 | try writer.writeByte('{'); |
| 885 | const c_len = ty.arrayLenIncludingSentinel(); | 892 | const c_len = ty.arrayLenIncludingSentinel(); |
| 886 | var index: usize = 0; | 893 | var index: u64 = 0; |
| 887 | while (index < c_len) : (index += 1) { | 894 | while (index < c_len) : (index += 1) { |
| 888 | if (index > 0) try writer.writeAll(", "); | 895 | if (index > 0) try writer.writeAll(", "); |
| 889 | try dg.renderValue(writer, ty.childType(), val, initializer_type); | 896 | try dg.renderValue(writer, ty.childType(), val, initializer_type); |
| ... | @@ -957,7 +964,7 @@ pub const DeclGen = struct { | ... | @@ -957,7 +964,7 @@ pub const DeclGen = struct { |
| 957 | try writer.writeByte(' '); | 964 | try writer.writeByte(' '); |
| 958 | var empty = true; | 965 | var empty = true; |
| 959 | if (std.math.isFinite(f128_val)) { | 966 | if (std.math.isFinite(f128_val)) { |
| 960 | try writer.writeAll("zig_as_"); | 967 | try writer.writeAll("zig_make_"); |
| 961 | try dg.renderTypeForBuiltinFnName(writer, ty); | 968 | try dg.renderTypeForBuiltinFnName(writer, ty); |
| 962 | try writer.writeByte('('); | 969 | try writer.writeByte('('); |
| 963 | switch (bits) { | 970 | switch (bits) { |
| ... | @@ -992,7 +999,7 @@ pub const DeclGen = struct { | ... | @@ -992,7 +999,7 @@ pub const DeclGen = struct { |
| 992 | // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); | 999 | // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); |
| 993 | } | 1000 | } |
| 994 | 1001 | ||
| 995 | try writer.writeAll("zig_as_special_"); | 1002 | try writer.writeAll("zig_make_special_"); |
| 996 | if (location == .StaticInitializer) try writer.writeAll("constant_"); | 1003 | if (location == .StaticInitializer) try writer.writeAll("constant_"); |
| 997 | try dg.renderTypeForBuiltinFnName(writer, ty); | 1004 | try dg.renderTypeForBuiltinFnName(writer, ty); |
| 998 | try writer.writeByte('('); | 1005 | try writer.writeByte('('); |
| ... | @@ -1028,7 +1035,7 @@ pub const DeclGen = struct { | ... | @@ -1028,7 +1035,7 @@ pub const DeclGen = struct { |
| 1028 | return dg.renderValue(writer, ty, slice_val, location); | 1035 | return dg.renderValue(writer, ty, slice_val, location); |
| 1029 | } else { | 1036 | } else { |
| 1030 | try writer.writeAll("(("); | 1037 | try writer.writeAll("(("); |
| 1031 | try dg.renderTypecast(writer, ty); | 1038 | try dg.renderType(writer, ty); |
| 1032 | try writer.writeAll(")NULL)"); | 1039 | try writer.writeAll(")NULL)"); |
| 1033 | }, | 1040 | }, |
| 1034 | .variable => { | 1041 | .variable => { |
| ... | @@ -1038,7 +1045,7 @@ pub const DeclGen = struct { | ... | @@ -1038,7 +1045,7 @@ pub const DeclGen = struct { |
| 1038 | .slice => { | 1045 | .slice => { |
| 1039 | if (!location.isInitializer()) { | 1046 | if (!location.isInitializer()) { |
| 1040 | try writer.writeByte('('); | 1047 | try writer.writeByte('('); |
| 1041 | try dg.renderTypecast(writer, ty); | 1048 | try dg.renderType(writer, ty); |
| 1042 | try writer.writeByte(')'); | 1049 | try writer.writeByte(')'); |
| 1043 | } | 1050 | } |
| 1044 | 1051 | ||
| ... | @@ -1061,7 +1068,7 @@ pub const DeclGen = struct { | ... | @@ -1061,7 +1068,7 @@ pub const DeclGen = struct { |
| 1061 | }, | 1068 | }, |
| 1062 | .int_u64, .one => { | 1069 | .int_u64, .one => { |
| 1063 | try writer.writeAll("(("); | 1070 | try writer.writeAll("(("); |
| 1064 | try dg.renderTypecast(writer, ty); | 1071 | try dg.renderType(writer, ty); |
| 1065 | return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); | 1072 | return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); |
| 1066 | }, | 1073 | }, |
| 1067 | .field_ptr, | 1074 | .field_ptr, |
| ... | @@ -1076,15 +1083,15 @@ pub const DeclGen = struct { | ... | @@ -1076,15 +1083,15 @@ pub const DeclGen = struct { |
| 1076 | .Array, .Vector => { | 1083 | .Array, .Vector => { |
| 1077 | if (location == .FunctionArgument) { | 1084 | if (location == .FunctionArgument) { |
| 1078 | try writer.writeByte('('); | 1085 | try writer.writeByte('('); |
| 1079 | try dg.renderTypecast(writer, ty); | 1086 | try dg.renderType(writer, ty); |
| 1080 | try writer.writeByte(')'); | 1087 | try writer.writeByte(')'); |
| 1081 | } | 1088 | } |
| 1082 | 1089 | ||
| 1083 | // First try specific tag representations for more efficiency. | 1090 | // First try specific tag representations for more efficiency. |
| 1084 | switch (val.tag()) { | 1091 | switch (val.tag()) { |
| 1085 | .undef, .empty_struct_value, .empty_array => { | 1092 | .undef, .empty_struct_value, .empty_array => { |
| 1086 | try writer.writeByte('{'); | ||
| 1087 | const ai = ty.arrayInfo(); | 1093 | const ai = ty.arrayInfo(); |
| 1094 | try writer.writeByte('{'); | ||
| 1088 | if (ai.sentinel) |s| { | 1095 | if (ai.sentinel) |s| { |
| 1089 | try dg.renderValue(writer, ai.elem_type, s, initializer_type); | 1096 | try dg.renderValue(writer, ai.elem_type, s, initializer_type); |
| 1090 | } else { | 1097 | } else { |
| ... | @@ -1092,13 +1099,19 @@ pub const DeclGen = struct { | ... | @@ -1092,13 +1099,19 @@ pub const DeclGen = struct { |
| 1092 | } | 1099 | } |
| 1093 | try writer.writeByte('}'); | 1100 | try writer.writeByte('}'); |
| 1094 | }, | 1101 | }, |
| 1095 | .bytes => { | 1102 | .bytes, .str_lit => |t| { |
| 1096 | try writer.print("{s}", .{fmtStringLiteral(val.castTag(.bytes).?.data)}); | 1103 | const bytes = switch (t) { |
| 1097 | }, | 1104 | .bytes => val.castTag(.bytes).?.data, |
| 1098 | .str_lit => { | 1105 | .str_lit => bytes: { |
| 1099 | const str_lit = val.castTag(.str_lit).?.data; | 1106 | const str_lit = val.castTag(.str_lit).?.data; |
| 1100 | const bytes = dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; | 1107 | break :bytes dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; |
| 1101 | try writer.print("{s}", .{fmtStringLiteral(bytes)}); | 1108 | }, |
| 1109 | else => unreachable, | ||
| 1110 | }; | ||
| 1111 | const sentinel = if (ty.sentinel()) |sentinel| @intCast(u8, sentinel.toUnsignedInt(target)) else null; | ||
| 1112 | try writer.print("{s}", .{ | ||
| 1113 | fmtStringLiteral(bytes[0..@intCast(usize, ty.arrayLen())], sentinel), | ||
| 1114 | }); | ||
| 1102 | }, | 1115 | }, |
| 1103 | else => { | 1116 | else => { |
| 1104 | // Fall back to generic implementation. | 1117 | // Fall back to generic implementation. |
| ... | @@ -1122,7 +1135,7 @@ pub const DeclGen = struct { | ... | @@ -1122,7 +1135,7 @@ pub const DeclGen = struct { |
| 1122 | } | 1135 | } |
| 1123 | if (ai.sentinel) |s| { | 1136 | if (ai.sentinel) |s| { |
| 1124 | const s_u8 = @intCast(u8, s.toUnsignedInt(target)); | 1137 | const s_u8 = @intCast(u8, s.toUnsignedInt(target)); |
| 1125 | try literal.writeChar(s_u8); | 1138 | if (s_u8 != 0) try literal.writeChar(s_u8); |
| 1126 | } | 1139 | } |
| 1127 | try literal.end(); | 1140 | try literal.end(); |
| 1128 | } else { | 1141 | } else { |
| ... | @@ -1179,7 +1192,7 @@ pub const DeclGen = struct { | ... | @@ -1179,7 +1192,7 @@ pub const DeclGen = struct { |
| 1179 | 1192 | ||
| 1180 | if (!location.isInitializer()) { | 1193 | if (!location.isInitializer()) { |
| 1181 | try writer.writeByte('('); | 1194 | try writer.writeByte('('); |
| 1182 | try dg.renderTypecast(writer, ty); | 1195 | try dg.renderType(writer, ty); |
| 1183 | try writer.writeByte(')'); | 1196 | try writer.writeByte(')'); |
| 1184 | } | 1197 | } |
| 1185 | 1198 | ||
| ... | @@ -1213,7 +1226,7 @@ pub const DeclGen = struct { | ... | @@ -1213,7 +1226,7 @@ pub const DeclGen = struct { |
| 1213 | 1226 | ||
| 1214 | if (!location.isInitializer()) { | 1227 | if (!location.isInitializer()) { |
| 1215 | try writer.writeByte('('); | 1228 | try writer.writeByte('('); |
| 1216 | try dg.renderTypecast(writer, ty); | 1229 | try dg.renderType(writer, ty); |
| 1217 | try writer.writeByte(')'); | 1230 | try writer.writeByte(')'); |
| 1218 | } | 1231 | } |
| 1219 | 1232 | ||
| ... | @@ -1277,7 +1290,7 @@ pub const DeclGen = struct { | ... | @@ -1277,7 +1290,7 @@ pub const DeclGen = struct { |
| 1277 | 1290 | ||
| 1278 | if (!location.isInitializer()) { | 1291 | if (!location.isInitializer()) { |
| 1279 | try writer.writeByte('('); | 1292 | try writer.writeByte('('); |
| 1280 | try dg.renderTypecast(writer, ty); | 1293 | try dg.renderType(writer, ty); |
| 1281 | try writer.writeByte(')'); | 1294 | try writer.writeByte(')'); |
| 1282 | } | 1295 | } |
| 1283 | 1296 | ||
| ... | @@ -1292,7 +1305,6 @@ pub const DeclGen = struct { | ... | @@ -1292,7 +1305,6 @@ pub const DeclGen = struct { |
| 1292 | 1305 | ||
| 1293 | empty = false; | 1306 | empty = false; |
| 1294 | } | 1307 | } |
| 1295 | if (empty) try writer.print("{}", .{try dg.fmtIntLiteral(Type.u8, Value.zero)}); | ||
| 1296 | try writer.writeByte('}'); | 1308 | try writer.writeByte('}'); |
| 1297 | }, | 1309 | }, |
| 1298 | .Packed => { | 1310 | .Packed => { |
| ... | @@ -1309,7 +1321,7 @@ pub const DeclGen = struct { | ... | @@ -1309,7 +1321,7 @@ pub const DeclGen = struct { |
| 1309 | const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); | 1321 | const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); |
| 1310 | 1322 | ||
| 1311 | var eff_num_fields: usize = 0; | 1323 | var eff_num_fields: usize = 0; |
| 1312 | for (field_vals, 0..) |_, index| { | 1324 | for (0..field_vals.len) |index| { |
| 1313 | const field_ty = ty.structFieldType(index); | 1325 | const field_ty = ty.structFieldType(index); |
| 1314 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; | 1326 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 1315 | 1327 | ||
| ... | @@ -1365,7 +1377,7 @@ pub const DeclGen = struct { | ... | @@ -1365,7 +1377,7 @@ pub const DeclGen = struct { |
| 1365 | 1377 | ||
| 1366 | if (!empty) try writer.writeAll(" | "); | 1378 | if (!empty) try writer.writeAll(" | "); |
| 1367 | try writer.writeByte('('); | 1379 | try writer.writeByte('('); |
| 1368 | try dg.renderTypecast(writer, ty); | 1380 | try dg.renderType(writer, ty); |
| 1369 | try writer.writeByte(')'); | 1381 | try writer.writeByte(')'); |
| 1370 | 1382 | ||
| 1371 | if (bit_offset_val_pl.data != 0) { | 1383 | if (bit_offset_val_pl.data != 0) { |
| ... | @@ -1388,7 +1400,7 @@ pub const DeclGen = struct { | ... | @@ -1388,7 +1400,7 @@ pub const DeclGen = struct { |
| 1388 | 1400 | ||
| 1389 | if (!location.isInitializer()) { | 1401 | if (!location.isInitializer()) { |
| 1390 | try writer.writeByte('('); | 1402 | try writer.writeByte('('); |
| 1391 | try dg.renderTypecast(writer, ty); | 1403 | try dg.renderType(writer, ty); |
| 1392 | try writer.writeByte(')'); | 1404 | try writer.writeByte(')'); |
| 1393 | } | 1405 | } |
| 1394 | 1406 | ||
| ... | @@ -1399,11 +1411,11 @@ pub const DeclGen = struct { | ... | @@ -1399,11 +1411,11 @@ pub const DeclGen = struct { |
| 1399 | if (field_ty.hasRuntimeBits()) { | 1411 | if (field_ty.hasRuntimeBits()) { |
| 1400 | if (field_ty.isPtrAtRuntime()) { | 1412 | if (field_ty.isPtrAtRuntime()) { |
| 1401 | try writer.writeByte('('); | 1413 | try writer.writeByte('('); |
| 1402 | try dg.renderTypecast(writer, ty); | 1414 | try dg.renderType(writer, ty); |
| 1403 | try writer.writeByte(')'); | 1415 | try writer.writeByte(')'); |
| 1404 | } else if (field_ty.zigTypeTag() == .Float) { | 1416 | } else if (field_ty.zigTypeTag() == .Float) { |
| 1405 | try writer.writeByte('('); | 1417 | try writer.writeByte('('); |
| 1406 | try dg.renderTypecast(writer, ty); | 1418 | try dg.renderType(writer, ty); |
| 1407 | try writer.writeByte(')'); | 1419 | try writer.writeByte(')'); |
| 1408 | } | 1420 | } |
| 1409 | try dg.renderValue(writer, field_ty, union_obj.val, initializer_type); | 1421 | try dg.renderValue(writer, field_ty, union_obj.val, initializer_type); |
| ... | @@ -1413,6 +1425,7 @@ pub const DeclGen = struct { | ... | @@ -1413,6 +1425,7 @@ pub const DeclGen = struct { |
| 1413 | return; | 1425 | return; |
| 1414 | } | 1426 | } |
| 1415 | 1427 | ||
| 1428 | var has_payload_init = false; | ||
| 1416 | try writer.writeByte('{'); | 1429 | try writer.writeByte('{'); |
| 1417 | if (ty.unionTagTypeSafety()) |tag_ty| { | 1430 | if (ty.unionTagTypeSafety()) |tag_ty| { |
| 1418 | const layout = ty.unionGetLayout(target); | 1431 | const layout = ty.unionGetLayout(target); |
| ... | @@ -1421,7 +1434,10 @@ pub const DeclGen = struct { | ... | @@ -1421,7 +1434,10 @@ pub const DeclGen = struct { |
| 1421 | try dg.renderValue(writer, tag_ty, union_obj.tag, initializer_type); | 1434 | try dg.renderValue(writer, tag_ty, union_obj.tag, initializer_type); |
| 1422 | try writer.writeAll(", "); | 1435 | try writer.writeAll(", "); |
| 1423 | } | 1436 | } |
| 1424 | try writer.writeAll(".payload = {"); | 1437 | if (!ty.unionHasAllZeroBitFieldTypes()) { |
| 1438 | try writer.writeAll(".payload = {"); | ||
| 1439 | has_payload_init = true; | ||
| 1440 | } | ||
| 1425 | } | 1441 | } |
| 1426 | 1442 | ||
| 1427 | var it = ty.unionFields().iterator(); | 1443 | var it = ty.unionFields().iterator(); |
| ... | @@ -1433,8 +1449,8 @@ pub const DeclGen = struct { | ... | @@ -1433,8 +1449,8 @@ pub const DeclGen = struct { |
| 1433 | try writer.print(".{ } = ", .{fmtIdent(field.key_ptr.*)}); | 1449 | try writer.print(".{ } = ", .{fmtIdent(field.key_ptr.*)}); |
| 1434 | try dg.renderValue(writer, field.value_ptr.ty, Value.undef, initializer_type); | 1450 | try dg.renderValue(writer, field.value_ptr.ty, Value.undef, initializer_type); |
| 1435 | break; | 1451 | break; |
| 1436 | } else try writer.writeAll(".empty_union = 0"); | 1452 | } |
| 1437 | if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); | 1453 | if (has_payload_init) try writer.writeByte('}'); |
| 1438 | try writer.writeByte('}'); | 1454 | try writer.writeByte('}'); |
| 1439 | }, | 1455 | }, |
| 1440 | 1456 | ||
| ... | @@ -1456,778 +1472,112 @@ pub const DeclGen = struct { | ... | @@ -1456,778 +1472,112 @@ pub const DeclGen = struct { |
| 1456 | } | 1472 | } |
| 1457 | } | 1473 | } |
| 1458 | 1474 | ||
| 1459 | fn renderFunctionSignature(dg: *DeclGen, w: anytype, kind: TypedefKind, export_index: u32) !void { | 1475 | fn renderFunctionSignature( |
| 1460 | const fn_info = dg.decl.ty.fnInfo(); | ||
| 1461 | if (fn_info.cc == .Naked) { | ||
| 1462 | switch (kind) { | ||
| 1463 | .Forward => try w.writeAll("zig_naked_decl "), | ||
| 1464 | .Complete => try w.writeAll("zig_naked "), | ||
| 1465 | } | ||
| 1466 | } | ||
| 1467 | if (dg.decl.val.castTag(.function)) |func_payload| | ||
| 1468 | if (func_payload.data.is_cold) try w.writeAll("zig_cold "); | ||
| 1469 | |||
| 1470 | const target = dg.module.getTarget(); | ||
| 1471 | var ret_buf: LowerFnRetTyBuffer = undefined; | ||
| 1472 | const ret_ty = lowerFnRetTy(fn_info.return_type, &ret_buf, target); | ||
| 1473 | |||
| 1474 | try dg.renderType(w, ret_ty, kind); | ||
| 1475 | try w.writeByte(' '); | ||
| 1476 | |||
| 1477 | if (toCallingConvention(fn_info.cc)) |call_conv| { | ||
| 1478 | try w.print("zig_callconv({s}) ", .{call_conv}); | ||
| 1479 | } | ||
| 1480 | |||
| 1481 | if (fn_info.alignment > 0 and kind == .Complete) try w.print(" zig_align_fn({})", .{fn_info.alignment}); | ||
| 1482 | |||
| 1483 | try dg.renderDeclName(w, dg.decl_index, export_index); | ||
| 1484 | try w.writeByte('('); | ||
| 1485 | |||
| 1486 | var index: usize = 0; | ||
| 1487 | for (fn_info.param_types) |param_type| { | ||
| 1488 | if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1489 | if (index > 0) try w.writeAll(", "); | ||
| 1490 | const name = CValue{ .arg = index }; | ||
| 1491 | try dg.renderTypeAndName(w, param_type, name, .ConstArgument, 0, kind); | ||
| 1492 | index += 1; | ||
| 1493 | } | ||
| 1494 | |||
| 1495 | if (fn_info.is_var_args) { | ||
| 1496 | if (index > 0) try w.writeAll(", "); | ||
| 1497 | try w.writeAll("..."); | ||
| 1498 | } else if (index == 0) { | ||
| 1499 | try dg.renderType(w, Type.void, kind); | ||
| 1500 | } | ||
| 1501 | try w.writeByte(')'); | ||
| 1502 | if (fn_info.alignment > 0 and kind == .Forward) try w.print(" zig_align_fn({})", .{fn_info.alignment}); | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | fn renderPtrToFnTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | ||
| 1506 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1507 | defer buffer.deinit(); | ||
| 1508 | const bw = buffer.writer(); | ||
| 1509 | |||
| 1510 | const fn_info = t.fnInfo(); | ||
| 1511 | |||
| 1512 | const target = dg.module.getTarget(); | ||
| 1513 | var ret_buf: LowerFnRetTyBuffer = undefined; | ||
| 1514 | const ret_ty = lowerFnRetTy(fn_info.return_type, &ret_buf, target); | ||
| 1515 | |||
| 1516 | try bw.writeAll("typedef "); | ||
| 1517 | try dg.renderType(bw, ret_ty, .Forward); | ||
| 1518 | try bw.writeAll(" (*"); | ||
| 1519 | const name_begin = buffer.items.len; | ||
| 1520 | try bw.print("zig_F_{}", .{typeToCIdentifier(t, dg.module)}); | ||
| 1521 | const name_end = buffer.items.len; | ||
| 1522 | try bw.writeAll(")("); | ||
| 1523 | |||
| 1524 | const param_len = fn_info.param_types.len; | ||
| 1525 | |||
| 1526 | var params_written: usize = 0; | ||
| 1527 | var index: usize = 0; | ||
| 1528 | while (index < param_len) : (index += 1) { | ||
| 1529 | const param_ty = fn_info.param_types[index]; | ||
| 1530 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1531 | if (params_written > 0) { | ||
| 1532 | try bw.writeAll(", "); | ||
| 1533 | } | ||
| 1534 | try dg.renderTypeAndName(bw, param_ty, .{ .bytes = "" }, .Mut, 0, .Forward); | ||
| 1535 | params_written += 1; | ||
| 1536 | } | ||
| 1537 | |||
| 1538 | if (fn_info.is_var_args) { | ||
| 1539 | if (params_written != 0) try bw.writeAll(", "); | ||
| 1540 | try bw.writeAll("..."); | ||
| 1541 | } else if (params_written == 0) { | ||
| 1542 | try dg.renderType(bw, Type.void, .Forward); | ||
| 1543 | } | ||
| 1544 | try bw.writeAll(");\n"); | ||
| 1545 | |||
| 1546 | const rendered = try buffer.toOwnedSlice(); | ||
| 1547 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 1548 | const name = rendered[name_begin..name_end]; | ||
| 1549 | |||
| 1550 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 1551 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 1552 | try t.copy(dg.typedefs_arena), | ||
| 1553 | .{ .name = name, .rendered = rendered }, | ||
| 1554 | ); | ||
| 1555 | |||
| 1556 | return name; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | fn renderSliceTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | ||
| 1560 | std.debug.assert(t.sentinel() == null); // expected canonical type | ||
| 1561 | |||
| 1562 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1563 | defer buffer.deinit(); | ||
| 1564 | const bw = buffer.writer(); | ||
| 1565 | |||
| 1566 | var ptr_ty_buf: Type.SlicePtrFieldTypeBuffer = undefined; | ||
| 1567 | const ptr_ty = t.slicePtrFieldType(&ptr_ty_buf); | ||
| 1568 | const ptr_name = CValue{ .identifier = "ptr" }; | ||
| 1569 | const len_ty = Type.usize; | ||
| 1570 | const len_name = CValue{ .identifier = "len" }; | ||
| 1571 | |||
| 1572 | try bw.writeAll("typedef struct {\n "); | ||
| 1573 | try dg.renderTypeAndName(bw, ptr_ty, ptr_name, .Mut, 0, .Complete); | ||
| 1574 | try bw.writeAll(";\n "); | ||
| 1575 | try dg.renderTypeAndName(bw, len_ty, len_name, .Mut, 0, .Complete); | ||
| 1576 | |||
| 1577 | try bw.writeAll(";\n} "); | ||
| 1578 | const name_begin = buffer.items.len; | ||
| 1579 | try bw.print("zig_{c}_{}", .{ | ||
| 1580 | @as(u8, if (t.isConstPtr()) 'L' else 'M'), | ||
| 1581 | typeToCIdentifier(t.childType(), dg.module), | ||
| 1582 | }); | ||
| 1583 | const name_end = buffer.items.len; | ||
| 1584 | try bw.writeAll(";\n"); | ||
| 1585 | |||
| 1586 | const rendered = try buffer.toOwnedSlice(); | ||
| 1587 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 1588 | const name = rendered[name_begin..name_end]; | ||
| 1589 | |||
| 1590 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 1591 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 1592 | try t.copy(dg.typedefs_arena), | ||
| 1593 | .{ .name = name, .rendered = rendered }, | ||
| 1594 | ); | ||
| 1595 | |||
| 1596 | return name; | ||
| 1597 | } | ||
| 1598 | |||
| 1599 | fn renderFwdTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | ||
| 1600 | // The forward declaration for T is stored with a key of *const T. | ||
| 1601 | const child_ty = t.childType(); | ||
| 1602 | |||
| 1603 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1604 | defer buffer.deinit(); | ||
| 1605 | const bw = buffer.writer(); | ||
| 1606 | |||
| 1607 | const tag = switch (child_ty.zigTypeTag()) { | ||
| 1608 | .Struct, .ErrorUnion, .Optional => "struct", | ||
| 1609 | .Union => if (child_ty.unionTagTypeSafety()) |_| "struct" else "union", | ||
| 1610 | else => unreachable, | ||
| 1611 | }; | ||
| 1612 | try bw.writeAll("typedef "); | ||
| 1613 | try bw.writeAll(tag); | ||
| 1614 | const name_begin = buffer.items.len + " ".len; | ||
| 1615 | try bw.writeAll(" zig_"); | ||
| 1616 | switch (child_ty.zigTypeTag()) { | ||
| 1617 | .Struct, .Union => { | ||
| 1618 | var fqn_buf = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1619 | defer fqn_buf.deinit(); | ||
| 1620 | |||
| 1621 | const owner_decl_index = child_ty.getOwnerDecl(); | ||
| 1622 | const owner_decl = dg.module.declPtr(owner_decl_index); | ||
| 1623 | try owner_decl.renderFullyQualifiedName(dg.module, fqn_buf.writer()); | ||
| 1624 | |||
| 1625 | try bw.print("S_{}__{d}", .{ fmtIdent(fqn_buf.items), @enumToInt(owner_decl_index) }); | ||
| 1626 | }, | ||
| 1627 | .ErrorUnion => { | ||
| 1628 | try bw.print("E_{}", .{typeToCIdentifier(child_ty.errorUnionPayload(), dg.module)}); | ||
| 1629 | }, | ||
| 1630 | .Optional => { | ||
| 1631 | var opt_buf: Type.Payload.ElemType = undefined; | ||
| 1632 | try bw.print("Q_{}", .{typeToCIdentifier(child_ty.optionalChild(&opt_buf), dg.module)}); | ||
| 1633 | }, | ||
| 1634 | else => unreachable, | ||
| 1635 | } | ||
| 1636 | const name_end = buffer.items.len; | ||
| 1637 | try buffer.ensureUnusedCapacity(" ".len + (name_end - name_begin) + ";\n".len); | ||
| 1638 | buffer.appendAssumeCapacity(' '); | ||
| 1639 | buffer.appendSliceAssumeCapacity(buffer.items[name_begin..name_end]); | ||
| 1640 | buffer.appendSliceAssumeCapacity(";\n"); | ||
| 1641 | |||
| 1642 | const rendered = try buffer.toOwnedSlice(); | ||
| 1643 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 1644 | const name = rendered[name_begin..name_end]; | ||
| 1645 | |||
| 1646 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 1647 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 1648 | try t.copy(dg.typedefs_arena), | ||
| 1649 | .{ .name = name, .rendered = rendered }, | ||
| 1650 | ); | ||
| 1651 | |||
| 1652 | return name; | ||
| 1653 | } | ||
| 1654 | |||
| 1655 | fn renderStructTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | ||
| 1656 | var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, .data = t }; | ||
| 1657 | const ptr_ty = Type.initPayload(&ptr_pl.base); | ||
| 1658 | const name = dg.getTypedefName(ptr_ty) orelse | ||
| 1659 | try dg.renderFwdTypedef(ptr_ty); | ||
| 1660 | |||
| 1661 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1662 | defer buffer.deinit(); | ||
| 1663 | |||
| 1664 | try buffer.appendSlice("struct "); | ||
| 1665 | |||
| 1666 | var needs_pack_attr = false; | ||
| 1667 | { | ||
| 1668 | var it = t.structFields().iterator(); | ||
| 1669 | while (it.next()) |field| { | ||
| 1670 | const field_ty = field.value_ptr.ty; | ||
| 1671 | if (!field_ty.hasRuntimeBits()) continue; | ||
| 1672 | const alignment = field.value_ptr.abi_align; | ||
| 1673 | if (alignment != 0 and alignment < field_ty.abiAlignment(dg.module.getTarget())) { | ||
| 1674 | needs_pack_attr = true; | ||
| 1675 | try buffer.appendSlice("zig_packed("); | ||
| 1676 | break; | ||
| 1677 | } | ||
| 1678 | } | ||
| 1679 | } | ||
| 1680 | |||
| 1681 | try buffer.appendSlice(name); | ||
| 1682 | try buffer.appendSlice(" {\n"); | ||
| 1683 | { | ||
| 1684 | var it = t.structFields().iterator(); | ||
| 1685 | var empty = true; | ||
| 1686 | while (it.next()) |field| { | ||
| 1687 | const field_ty = field.value_ptr.ty; | ||
| 1688 | if (!field_ty.hasRuntimeBits()) continue; | ||
| 1689 | |||
| 1690 | const alignment = field.value_ptr.alignment(dg.module.getTarget(), t.containerLayout()); | ||
| 1691 | const field_name = CValue{ .identifier = field.key_ptr.* }; | ||
| 1692 | try buffer.append(' '); | ||
| 1693 | try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete); | ||
| 1694 | try buffer.appendSlice(";\n"); | ||
| 1695 | |||
| 1696 | empty = false; | ||
| 1697 | } | ||
| 1698 | if (empty) try buffer.appendSlice(" char empty_struct;\n"); | ||
| 1699 | } | ||
| 1700 | if (needs_pack_attr) try buffer.appendSlice("});\n") else try buffer.appendSlice("};\n"); | ||
| 1701 | |||
| 1702 | const rendered = try buffer.toOwnedSlice(); | ||
| 1703 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 1704 | |||
| 1705 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 1706 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 1707 | try t.copy(dg.typedefs_arena), | ||
| 1708 | .{ .name = name, .rendered = rendered }, | ||
| 1709 | ); | ||
| 1710 | |||
| 1711 | return name; | ||
| 1712 | } | ||
| 1713 | |||
| 1714 | fn renderTupleTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | ||
| 1715 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1716 | defer buffer.deinit(); | ||
| 1717 | |||
| 1718 | try buffer.appendSlice("typedef struct {\n"); | ||
| 1719 | { | ||
| 1720 | const fields = t.tupleFields(); | ||
| 1721 | var field_id: usize = 0; | ||
| 1722 | for (fields.types, 0..) |field_ty, i| { | ||
| 1723 | if (!field_ty.hasRuntimeBits() or fields.values[i].tag() != .unreachable_value) continue; | ||
| 1724 | |||
| 1725 | try buffer.append(' '); | ||
| 1726 | try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .field = field_id }, .Mut, 0, .Complete); | ||
| 1727 | try buffer.appendSlice(";\n"); | ||
| 1728 | |||
| 1729 | field_id += 1; | ||
| 1730 | } | ||
| 1731 | if (field_id == 0) try buffer.appendSlice(" char empty_tuple;\n"); | ||
| 1732 | } | ||
| 1733 | const name_begin = buffer.items.len + "} ".len; | ||
| 1734 | try buffer.writer().print("}} zig_T_{}_{d};\n", .{ typeToCIdentifier(t, dg.module), @truncate(u16, t.hash(dg.module)) }); | ||
| 1735 | const name_end = buffer.items.len - ";\n".len; | ||
| 1736 | |||
| 1737 | const rendered = try buffer.toOwnedSlice(); | ||
| 1738 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 1739 | const name = rendered[name_begin..name_end]; | ||
| 1740 | |||
| 1741 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 1742 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 1743 | try t.copy(dg.typedefs_arena), | ||
| 1744 | .{ .name = name, .rendered = rendered }, | ||
| 1745 | ); | ||
| 1746 | |||
| 1747 | return name; | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | fn renderUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | ||
| 1751 | var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, .data = t }; | ||
| 1752 | const ptr_ty = Type.initPayload(&ptr_pl.base); | ||
| 1753 | const name = dg.getTypedefName(ptr_ty) orelse | ||
| 1754 | try dg.renderFwdTypedef(ptr_ty); | ||
| 1755 | |||
| 1756 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1757 | defer buffer.deinit(); | ||
| 1758 | |||
| 1759 | try buffer.appendSlice(if (t.unionTagTypeSafety()) |_| "struct " else "union "); | ||
| 1760 | try buffer.appendSlice(name); | ||
| 1761 | try buffer.appendSlice(" {\n"); | ||
| 1762 | |||
| 1763 | const indent = if (t.unionTagTypeSafety()) |tag_ty| indent: { | ||
| 1764 | const target = dg.module.getTarget(); | ||
| 1765 | const layout = t.unionGetLayout(target); | ||
| 1766 | if (layout.tag_size != 0) { | ||
| 1767 | try buffer.append(' '); | ||
| 1768 | try dg.renderTypeAndName(buffer.writer(), tag_ty, .{ .identifier = "tag" }, .Mut, 0, .Complete); | ||
| 1769 | try buffer.appendSlice(";\n"); | ||
| 1770 | } | ||
| 1771 | try buffer.appendSlice(" union {\n"); | ||
| 1772 | break :indent " "; | ||
| 1773 | } else " "; | ||
| 1774 | |||
| 1775 | { | ||
| 1776 | var it = t.unionFields().iterator(); | ||
| 1777 | var empty = true; | ||
| 1778 | while (it.next()) |field| { | ||
| 1779 | const field_ty = field.value_ptr.ty; | ||
| 1780 | if (!field_ty.hasRuntimeBits()) continue; | ||
| 1781 | |||
| 1782 | const alignment = field.value_ptr.abi_align; | ||
| 1783 | const field_name = CValue{ .identifier = field.key_ptr.* }; | ||
| 1784 | try buffer.appendSlice(indent); | ||
| 1785 | try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete); | ||
| 1786 | try buffer.appendSlice(";\n"); | ||
| 1787 | |||
| 1788 | empty = false; | ||
| 1789 | } | ||
| 1790 | if (empty) { | ||
| 1791 | try buffer.appendSlice(indent); | ||
| 1792 | try buffer.appendSlice("char empty_union;\n"); | ||
| 1793 | } | ||
| 1794 | } | ||
| 1795 | |||
| 1796 | if (t.unionTagTypeSafety()) |_| try buffer.appendSlice(" } payload;\n"); | ||
| 1797 | try buffer.appendSlice("};\n"); | ||
| 1798 | |||
| 1799 | const rendered = try buffer.toOwnedSlice(); | ||
| 1800 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 1801 | |||
| 1802 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 1803 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 1804 | try t.copy(dg.typedefs_arena), | ||
| 1805 | .{ .name = name, .rendered = rendered }, | ||
| 1806 | ); | ||
| 1807 | |||
| 1808 | return name; | ||
| 1809 | } | ||
| 1810 | |||
| 1811 | fn renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | ||
| 1812 | assert(t.errorUnionSet().tag() == .anyerror); | ||
| 1813 | |||
| 1814 | var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, .data = t }; | ||
| 1815 | const ptr_ty = Type.initPayload(&ptr_pl.base); | ||
| 1816 | const name = dg.getTypedefName(ptr_ty) orelse | ||
| 1817 | try dg.renderFwdTypedef(ptr_ty); | ||
| 1818 | |||
| 1819 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1820 | defer buffer.deinit(); | ||
| 1821 | const bw = buffer.writer(); | ||
| 1822 | |||
| 1823 | const payload_ty = t.errorUnionPayload(); | ||
| 1824 | const payload_name = CValue{ .identifier = "payload" }; | ||
| 1825 | const error_ty = t.errorUnionSet(); | ||
| 1826 | const error_name = CValue{ .identifier = "error" }; | ||
| 1827 | |||
| 1828 | const target = dg.module.getTarget(); | ||
| 1829 | const payload_align = payload_ty.abiAlignment(target); | ||
| 1830 | const error_align = error_ty.abiAlignment(target); | ||
| 1831 | try bw.writeAll("struct "); | ||
| 1832 | try bw.writeAll(name); | ||
| 1833 | try bw.writeAll(" {\n "); | ||
| 1834 | if (error_align > payload_align) { | ||
| 1835 | try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete); | ||
| 1836 | try bw.writeAll(";\n "); | ||
| 1837 | try dg.renderTypeAndName(bw, error_ty, error_name, .Mut, 0, .Complete); | ||
| 1838 | } else { | ||
| 1839 | try dg.renderTypeAndName(bw, error_ty, error_name, .Mut, 0, .Complete); | ||
| 1840 | try bw.writeAll(";\n "); | ||
| 1841 | try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete); | ||
| 1842 | } | ||
| 1843 | try bw.writeAll(";\n};\n"); | ||
| 1844 | |||
| 1845 | const rendered = try buffer.toOwnedSlice(); | ||
| 1846 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 1847 | |||
| 1848 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 1849 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 1850 | try t.copy(dg.typedefs_arena), | ||
| 1851 | .{ .name = name, .rendered = rendered }, | ||
| 1852 | ); | ||
| 1853 | |||
| 1854 | return name; | ||
| 1855 | } | ||
| 1856 | |||
| 1857 | fn renderArrayTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | ||
| 1858 | const info = t.arrayInfo(); | ||
| 1859 | std.debug.assert(info.sentinel == null); // expected canonical type | ||
| 1860 | |||
| 1861 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1862 | defer buffer.deinit(); | ||
| 1863 | const bw = buffer.writer(); | ||
| 1864 | |||
| 1865 | try bw.writeAll("typedef "); | ||
| 1866 | try dg.renderType(bw, info.elem_type, .Complete); | ||
| 1867 | |||
| 1868 | const name_begin = buffer.items.len + " ".len; | ||
| 1869 | try bw.print(" zig_A_{}_{d}", .{ typeToCIdentifier(info.elem_type, dg.module), info.len }); | ||
| 1870 | const name_end = buffer.items.len; | ||
| 1871 | |||
| 1872 | const c_len = if (info.len > 0) info.len else 1; | ||
| 1873 | var c_len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = c_len }; | ||
| 1874 | const c_len_val = Value.initPayload(&c_len_pl.base); | ||
| 1875 | try bw.print("[{}];\n", .{try dg.fmtIntLiteral(Type.usize, c_len_val)}); | ||
| 1876 | |||
| 1877 | const rendered = try buffer.toOwnedSlice(); | ||
| 1878 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 1879 | const name = rendered[name_begin..name_end]; | ||
| 1880 | |||
| 1881 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 1882 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 1883 | try t.copy(dg.typedefs_arena), | ||
| 1884 | .{ .name = name, .rendered = rendered }, | ||
| 1885 | ); | ||
| 1886 | |||
| 1887 | return name; | ||
| 1888 | } | ||
| 1889 | |||
| 1890 | fn renderOptionalTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | ||
| 1891 | var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, .data = t }; | ||
| 1892 | const ptr_ty = Type.initPayload(&ptr_pl.base); | ||
| 1893 | const name = dg.getTypedefName(ptr_ty) orelse | ||
| 1894 | try dg.renderFwdTypedef(ptr_ty); | ||
| 1895 | |||
| 1896 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1897 | defer buffer.deinit(); | ||
| 1898 | const bw = buffer.writer(); | ||
| 1899 | |||
| 1900 | var opt_buf: Type.Payload.ElemType = undefined; | ||
| 1901 | const child_ty = t.optionalChild(&opt_buf); | ||
| 1902 | |||
| 1903 | try bw.writeAll("struct "); | ||
| 1904 | try bw.writeAll(name); | ||
| 1905 | try bw.writeAll(" {\n"); | ||
| 1906 | try dg.renderTypeAndName(bw, child_ty, .{ .identifier = "payload" }, .Mut, 0, .Complete); | ||
| 1907 | try bw.writeAll(";\n "); | ||
| 1908 | try dg.renderTypeAndName(bw, Type.bool, .{ .identifier = "is_null" }, .Mut, 0, .Complete); | ||
| 1909 | try bw.writeAll(";\n};\n"); | ||
| 1910 | |||
| 1911 | const rendered = try buffer.toOwnedSlice(); | ||
| 1912 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 1913 | |||
| 1914 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 1915 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 1916 | try t.copy(dg.typedefs_arena), | ||
| 1917 | .{ .name = name, .rendered = rendered }, | ||
| 1918 | ); | ||
| 1919 | |||
| 1920 | return name; | ||
| 1921 | } | ||
| 1922 | |||
| 1923 | fn renderOpaqueTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | ||
| 1924 | const opaque_ty = t.cast(Type.Payload.Opaque).?.data; | ||
| 1925 | const unqualified_name = dg.module.declPtr(opaque_ty.owner_decl).name; | ||
| 1926 | const fqn = try opaque_ty.getFullyQualifiedName(dg.module); | ||
| 1927 | defer dg.typedefs.allocator.free(fqn); | ||
| 1928 | |||
| 1929 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 1930 | defer buffer.deinit(); | ||
| 1931 | |||
| 1932 | try buffer.writer().print("typedef struct { } ", .{fmtIdent(std.mem.span(unqualified_name))}); | ||
| 1933 | |||
| 1934 | const name_begin = buffer.items.len; | ||
| 1935 | try buffer.writer().print("zig_O_{}", .{fmtIdent(fqn)}); | ||
| 1936 | const name_end = buffer.items.len; | ||
| 1937 | try buffer.appendSlice(";\n"); | ||
| 1938 | |||
| 1939 | const rendered = try buffer.toOwnedSlice(); | ||
| 1940 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 1941 | const name = rendered[name_begin..name_end]; | ||
| 1942 | |||
| 1943 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 1944 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 1945 | try t.copy(dg.typedefs_arena), | ||
| 1946 | .{ .name = name, .rendered = rendered }, | ||
| 1947 | ); | ||
| 1948 | |||
| 1949 | return name; | ||
| 1950 | } | ||
| 1951 | |||
| 1952 | /// Renders a type as a single identifier, generating intermediate typedefs | ||
| 1953 | /// if necessary. | ||
| 1954 | /// | ||
| 1955 | /// This is guaranteed to be valid in both typedefs and declarations/definitions. | ||
| 1956 | /// | ||
| 1957 | /// There are three type formats in total that we support rendering: | ||
| 1958 | /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | | ||
| 1959 | /// |---------------------|-----------------|---------------------| | ||
| 1960 | /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | | ||
| 1961 | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | | ||
| 1962 | /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | | ||
| 1963 | /// | ||
| 1964 | fn renderType( | ||
| 1965 | dg: *DeclGen, | 1476 | dg: *DeclGen, |
| 1966 | w: anytype, | 1477 | w: anytype, |
| 1967 | t: Type, | 1478 | fn_decl_index: Decl.Index, |
| 1968 | kind: TypedefKind, | 1479 | kind: CType.Kind, |
| 1969 | ) error{ OutOfMemory, AnalysisFail }!void { | 1480 | name: union(enum) { |
| 1970 | const target = dg.module.getTarget(); | 1481 | export_index: u32, |
| 1971 | 1482 | string: []const u8, | |
| 1972 | switch (t.zigTypeTag()) { | 1483 | }, |
| 1973 | .Void => try w.writeAll("void"), | 1484 | ) !void { |
| 1974 | .Bool => try w.writeAll("bool"), | 1485 | const store = &dg.ctypes.set; |
| 1975 | .NoReturn, .Float => { | 1486 | const module = dg.module; |
| 1976 | try w.writeAll("zig_"); | ||
| 1977 | try t.print(w, dg.module); | ||
| 1978 | }, | ||
| 1979 | .Int => { | ||
| 1980 | if (t.isNamedInt()) { | ||
| 1981 | try w.writeAll("zig_"); | ||
| 1982 | try t.print(w, dg.module); | ||
| 1983 | } else { | ||
| 1984 | return renderTypeUnnamed(dg, w, t, kind); | ||
| 1985 | } | ||
| 1986 | }, | ||
| 1987 | .ErrorSet => { | ||
| 1988 | return renderTypeUnnamed(dg, w, t, kind); | ||
| 1989 | }, | ||
| 1990 | .Pointer => { | ||
| 1991 | const ptr_info = t.ptrInfo().data; | ||
| 1992 | if (ptr_info.size == .Slice) { | ||
| 1993 | var slice_pl = Type.Payload.ElemType{ | ||
| 1994 | .base = .{ .tag = if (t.ptrIsMutable()) .mut_slice else .const_slice }, | ||
| 1995 | .data = ptr_info.pointee_type, | ||
| 1996 | }; | ||
| 1997 | const slice_ty = Type.initPayload(&slice_pl.base); | ||
| 1998 | |||
| 1999 | const name = dg.getTypedefName(slice_ty) orelse | ||
| 2000 | try dg.renderSliceTypedef(slice_ty); | ||
| 2001 | |||
| 2002 | return w.writeAll(name); | ||
| 2003 | } | ||
| 2004 | |||
| 2005 | if (ptr_info.pointee_type.zigTypeTag() == .Fn) { | ||
| 2006 | const name = dg.getTypedefName(ptr_info.pointee_type) orelse | ||
| 2007 | try dg.renderPtrToFnTypedef(ptr_info.pointee_type); | ||
| 2008 | |||
| 2009 | return w.writeAll(name); | ||
| 2010 | } | ||
| 2011 | |||
| 2012 | if (ptr_info.host_size != 0) { | ||
| 2013 | var host_pl = Type.Payload.Bits{ | ||
| 2014 | .base = .{ .tag = .int_unsigned }, | ||
| 2015 | .data = ptr_info.host_size * 8, | ||
| 2016 | }; | ||
| 2017 | const host_ty = Type.initPayload(&host_pl.base); | ||
| 2018 | |||
| 2019 | try dg.renderType(w, host_ty, .Forward); | ||
| 2020 | } else if (t.isCPtr() and ptr_info.pointee_type.eql(Type.u8, dg.module) and | ||
| 2021 | (dg.decl.val.tag() == .extern_fn or | ||
| 2022 | std.mem.eql(u8, std.mem.span(dg.decl.name), "main"))) | ||
| 2023 | { | ||
| 2024 | // This is a hack, since the c compiler expects a lot of external | ||
| 2025 | // library functions to have char pointers in their signatures, but | ||
| 2026 | // u8 and i8 produce unsigned char and signed char respectively, | ||
| 2027 | // which in C are (not very usefully) different than char. | ||
| 2028 | try w.writeAll("char"); | ||
| 2029 | } else try dg.renderType(w, switch (ptr_info.pointee_type.tag()) { | ||
| 2030 | .anyopaque => Type.void, | ||
| 2031 | else => ptr_info.pointee_type, | ||
| 2032 | }, .Forward); | ||
| 2033 | if (t.isConstPtr()) try w.writeAll(" const"); | ||
| 2034 | if (t.isVolatilePtr()) try w.writeAll(" volatile"); | ||
| 2035 | return w.writeAll(" *"); | ||
| 2036 | }, | ||
| 2037 | .Array, .Vector => { | ||
| 2038 | var array_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{ | ||
| 2039 | .len = t.arrayLenIncludingSentinel(), | ||
| 2040 | .elem_type = t.childType(), | ||
| 2041 | } }; | ||
| 2042 | const array_ty = Type.initPayload(&array_pl.base); | ||
| 2043 | |||
| 2044 | const name = dg.getTypedefName(array_ty) orelse | ||
| 2045 | try dg.renderArrayTypedef(array_ty); | ||
| 2046 | |||
| 2047 | return w.writeAll(name); | ||
| 2048 | }, | ||
| 2049 | .Optional => { | ||
| 2050 | var opt_buf: Type.Payload.ElemType = undefined; | ||
| 2051 | const child_ty = t.optionalChild(&opt_buf); | ||
| 2052 | |||
| 2053 | if (!child_ty.hasRuntimeBitsIgnoreComptime()) | ||
| 2054 | return dg.renderType(w, Type.bool, kind); | ||
| 2055 | |||
| 2056 | if (t.optionalReprIsPayload()) | ||
| 2057 | return dg.renderType(w, child_ty, kind); | ||
| 2058 | |||
| 2059 | switch (kind) { | ||
| 2060 | .Complete => { | ||
| 2061 | const name = dg.getTypedefName(t) orelse | ||
| 2062 | try dg.renderOptionalTypedef(t); | ||
| 2063 | |||
| 2064 | try w.writeAll(name); | ||
| 2065 | }, | ||
| 2066 | .Forward => { | ||
| 2067 | var ptr_pl = Type.Payload.ElemType{ | ||
| 2068 | .base = .{ .tag = .single_const_pointer }, | ||
| 2069 | .data = t, | ||
| 2070 | }; | ||
| 2071 | const ptr_ty = Type.initPayload(&ptr_pl.base); | ||
| 2072 | |||
| 2073 | const name = dg.getTypedefName(ptr_ty) orelse | ||
| 2074 | try dg.renderFwdTypedef(ptr_ty); | ||
| 2075 | |||
| 2076 | try w.writeAll(name); | ||
| 2077 | }, | ||
| 2078 | } | ||
| 2079 | }, | ||
| 2080 | .ErrorUnion => { | ||
| 2081 | const payload_ty = t.errorUnionPayload(); | ||
| 2082 | |||
| 2083 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) | ||
| 2084 | return dg.renderType(w, Type.anyerror, kind); | ||
| 2085 | |||
| 2086 | var error_union_pl = Type.Payload.ErrorUnion{ | ||
| 2087 | .data = .{ .error_set = Type.anyerror, .payload = payload_ty }, | ||
| 2088 | }; | ||
| 2089 | const error_union_ty = Type.initPayload(&error_union_pl.base); | ||
| 2090 | |||
| 2091 | switch (kind) { | ||
| 2092 | .Complete => { | ||
| 2093 | const name = dg.getTypedefName(error_union_ty) orelse | ||
| 2094 | try dg.renderErrorUnionTypedef(error_union_ty); | ||
| 2095 | |||
| 2096 | try w.writeAll(name); | ||
| 2097 | }, | ||
| 2098 | .Forward => { | ||
| 2099 | var ptr_pl = Type.Payload.ElemType{ | ||
| 2100 | .base = .{ .tag = .single_const_pointer }, | ||
| 2101 | .data = error_union_ty, | ||
| 2102 | }; | ||
| 2103 | const ptr_ty = Type.initPayload(&ptr_pl.base); | ||
| 2104 | |||
| 2105 | const name = dg.getTypedefName(ptr_ty) orelse | ||
| 2106 | try dg.renderFwdTypedef(ptr_ty); | ||
| 2107 | |||
| 2108 | try w.writeAll(name); | ||
| 2109 | }, | ||
| 2110 | } | ||
| 2111 | }, | ||
| 2112 | .Struct, .Union => |tag| if (t.containerLayout() == .Packed) { | ||
| 2113 | if (t.castTag(.@"struct")) |struct_obj| { | ||
| 2114 | try dg.renderType(w, struct_obj.data.backing_int_ty, kind); | ||
| 2115 | } else { | ||
| 2116 | var buf: Type.Payload.Bits = .{ | ||
| 2117 | .base = .{ .tag = .int_unsigned }, | ||
| 2118 | .data = @intCast(u16, t.bitSize(target)), | ||
| 2119 | }; | ||
| 2120 | try dg.renderType(w, Type.initPayload(&buf.base), kind); | ||
| 2121 | } | ||
| 2122 | } else if (t.isSimpleTupleOrAnonStruct()) { | ||
| 2123 | const ExpectedContents = struct { types: [8]Type, values: [8]Value }; | ||
| 2124 | var stack align(@alignOf(ExpectedContents)) = | ||
| 2125 | std.heap.stackFallback(@sizeOf(ExpectedContents), dg.gpa); | ||
| 2126 | const allocator = stack.get(); | ||
| 2127 | |||
| 2128 | var tuple_storage = std.MultiArrayList(struct { type: Type, value: Value }){}; | ||
| 2129 | defer tuple_storage.deinit(allocator); | ||
| 2130 | try tuple_storage.ensureTotalCapacity(allocator, t.structFieldCount()); | ||
| 2131 | |||
| 2132 | const fields = t.tupleFields(); | ||
| 2133 | for (fields.values, 0..) |value, index| | ||
| 2134 | if (value.tag() == .unreachable_value) | ||
| 2135 | tuple_storage.appendAssumeCapacity(.{ | ||
| 2136 | .type = fields.types[index], | ||
| 2137 | .value = value, | ||
| 2138 | }); | ||
| 2139 | 1487 | ||
| 2140 | const tuple_slice = tuple_storage.slice(); | 1488 | const fn_decl = module.declPtr(fn_decl_index); |
| 2141 | var tuple_pl = Type.Payload.Tuple{ .data = .{ | 1489 | const fn_cty_idx = try dg.typeToIndex(fn_decl.ty, kind); |
| 2142 | .types = tuple_slice.items(.type), | ||
| 2143 | .values = tuple_slice.items(.value), | ||
| 2144 | } }; | ||
| 2145 | const tuple_ty = Type.initPayload(&tuple_pl.base); | ||
| 2146 | |||
| 2147 | const name = dg.getTypedefName(tuple_ty) orelse | ||
| 2148 | try dg.renderTupleTypedef(tuple_ty); | ||
| 2149 | |||
| 2150 | try w.writeAll(name); | ||
| 2151 | } else switch (kind) { | ||
| 2152 | .Complete => { | ||
| 2153 | const name = dg.getTypedefName(t) orelse switch (tag) { | ||
| 2154 | .Struct => try dg.renderStructTypedef(t), | ||
| 2155 | .Union => try dg.renderUnionTypedef(t), | ||
| 2156 | else => unreachable, | ||
| 2157 | }; | ||
| 2158 | 1490 | ||
| 2159 | try w.writeAll(name); | 1491 | const fn_info = fn_decl.ty.fnInfo(); |
| 2160 | }, | 1492 | if (fn_info.cc == .Naked) { |
| 2161 | .Forward => { | 1493 | switch (kind) { |
| 2162 | var ptr_pl = Type.Payload.ElemType{ | 1494 | .forward => try w.writeAll("zig_naked_decl "), |
| 2163 | .base = .{ .tag = .single_const_pointer }, | 1495 | .complete => try w.writeAll("zig_naked "), |
| 2164 | .data = t, | 1496 | else => unreachable, |
| 2165 | }; | 1497 | } |
| 2166 | const ptr_ty = Type.initPayload(&ptr_pl.base); | 1498 | } |
| 1499 | if (fn_decl.val.castTag(.function)) |func_payload| | ||
| 1500 | if (func_payload.data.is_cold) try w.writeAll("zig_cold "); | ||
| 1501 | if (fn_info.return_type.tag() == .noreturn) try w.writeAll("zig_noreturn "); | ||
| 1502 | |||
| 1503 | const trailing = try renderTypePrefix( | ||
| 1504 | dg.decl_index, | ||
| 1505 | store.*, | ||
| 1506 | module, | ||
| 1507 | w, | ||
| 1508 | fn_cty_idx, | ||
| 1509 | .suffix, | ||
| 1510 | .{}, | ||
| 1511 | ); | ||
| 1512 | try w.print("{}", .{trailing}); | ||
| 2167 | 1513 | ||
| 2168 | const name = dg.getTypedefName(ptr_ty) orelse | 1514 | if (toCallingConvention(fn_info.cc)) |call_conv| { |
| 2169 | try dg.renderFwdTypedef(ptr_ty); | 1515 | try w.print("zig_callconv({s}) ", .{call_conv}); |
| 1516 | } | ||
| 2170 | 1517 | ||
| 2171 | try w.writeAll(name); | 1518 | switch (kind) { |
| 2172 | }, | 1519 | .forward => {}, |
| 2173 | }, | 1520 | .complete => if (fn_info.alignment > 0) |
| 2174 | .Enum => { | 1521 | try w.print(" zig_align_fn({})", .{fn_info.alignment}), |
| 2175 | // For enums, we simply use the integer tag type. | 1522 | else => unreachable, |
| 2176 | var int_tag_buf: Type.Payload.Bits = undefined; | 1523 | } |
| 2177 | const int_tag_ty = t.intTagType(&int_tag_buf); | ||
| 2178 | 1524 | ||
| 2179 | try dg.renderType(w, int_tag_ty, kind); | 1525 | switch (name) { |
| 2180 | }, | 1526 | .export_index => |export_index| try dg.renderDeclName(w, fn_decl_index, export_index), |
| 2181 | .Opaque => switch (t.tag()) { | 1527 | .string => |string| try w.writeAll(string), |
| 2182 | .@"opaque" => { | 1528 | } |
| 2183 | const name = dg.getTypedefName(t) orelse | ||
| 2184 | try dg.renderOpaqueTypedef(t); | ||
| 2185 | 1529 | ||
| 2186 | try w.writeAll(name); | 1530 | try renderTypeSuffix( |
| 2187 | }, | 1531 | dg.decl_index, |
| 1532 | store.*, | ||
| 1533 | module, | ||
| 1534 | w, | ||
| 1535 | fn_cty_idx, | ||
| 1536 | .suffix, | ||
| 1537 | CQualifiers.init(.{ .@"const" = switch (kind) { | ||
| 1538 | .forward => false, | ||
| 1539 | .complete => true, | ||
| 2188 | else => unreachable, | 1540 | else => unreachable, |
| 2189 | }, | 1541 | } }), |
| 1542 | ); | ||
| 2190 | 1543 | ||
| 2191 | .Frame, | 1544 | switch (kind) { |
| 2192 | .AnyFrame, | 1545 | .forward => if (fn_info.alignment > 0) |
| 2193 | => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{ | 1546 | try w.print(" zig_align_fn({})", .{fn_info.alignment}), |
| 2194 | @tagName(tag), | 1547 | .complete => {}, |
| 2195 | }), | 1548 | else => unreachable, |
| 1549 | } | ||
| 1550 | } | ||
| 2196 | 1551 | ||
| 2197 | .Fn => unreachable, // This is a function body, not a function pointer. | 1552 | fn indexToCType(dg: *DeclGen, idx: CType.Index) CType { |
| 1553 | return dg.ctypes.indexToCType(idx); | ||
| 1554 | } | ||
| 2198 | 1555 | ||
| 2199 | .Null, | 1556 | fn typeToIndex(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType.Index { |
| 2200 | .Undefined, | 1557 | return dg.ctypes.typeToIndex(dg.gpa, ty, dg.module, kind); |
| 2201 | .EnumLiteral, | ||
| 2202 | .ComptimeFloat, | ||
| 2203 | .ComptimeInt, | ||
| 2204 | .Type, | ||
| 2205 | => unreachable, // must be const or comptime | ||
| 2206 | } | ||
| 2207 | } | 1558 | } |
| 2208 | 1559 | ||
| 2209 | fn renderTypeUnnamed( | 1560 | fn typeToCType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType { |
| 2210 | dg: *DeclGen, | 1561 | return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind); |
| 2211 | w: anytype, | 1562 | } |
| 2212 | t: Type, | ||
| 2213 | kind: TypedefKind, | ||
| 2214 | ) error{ OutOfMemory, AnalysisFail }!void { | ||
| 2215 | const target = dg.module.getTarget(); | ||
| 2216 | const int_info = t.intInfo(target); | ||
| 2217 | if (toCIntBits(int_info.bits)) |c_bits| | ||
| 2218 | return w.print("zig_{c}{d}", .{ signAbbrev(int_info.signedness), c_bits }) | ||
| 2219 | else if (loweredArrayInfo(t, target)) |array_info| { | ||
| 2220 | assert(array_info.sentinel == null); | ||
| 2221 | var array_pl = Type.Payload.Array{ | ||
| 2222 | .base = .{ .tag = .array }, | ||
| 2223 | .data = .{ .len = array_info.len, .elem_type = array_info.elem_type }, | ||
| 2224 | }; | ||
| 2225 | const array_ty = Type.initPayload(&array_pl.base); | ||
| 2226 | 1563 | ||
| 2227 | return dg.renderType(w, array_ty, kind); | 1564 | /// Renders a type as a single identifier, generating intermediate typedefs |
| 2228 | } else return dg.fail("C backend: Unable to lower unnamed integer type {}", .{ | 1565 | /// if necessary. |
| 2229 | t.fmt(dg.module), | 1566 | /// |
| 2230 | }); | 1567 | /// This is guaranteed to be valid in both typedefs and declarations/definitions. |
| 1568 | /// | ||
| 1569 | /// There are three type formats in total that we support rendering: | ||
| 1570 | /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | | ||
| 1571 | /// |---------------------|-----------------|---------------------| | ||
| 1572 | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | | ||
| 1573 | /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | | ||
| 1574 | /// | ||
| 1575 | fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { | ||
| 1576 | const store = &dg.ctypes.set; | ||
| 1577 | const module = dg.module; | ||
| 1578 | const idx = try dg.typeToIndex(t, .complete); | ||
| 1579 | _ = try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); | ||
| 1580 | try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); | ||
| 2231 | } | 1581 | } |
| 2232 | 1582 | ||
| 2233 | const IntCastContext = union(enum) { | 1583 | const IntCastContext = union(enum) { |
| ... | @@ -2254,16 +1604,16 @@ pub const DeclGen = struct { | ... | @@ -2254,16 +1604,16 @@ pub const DeclGen = struct { |
| 2254 | /// Renders a cast to an int type, from either an int or a pointer. | 1604 | /// Renders a cast to an int type, from either an int or a pointer. |
| 2255 | /// | 1605 | /// |
| 2256 | /// Some platforms don't have 128 bit integers, so we need to use | 1606 | /// Some platforms don't have 128 bit integers, so we need to use |
| 2257 | /// the zig_as_ and zig_lo_ macros in those cases. | 1607 | /// the zig_make_ and zig_lo_ macros in those cases. |
| 2258 | /// | 1608 | /// |
| 2259 | /// | Dest type bits | Src type | Result | 1609 | /// | Dest type bits | Src type | Result |
| 2260 | /// |------------------|------------------|---------------------------| | 1610 | /// |------------------|------------------|---------------------------| |
| 2261 | /// | < 64 bit integer | pointer | (zig_<dest_ty>)(zig_<u|i>size)src | 1611 | /// | < 64 bit integer | pointer | (zig_<dest_ty>)(zig_<u|i>size)src |
| 2262 | /// | < 64 bit integer | < 64 bit integer | (zig_<dest_ty>)src | 1612 | /// | < 64 bit integer | < 64 bit integer | (zig_<dest_ty>)src |
| 2263 | /// | < 64 bit integer | > 64 bit integer | zig_lo(src) | 1613 | /// | < 64 bit integer | > 64 bit integer | zig_lo(src) |
| 2264 | /// | > 64 bit integer | pointer | zig_as_<dest_ty>(0, (zig_<u|i>size)src) | 1614 | /// | > 64 bit integer | pointer | zig_make_<dest_ty>(0, (zig_<u|i>size)src) |
| 2265 | /// | > 64 bit integer | < 64 bit integer | zig_as_<dest_ty>(0, src) | 1615 | /// | > 64 bit integer | < 64 bit integer | zig_make_<dest_ty>(0, src) |
| 2266 | /// | > 64 bit integer | > 64 bit integer | zig_as_<dest_ty>(zig_hi_<src_ty>(src), zig_lo_<src_ty>(src)) | 1616 | /// | > 64 bit integer | > 64 bit integer | zig_make_<dest_ty>(zig_hi_<src_ty>(src), zig_lo_<src_ty>(src)) |
| 2267 | fn renderIntCast(dg: *DeclGen, w: anytype, dest_ty: Type, context: IntCastContext, src_ty: Type, location: ValueRenderLocation) !void { | 1617 | fn renderIntCast(dg: *DeclGen, w: anytype, dest_ty: Type, context: IntCastContext, src_ty: Type, location: ValueRenderLocation) !void { |
| 2268 | const target = dg.module.getTarget(); | 1618 | const target = dg.module.getTarget(); |
| 2269 | const dest_bits = dest_ty.bitSize(target); | 1619 | const dest_bits = dest_ty.bitSize(target); |
| ... | @@ -2284,36 +1634,41 @@ pub const DeclGen = struct { | ... | @@ -2284,36 +1634,41 @@ pub const DeclGen = struct { |
| 2284 | 1634 | ||
| 2285 | if (needs_cast) { | 1635 | if (needs_cast) { |
| 2286 | try w.writeByte('('); | 1636 | try w.writeByte('('); |
| 2287 | try dg.renderTypecast(w, dest_ty); | 1637 | try dg.renderType(w, dest_ty); |
| 2288 | try w.writeByte(')'); | 1638 | try w.writeByte(')'); |
| 2289 | } | 1639 | } |
| 2290 | if (src_is_ptr) { | 1640 | if (src_is_ptr) { |
| 2291 | try w.writeByte('('); | 1641 | try w.writeByte('('); |
| 2292 | try dg.renderTypecast(w, src_eff_ty); | 1642 | try dg.renderType(w, src_eff_ty); |
| 2293 | try w.writeByte(')'); | 1643 | try w.writeByte(')'); |
| 2294 | } | 1644 | } |
| 2295 | try context.writeValue(dg, w, src_ty, location); | 1645 | try context.writeValue(dg, w, src_ty, location); |
| 2296 | } else if (dest_bits <= 64 and src_bits > 64) { | 1646 | } else if (dest_bits <= 64 and src_bits > 64) { |
| 2297 | assert(!src_is_ptr); | 1647 | assert(!src_is_ptr); |
| 1648 | if (dest_bits < 64) { | ||
| 1649 | try w.writeByte('('); | ||
| 1650 | try dg.renderType(w, dest_ty); | ||
| 1651 | try w.writeByte(')'); | ||
| 1652 | } | ||
| 2298 | try w.writeAll("zig_lo_"); | 1653 | try w.writeAll("zig_lo_"); |
| 2299 | try dg.renderTypeForBuiltinFnName(w, src_eff_ty); | 1654 | try dg.renderTypeForBuiltinFnName(w, src_eff_ty); |
| 2300 | try w.writeByte('('); | 1655 | try w.writeByte('('); |
| 2301 | try context.writeValue(dg, w, src_ty, .FunctionArgument); | 1656 | try context.writeValue(dg, w, src_ty, .FunctionArgument); |
| 2302 | try w.writeByte(')'); | 1657 | try w.writeByte(')'); |
| 2303 | } else if (dest_bits > 64 and src_bits <= 64) { | 1658 | } else if (dest_bits > 64 and src_bits <= 64) { |
| 2304 | try w.writeAll("zig_as_"); | 1659 | try w.writeAll("zig_make_"); |
| 2305 | try dg.renderTypeForBuiltinFnName(w, dest_ty); | 1660 | try dg.renderTypeForBuiltinFnName(w, dest_ty); |
| 2306 | try w.writeAll("(0, "); // TODO: Should the 0 go through fmtIntLiteral? | 1661 | try w.writeAll("(0, "); // TODO: Should the 0 go through fmtIntLiteral? |
| 2307 | if (src_is_ptr) { | 1662 | if (src_is_ptr) { |
| 2308 | try w.writeByte('('); | 1663 | try w.writeByte('('); |
| 2309 | try dg.renderTypecast(w, src_eff_ty); | 1664 | try dg.renderType(w, src_eff_ty); |
| 2310 | try w.writeByte(')'); | 1665 | try w.writeByte(')'); |
| 2311 | } | 1666 | } |
| 2312 | try context.writeValue(dg, w, src_ty, .FunctionArgument); | 1667 | try context.writeValue(dg, w, src_ty, .FunctionArgument); |
| 2313 | try w.writeByte(')'); | 1668 | try w.writeByte(')'); |
| 2314 | } else { | 1669 | } else { |
| 2315 | assert(!src_is_ptr); | 1670 | assert(!src_is_ptr); |
| 2316 | try w.writeAll("zig_as_"); | 1671 | try w.writeAll("zig_make_"); |
| 2317 | try dg.renderTypeForBuiltinFnName(w, dest_ty); | 1672 | try dg.renderTypeForBuiltinFnName(w, dest_ty); |
| 2318 | try w.writeAll("(zig_hi_"); | 1673 | try w.writeAll("(zig_hi_"); |
| 2319 | try dg.renderTypeForBuiltinFnName(w, src_eff_ty); | 1674 | try dg.renderTypeForBuiltinFnName(w, src_eff_ty); |
| ... | @@ -2327,151 +1682,50 @@ pub const DeclGen = struct { | ... | @@ -2327,151 +1682,50 @@ pub const DeclGen = struct { |
| 2327 | } | 1682 | } |
| 2328 | } | 1683 | } |
| 2329 | 1684 | ||
| 2330 | /// Renders a type in C typecast format. | ||
| 2331 | /// | ||
| 2332 | /// This is guaranteed to be valid in a typecast expression, but not | ||
| 2333 | /// necessarily in a variable/field declaration. | ||
| 2334 | /// | ||
| 2335 | /// There are three type formats in total that we support rendering: | ||
| 2336 | /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | | ||
| 2337 | /// |---------------------|-----------------|---------------------| | ||
| 2338 | /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | | ||
| 2339 | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | | ||
| 2340 | /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | | ||
| 2341 | /// | ||
| 2342 | fn renderTypecast(dg: *DeclGen, w: anytype, ty: Type) error{ OutOfMemory, AnalysisFail }!void { | ||
| 2343 | return renderTypeAndName(dg, w, ty, .{ .bytes = "" }, .Mut, 0, .Complete); | ||
| 2344 | } | ||
| 2345 | |||
| 2346 | /// Renders a type and name in field declaration/definition format. | 1685 | /// Renders a type and name in field declaration/definition format. |
| 2347 | /// | 1686 | /// |
| 2348 | /// There are three type formats in total that we support rendering: | 1687 | /// There are three type formats in total that we support rendering: |
| 2349 | /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | | 1688 | /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | |
| 2350 | /// |---------------------|-----------------|---------------------| | 1689 | /// |---------------------|-----------------|---------------------| |
| 2351 | /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | | ||
| 2352 | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | | 1690 | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | |
| 2353 | /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | | 1691 | /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | |
| 2354 | /// | 1692 | /// |
| 2355 | fn renderTypeAndName( | 1693 | fn renderTypeAndName( |
| 2356 | dg: *DeclGen, | 1694 | dg: *DeclGen, |
| 2357 | w: anytype, | 1695 | w: anytype, |
| 2358 | ty: Type, | 1696 | ty: Type, |
| 2359 | name: CValue, | 1697 | name: CValue, |
| 2360 | mutability: Mutability, | 1698 | qualifiers: CQualifiers, |
| 2361 | alignment: u32, | 1699 | alignment: u32, |
| 2362 | kind: TypedefKind, | 1700 | kind: CType.Kind, |
| 2363 | ) error{ OutOfMemory, AnalysisFail }!void { | 1701 | ) error{ OutOfMemory, AnalysisFail }!void { |
| 2364 | var suffix = std.ArrayList(u8).init(dg.gpa); | ||
| 2365 | defer suffix.deinit(); | ||
| 2366 | const suffix_writer = suffix.writer(); | ||
| 2367 | |||
| 2368 | // Any top-level array types are rendered here as a suffix, which | ||
| 2369 | // avoids creating typedefs for every array type | ||
| 2370 | const target = dg.module.getTarget(); | 1702 | const target = dg.module.getTarget(); |
| 2371 | var render_ty = ty; | 1703 | const alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)); |
| 2372 | var depth: u32 = 0; | 1704 | try dg.renderCTypeAndName(w, try dg.typeToIndex(ty, kind), name, qualifiers, alignas); |
| 2373 | while (loweredArrayInfo(render_ty, target)) |array_info| { | ||
| 2374 | const c_len = array_info.len + @boolToInt(array_info.sentinel != null); | ||
| 2375 | var c_len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = c_len }; | ||
| 2376 | const c_len_val = Value.initPayload(&c_len_pl.base); | ||
| 2377 | |||
| 2378 | try suffix_writer.writeByte('['); | ||
| 2379 | if (mutability == .ConstArgument and depth == 0) try suffix_writer.writeAll("zig_const_arr "); | ||
| 2380 | try suffix.writer().print("{}]", .{try dg.fmtIntLiteral(Type.usize, c_len_val)}); | ||
| 2381 | render_ty = array_info.elem_type; | ||
| 2382 | depth += 1; | ||
| 2383 | } | ||
| 2384 | |||
| 2385 | if (alignment != 0) { | ||
| 2386 | const abi_alignment = ty.abiAlignment(target); | ||
| 2387 | if (alignment < abi_alignment) { | ||
| 2388 | try w.print("zig_under_align({}) ", .{alignment}); | ||
| 2389 | } else if (alignment > abi_alignment) { | ||
| 2390 | try w.print("zig_align({}) ", .{alignment}); | ||
| 2391 | } | ||
| 2392 | } | ||
| 2393 | try dg.renderType(w, render_ty, kind); | ||
| 2394 | |||
| 2395 | const const_prefix = switch (mutability) { | ||
| 2396 | .Const, .ConstArgument => "const ", | ||
| 2397 | .Mut => "", | ||
| 2398 | }; | ||
| 2399 | try w.print(" {s}", .{const_prefix}); | ||
| 2400 | try dg.writeCValue(w, name); | ||
| 2401 | try w.writeAll(suffix.items); | ||
| 2402 | } | 1705 | } |
| 2403 | 1706 | ||
| 2404 | fn renderTagNameFn(dg: *DeclGen, enum_ty: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { | 1707 | fn renderCTypeAndName( |
| 2405 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | 1708 | dg: *DeclGen, |
| 2406 | defer buffer.deinit(); | 1709 | w: anytype, |
| 2407 | const bw = buffer.writer(); | 1710 | cty_idx: CType.Index, |
| 2408 | 1711 | name: CValue, | |
| 2409 | const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); | 1712 | qualifiers: CQualifiers, |
| 2410 | 1713 | alignas: CType.AlignAs, | |
| 2411 | try buffer.appendSlice("static "); | 1714 | ) error{ OutOfMemory, AnalysisFail }!void { |
| 2412 | try dg.renderType(bw, name_slice_ty, .Complete); | 1715 | const store = &dg.ctypes.set; |
| 2413 | const name_begin = buffer.items.len + " ".len; | 1716 | const module = dg.module; |
| 2414 | try bw.print(" zig_tagName_{}_{d}(", .{ typeToCIdentifier(enum_ty, dg.module), @enumToInt(enum_ty.getOwnerDecl()) }); | ||
| 2415 | const name_end = buffer.items.len - "(".len; | ||
| 2416 | try dg.renderTypeAndName(bw, enum_ty, .{ .identifier = "tag" }, .Const, 0, .Complete); | ||
| 2417 | try buffer.appendSlice(") {\n switch (tag) {\n"); | ||
| 2418 | for (enum_ty.enumFields().keys(), 0..) |name, index| { | ||
| 2419 | const name_z = try dg.typedefs.allocator.dupeZ(u8, name); | ||
| 2420 | defer dg.typedefs.allocator.free(name_z); | ||
| 2421 | const name_bytes = name_z[0 .. name_z.len + 1]; | ||
| 2422 | |||
| 2423 | var tag_pl: Value.Payload.U32 = .{ | ||
| 2424 | .base = .{ .tag = .enum_field_index }, | ||
| 2425 | .data = @intCast(u32, index), | ||
| 2426 | }; | ||
| 2427 | const tag_val = Value.initPayload(&tag_pl.base); | ||
| 2428 | |||
| 2429 | var int_pl: Value.Payload.U64 = undefined; | ||
| 2430 | const int_val = tag_val.enumToInt(enum_ty, &int_pl); | ||
| 2431 | |||
| 2432 | var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; | ||
| 2433 | const name_ty = Type.initPayload(&name_ty_pl.base); | ||
| 2434 | |||
| 2435 | var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_bytes }; | ||
| 2436 | const name_val = Value.initPayload(&name_pl.base); | ||
| 2437 | |||
| 2438 | var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; | ||
| 2439 | const len_val = Value.initPayload(&len_pl.base); | ||
| 2440 | |||
| 2441 | try bw.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val)}); | ||
| 2442 | try dg.renderTypeAndName(bw, name_ty, .{ .identifier = "name" }, .Const, 0, .Complete); | ||
| 2443 | try buffer.appendSlice(" = "); | ||
| 2444 | try dg.renderValue(bw, name_ty, name_val, .Initializer); | ||
| 2445 | try buffer.appendSlice(";\n return ("); | ||
| 2446 | try dg.renderTypecast(bw, name_slice_ty); | ||
| 2447 | try bw.print("){{{}, {}}};\n", .{ | ||
| 2448 | fmtIdent("name"), try dg.fmtIntLiteral(Type.usize, len_val), | ||
| 2449 | }); | ||
| 2450 | 1717 | ||
| 2451 | try buffer.appendSlice(" }\n"); | 1718 | switch (std.math.order(alignas.@"align", alignas.abi)) { |
| 1719 | .lt => try w.print("zig_under_align({}) ", .{alignas.getAlign()}), | ||
| 1720 | .eq => {}, | ||
| 1721 | .gt => try w.print("zig_align({}) ", .{alignas.getAlign()}), | ||
| 2452 | } | 1722 | } |
| 2453 | try buffer.appendSlice(" }\n while ("); | ||
| 2454 | try dg.renderValue(bw, Type.bool, Value.true, .Other); | ||
| 2455 | try buffer.appendSlice(") "); | ||
| 2456 | _ = try airBreakpoint(bw); | ||
| 2457 | try buffer.appendSlice("}\n"); | ||
| 2458 | |||
| 2459 | const rendered = try buffer.toOwnedSlice(); | ||
| 2460 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 2461 | const name = rendered[name_begin..name_end]; | ||
| 2462 | |||
| 2463 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 2464 | dg.typedefs.putAssumeCapacityNoClobber( | ||
| 2465 | try enum_ty.copy(dg.typedefs_arena), | ||
| 2466 | .{ .name = name, .rendered = rendered }, | ||
| 2467 | ); | ||
| 2468 | |||
| 2469 | return name; | ||
| 2470 | } | ||
| 2471 | 1723 | ||
| 2472 | fn getTagNameFn(dg: *DeclGen, enum_ty: Type) ![]const u8 { | 1724 | const trailing = |
| 2473 | return dg.getTypedefName(enum_ty) orelse | 1725 | try renderTypePrefix(dg.decl_index, store.*, module, w, cty_idx, .suffix, qualifiers); |
| 2474 | try dg.renderTagNameFn(enum_ty); | 1726 | try w.print("{}", .{trailing}); |
| 1727 | try dg.writeCValue(w, name); | ||
| 1728 | try renderTypeSuffix(dg.decl_index, store.*, module, w, cty_idx, .suffix, .{}); | ||
| 2475 | } | 1729 | } |
| 2476 | 1730 | ||
| 2477 | fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool { | 1731 | fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool { |
| ... | @@ -2492,10 +1746,11 @@ pub const DeclGen = struct { | ... | @@ -2492,10 +1746,11 @@ pub const DeclGen = struct { |
| 2492 | fn writeCValue(dg: *DeclGen, w: anytype, c_value: CValue) !void { | 1746 | fn writeCValue(dg: *DeclGen, w: anytype, c_value: CValue) !void { |
| 2493 | switch (c_value) { | 1747 | switch (c_value) { |
| 2494 | .none => unreachable, | 1748 | .none => unreachable, |
| 2495 | .local => |i| return w.print("t{d}", .{i}), | 1749 | .local, .new_local => |i| return w.print("t{d}", .{i}), |
| 2496 | .local_ref => |i| return w.print("&t{d}", .{i}), | 1750 | .local_ref => |i| return w.print("&t{d}", .{i}), |
| 2497 | .constant => unreachable, | 1751 | .constant => unreachable, |
| 2498 | .arg => |i| return w.print("a{d}", .{i}), | 1752 | .arg => |i| return w.print("a{d}", .{i}), |
| 1753 | .arg_array => |i| return dg.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }), | ||
| 2499 | .field => |i| return w.print("f{d}", .{i}), | 1754 | .field => |i| return w.print("f{d}", .{i}), |
| 2500 | .decl => |decl| return dg.renderDeclName(w, decl, 0), | 1755 | .decl => |decl| return dg.renderDeclName(w, decl, 0), |
| 2501 | .decl_ref => |decl| { | 1756 | .decl_ref => |decl| { |
| ... | @@ -2504,6 +1759,10 @@ pub const DeclGen = struct { | ... | @@ -2504,6 +1759,10 @@ pub const DeclGen = struct { |
| 2504 | }, | 1759 | }, |
| 2505 | .undef => |ty| return dg.renderValue(w, ty, Value.undef, .Other), | 1760 | .undef => |ty| return dg.renderValue(w, ty, Value.undef, .Other), |
| 2506 | .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}), | 1761 | .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}), |
| 1762 | .payload_identifier => |ident| return w.print("{ }.{ }", .{ | ||
| 1763 | fmtIdent("payload"), | ||
| 1764 | fmtIdent(ident), | ||
| 1765 | }), | ||
| 2507 | .bytes => |bytes| return w.writeAll(bytes), | 1766 | .bytes => |bytes| return w.writeAll(bytes), |
| 2508 | } | 1767 | } |
| 2509 | } | 1768 | } |
| ... | @@ -2511,10 +1770,15 @@ pub const DeclGen = struct { | ... | @@ -2511,10 +1770,15 @@ pub const DeclGen = struct { |
| 2511 | fn writeCValueDeref(dg: *DeclGen, w: anytype, c_value: CValue) !void { | 1770 | fn writeCValueDeref(dg: *DeclGen, w: anytype, c_value: CValue) !void { |
| 2512 | switch (c_value) { | 1771 | switch (c_value) { |
| 2513 | .none => unreachable, | 1772 | .none => unreachable, |
| 2514 | .local => |i| return w.print("(*t{d})", .{i}), | 1773 | .local, .new_local => |i| return w.print("(*t{d})", .{i}), |
| 2515 | .local_ref => |i| return w.print("t{d}", .{i}), | 1774 | .local_ref => |i| return w.print("t{d}", .{i}), |
| 2516 | .constant => unreachable, | 1775 | .constant => unreachable, |
| 2517 | .arg => |i| return w.print("(*a{d})", .{i}), | 1776 | .arg => |i| return w.print("(*a{d})", .{i}), |
| 1777 | .arg_array => |i| { | ||
| 1778 | try w.writeAll("(*"); | ||
| 1779 | try dg.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }); | ||
| 1780 | return w.writeByte(')'); | ||
| 1781 | }, | ||
| 2518 | .field => |i| return w.print("f{d}", .{i}), | 1782 | .field => |i| return w.print("f{d}", .{i}), |
| 2519 | .decl => |decl| { | 1783 | .decl => |decl| { |
| 2520 | try w.writeAll("(*"); | 1784 | try w.writeAll("(*"); |
| ... | @@ -2524,6 +1788,10 @@ pub const DeclGen = struct { | ... | @@ -2524,6 +1788,10 @@ pub const DeclGen = struct { |
| 2524 | .decl_ref => |decl| return dg.renderDeclName(w, decl, 0), | 1788 | .decl_ref => |decl| return dg.renderDeclName(w, decl, 0), |
| 2525 | .undef => unreachable, | 1789 | .undef => unreachable, |
| 2526 | .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}), | 1790 | .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}), |
| 1791 | .payload_identifier => |ident| return w.print("(*{ }.{ })", .{ | ||
| 1792 | fmtIdent("payload"), | ||
| 1793 | fmtIdent(ident), | ||
| 1794 | }), | ||
| 2527 | .bytes => |bytes| { | 1795 | .bytes => |bytes| { |
| 2528 | try w.writeAll("(*"); | 1796 | try w.writeAll("(*"); |
| 2529 | try w.writeAll(bytes); | 1797 | try w.writeAll(bytes); |
| ... | @@ -2541,7 +1809,7 @@ pub const DeclGen = struct { | ... | @@ -2541,7 +1809,7 @@ pub const DeclGen = struct { |
| 2541 | fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { | 1809 | fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { |
| 2542 | switch (c_value) { | 1810 | switch (c_value) { |
| 2543 | .none, .constant, .field, .undef => unreachable, | 1811 | .none, .constant, .field, .undef => unreachable, |
| 2544 | .local, .arg, .decl, .identifier, .bytes => { | 1812 | .new_local, .local, .arg, .arg_array, .decl, .identifier, .payload_identifier, .bytes => { |
| 2545 | try dg.writeCValue(writer, c_value); | 1813 | try dg.writeCValue(writer, c_value); |
| 2546 | try writer.writeAll("->"); | 1814 | try writer.writeAll("->"); |
| 2547 | }, | 1815 | }, |
| ... | @@ -2668,13 +1936,466 @@ pub const DeclGen = struct { | ... | @@ -2668,13 +1936,466 @@ pub const DeclGen = struct { |
| 2668 | } | 1936 | } |
| 2669 | }; | 1937 | }; |
| 2670 | 1938 | ||
| 2671 | pub fn genGlobalAsm(mod: *Module, code: *std.ArrayList(u8)) !void { | 1939 | const CTypeFix = enum { prefix, suffix }; |
| 2672 | var it = mod.global_assembly.valueIterator(); | 1940 | const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict }); |
| 2673 | while (it.next()) |asm_source| { | 1941 | const Const = CQualifiers.init(.{ .@"const" = true }); |
| 2674 | try code.writer().print("__asm({s});\n", .{fmtStringLiteral(asm_source.*)}); | 1942 | const RenderCTypeTrailing = enum { |
| 1943 | no_space, | ||
| 1944 | maybe_space, | ||
| 1945 | |||
| 1946 | pub fn format( | ||
| 1947 | self: @This(), | ||
| 1948 | comptime fmt: []const u8, | ||
| 1949 | _: std.fmt.FormatOptions, | ||
| 1950 | w: anytype, | ||
| 1951 | ) @TypeOf(w).Error!void { | ||
| 1952 | if (fmt.len != 0) | ||
| 1953 | @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ | ||
| 1954 | @typeName(@This()) ++ "'"); | ||
| 1955 | comptime assert(fmt.len == 0); | ||
| 1956 | switch (self) { | ||
| 1957 | .no_space => {}, | ||
| 1958 | .maybe_space => try w.writeByte(' '), | ||
| 1959 | } | ||
| 1960 | } | ||
| 1961 | }; | ||
| 1962 | fn renderTypeName( | ||
| 1963 | mod: *Module, | ||
| 1964 | w: anytype, | ||
| 1965 | idx: CType.Index, | ||
| 1966 | cty: CType, | ||
| 1967 | attributes: []const u8, | ||
| 1968 | ) !void { | ||
| 1969 | switch (cty.tag()) { | ||
| 1970 | else => unreachable, | ||
| 1971 | |||
| 1972 | .fwd_anon_struct, | ||
| 1973 | .fwd_anon_union, | ||
| 1974 | => |tag| try w.print("{s} {s}anon__lazy_{d}", .{ | ||
| 1975 | @tagName(tag)["fwd_anon_".len..], | ||
| 1976 | attributes, | ||
| 1977 | idx, | ||
| 1978 | }), | ||
| 1979 | |||
| 1980 | .fwd_struct, | ||
| 1981 | .fwd_union, | ||
| 1982 | => |tag| { | ||
| 1983 | const owner_decl = cty.cast(CType.Payload.FwdDecl).?.data; | ||
| 1984 | try w.print("{s} {s}{}__{d}", .{ | ||
| 1985 | @tagName(tag)["fwd_".len..], | ||
| 1986 | attributes, | ||
| 1987 | fmtIdent(mem.span(mod.declPtr(owner_decl).name)), | ||
| 1988 | @enumToInt(owner_decl), | ||
| 1989 | }); | ||
| 1990 | }, | ||
| 1991 | } | ||
| 1992 | } | ||
| 1993 | fn renderTypePrefix( | ||
| 1994 | decl: Decl.OptionalIndex, | ||
| 1995 | store: CType.Store.Set, | ||
| 1996 | mod: *Module, | ||
| 1997 | w: anytype, | ||
| 1998 | idx: CType.Index, | ||
| 1999 | parent_fix: CTypeFix, | ||
| 2000 | qualifiers: CQualifiers, | ||
| 2001 | ) @TypeOf(w).Error!RenderCTypeTrailing { | ||
| 2002 | var trailing = RenderCTypeTrailing.maybe_space; | ||
| 2003 | |||
| 2004 | const cty = store.indexToCType(idx); | ||
| 2005 | switch (cty.tag()) { | ||
| 2006 | .void, | ||
| 2007 | .char, | ||
| 2008 | .@"signed char", | ||
| 2009 | .short, | ||
| 2010 | .int, | ||
| 2011 | .long, | ||
| 2012 | .@"long long", | ||
| 2013 | ._Bool, | ||
| 2014 | .@"unsigned char", | ||
| 2015 | .@"unsigned short", | ||
| 2016 | .@"unsigned int", | ||
| 2017 | .@"unsigned long", | ||
| 2018 | .@"unsigned long long", | ||
| 2019 | .float, | ||
| 2020 | .double, | ||
| 2021 | .@"long double", | ||
| 2022 | .bool, | ||
| 2023 | .size_t, | ||
| 2024 | .ptrdiff_t, | ||
| 2025 | .uint8_t, | ||
| 2026 | .int8_t, | ||
| 2027 | .uint16_t, | ||
| 2028 | .int16_t, | ||
| 2029 | .uint32_t, | ||
| 2030 | .int32_t, | ||
| 2031 | .uint64_t, | ||
| 2032 | .int64_t, | ||
| 2033 | .uintptr_t, | ||
| 2034 | .intptr_t, | ||
| 2035 | .zig_u128, | ||
| 2036 | .zig_i128, | ||
| 2037 | .zig_f16, | ||
| 2038 | .zig_f32, | ||
| 2039 | .zig_f64, | ||
| 2040 | .zig_f80, | ||
| 2041 | .zig_f128, | ||
| 2042 | .zig_c_longdouble, | ||
| 2043 | => |tag| try w.writeAll(@tagName(tag)), | ||
| 2044 | |||
| 2045 | .pointer, | ||
| 2046 | .pointer_const, | ||
| 2047 | .pointer_volatile, | ||
| 2048 | .pointer_const_volatile, | ||
| 2049 | => |tag| { | ||
| 2050 | const child_idx = cty.cast(CType.Payload.Child).?.data; | ||
| 2051 | const child_trailing = try renderTypePrefix( | ||
| 2052 | decl, | ||
| 2053 | store, | ||
| 2054 | mod, | ||
| 2055 | w, | ||
| 2056 | child_idx, | ||
| 2057 | .prefix, | ||
| 2058 | CQualifiers.init(.{ .@"const" = switch (tag) { | ||
| 2059 | .pointer, .pointer_volatile => false, | ||
| 2060 | .pointer_const, .pointer_const_volatile => true, | ||
| 2061 | else => unreachable, | ||
| 2062 | }, .@"volatile" = switch (tag) { | ||
| 2063 | .pointer, .pointer_const => false, | ||
| 2064 | .pointer_volatile, .pointer_const_volatile => true, | ||
| 2065 | else => unreachable, | ||
| 2066 | } }), | ||
| 2067 | ); | ||
| 2068 | try w.print("{}*", .{child_trailing}); | ||
| 2069 | trailing = .no_space; | ||
| 2070 | }, | ||
| 2071 | |||
| 2072 | .array, | ||
| 2073 | .vector, | ||
| 2074 | => { | ||
| 2075 | const child_idx = cty.cast(CType.Payload.Sequence).?.data.elem_type; | ||
| 2076 | const child_trailing = try renderTypePrefix( | ||
| 2077 | decl, | ||
| 2078 | store, | ||
| 2079 | mod, | ||
| 2080 | w, | ||
| 2081 | child_idx, | ||
| 2082 | .suffix, | ||
| 2083 | qualifiers, | ||
| 2084 | ); | ||
| 2085 | switch (parent_fix) { | ||
| 2086 | .prefix => { | ||
| 2087 | try w.print("{}(", .{child_trailing}); | ||
| 2088 | return .no_space; | ||
| 2089 | }, | ||
| 2090 | .suffix => return child_trailing, | ||
| 2091 | } | ||
| 2092 | }, | ||
| 2093 | |||
| 2094 | .fwd_anon_struct, | ||
| 2095 | .fwd_anon_union, | ||
| 2096 | => if (decl.unwrap()) |decl_index| | ||
| 2097 | try w.print("anon__{d}_{d}", .{ @enumToInt(decl_index), idx }) | ||
| 2098 | else | ||
| 2099 | try renderTypeName(mod, w, idx, cty, ""), | ||
| 2100 | |||
| 2101 | .fwd_struct, | ||
| 2102 | .fwd_union, | ||
| 2103 | => try renderTypeName(mod, w, idx, cty, ""), | ||
| 2104 | |||
| 2105 | .unnamed_struct, | ||
| 2106 | .unnamed_union, | ||
| 2107 | .packed_unnamed_struct, | ||
| 2108 | .packed_unnamed_union, | ||
| 2109 | => |tag| { | ||
| 2110 | try w.print("{s} {s}", .{ | ||
| 2111 | @tagName(tag)["unnamed_".len..], | ||
| 2112 | if (cty.isPacked()) "zig_packed(" else "", | ||
| 2113 | }); | ||
| 2114 | try renderAggregateFields(mod, w, store, cty, 1); | ||
| 2115 | if (cty.isPacked()) try w.writeByte(')'); | ||
| 2116 | }, | ||
| 2117 | |||
| 2118 | .anon_struct, | ||
| 2119 | .anon_union, | ||
| 2120 | .@"struct", | ||
| 2121 | .@"union", | ||
| 2122 | .packed_struct, | ||
| 2123 | .packed_union, | ||
| 2124 | => return renderTypePrefix( | ||
| 2125 | decl, | ||
| 2126 | store, | ||
| 2127 | mod, | ||
| 2128 | w, | ||
| 2129 | cty.cast(CType.Payload.Aggregate).?.data.fwd_decl, | ||
| 2130 | parent_fix, | ||
| 2131 | qualifiers, | ||
| 2132 | ), | ||
| 2133 | |||
| 2134 | .function, | ||
| 2135 | .varargs_function, | ||
| 2136 | => { | ||
| 2137 | const child_trailing = try renderTypePrefix( | ||
| 2138 | decl, | ||
| 2139 | store, | ||
| 2140 | mod, | ||
| 2141 | w, | ||
| 2142 | cty.cast(CType.Payload.Function).?.data.return_type, | ||
| 2143 | .suffix, | ||
| 2144 | .{}, | ||
| 2145 | ); | ||
| 2146 | switch (parent_fix) { | ||
| 2147 | .prefix => { | ||
| 2148 | try w.print("{}(", .{child_trailing}); | ||
| 2149 | return .no_space; | ||
| 2150 | }, | ||
| 2151 | .suffix => return child_trailing, | ||
| 2152 | } | ||
| 2153 | }, | ||
| 2154 | } | ||
| 2155 | |||
| 2156 | var qualifier_it = qualifiers.iterator(); | ||
| 2157 | while (qualifier_it.next()) |qualifier| { | ||
| 2158 | try w.print("{}{s}", .{ trailing, @tagName(qualifier) }); | ||
| 2159 | trailing = .maybe_space; | ||
| 2160 | } | ||
| 2161 | |||
| 2162 | return trailing; | ||
| 2163 | } | ||
| 2164 | fn renderTypeSuffix( | ||
| 2165 | decl: Decl.OptionalIndex, | ||
| 2166 | store: CType.Store.Set, | ||
| 2167 | mod: *Module, | ||
| 2168 | w: anytype, | ||
| 2169 | idx: CType.Index, | ||
| 2170 | parent_fix: CTypeFix, | ||
| 2171 | qualifiers: CQualifiers, | ||
| 2172 | ) @TypeOf(w).Error!void { | ||
| 2173 | const cty = store.indexToCType(idx); | ||
| 2174 | switch (cty.tag()) { | ||
| 2175 | .void, | ||
| 2176 | .char, | ||
| 2177 | .@"signed char", | ||
| 2178 | .short, | ||
| 2179 | .int, | ||
| 2180 | .long, | ||
| 2181 | .@"long long", | ||
| 2182 | ._Bool, | ||
| 2183 | .@"unsigned char", | ||
| 2184 | .@"unsigned short", | ||
| 2185 | .@"unsigned int", | ||
| 2186 | .@"unsigned long", | ||
| 2187 | .@"unsigned long long", | ||
| 2188 | .float, | ||
| 2189 | .double, | ||
| 2190 | .@"long double", | ||
| 2191 | .bool, | ||
| 2192 | .size_t, | ||
| 2193 | .ptrdiff_t, | ||
| 2194 | .uint8_t, | ||
| 2195 | .int8_t, | ||
| 2196 | .uint16_t, | ||
| 2197 | .int16_t, | ||
| 2198 | .uint32_t, | ||
| 2199 | .int32_t, | ||
| 2200 | .uint64_t, | ||
| 2201 | .int64_t, | ||
| 2202 | .uintptr_t, | ||
| 2203 | .intptr_t, | ||
| 2204 | .zig_u128, | ||
| 2205 | .zig_i128, | ||
| 2206 | .zig_f16, | ||
| 2207 | .zig_f32, | ||
| 2208 | .zig_f64, | ||
| 2209 | .zig_f80, | ||
| 2210 | .zig_f128, | ||
| 2211 | .zig_c_longdouble, | ||
| 2212 | => {}, | ||
| 2213 | |||
| 2214 | .pointer, | ||
| 2215 | .pointer_const, | ||
| 2216 | .pointer_volatile, | ||
| 2217 | .pointer_const_volatile, | ||
| 2218 | => try renderTypeSuffix( | ||
| 2219 | decl, | ||
| 2220 | store, | ||
| 2221 | mod, | ||
| 2222 | w, | ||
| 2223 | cty.cast(CType.Payload.Child).?.data, | ||
| 2224 | .prefix, | ||
| 2225 | .{}, | ||
| 2226 | ), | ||
| 2227 | |||
| 2228 | .array, | ||
| 2229 | .vector, | ||
| 2230 | => { | ||
| 2231 | switch (parent_fix) { | ||
| 2232 | .prefix => try w.writeByte(')'), | ||
| 2233 | .suffix => {}, | ||
| 2234 | } | ||
| 2235 | |||
| 2236 | try w.print("[{}]", .{cty.cast(CType.Payload.Sequence).?.data.len}); | ||
| 2237 | try renderTypeSuffix( | ||
| 2238 | decl, | ||
| 2239 | store, | ||
| 2240 | mod, | ||
| 2241 | w, | ||
| 2242 | cty.cast(CType.Payload.Sequence).?.data.elem_type, | ||
| 2243 | .suffix, | ||
| 2244 | .{}, | ||
| 2245 | ); | ||
| 2246 | }, | ||
| 2247 | |||
| 2248 | .fwd_anon_struct, | ||
| 2249 | .fwd_anon_union, | ||
| 2250 | .fwd_struct, | ||
| 2251 | .fwd_union, | ||
| 2252 | .unnamed_struct, | ||
| 2253 | .unnamed_union, | ||
| 2254 | .packed_unnamed_struct, | ||
| 2255 | .packed_unnamed_union, | ||
| 2256 | .anon_struct, | ||
| 2257 | .anon_union, | ||
| 2258 | .@"struct", | ||
| 2259 | .@"union", | ||
| 2260 | .packed_struct, | ||
| 2261 | .packed_union, | ||
| 2262 | => {}, | ||
| 2263 | |||
| 2264 | .function, | ||
| 2265 | .varargs_function, | ||
| 2266 | => |tag| { | ||
| 2267 | switch (parent_fix) { | ||
| 2268 | .prefix => try w.writeByte(')'), | ||
| 2269 | .suffix => {}, | ||
| 2270 | } | ||
| 2271 | |||
| 2272 | const data = cty.cast(CType.Payload.Function).?.data; | ||
| 2273 | |||
| 2274 | try w.writeByte('('); | ||
| 2275 | var need_comma = false; | ||
| 2276 | for (data.param_types, 0..) |param_type, param_i| { | ||
| 2277 | if (need_comma) try w.writeAll(", "); | ||
| 2278 | need_comma = true; | ||
| 2279 | const trailing = | ||
| 2280 | try renderTypePrefix(decl, store, mod, w, param_type, .suffix, qualifiers); | ||
| 2281 | if (qualifiers.contains(.@"const")) try w.print("{}a{d}", .{ trailing, param_i }); | ||
| 2282 | try renderTypeSuffix(decl, store, mod, w, param_type, .suffix, .{}); | ||
| 2283 | } | ||
| 2284 | switch (tag) { | ||
| 2285 | .function => {}, | ||
| 2286 | .varargs_function => { | ||
| 2287 | if (need_comma) try w.writeAll(", "); | ||
| 2288 | need_comma = true; | ||
| 2289 | try w.writeAll("..."); | ||
| 2290 | }, | ||
| 2291 | else => unreachable, | ||
| 2292 | } | ||
| 2293 | if (!need_comma) try w.writeAll("void"); | ||
| 2294 | try w.writeByte(')'); | ||
| 2295 | |||
| 2296 | try renderTypeSuffix(decl, store, mod, w, data.return_type, .suffix, .{}); | ||
| 2297 | }, | ||
| 2298 | } | ||
| 2299 | } | ||
| 2300 | fn renderAggregateFields( | ||
| 2301 | mod: *Module, | ||
| 2302 | writer: anytype, | ||
| 2303 | store: CType.Store.Set, | ||
| 2304 | cty: CType, | ||
| 2305 | indent: usize, | ||
| 2306 | ) !void { | ||
| 2307 | try writer.writeAll("{\n"); | ||
| 2308 | const fields = cty.fields(); | ||
| 2309 | for (fields) |field| { | ||
| 2310 | try writer.writeByteNTimes(' ', indent + 1); | ||
| 2311 | switch (std.math.order(field.alignas.@"align", field.alignas.abi)) { | ||
| 2312 | .lt => try writer.print("zig_under_align({}) ", .{field.alignas.getAlign()}), | ||
| 2313 | .eq => {}, | ||
| 2314 | .gt => try writer.print("zig_align({}) ", .{field.alignas.getAlign()}), | ||
| 2315 | } | ||
| 2316 | const trailing = try renderTypePrefix(.none, store, mod, writer, field.type, .suffix, .{}); | ||
| 2317 | try writer.print("{}{ }", .{ trailing, fmtIdent(mem.span(field.name)) }); | ||
| 2318 | try renderTypeSuffix(.none, store, mod, writer, field.type, .suffix, .{}); | ||
| 2319 | try writer.writeAll(";\n"); | ||
| 2320 | } | ||
| 2321 | try writer.writeByteNTimes(' ', indent); | ||
| 2322 | try writer.writeByte('}'); | ||
| 2323 | } | ||
| 2324 | |||
| 2325 | pub fn genTypeDecl( | ||
| 2326 | mod: *Module, | ||
| 2327 | writer: anytype, | ||
| 2328 | global_store: CType.Store.Set, | ||
| 2329 | global_idx: CType.Index, | ||
| 2330 | decl: Decl.OptionalIndex, | ||
| 2331 | decl_store: CType.Store.Set, | ||
| 2332 | decl_idx: CType.Index, | ||
| 2333 | found_existing: bool, | ||
| 2334 | ) !void { | ||
| 2335 | const global_cty = global_store.indexToCType(global_idx); | ||
| 2336 | switch (global_cty.tag()) { | ||
| 2337 | .fwd_anon_struct => if (decl != .none) { | ||
| 2338 | try writer.writeAll("typedef "); | ||
| 2339 | _ = try renderTypePrefix(.none, global_store, mod, writer, global_idx, .suffix, .{}); | ||
| 2340 | try writer.writeByte(' '); | ||
| 2341 | _ = try renderTypePrefix(decl, decl_store, mod, writer, decl_idx, .suffix, .{}); | ||
| 2342 | try writer.writeAll(";\n"); | ||
| 2343 | }, | ||
| 2344 | |||
| 2345 | .fwd_struct, | ||
| 2346 | .fwd_union, | ||
| 2347 | .anon_struct, | ||
| 2348 | .anon_union, | ||
| 2349 | .@"struct", | ||
| 2350 | .@"union", | ||
| 2351 | .packed_struct, | ||
| 2352 | .packed_union, | ||
| 2353 | => |tag| if (!found_existing) { | ||
| 2354 | switch (tag) { | ||
| 2355 | .fwd_struct, | ||
| 2356 | .fwd_union, | ||
| 2357 | => { | ||
| 2358 | const owner_decl = global_cty.cast(CType.Payload.FwdDecl).?.data; | ||
| 2359 | _ = try renderTypePrefix(.none, global_store, mod, writer, global_idx, .suffix, .{}); | ||
| 2360 | try writer.writeAll("; // "); | ||
| 2361 | try mod.declPtr(owner_decl).renderFullyQualifiedName(mod, writer); | ||
| 2362 | try writer.writeByte('\n'); | ||
| 2363 | }, | ||
| 2364 | |||
| 2365 | .anon_struct, | ||
| 2366 | .anon_union, | ||
| 2367 | .@"struct", | ||
| 2368 | .@"union", | ||
| 2369 | .packed_struct, | ||
| 2370 | .packed_union, | ||
| 2371 | => { | ||
| 2372 | const fwd_idx = global_cty.cast(CType.Payload.Aggregate).?.data.fwd_decl; | ||
| 2373 | try renderTypeName( | ||
| 2374 | mod, | ||
| 2375 | writer, | ||
| 2376 | fwd_idx, | ||
| 2377 | global_store.indexToCType(fwd_idx), | ||
| 2378 | if (global_cty.isPacked()) "zig_packed(" else "", | ||
| 2379 | ); | ||
| 2380 | try writer.writeByte(' '); | ||
| 2381 | try renderAggregateFields(mod, writer, global_store, global_cty, 0); | ||
| 2382 | if (global_cty.isPacked()) try writer.writeByte(')'); | ||
| 2383 | try writer.writeAll(";\n"); | ||
| 2384 | }, | ||
| 2385 | |||
| 2386 | else => unreachable, | ||
| 2387 | } | ||
| 2388 | }, | ||
| 2389 | |||
| 2390 | else => {}, | ||
| 2675 | } | 2391 | } |
| 2676 | } | 2392 | } |
| 2677 | 2393 | ||
| 2394 | pub fn genGlobalAsm(mod: *Module, writer: anytype) !void { | ||
| 2395 | var it = mod.global_assembly.valueIterator(); | ||
| 2396 | while (it.next()) |asm_source| try writer.print("__asm({s});\n", .{fmtStringLiteral(asm_source.*, null)}); | ||
| 2397 | } | ||
| 2398 | |||
| 2678 | pub fn genErrDecls(o: *Object) !void { | 2399 | pub fn genErrDecls(o: *Object) !void { |
| 2679 | const writer = o.writer(); | 2400 | const writer = o.writer(); |
| 2680 | 2401 | ||
| ... | @@ -2690,26 +2411,24 @@ pub fn genErrDecls(o: *Object) !void { | ... | @@ -2690,26 +2411,24 @@ pub fn genErrDecls(o: *Object) !void { |
| 2690 | o.indent_writer.popIndent(); | 2411 | o.indent_writer.popIndent(); |
| 2691 | try writer.writeAll("};\n"); | 2412 | try writer.writeAll("};\n"); |
| 2692 | 2413 | ||
| 2693 | const name_prefix = "zig_errorName"; | 2414 | const array_identifier = "zig_errorName"; |
| 2694 | const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + "_".len + max_name_len + 1); | 2415 | const name_prefix = array_identifier ++ "_"; |
| 2416 | const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + max_name_len); | ||
| 2695 | defer o.dg.gpa.free(name_buf); | 2417 | defer o.dg.gpa.free(name_buf); |
| 2696 | 2418 | ||
| 2697 | std.mem.copy(u8, name_buf, name_prefix ++ "_"); | 2419 | std.mem.copy(u8, name_buf, name_prefix); |
| 2698 | for (o.dg.module.error_name_list.items) |name| { | 2420 | for (o.dg.module.error_name_list.items) |name| { |
| 2699 | std.mem.copy(u8, name_buf[name_prefix.len + "_".len ..], name); | 2421 | std.mem.copy(u8, name_buf[name_prefix.len..], name); |
| 2700 | name_buf[name_prefix.len + "_".len + name.len] = 0; | 2422 | const identifier = name_buf[0 .. name_prefix.len + name.len]; |
| 2701 | |||
| 2702 | const identifier = name_buf[0 .. name_prefix.len + "_".len + name.len :0]; | ||
| 2703 | const name_z = identifier[name_prefix.len + "_".len ..]; | ||
| 2704 | 2423 | ||
| 2705 | var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; | 2424 | var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; |
| 2706 | const name_ty = Type.initPayload(&name_ty_pl.base); | 2425 | const name_ty = Type.initPayload(&name_ty_pl.base); |
| 2707 | 2426 | ||
| 2708 | var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_z }; | 2427 | var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; |
| 2709 | const name_val = Value.initPayload(&name_pl.base); | 2428 | const name_val = Value.initPayload(&name_pl.base); |
| 2710 | 2429 | ||
| 2711 | try writer.writeAll("static "); | 2430 | try writer.writeAll("static "); |
| 2712 | try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .Const, 0, .Complete); | 2431 | try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, Const, 0, .complete); |
| 2713 | try writer.writeAll(" = "); | 2432 | try writer.writeAll(" = "); |
| 2714 | try o.dg.renderValue(writer, name_ty, name_val, .StaticInitializer); | 2433 | try o.dg.renderValue(writer, name_ty, name_val, .StaticInitializer); |
| 2715 | try writer.writeAll(";\n"); | 2434 | try writer.writeAll(";\n"); |
| ... | @@ -2722,7 +2441,7 @@ pub fn genErrDecls(o: *Object) !void { | ... | @@ -2722,7 +2441,7 @@ pub fn genErrDecls(o: *Object) !void { |
| 2722 | const name_array_ty = Type.initPayload(&name_array_ty_pl.base); | 2441 | const name_array_ty = Type.initPayload(&name_array_ty_pl.base); |
| 2723 | 2442 | ||
| 2724 | try writer.writeAll("static "); | 2443 | try writer.writeAll("static "); |
| 2725 | try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, .Const, 0, .Complete); | 2444 | try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = array_identifier }, Const, 0, .complete); |
| 2726 | try writer.writeAll(" = {"); | 2445 | try writer.writeAll(" = {"); |
| 2727 | for (o.dg.module.error_name_list.items, 0..) |name, value| { | 2446 | for (o.dg.module.error_name_list.items, 0..) |name, value| { |
| 2728 | if (value != 0) try writer.writeByte(','); | 2447 | if (value != 0) try writer.writeByte(','); |
| ... | @@ -2730,7 +2449,7 @@ pub fn genErrDecls(o: *Object) !void { | ... | @@ -2730,7 +2449,7 @@ pub fn genErrDecls(o: *Object) !void { |
| 2730 | var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; | 2449 | var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; |
| 2731 | const len_val = Value.initPayload(&len_pl.base); | 2450 | const len_val = Value.initPayload(&len_pl.base); |
| 2732 | 2451 | ||
| 2733 | try writer.print("{{" ++ name_prefix ++ "_{}, {}}}", .{ | 2452 | try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{ |
| 2734 | fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val), | 2453 | fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val), |
| 2735 | }); | 2454 | }); |
| 2736 | } | 2455 | } |
| ... | @@ -2742,14 +2461,95 @@ fn genExports(o: *Object) !void { | ... | @@ -2742,14 +2461,95 @@ fn genExports(o: *Object) !void { |
| 2742 | defer tracy.end(); | 2461 | defer tracy.end(); |
| 2743 | 2462 | ||
| 2744 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | 2463 | const fwd_decl_writer = o.dg.fwd_decl.writer(); |
| 2745 | if (o.dg.module.decl_exports.get(o.dg.decl_index)) |exports| for (exports.items[1..], 0..) |@"export", i| { | 2464 | if (o.dg.module.decl_exports.get(o.dg.decl_index.unwrap().?)) |exports| { |
| 2746 | try fwd_decl_writer.writeAll("zig_export("); | 2465 | for (exports.items[1..], 1..) |@"export", i| { |
| 2747 | try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, @intCast(u32, 1 + i)); | 2466 | try fwd_decl_writer.writeAll("zig_export("); |
| 2748 | try fwd_decl_writer.print(", {s}, {s});\n", .{ | 2467 | try o.dg.renderFunctionSignature(fwd_decl_writer, o.dg.decl_index.unwrap().?, .forward, .{ .export_index = @intCast(u32, i) }); |
| 2749 | fmtStringLiteral(exports.items[0].options.name), | 2468 | try fwd_decl_writer.print(", {s}, {s});\n", .{ |
| 2750 | fmtStringLiteral(@"export".options.name), | 2469 | fmtStringLiteral(exports.items[0].options.name, null), |
| 2751 | }); | 2470 | fmtStringLiteral(@"export".options.name, null), |
| 2752 | }; | 2471 | }); |
| 2472 | } | ||
| 2473 | } | ||
| 2474 | } | ||
| 2475 | |||
| 2476 | pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { | ||
| 2477 | const w = o.writer(); | ||
| 2478 | const key = lazy_fn.key_ptr.*; | ||
| 2479 | const val = lazy_fn.value_ptr; | ||
| 2480 | const fn_name = val.fn_name; | ||
| 2481 | switch (key) { | ||
| 2482 | .tag_name => { | ||
| 2483 | const enum_ty = val.data.tag_name; | ||
| 2484 | |||
| 2485 | const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); | ||
| 2486 | |||
| 2487 | try w.writeAll("static "); | ||
| 2488 | try o.dg.renderType(w, name_slice_ty); | ||
| 2489 | try w.writeByte(' '); | ||
| 2490 | try w.writeAll(fn_name); | ||
| 2491 | try w.writeByte('('); | ||
| 2492 | try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, 0, .complete); | ||
| 2493 | try w.writeAll(") {\n switch (tag) {\n"); | ||
| 2494 | for (enum_ty.enumFields().keys(), 0..) |name, index| { | ||
| 2495 | var tag_pl: Value.Payload.U32 = .{ | ||
| 2496 | .base = .{ .tag = .enum_field_index }, | ||
| 2497 | .data = @intCast(u32, index), | ||
| 2498 | }; | ||
| 2499 | const tag_val = Value.initPayload(&tag_pl.base); | ||
| 2500 | |||
| 2501 | var int_pl: Value.Payload.U64 = undefined; | ||
| 2502 | const int_val = tag_val.enumToInt(enum_ty, &int_pl); | ||
| 2503 | |||
| 2504 | var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; | ||
| 2505 | const name_ty = Type.initPayload(&name_ty_pl.base); | ||
| 2506 | |||
| 2507 | var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; | ||
| 2508 | const name_val = Value.initPayload(&name_pl.base); | ||
| 2509 | |||
| 2510 | var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; | ||
| 2511 | const len_val = Value.initPayload(&len_pl.base); | ||
| 2512 | |||
| 2513 | try w.print(" case {}: {{\n static ", .{try o.dg.fmtIntLiteral(enum_ty, int_val)}); | ||
| 2514 | try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete); | ||
| 2515 | try w.writeAll(" = "); | ||
| 2516 | try o.dg.renderValue(w, name_ty, name_val, .Initializer); | ||
| 2517 | try w.writeAll(";\n return ("); | ||
| 2518 | try o.dg.renderType(w, name_slice_ty); | ||
| 2519 | try w.print("){{{}, {}}};\n", .{ | ||
| 2520 | fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val), | ||
| 2521 | }); | ||
| 2522 | |||
| 2523 | try w.writeAll(" }\n"); | ||
| 2524 | } | ||
| 2525 | try w.writeAll(" }\n while ("); | ||
| 2526 | try o.dg.renderValue(w, Type.bool, Value.true, .Other); | ||
| 2527 | try w.writeAll(") "); | ||
| 2528 | _ = try airBreakpoint(w); | ||
| 2529 | try w.writeAll("}\n"); | ||
| 2530 | }, | ||
| 2531 | .never_tail, .never_inline => |fn_decl_index| { | ||
| 2532 | const fn_decl = o.dg.module.declPtr(fn_decl_index); | ||
| 2533 | const fn_cty = try o.dg.typeToCType(fn_decl.ty, .complete); | ||
| 2534 | const fn_info = fn_cty.cast(CType.Payload.Function).?.data; | ||
| 2535 | |||
| 2536 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | ||
| 2537 | try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)}); | ||
| 2538 | try o.dg.renderFunctionSignature(fwd_decl_writer, fn_decl_index, .forward, .{ .string = fn_name }); | ||
| 2539 | try fwd_decl_writer.writeAll(";\n"); | ||
| 2540 | |||
| 2541 | try w.print("static zig_{s} ", .{@tagName(key)}); | ||
| 2542 | try o.dg.renderFunctionSignature(w, fn_decl_index, .complete, .{ .string = fn_name }); | ||
| 2543 | try w.writeAll(" {\n return "); | ||
| 2544 | try o.dg.renderDeclName(w, fn_decl_index, 0); | ||
| 2545 | try w.writeByte('('); | ||
| 2546 | for (0..fn_info.param_types.len) |arg| { | ||
| 2547 | if (arg > 0) try w.writeAll(", "); | ||
| 2548 | try o.dg.writeCValue(w, .{ .arg = arg }); | ||
| 2549 | } | ||
| 2550 | try w.writeAll(");\n}\n"); | ||
| 2551 | }, | ||
| 2552 | } | ||
| 2753 | } | 2553 | } |
| 2754 | 2554 | ||
| 2755 | pub fn genFunc(f: *Function) !void { | 2555 | pub fn genFunc(f: *Function) !void { |
| ... | @@ -2758,9 +2558,10 @@ pub fn genFunc(f: *Function) !void { | ... | @@ -2758,9 +2558,10 @@ pub fn genFunc(f: *Function) !void { |
| 2758 | 2558 | ||
| 2759 | const o = &f.object; | 2559 | const o = &f.object; |
| 2760 | const gpa = o.dg.gpa; | 2560 | const gpa = o.dg.gpa; |
| 2561 | const decl_index = o.dg.decl_index.unwrap().?; | ||
| 2761 | const tv: TypedValue = .{ | 2562 | const tv: TypedValue = .{ |
| 2762 | .ty = o.dg.decl.ty, | 2563 | .ty = o.dg.decl.?.ty, |
| 2763 | .val = o.dg.decl.val, | 2564 | .val = o.dg.decl.?.val, |
| 2764 | }; | 2565 | }; |
| 2765 | 2566 | ||
| 2766 | o.code_header = std.ArrayList(u8).init(gpa); | 2567 | o.code_header = std.ArrayList(u8).init(gpa); |
| ... | @@ -2769,13 +2570,13 @@ pub fn genFunc(f: *Function) !void { | ... | @@ -2769,13 +2570,13 @@ pub fn genFunc(f: *Function) !void { |
| 2769 | const is_global = o.dg.declIsGlobal(tv); | 2570 | const is_global = o.dg.declIsGlobal(tv); |
| 2770 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | 2571 | const fwd_decl_writer = o.dg.fwd_decl.writer(); |
| 2771 | try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); | 2572 | try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); |
| 2772 | try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, 0); | 2573 | try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 }); |
| 2773 | try fwd_decl_writer.writeAll(";\n"); | 2574 | try fwd_decl_writer.writeAll(";\n"); |
| 2774 | try genExports(o); | 2575 | try genExports(o); |
| 2775 | 2576 | ||
| 2776 | try o.indent_writer.insertNewline(); | 2577 | try o.indent_writer.insertNewline(); |
| 2777 | if (!is_global) try o.writer().writeAll("static "); | 2578 | if (!is_global) try o.writer().writeAll("static "); |
| 2778 | try o.dg.renderFunctionSignature(o.writer(), .Complete, 0); | 2579 | try o.dg.renderFunctionSignature(o.writer(), decl_index, .complete, .{ .export_index = 0 }); |
| 2779 | try o.writer().writeByte(' '); | 2580 | try o.writer().writeByte(' '); |
| 2780 | 2581 | ||
| 2781 | // In case we need to use the header, populate it with a copy of the function | 2582 | // In case we need to use the header, populate it with a copy of the function |
| ... | @@ -2799,41 +2600,31 @@ pub fn genFunc(f: *Function) !void { | ... | @@ -2799,41 +2600,31 @@ pub fn genFunc(f: *Function) !void { |
| 2799 | // missing. These are added now to complete the map. Then we can sort by | 2600 | // missing. These are added now to complete the map. Then we can sort by |
| 2800 | // alignment, descending. | 2601 | // alignment, descending. |
| 2801 | const free_locals = f.getFreeLocals(); | 2602 | const free_locals = f.getFreeLocals(); |
| 2802 | const values = f.allocs.values(); | 2603 | for (f.allocs.keys(), f.allocs.values()) |local_index, value| { |
| 2803 | for (f.allocs.keys(), 0..) |local_index, i| { | 2604 | if (value) continue; // static |
| 2804 | if (values[i]) continue; // static | ||
| 2805 | const local = f.locals.items[local_index]; | 2605 | const local = f.locals.items[local_index]; |
| 2806 | log.debug("inserting local {d} into free_locals", .{local_index}); | 2606 | log.debug("inserting local {d} into free_locals", .{local_index}); |
| 2807 | const gop = try free_locals.getOrPutContext(gpa, local.ty, f.tyHashCtx()); | 2607 | const gop = try free_locals.getOrPut(gpa, local.getType()); |
| 2808 | if (!gop.found_existing) gop.value_ptr.* = .{}; | 2608 | if (!gop.found_existing) gop.value_ptr.* = .{}; |
| 2809 | try gop.value_ptr.append(gpa, local_index); | 2609 | try gop.value_ptr.putNoClobber(gpa, local_index, {}); |
| 2810 | } | 2610 | } |
| 2811 | 2611 | ||
| 2812 | const SortContext = struct { | 2612 | const SortContext = struct { |
| 2813 | target: std.Target, | 2613 | keys: []const LocalType, |
| 2814 | keys: []const Type, | ||
| 2815 | 2614 | ||
| 2816 | pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { | 2615 | pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { |
| 2817 | const a_ty = ctx.keys[a_index]; | 2616 | const lhs_ty = ctx.keys[lhs_index]; |
| 2818 | const b_ty = ctx.keys[b_index]; | 2617 | const rhs_ty = ctx.keys[rhs_index]; |
| 2819 | return b_ty.abiAlignment(ctx.target) < a_ty.abiAlignment(ctx.target); | 2618 | return lhs_ty.alignas.getAlign() > rhs_ty.alignas.getAlign(); |
| 2820 | } | 2619 | } |
| 2821 | }; | 2620 | }; |
| 2822 | const target = o.dg.module.getTarget(); | 2621 | free_locals.sort(SortContext{ .keys = free_locals.keys() }); |
| 2823 | free_locals.sort(SortContext{ .target = target, .keys = free_locals.keys() }); | ||
| 2824 | 2622 | ||
| 2825 | const w = o.code_header.writer(); | 2623 | const w = o.code_header.writer(); |
| 2826 | for (free_locals.values()) |list| { | 2624 | for (free_locals.values()) |list| { |
| 2827 | for (list.items) |local_index| { | 2625 | for (list.keys()) |local_index| { |
| 2828 | const local = f.locals.items[local_index]; | 2626 | const local = f.locals.items[local_index]; |
| 2829 | try o.dg.renderTypeAndName( | 2627 | try o.dg.renderCTypeAndName(w, local.cty_idx, .{ .local = local_index }, .{}, local.alignas); |
| 2830 | w, | ||
| 2831 | local.ty, | ||
| 2832 | .{ .local = local_index }, | ||
| 2833 | .Mut, | ||
| 2834 | local.alignment, | ||
| 2835 | .Complete, | ||
| 2836 | ); | ||
| 2837 | try w.writeAll(";\n "); | 2628 | try w.writeAll(";\n "); |
| 2838 | } | 2629 | } |
| 2839 | } | 2630 | } |
| ... | @@ -2850,15 +2641,15 @@ pub fn genDecl(o: *Object) !void { | ... | @@ -2850,15 +2641,15 @@ pub fn genDecl(o: *Object) !void { |
| 2850 | const tracy = trace(@src()); | 2641 | const tracy = trace(@src()); |
| 2851 | defer tracy.end(); | 2642 | defer tracy.end(); |
| 2852 | 2643 | ||
| 2853 | const tv: TypedValue = .{ | 2644 | const decl = o.dg.decl.?; |
| 2854 | .ty = o.dg.decl.ty, | 2645 | const decl_c_value = .{ .decl = o.dg.decl_index.unwrap().? }; |
| 2855 | .val = o.dg.decl.val, | 2646 | const tv: TypedValue = .{ .ty = decl.ty, .val = decl.val }; |
| 2856 | }; | 2647 | |
| 2857 | if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime()) return; | 2648 | if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime()) return; |
| 2858 | if (tv.val.tag() == .extern_fn) { | 2649 | if (tv.val.tag() == .extern_fn) { |
| 2859 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | 2650 | const fwd_decl_writer = o.dg.fwd_decl.writer(); |
| 2860 | try fwd_decl_writer.writeAll("zig_extern "); | 2651 | try fwd_decl_writer.writeAll("zig_extern "); |
| 2861 | try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, 0); | 2652 | try o.dg.renderFunctionSignature(fwd_decl_writer, decl_c_value.decl, .forward, .{ .export_index = 0 }); |
| 2862 | try fwd_decl_writer.writeAll(";\n"); | 2653 | try fwd_decl_writer.writeAll(";\n"); |
| 2863 | try genExports(o); | 2654 | try genExports(o); |
| 2864 | } else if (tv.val.castTag(.variable)) |var_payload| { | 2655 | } else if (tv.val.castTag(.variable)) |var_payload| { |
| ... | @@ -2867,11 +2658,9 @@ pub fn genDecl(o: *Object) !void { | ... | @@ -2867,11 +2658,9 @@ pub fn genDecl(o: *Object) !void { |
| 2867 | const is_global = o.dg.declIsGlobal(tv) or variable.is_extern; | 2658 | const is_global = o.dg.declIsGlobal(tv) or variable.is_extern; |
| 2868 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | 2659 | const fwd_decl_writer = o.dg.fwd_decl.writer(); |
| 2869 | 2660 | ||
| 2870 | const decl_c_value = CValue{ .decl = o.dg.decl_index }; | ||
| 2871 | |||
| 2872 | try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); | 2661 | try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); |
| 2873 | if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal "); | 2662 | if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal "); |
| 2874 | try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); | 2663 | try o.dg.renderTypeAndName(fwd_decl_writer, decl.ty, decl_c_value, .{}, decl.@"align", .complete); |
| 2875 | try fwd_decl_writer.writeAll(";\n"); | 2664 | try fwd_decl_writer.writeAll(";\n"); |
| 2876 | try genExports(o); | 2665 | try genExports(o); |
| 2877 | 2666 | ||
| ... | @@ -2880,27 +2669,26 @@ pub fn genDecl(o: *Object) !void { | ... | @@ -2880,27 +2669,26 @@ pub fn genDecl(o: *Object) !void { |
| 2880 | const w = o.writer(); | 2669 | const w = o.writer(); |
| 2881 | if (!is_global) try w.writeAll("static "); | 2670 | if (!is_global) try w.writeAll("static "); |
| 2882 | if (variable.is_threadlocal) try w.writeAll("zig_threadlocal "); | 2671 | if (variable.is_threadlocal) try w.writeAll("zig_threadlocal "); |
| 2883 | if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); | 2672 | if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); |
| 2884 | try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); | 2673 | try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .{}, decl.@"align", .complete); |
| 2885 | if (o.dg.decl.@"linksection" != null) try w.writeAll(", read, write)"); | 2674 | if (decl.@"linksection" != null) try w.writeAll(", read, write)"); |
| 2886 | try w.writeAll(" = "); | 2675 | try w.writeAll(" = "); |
| 2887 | try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer); | 2676 | try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer); |
| 2888 | try w.writeByte(';'); | 2677 | try w.writeByte(';'); |
| 2889 | try o.indent_writer.insertNewline(); | 2678 | try o.indent_writer.insertNewline(); |
| 2890 | } else { | 2679 | } else { |
| 2891 | const is_global = o.dg.module.decl_exports.contains(o.dg.decl_index); | 2680 | const is_global = o.dg.module.decl_exports.contains(decl_c_value.decl); |
| 2892 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | 2681 | const fwd_decl_writer = o.dg.fwd_decl.writer(); |
| 2893 | const decl_c_value: CValue = .{ .decl = o.dg.decl_index }; | ||
| 2894 | 2682 | ||
| 2895 | try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); | 2683 | try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); |
| 2896 | try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete); | 2684 | try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, Const, decl.@"align", .complete); |
| 2897 | try fwd_decl_writer.writeAll(";\n"); | 2685 | try fwd_decl_writer.writeAll(";\n"); |
| 2898 | 2686 | ||
| 2899 | const w = o.writer(); | 2687 | const w = o.writer(); |
| 2900 | if (!is_global) try w.writeAll("static "); | 2688 | if (!is_global) try w.writeAll("static "); |
| 2901 | if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); | 2689 | if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); |
| 2902 | try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete); | 2690 | try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, Const, decl.@"align", .complete); |
| 2903 | if (o.dg.decl.@"linksection" != null) try w.writeAll(", read)"); | 2691 | if (decl.@"linksection" != null) try w.writeAll(", read)"); |
| 2904 | try w.writeAll(" = "); | 2692 | try w.writeAll(" = "); |
| 2905 | try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer); | 2693 | try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer); |
| 2906 | try w.writeAll(";\n"); | 2694 | try w.writeAll(";\n"); |
| ... | @@ -2912,8 +2700,8 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { | ... | @@ -2912,8 +2700,8 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { |
| 2912 | defer tracy.end(); | 2700 | defer tracy.end(); |
| 2913 | 2701 | ||
| 2914 | const tv: TypedValue = .{ | 2702 | const tv: TypedValue = .{ |
| 2915 | .ty = dg.decl.ty, | 2703 | .ty = dg.decl.?.ty, |
| 2916 | .val = dg.decl.val, | 2704 | .val = dg.decl.?.val, |
| 2917 | }; | 2705 | }; |
| 2918 | const writer = dg.fwd_decl.writer(); | 2706 | const writer = dg.fwd_decl.writer(); |
| 2919 | 2707 | ||
| ... | @@ -2922,7 +2710,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { | ... | @@ -2922,7 +2710,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { |
| 2922 | const is_global = dg.declIsGlobal(tv); | 2710 | const is_global = dg.declIsGlobal(tv); |
| 2923 | if (is_global) { | 2711 | if (is_global) { |
| 2924 | try writer.writeAll("zig_extern "); | 2712 | try writer.writeAll("zig_extern "); |
| 2925 | try dg.renderFunctionSignature(writer, .Complete, 0); | 2713 | try dg.renderFunctionSignature(writer, dg.decl_index.unwrap().?, .complete, .{ .export_index = 0 }); |
| 2926 | try dg.fwd_decl.appendSlice(";\n"); | 2714 | try dg.fwd_decl.appendSlice(";\n"); |
| 2927 | } | 2715 | } |
| 2928 | }, | 2716 | }, |
| ... | @@ -2951,7 +2739,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -2951,7 +2739,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 2951 | // zig fmt: off | 2739 | // zig fmt: off |
| 2952 | .constant => unreachable, // excluded from function bodies | 2740 | .constant => unreachable, // excluded from function bodies |
| 2953 | .const_ty => unreachable, // excluded from function bodies | 2741 | .const_ty => unreachable, // excluded from function bodies |
| 2954 | .arg => airArg(f), | 2742 | .arg => try airArg(f, inst), |
| 2955 | 2743 | ||
| 2956 | .breakpoint => try airBreakpoint(f.object.writer()), | 2744 | .breakpoint => try airBreakpoint(f.object.writer()), |
| 2957 | .ret_addr => try airRetAddr(f, inst), | 2745 | .ret_addr => try airRetAddr(f, inst), |
| ... | @@ -3112,10 +2900,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -3112,10 +2900,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 3112 | 2900 | ||
| 3113 | .dbg_block_begin, | 2901 | .dbg_block_begin, |
| 3114 | .dbg_block_end, | 2902 | .dbg_block_end, |
| 3115 | => CValue{ .none = {} }, | 2903 | => .none, |
| 3116 | 2904 | ||
| 3117 | .call => try airCall(f, inst, .auto), | 2905 | .call => try airCall(f, inst, .auto), |
| 3118 | .call_always_tail => try airCall(f, inst, .always_tail), | 2906 | .call_always_tail => .none, |
| 3119 | .call_never_tail => try airCall(f, inst, .never_tail), | 2907 | .call_never_tail => try airCall(f, inst, .never_tail), |
| 3120 | .call_never_inline => try airCall(f, inst, .never_inline), | 2908 | .call_never_inline => try airCall(f, inst, .never_inline), |
| 3121 | 2909 | ||
| ... | @@ -3194,19 +2982,20 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -3194,19 +2982,20 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 3194 | .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), | 2982 | .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), |
| 3195 | .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}), | 2983 | .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}), |
| 3196 | 2984 | ||
| 3197 | .c_va_arg => return f.fail("TODO implement c_va_arg", .{}), | 2985 | .c_va_start => try airCVaStart(f, inst), |
| 3198 | .c_va_copy => return f.fail("TODO implement c_va_copy", .{}), | 2986 | .c_va_arg => try airCVaArg(f, inst), |
| 3199 | .c_va_end => return f.fail("TODO implement c_va_end", .{}), | 2987 | .c_va_end => try airCVaEnd(f, inst), |
| 3200 | .c_va_start => return f.fail("TODO implement c_va_start", .{}), | 2988 | .c_va_copy => try airCVaCopy(f, inst), |
| 3201 | // zig fmt: on | 2989 | // zig fmt: on |
| 3202 | }; | 2990 | }; |
| 3203 | if (result_value == .local) { | 2991 | if (result_value == .new_local) { |
| 3204 | log.debug("map %{d} to t{d}", .{ inst, result_value.local }); | 2992 | log.debug("map %{d} to t{d}", .{ inst, result_value.new_local }); |
| 3205 | } | ||
| 3206 | switch (result_value) { | ||
| 3207 | .none => {}, | ||
| 3208 | else => try f.value_map.putNoClobber(Air.indexToRef(inst), result_value), | ||
| 3209 | } | 2993 | } |
| 2994 | try f.value_map.putNoClobber(Air.indexToRef(inst), switch (result_value) { | ||
| 2995 | .none => continue, | ||
| 2996 | .new_local => |i| .{ .local = i }, | ||
| 2997 | else => result_value, | ||
| 2998 | }); | ||
| 3210 | } | 2999 | } |
| 3211 | } | 3000 | } |
| 3212 | 3001 | ||
| ... | @@ -3215,7 +3004,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [ | ... | @@ -3215,7 +3004,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [ |
| 3215 | 3004 | ||
| 3216 | if (f.liveness.isUnused(inst)) { | 3005 | if (f.liveness.isUnused(inst)) { |
| 3217 | try reap(f, inst, &.{ty_op.operand}); | 3006 | try reap(f, inst, &.{ty_op.operand}); |
| 3218 | return CValue.none; | 3007 | return .none; |
| 3219 | } | 3008 | } |
| 3220 | 3009 | ||
| 3221 | const inst_ty = f.air.typeOfIndex(inst); | 3010 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -3241,7 +3030,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3241,7 +3030,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3241 | !inst_ty.hasRuntimeBitsIgnoreComptime()) | 3030 | !inst_ty.hasRuntimeBitsIgnoreComptime()) |
| 3242 | { | 3031 | { |
| 3243 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3032 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3244 | return CValue.none; | 3033 | return .none; |
| 3245 | } | 3034 | } |
| 3246 | 3035 | ||
| 3247 | const ptr = try f.resolveInst(bin_op.lhs); | 3036 | const ptr = try f.resolveInst(bin_op.lhs); |
| ... | @@ -3267,7 +3056,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3267,7 +3056,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3267 | try writer.writeByte(']'); | 3056 | try writer.writeByte(']'); |
| 3268 | if (is_array) { | 3057 | if (is_array) { |
| 3269 | try writer.writeAll(", sizeof("); | 3058 | try writer.writeAll(", sizeof("); |
| 3270 | try f.renderTypecast(writer, inst_ty); | 3059 | try f.renderType(writer, inst_ty); |
| 3271 | try writer.writeAll("))"); | 3060 | try writer.writeAll("))"); |
| 3272 | } | 3061 | } |
| 3273 | try writer.writeAll(";\n"); | 3062 | try writer.writeAll(";\n"); |
| ... | @@ -3280,9 +3069,10 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3280,9 +3069,10 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3280 | 3069 | ||
| 3281 | if (f.liveness.isUnused(inst)) { | 3070 | if (f.liveness.isUnused(inst)) { |
| 3282 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3071 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3283 | return CValue.none; | 3072 | return .none; |
| 3284 | } | 3073 | } |
| 3285 | 3074 | ||
| 3075 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 3286 | const ptr_ty = f.air.typeOf(bin_op.lhs); | 3076 | const ptr_ty = f.air.typeOf(bin_op.lhs); |
| 3287 | const child_ty = ptr_ty.childType(); | 3077 | const child_ty = ptr_ty.childType(); |
| 3288 | 3078 | ||
| ... | @@ -3297,7 +3087,9 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3297,7 +3087,9 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3297 | const writer = f.object.writer(); | 3087 | const writer = f.object.writer(); |
| 3298 | const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); | 3088 | const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); |
| 3299 | try f.writeCValue(writer, local, .Other); | 3089 | try f.writeCValue(writer, local, .Other); |
| 3300 | try writer.writeAll(" = &("); | 3090 | try writer.writeAll(" = ("); |
| 3091 | try f.renderType(writer, inst_ty); | ||
| 3092 | try writer.writeAll(")&("); | ||
| 3301 | if (ptr_ty.ptrSize() == .One) { | 3093 | if (ptr_ty.ptrSize() == .One) { |
| 3302 | // It's a pointer to an array, so we need to de-reference. | 3094 | // It's a pointer to an array, so we need to de-reference. |
| 3303 | try f.writeCValueDeref(writer, ptr); | 3095 | try f.writeCValueDeref(writer, ptr); |
| ... | @@ -3318,7 +3110,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3318,7 +3110,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3318 | !inst_ty.hasRuntimeBitsIgnoreComptime()) | 3110 | !inst_ty.hasRuntimeBitsIgnoreComptime()) |
| 3319 | { | 3111 | { |
| 3320 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3112 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3321 | return CValue.none; | 3113 | return .none; |
| 3322 | } | 3114 | } |
| 3323 | 3115 | ||
| 3324 | const slice = try f.resolveInst(bin_op.lhs); | 3116 | const slice = try f.resolveInst(bin_op.lhs); |
| ... | @@ -3344,7 +3136,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3344,7 +3136,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3344 | try writer.writeByte(']'); | 3136 | try writer.writeByte(']'); |
| 3345 | if (is_array) { | 3137 | if (is_array) { |
| 3346 | try writer.writeAll(", sizeof("); | 3138 | try writer.writeAll(", sizeof("); |
| 3347 | try f.renderTypecast(writer, inst_ty); | 3139 | try f.renderType(writer, inst_ty); |
| 3348 | try writer.writeAll("))"); | 3140 | try writer.writeAll("))"); |
| 3349 | } | 3141 | } |
| 3350 | try writer.writeAll(";\n"); | 3142 | try writer.writeAll(";\n"); |
| ... | @@ -3357,7 +3149,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3357,7 +3149,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3357 | 3149 | ||
| 3358 | if (f.liveness.isUnused(inst)) { | 3150 | if (f.liveness.isUnused(inst)) { |
| 3359 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3151 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3360 | return CValue.none; | 3152 | return .none; |
| 3361 | } | 3153 | } |
| 3362 | 3154 | ||
| 3363 | const slice_ty = f.air.typeOf(bin_op.lhs); | 3155 | const slice_ty = f.air.typeOf(bin_op.lhs); |
| ... | @@ -3387,7 +3179,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3387,7 +3179,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3387 | const inst_ty = f.air.typeOfIndex(inst); | 3179 | const inst_ty = f.air.typeOfIndex(inst); |
| 3388 | if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) { | 3180 | if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3389 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3181 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3390 | return CValue.none; | 3182 | return .none; |
| 3391 | } | 3183 | } |
| 3392 | 3184 | ||
| 3393 | const array = try f.resolveInst(bin_op.lhs); | 3185 | const array = try f.resolveInst(bin_op.lhs); |
| ... | @@ -3413,7 +3205,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3413,7 +3205,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3413 | try writer.writeByte(']'); | 3205 | try writer.writeByte(']'); |
| 3414 | if (is_array) { | 3206 | if (is_array) { |
| 3415 | try writer.writeAll(", sizeof("); | 3207 | try writer.writeAll(", sizeof("); |
| 3416 | try f.renderTypecast(writer, inst_ty); | 3208 | try f.renderType(writer, inst_ty); |
| 3417 | try writer.writeAll("))"); | 3209 | try writer.writeAll("))"); |
| 3418 | } | 3210 | } |
| 3419 | try writer.writeAll(";\n"); | 3211 | try writer.writeAll(";\n"); |
| ... | @@ -3425,16 +3217,19 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3425,16 +3217,19 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3425 | 3217 | ||
| 3426 | const elem_type = inst_ty.elemType(); | 3218 | const elem_type = inst_ty.elemType(); |
| 3427 | if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) { | 3219 | if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) { |
| 3428 | return CValue{ .undef = inst_ty }; | 3220 | return .{ .undef = inst_ty }; |
| 3429 | } | 3221 | } |
| 3430 | 3222 | ||
| 3431 | const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; | ||
| 3432 | const target = f.object.dg.module.getTarget(); | 3223 | const target = f.object.dg.module.getTarget(); |
| 3433 | const local = try f.allocAlignedLocal(elem_type, mutability, inst_ty.ptrAlignment(target)); | 3224 | const local = try f.allocAlignedLocal( |
| 3434 | log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local }); | 3225 | elem_type, |
| 3226 | CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }), | ||
| 3227 | inst_ty.ptrAlignment(target), | ||
| 3228 | ); | ||
| 3229 | log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); | ||
| 3435 | const gpa = f.object.dg.module.gpa; | 3230 | const gpa = f.object.dg.module.gpa; |
| 3436 | try f.allocs.put(gpa, local.local, false); | 3231 | try f.allocs.put(gpa, local.new_local, false); |
| 3437 | return CValue{ .local_ref = local.local }; | 3232 | return .{ .local_ref = local.new_local }; |
| 3438 | } | 3233 | } |
| 3439 | 3234 | ||
| 3440 | fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { | 3235 | fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -3442,22 +3237,31 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3442,22 +3237,31 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3442 | 3237 | ||
| 3443 | const elem_ty = inst_ty.elemType(); | 3238 | const elem_ty = inst_ty.elemType(); |
| 3444 | if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { | 3239 | if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { |
| 3445 | return CValue{ .undef = inst_ty }; | 3240 | return .{ .undef = inst_ty }; |
| 3446 | } | 3241 | } |
| 3447 | 3242 | ||
| 3448 | const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; | ||
| 3449 | const target = f.object.dg.module.getTarget(); | 3243 | const target = f.object.dg.module.getTarget(); |
| 3450 | const local = try f.allocAlignedLocal(elem_ty, mutability, inst_ty.ptrAlignment(target)); | 3244 | const local = try f.allocAlignedLocal( |
| 3451 | log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local }); | 3245 | elem_ty, |
| 3246 | CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }), | ||
| 3247 | inst_ty.ptrAlignment(target), | ||
| 3248 | ); | ||
| 3249 | log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); | ||
| 3452 | const gpa = f.object.dg.module.gpa; | 3250 | const gpa = f.object.dg.module.gpa; |
| 3453 | try f.allocs.put(gpa, local.local, false); | 3251 | try f.allocs.put(gpa, local.new_local, false); |
| 3454 | return CValue{ .local_ref = local.local }; | 3252 | return .{ .local_ref = local.new_local }; |
| 3455 | } | 3253 | } |
| 3456 | 3254 | ||
| 3457 | fn airArg(f: *Function) CValue { | 3255 | fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3256 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 3257 | const inst_cty = try f.typeToIndex(inst_ty, .parameter); | ||
| 3258 | |||
| 3458 | const i = f.next_arg_index; | 3259 | const i = f.next_arg_index; |
| 3459 | f.next_arg_index += 1; | 3260 | f.next_arg_index += 1; |
| 3460 | return .{ .arg = i }; | 3261 | return if (inst_cty != try f.typeToIndex(inst_ty, .complete)) |
| 3262 | .{ .arg_array = i } | ||
| 3263 | else | ||
| 3264 | .{ .arg = i }; | ||
| 3461 | } | 3265 | } |
| 3462 | 3266 | ||
| 3463 | fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | 3267 | fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -3469,7 +3273,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3469,7 +3273,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3469 | (!ptr_info.@"volatile" and f.liveness.isUnused(inst))) | 3273 | (!ptr_info.@"volatile" and f.liveness.isUnused(inst))) |
| 3470 | { | 3274 | { |
| 3471 | try reap(f, inst, &.{ty_op.operand}); | 3275 | try reap(f, inst, &.{ty_op.operand}); |
| 3472 | return CValue.none; | 3276 | return .none; |
| 3473 | } | 3277 | } |
| 3474 | 3278 | ||
| 3475 | const operand = try f.resolveInst(ty_op.operand); | 3279 | const operand = try f.resolveInst(ty_op.operand); |
| ... | @@ -3491,7 +3295,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3491,7 +3295,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3491 | try writer.writeAll(", (const char *)"); | 3295 | try writer.writeAll(", (const char *)"); |
| 3492 | try f.writeCValue(writer, operand, .Other); | 3296 | try f.writeCValue(writer, operand, .Other); |
| 3493 | try writer.writeAll(", sizeof("); | 3297 | try writer.writeAll(", sizeof("); |
| 3494 | try f.renderTypecast(writer, src_ty); | 3298 | try f.renderType(writer, src_ty); |
| 3495 | try writer.writeAll("))"); | 3299 | try writer.writeAll("))"); |
| 3496 | } else if (ptr_info.host_size != 0) { | 3300 | } else if (ptr_info.host_size != 0) { |
| 3497 | var host_pl = Type.Payload.Bits{ | 3301 | var host_pl = Type.Payload.Bits{ |
| ... | @@ -3520,11 +3324,11 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3520,11 +3324,11 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3520 | 3324 | ||
| 3521 | try f.writeCValue(writer, local, .Other); | 3325 | try f.writeCValue(writer, local, .Other); |
| 3522 | try writer.writeAll(" = ("); | 3326 | try writer.writeAll(" = ("); |
| 3523 | try f.renderTypecast(writer, src_ty); | 3327 | try f.renderType(writer, src_ty); |
| 3524 | try writer.writeAll(")zig_wrap_"); | 3328 | try writer.writeAll(")zig_wrap_"); |
| 3525 | try f.object.dg.renderTypeForBuiltinFnName(writer, field_ty); | 3329 | try f.object.dg.renderTypeForBuiltinFnName(writer, field_ty); |
| 3526 | try writer.writeAll("(("); | 3330 | try writer.writeAll("(("); |
| 3527 | try f.renderTypecast(writer, field_ty); | 3331 | try f.renderType(writer, field_ty); |
| 3528 | try writer.writeByte(')'); | 3332 | try writer.writeByte(')'); |
| 3529 | const cant_cast = host_ty.isInt() and host_ty.bitSize(target) > 64; | 3333 | const cant_cast = host_ty.isInt() and host_ty.bitSize(target) > 64; |
| 3530 | if (cant_cast) { | 3334 | if (cant_cast) { |
| ... | @@ -3554,20 +3358,24 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { | ... | @@ -3554,20 +3358,24 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { |
| 3554 | const un_op = f.air.instructions.items(.data)[inst].un_op; | 3358 | const un_op = f.air.instructions.items(.data)[inst].un_op; |
| 3555 | const writer = f.object.writer(); | 3359 | const writer = f.object.writer(); |
| 3556 | const target = f.object.dg.module.getTarget(); | 3360 | const target = f.object.dg.module.getTarget(); |
| 3361 | const op_inst = Air.refToIndex(un_op); | ||
| 3557 | const op_ty = f.air.typeOf(un_op); | 3362 | const op_ty = f.air.typeOf(un_op); |
| 3558 | const ret_ty = if (is_ptr) op_ty.childType() else op_ty; | 3363 | const ret_ty = if (is_ptr) op_ty.childType() else op_ty; |
| 3559 | var lowered_ret_buf: LowerFnRetTyBuffer = undefined; | 3364 | var lowered_ret_buf: LowerFnRetTyBuffer = undefined; |
| 3560 | const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); | 3365 | const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); |
| 3561 | 3366 | ||
| 3562 | if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { | 3367 | if (op_inst != null and f.air.instructions.items(.tag)[op_inst.?] == .call_always_tail) { |
| 3563 | var deref = is_ptr; | 3368 | try reap(f, inst, &.{un_op}); |
| 3369 | _ = try airCall(f, op_inst.?, .always_tail); | ||
| 3370 | } else if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { | ||
| 3564 | const operand = try f.resolveInst(un_op); | 3371 | const operand = try f.resolveInst(un_op); |
| 3565 | try reap(f, inst, &.{un_op}); | 3372 | try reap(f, inst, &.{un_op}); |
| 3373 | var deref = is_ptr; | ||
| 3566 | const is_array = lowersToArray(ret_ty, target); | 3374 | const is_array = lowersToArray(ret_ty, target); |
| 3567 | const ret_val = if (is_array) ret_val: { | 3375 | const ret_val = if (is_array) ret_val: { |
| 3568 | const array_local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator())); | 3376 | const array_local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator())); |
| 3569 | try writer.writeAll("memcpy("); | 3377 | try writer.writeAll("memcpy("); |
| 3570 | try f.writeCValueMember(writer, array_local, .{ .field = 0 }); | 3378 | try f.writeCValueMember(writer, array_local, .{ .identifier = "array" }); |
| 3571 | try writer.writeAll(", "); | 3379 | try writer.writeAll(", "); |
| 3572 | if (deref) | 3380 | if (deref) |
| 3573 | try f.writeCValueDeref(writer, operand) | 3381 | try f.writeCValueDeref(writer, operand) |
| ... | @@ -3575,7 +3383,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { | ... | @@ -3575,7 +3383,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { |
| 3575 | try f.writeCValue(writer, operand, .FunctionArgument); | 3383 | try f.writeCValue(writer, operand, .FunctionArgument); |
| 3576 | deref = false; | 3384 | deref = false; |
| 3577 | try writer.writeAll(", sizeof("); | 3385 | try writer.writeAll(", sizeof("); |
| 3578 | try f.renderTypecast(writer, ret_ty); | 3386 | try f.renderType(writer, ret_ty); |
| 3579 | try writer.writeAll("));\n"); | 3387 | try writer.writeAll("));\n"); |
| 3580 | break :ret_val array_local; | 3388 | break :ret_val array_local; |
| 3581 | } else operand; | 3389 | } else operand; |
| ... | @@ -3587,16 +3395,15 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { | ... | @@ -3587,16 +3395,15 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { |
| 3587 | try f.writeCValue(writer, ret_val, .Other); | 3395 | try f.writeCValue(writer, ret_val, .Other); |
| 3588 | try writer.writeAll(";\n"); | 3396 | try writer.writeAll(";\n"); |
| 3589 | if (is_array) { | 3397 | if (is_array) { |
| 3590 | try freeLocal(f, inst, ret_val.local, 0); | 3398 | try freeLocal(f, inst, ret_val.new_local, 0); |
| 3591 | } | 3399 | } |
| 3592 | } else { | 3400 | } else { |
| 3593 | try reap(f, inst, &.{un_op}); | 3401 | try reap(f, inst, &.{un_op}); |
| 3594 | if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) { | 3402 | // Not even allowed to return void in a naked function. |
| 3595 | // Not even allowed to return void in a naked function. | 3403 | if (if (f.object.dg.decl) |decl| decl.ty.fnCallingConvention() != .Naked else true) |
| 3596 | try writer.writeAll("return;\n"); | 3404 | try writer.writeAll("return;\n"); |
| 3597 | } | ||
| 3598 | } | 3405 | } |
| 3599 | return CValue.none; | 3406 | return .none; |
| 3600 | } | 3407 | } |
| 3601 | 3408 | ||
| 3602 | fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { | 3409 | fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -3604,7 +3411,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3604,7 +3411,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3604 | 3411 | ||
| 3605 | if (f.liveness.isUnused(inst)) { | 3412 | if (f.liveness.isUnused(inst)) { |
| 3606 | try reap(f, inst, &.{ty_op.operand}); | 3413 | try reap(f, inst, &.{ty_op.operand}); |
| 3607 | return CValue.none; | 3414 | return .none; |
| 3608 | } | 3415 | } |
| 3609 | 3416 | ||
| 3610 | const operand = try f.resolveInst(ty_op.operand); | 3417 | const operand = try f.resolveInst(ty_op.operand); |
| ... | @@ -3625,7 +3432,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3625,7 +3432,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3625 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | 3432 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 3626 | if (f.liveness.isUnused(inst)) { | 3433 | if (f.liveness.isUnused(inst)) { |
| 3627 | try reap(f, inst, &.{ty_op.operand}); | 3434 | try reap(f, inst, &.{ty_op.operand}); |
| 3628 | return CValue.none; | 3435 | return .none; |
| 3629 | } | 3436 | } |
| 3630 | 3437 | ||
| 3631 | const operand = try f.resolveInst(ty_op.operand); | 3438 | const operand = try f.resolveInst(ty_op.operand); |
| ... | @@ -3644,15 +3451,17 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3644,15 +3451,17 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3644 | try f.writeCValue(writer, local, .Other); | 3451 | try f.writeCValue(writer, local, .Other); |
| 3645 | try writer.writeAll(" = "); | 3452 | try writer.writeAll(" = "); |
| 3646 | 3453 | ||
| 3454 | if (dest_c_bits < 64) { | ||
| 3455 | try writer.writeByte('('); | ||
| 3456 | try f.renderType(writer, inst_ty); | ||
| 3457 | try writer.writeByte(')'); | ||
| 3458 | } | ||
| 3459 | |||
| 3647 | const needs_lo = operand_int_info.bits > 64 and dest_bits <= 64; | 3460 | const needs_lo = operand_int_info.bits > 64 and dest_bits <= 64; |
| 3648 | if (needs_lo) { | 3461 | if (needs_lo) { |
| 3649 | try writer.writeAll("zig_lo_"); | 3462 | try writer.writeAll("zig_lo_"); |
| 3650 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); | 3463 | try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); |
| 3651 | try writer.writeByte('('); | 3464 | try writer.writeByte('('); |
| 3652 | } else if (dest_c_bits <= 64) { | ||
| 3653 | try writer.writeByte('('); | ||
| 3654 | try f.renderTypecast(writer, inst_ty); | ||
| 3655 | try writer.writeByte(')'); | ||
| 3656 | } | 3465 | } |
| 3657 | 3466 | ||
| 3658 | if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { | 3467 | if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { |
| ... | @@ -3712,7 +3521,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3712,7 +3521,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3712 | const un_op = f.air.instructions.items(.data)[inst].un_op; | 3521 | const un_op = f.air.instructions.items(.data)[inst].un_op; |
| 3713 | if (f.liveness.isUnused(inst)) { | 3522 | if (f.liveness.isUnused(inst)) { |
| 3714 | try reap(f, inst, &.{un_op}); | 3523 | try reap(f, inst, &.{un_op}); |
| 3715 | return CValue.none; | 3524 | return .none; |
| 3716 | } | 3525 | } |
| 3717 | const operand = try f.resolveInst(un_op); | 3526 | const operand = try f.resolveInst(un_op); |
| 3718 | try reap(f, inst, &.{un_op}); | 3527 | try reap(f, inst, &.{un_op}); |
| ... | @@ -3732,10 +3541,10 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { | ... | @@ -3732,10 +3541,10 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { |
| 3732 | try writer.writeAll("memset("); | 3541 | try writer.writeAll("memset("); |
| 3733 | try f.writeCValue(writer, dest_ptr, .FunctionArgument); | 3542 | try f.writeCValue(writer, dest_ptr, .FunctionArgument); |
| 3734 | try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); | 3543 | try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); |
| 3735 | try f.renderTypecast(writer, lhs_child_ty); | 3544 | try f.renderType(writer, lhs_child_ty); |
| 3736 | try writer.writeAll("));\n"); | 3545 | try writer.writeAll("));\n"); |
| 3737 | } | 3546 | } |
| 3738 | return CValue.none; | 3547 | return .none; |
| 3739 | } | 3548 | } |
| 3740 | 3549 | ||
| 3741 | fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | 3550 | fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -3744,7 +3553,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3744,7 +3553,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3744 | const ptr_info = f.air.typeOf(bin_op.lhs).ptrInfo().data; | 3553 | const ptr_info = f.air.typeOf(bin_op.lhs).ptrInfo().data; |
| 3745 | if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) { | 3554 | if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) { |
| 3746 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3555 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3747 | return CValue.none; | 3556 | return .none; |
| 3748 | } | 3557 | } |
| 3749 | 3558 | ||
| 3750 | const ptr_val = try f.resolveInst(bin_op.lhs); | 3559 | const ptr_val = try f.resolveInst(bin_op.lhs); |
| ... | @@ -3793,10 +3602,10 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3793,10 +3602,10 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3793 | if (!is_array) try writer.writeByte('&'); | 3602 | if (!is_array) try writer.writeByte('&'); |
| 3794 | try f.writeCValue(writer, array_src, .FunctionArgument); | 3603 | try f.writeCValue(writer, array_src, .FunctionArgument); |
| 3795 | try writer.writeAll(", sizeof("); | 3604 | try writer.writeAll(", sizeof("); |
| 3796 | try f.renderTypecast(writer, src_ty); | 3605 | try f.renderType(writer, src_ty); |
| 3797 | try writer.writeAll("))"); | 3606 | try writer.writeAll("))"); |
| 3798 | if (src_val == .constant) { | 3607 | if (src_val == .constant) { |
| 3799 | try freeLocal(f, inst, array_src.local, 0); | 3608 | try freeLocal(f, inst, array_src.new_local, 0); |
| 3800 | } | 3609 | } |
| 3801 | } else if (ptr_info.host_size != 0) { | 3610 | } else if (ptr_info.host_size != 0) { |
| 3802 | const host_bits = ptr_info.host_size * 8; | 3611 | const host_bits = ptr_info.host_size * 8; |
| ... | @@ -3847,18 +3656,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3847,18 +3656,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3847 | const cant_cast = host_ty.isInt() and host_ty.bitSize(target) > 64; | 3656 | const cant_cast = host_ty.isInt() and host_ty.bitSize(target) > 64; |
| 3848 | if (cant_cast) { | 3657 | if (cant_cast) { |
| 3849 | if (src_ty.bitSize(target) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{}); | 3658 | if (src_ty.bitSize(target) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{}); |
| 3850 | try writer.writeAll("zig_as_"); | 3659 | try writer.writeAll("zig_make_"); |
| 3851 | try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); | 3660 | try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); |
| 3852 | try writer.writeAll("(0, "); | 3661 | try writer.writeAll("(0, "); |
| 3853 | } else { | 3662 | } else { |
| 3854 | try writer.writeByte('('); | 3663 | try writer.writeByte('('); |
| 3855 | try f.renderTypecast(writer, host_ty); | 3664 | try f.renderType(writer, host_ty); |
| 3856 | try writer.writeByte(')'); | 3665 | try writer.writeByte(')'); |
| 3857 | } | 3666 | } |
| 3858 | 3667 | ||
| 3859 | if (src_ty.isPtrAtRuntime()) { | 3668 | if (src_ty.isPtrAtRuntime()) { |
| 3860 | try writer.writeByte('('); | 3669 | try writer.writeByte('('); |
| 3861 | try f.renderTypecast(writer, Type.usize); | 3670 | try f.renderType(writer, Type.usize); |
| 3862 | try writer.writeByte(')'); | 3671 | try writer.writeByte(')'); |
| 3863 | } | 3672 | } |
| 3864 | try f.writeCValue(writer, src_val, .Other); | 3673 | try f.writeCValue(writer, src_val, .Other); |
| ... | @@ -3870,7 +3679,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3870,7 +3679,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3870 | try f.writeCValue(writer, src_val, .Other); | 3679 | try f.writeCValue(writer, src_val, .Other); |
| 3871 | } | 3680 | } |
| 3872 | try writer.writeAll(";\n"); | 3681 | try writer.writeAll(";\n"); |
| 3873 | return CValue.none; | 3682 | return .none; |
| 3874 | } | 3683 | } |
| 3875 | 3684 | ||
| 3876 | fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue { | 3685 | fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue { |
| ... | @@ -3879,7 +3688,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: | ... | @@ -3879,7 +3688,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: |
| 3879 | 3688 | ||
| 3880 | if (f.liveness.isUnused(inst)) { | 3689 | if (f.liveness.isUnused(inst)) { |
| 3881 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3690 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3882 | return CValue.none; | 3691 | return .none; |
| 3883 | } | 3692 | } |
| 3884 | 3693 | ||
| 3885 | const lhs = try f.resolveInst(bin_op.lhs); | 3694 | const lhs = try f.resolveInst(bin_op.lhs); |
| ... | @@ -3935,7 +3744,7 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3935,7 +3744,7 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3935 | 3744 | ||
| 3936 | if (f.liveness.isUnused(inst)) { | 3745 | if (f.liveness.isUnused(inst)) { |
| 3937 | try reap(f, inst, &.{ty_op.operand}); | 3746 | try reap(f, inst, &.{ty_op.operand}); |
| 3938 | return CValue.none; | 3747 | return .none; |
| 3939 | } | 3748 | } |
| 3940 | 3749 | ||
| 3941 | const op = try f.resolveInst(ty_op.operand); | 3750 | const op = try f.resolveInst(ty_op.operand); |
| ... | @@ -3970,7 +3779,7 @@ fn airBinOp( | ... | @@ -3970,7 +3779,7 @@ fn airBinOp( |
| 3970 | 3779 | ||
| 3971 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3780 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3972 | 3781 | ||
| 3973 | if (f.liveness.isUnused(inst)) return CValue.none; | 3782 | if (f.liveness.isUnused(inst)) return .none; |
| 3974 | 3783 | ||
| 3975 | const inst_ty = f.air.typeOfIndex(inst); | 3784 | const inst_ty = f.air.typeOfIndex(inst); |
| 3976 | 3785 | ||
| ... | @@ -3993,7 +3802,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: | ... | @@ -3993,7 +3802,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: |
| 3993 | 3802 | ||
| 3994 | if (f.liveness.isUnused(inst)) { | 3803 | if (f.liveness.isUnused(inst)) { |
| 3995 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3804 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 3996 | return CValue.none; | 3805 | return .none; |
| 3997 | } | 3806 | } |
| 3998 | 3807 | ||
| 3999 | const operand_ty = f.air.typeOf(bin_op.lhs); | 3808 | const operand_ty = f.air.typeOf(bin_op.lhs); |
| ... | @@ -4033,7 +3842,7 @@ fn airEquality( | ... | @@ -4033,7 +3842,7 @@ fn airEquality( |
| 4033 | 3842 | ||
| 4034 | if (f.liveness.isUnused(inst)) { | 3843 | if (f.liveness.isUnused(inst)) { |
| 4035 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3844 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 4036 | return CValue.none; | 3845 | return .none; |
| 4037 | } | 3846 | } |
| 4038 | 3847 | ||
| 4039 | const operand_ty = f.air.typeOf(bin_op.lhs); | 3848 | const operand_ty = f.air.typeOf(bin_op.lhs); |
| ... | @@ -4089,7 +3898,7 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4089,7 +3898,7 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4089 | 3898 | ||
| 4090 | if (f.liveness.isUnused(inst)) { | 3899 | if (f.liveness.isUnused(inst)) { |
| 4091 | try reap(f, inst, &.{un_op}); | 3900 | try reap(f, inst, &.{un_op}); |
| 4092 | return CValue.none; | 3901 | return .none; |
| 4093 | } | 3902 | } |
| 4094 | 3903 | ||
| 4095 | const inst_ty = f.air.typeOfIndex(inst); | 3904 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -4110,7 +3919,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { | ... | @@ -4110,7 +3919,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { |
| 4110 | const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; | 3919 | const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; |
| 4111 | if (f.liveness.isUnused(inst)) { | 3920 | if (f.liveness.isUnused(inst)) { |
| 4112 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3921 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 4113 | return CValue.none; | 3922 | return .none; |
| 4114 | } | 3923 | } |
| 4115 | 3924 | ||
| 4116 | const lhs = try f.resolveInst(bin_op.lhs); | 3925 | const lhs = try f.resolveInst(bin_op.lhs); |
| ... | @@ -4118,32 +3927,31 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { | ... | @@ -4118,32 +3927,31 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { |
| 4118 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3927 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 4119 | 3928 | ||
| 4120 | const inst_ty = f.air.typeOfIndex(inst); | 3929 | const inst_ty = f.air.typeOfIndex(inst); |
| 4121 | const elem_ty = switch (inst_ty.ptrSize()) { | 3930 | const elem_ty = inst_ty.elemType2(); |
| 4122 | .One => blk: { | ||
| 4123 | const array_ty = inst_ty.childType(); | ||
| 4124 | break :blk array_ty.childType(); | ||
| 4125 | }, | ||
| 4126 | else => inst_ty.childType(), | ||
| 4127 | }; | ||
| 4128 | 3931 | ||
| 4129 | // We must convert to and from integer types to prevent UB if the operation | 3932 | const local = try f.allocLocal(inst, inst_ty); |
| 4130 | // results in a NULL pointer, or if LHS is NULL. The operation is only UB | 3933 | const writer = f.object.writer(); |
| 4131 | // if the result is NULL and then dereferenced. | 3934 | try f.writeCValue(writer, local, .Other); |
| 4132 | const local = try f.allocLocal(inst, inst_ty); | 3935 | try writer.writeAll(" = "); |
| 4133 | const writer = f.object.writer(); | 3936 | |
| 4134 | try f.writeCValue(writer, local, .Other); | 3937 | if (elem_ty.hasRuntimeBitsIgnoreComptime()) { |
| 4135 | try writer.writeAll(" = ("); | 3938 | // We must convert to and from integer types to prevent UB if the operation |
| 4136 | try f.renderTypecast(writer, inst_ty); | 3939 | // results in a NULL pointer, or if LHS is NULL. The operation is only UB |
| 4137 | try writer.writeAll(")(((uintptr_t)"); | 3940 | // if the result is NULL and then dereferenced. |
| 4138 | try f.writeCValue(writer, lhs, .Other); | 3941 | try writer.writeByte('('); |
| 4139 | try writer.writeAll(") "); | 3942 | try f.renderType(writer, inst_ty); |
| 4140 | try writer.writeByte(operator); | 3943 | try writer.writeAll(")(((uintptr_t)"); |
| 4141 | try writer.writeAll(" ("); | 3944 | try f.writeCValue(writer, lhs, .Other); |
| 4142 | try f.writeCValue(writer, rhs, .Other); | 3945 | try writer.writeAll(") "); |
| 4143 | try writer.writeAll("*sizeof("); | 3946 | try writer.writeByte(operator); |
| 4144 | try f.renderTypecast(writer, elem_ty); | 3947 | try writer.writeAll(" ("); |
| 4145 | try writer.writeAll(")));\n"); | 3948 | try f.writeCValue(writer, rhs, .Other); |
| 3949 | try writer.writeAll("*sizeof("); | ||
| 3950 | try f.renderType(writer, elem_ty); | ||
| 3951 | try writer.writeAll(")))"); | ||
| 3952 | } else try f.writeCValue(writer, lhs, .Initializer); | ||
| 4146 | 3953 | ||
| 3954 | try writer.writeAll(";\n"); | ||
| 4147 | return local; | 3955 | return local; |
| 4148 | } | 3956 | } |
| 4149 | 3957 | ||
| ... | @@ -4152,7 +3960,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons | ... | @@ -4152,7 +3960,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons |
| 4152 | 3960 | ||
| 4153 | if (f.liveness.isUnused(inst)) { | 3961 | if (f.liveness.isUnused(inst)) { |
| 4154 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 3962 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 4155 | return CValue.none; | 3963 | return .none; |
| 4156 | } | 3964 | } |
| 4157 | 3965 | ||
| 4158 | const inst_ty = f.air.typeOfIndex(inst); | 3966 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -4191,7 +3999,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4191,7 +3999,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4191 | 3999 | ||
| 4192 | if (f.liveness.isUnused(inst)) { | 4000 | if (f.liveness.isUnused(inst)) { |
| 4193 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 4001 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 4194 | return CValue.none; | 4002 | return .none; |
| 4195 | } | 4003 | } |
| 4196 | 4004 | ||
| 4197 | const ptr = try f.resolveInst(bin_op.lhs); | 4005 | const ptr = try f.resolveInst(bin_op.lhs); |
| ... | @@ -4204,7 +4012,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4204,7 +4012,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4204 | try f.writeCValue(writer, local, .Other); | 4012 | try f.writeCValue(writer, local, .Other); |
| 4205 | try writer.writeAll(".ptr = ("); | 4013 | try writer.writeAll(".ptr = ("); |
| 4206 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 4014 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 4207 | try f.renderTypecast(writer, inst_ty.slicePtrFieldType(&buf)); | 4015 | try f.renderType(writer, inst_ty.slicePtrFieldType(&buf)); |
| 4208 | try writer.writeByte(')'); | 4016 | try writer.writeByte(')'); |
| 4209 | try f.writeCValue(writer, ptr, .Other); | 4017 | try f.writeCValue(writer, ptr, .Other); |
| 4210 | try writer.writeAll("; "); | 4018 | try writer.writeAll("; "); |
| ... | @@ -4222,24 +4030,41 @@ fn airCall( | ... | @@ -4222,24 +4030,41 @@ fn airCall( |
| 4222 | modifier: std.builtin.CallModifier, | 4030 | modifier: std.builtin.CallModifier, |
| 4223 | ) !CValue { | 4031 | ) !CValue { |
| 4224 | // Not even allowed to call panic in a naked function. | 4032 | // Not even allowed to call panic in a naked function. |
| 4225 | if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none; | 4033 | if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() == .Naked) return .none; |
| 4034 | |||
| 4226 | const gpa = f.object.dg.gpa; | 4035 | const gpa = f.object.dg.gpa; |
| 4036 | const module = f.object.dg.module; | ||
| 4037 | const target = module.getTarget(); | ||
| 4038 | const writer = f.object.writer(); | ||
| 4227 | 4039 | ||
| 4228 | switch (modifier) { | ||
| 4229 | .auto => {}, | ||
| 4230 | .always_tail => return f.fail("TODO: C backend: call with always_tail attribute", .{}), | ||
| 4231 | .never_tail => return f.fail("TODO: C backend: call with never_tail attribute", .{}), | ||
| 4232 | .never_inline => return f.fail("TODO: C backend: call with never_inline attribute", .{}), | ||
| 4233 | else => unreachable, | ||
| 4234 | } | ||
| 4235 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | 4040 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; |
| 4236 | const extra = f.air.extraData(Air.Call, pl_op.payload); | 4041 | const extra = f.air.extraData(Air.Call, pl_op.payload); |
| 4237 | const args = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); | 4042 | const args = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); |
| 4238 | 4043 | ||
| 4239 | const resolved_args = try gpa.alloc(CValue, args.len); | 4044 | const resolved_args = try gpa.alloc(CValue, args.len); |
| 4240 | defer gpa.free(resolved_args); | 4045 | defer gpa.free(resolved_args); |
| 4241 | for (args, 0..) |arg, i| { | 4046 | for (resolved_args, args) |*resolved_arg, arg| { |
| 4242 | resolved_args[i] = try f.resolveInst(arg); | 4047 | const arg_ty = f.air.typeOf(arg); |
| 4048 | const arg_cty = try f.typeToIndex(arg_ty, .parameter); | ||
| 4049 | if (f.indexToCType(arg_cty).tag() == .void) { | ||
| 4050 | resolved_arg.* = .none; | ||
| 4051 | continue; | ||
| 4052 | } | ||
| 4053 | resolved_arg.* = try f.resolveInst(arg); | ||
| 4054 | if (arg_cty != try f.typeToIndex(arg_ty, .complete)) { | ||
| 4055 | var lowered_arg_buf: LowerFnRetTyBuffer = undefined; | ||
| 4056 | const lowered_arg_ty = lowerFnRetTy(arg_ty, &lowered_arg_buf, target); | ||
| 4057 | |||
| 4058 | const array_local = try f.allocLocal(inst, try lowered_arg_ty.copy(f.arena.allocator())); | ||
| 4059 | try writer.writeAll("memcpy("); | ||
| 4060 | try f.writeCValueMember(writer, array_local, .{ .identifier = "array" }); | ||
| 4061 | try writer.writeAll(", "); | ||
| 4062 | try f.writeCValue(writer, resolved_arg.*, .FunctionArgument); | ||
| 4063 | try writer.writeAll(", sizeof("); | ||
| 4064 | try f.renderType(writer, lowered_arg_ty); | ||
| 4065 | try writer.writeAll("));\n"); | ||
| 4066 | resolved_arg.* = array_local; | ||
| 4067 | } | ||
| 4243 | } | 4068 | } |
| 4244 | 4069 | ||
| 4245 | const callee = try f.resolveInst(pl_op.operand); | 4070 | const callee = try f.resolveInst(pl_op.operand); |
| ... | @@ -4256,18 +4081,19 @@ fn airCall( | ... | @@ -4256,18 +4081,19 @@ fn airCall( |
| 4256 | .Pointer => callee_ty.childType(), | 4081 | .Pointer => callee_ty.childType(), |
| 4257 | else => unreachable, | 4082 | else => unreachable, |
| 4258 | }; | 4083 | }; |
| 4259 | const writer = f.object.writer(); | ||
| 4260 | 4084 | ||
| 4261 | const target = f.object.dg.module.getTarget(); | ||
| 4262 | const ret_ty = fn_ty.fnReturnType(); | 4085 | const ret_ty = fn_ty.fnReturnType(); |
| 4263 | var lowered_ret_buf: LowerFnRetTyBuffer = undefined; | 4086 | var lowered_ret_buf: LowerFnRetTyBuffer = undefined; |
| 4264 | const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); | 4087 | const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); |
| 4265 | 4088 | ||
| 4266 | const result_local: CValue = if (!lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) | 4089 | const result_local = if (modifier == .always_tail) r: { |
| 4090 | try writer.writeAll("zig_always_tail return "); | ||
| 4091 | break :r .none; | ||
| 4092 | } else if (!lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) | ||
| 4267 | .none | 4093 | .none |
| 4268 | else if (f.liveness.isUnused(inst)) r: { | 4094 | else if (f.liveness.isUnused(inst)) r: { |
| 4269 | try writer.writeByte('('); | 4095 | try writer.writeByte('('); |
| 4270 | try f.renderTypecast(writer, Type.void); | 4096 | try f.renderType(writer, Type.void); |
| 4271 | try writer.writeByte(')'); | 4097 | try writer.writeByte(')'); |
| 4272 | break :r .none; | 4098 | break :r .none; |
| 4273 | } else r: { | 4099 | } else r: { |
| ... | @@ -4277,48 +4103,44 @@ fn airCall( | ... | @@ -4277,48 +4103,44 @@ fn airCall( |
| 4277 | break :r local; | 4103 | break :r local; |
| 4278 | }; | 4104 | }; |
| 4279 | 4105 | ||
| 4280 | var is_extern = false; | ||
| 4281 | var name: [*:0]const u8 = ""; | ||
| 4282 | callee: { | 4106 | callee: { |
| 4283 | known: { | 4107 | known: { |
| 4284 | const fn_decl = fn_decl: { | 4108 | const fn_decl = fn_decl: { |
| 4285 | const callee_val = f.air.value(pl_op.operand) orelse break :known; | 4109 | const callee_val = f.air.value(pl_op.operand) orelse break :known; |
| 4286 | break :fn_decl switch (callee_val.tag()) { | 4110 | break :fn_decl switch (callee_val.tag()) { |
| 4287 | .extern_fn => blk: { | 4111 | .extern_fn => callee_val.castTag(.extern_fn).?.data.owner_decl, |
| 4288 | is_extern = true; | ||
| 4289 | break :blk callee_val.castTag(.extern_fn).?.data.owner_decl; | ||
| 4290 | }, | ||
| 4291 | .function => callee_val.castTag(.function).?.data.owner_decl, | 4112 | .function => callee_val.castTag(.function).?.data.owner_decl, |
| 4292 | .decl_ref => callee_val.castTag(.decl_ref).?.data, | 4113 | .decl_ref => callee_val.castTag(.decl_ref).?.data, |
| 4293 | else => break :known, | 4114 | else => break :known, |
| 4294 | }; | 4115 | }; |
| 4295 | }; | 4116 | }; |
| 4296 | name = f.object.dg.module.declPtr(fn_decl).name; | 4117 | switch (modifier) { |
| 4297 | try f.object.dg.renderDeclName(writer, fn_decl, 0); | 4118 | .auto, .always_tail => try f.object.dg.renderDeclName(writer, fn_decl, 0), |
| 4119 | inline .never_tail, .never_inline => |mod| try writer.writeAll(try f.getLazyFnName( | ||
| 4120 | @unionInit(LazyFnKey, @tagName(mod), fn_decl), | ||
| 4121 | @unionInit(LazyFnValue.Data, @tagName(mod), {}), | ||
| 4122 | )), | ||
| 4123 | else => unreachable, | ||
| 4124 | } | ||
| 4298 | break :callee; | 4125 | break :callee; |
| 4299 | } | 4126 | } |
| 4127 | switch (modifier) { | ||
| 4128 | .auto, .always_tail => {}, | ||
| 4129 | .never_tail => return f.fail("CBE: runtime callee with never_tail attribute unsupported", .{}), | ||
| 4130 | .never_inline => return f.fail("CBE: runtime callee with never_inline attribute unsupported", .{}), | ||
| 4131 | else => unreachable, | ||
| 4132 | } | ||
| 4300 | // Fall back to function pointer call. | 4133 | // Fall back to function pointer call. |
| 4301 | try f.writeCValue(writer, callee, .Other); | 4134 | try f.writeCValue(writer, callee, .Other); |
| 4302 | } | 4135 | } |
| 4303 | 4136 | ||
| 4304 | try writer.writeByte('('); | 4137 | try writer.writeByte('('); |
| 4305 | var args_written: usize = 0; | 4138 | var args_written: usize = 0; |
| 4306 | for (args, 0..) |arg, arg_i| { | 4139 | for (resolved_args) |resolved_arg| { |
| 4307 | const ty = f.air.typeOf(arg); | 4140 | if (resolved_arg == .none) continue; |
| 4308 | if (!ty.hasRuntimeBitsIgnoreComptime()) continue; | 4141 | if (args_written != 0) try writer.writeAll(", "); |
| 4309 | if (args_written != 0) { | 4142 | try f.writeCValue(writer, resolved_arg, .FunctionArgument); |
| 4310 | try writer.writeAll(", "); | 4143 | if (resolved_arg == .new_local) try freeLocal(f, inst, resolved_arg.new_local, 0); |
| 4311 | } | ||
| 4312 | if ((is_extern or std.mem.eql(u8, std.mem.span(name), "main")) and | ||
| 4313 | ty.isCPtr() and ty.childType().tag() == .u8) | ||
| 4314 | { | ||
| 4315 | // Corresponds with hack in renderType .Pointer case. | ||
| 4316 | try writer.writeAll("(char"); | ||
| 4317 | if (ty.isConstPtr()) try writer.writeAll(" const"); | ||
| 4318 | if (ty.isVolatilePtr()) try writer.writeAll(" volatile"); | ||
| 4319 | try writer.writeAll(" *)"); | ||
| 4320 | } | ||
| 4321 | try f.writeCValue(writer, resolved_args[arg_i], .FunctionArgument); | ||
| 4322 | args_written += 1; | 4144 | args_written += 1; |
| 4323 | } | 4145 | } |
| 4324 | try writer.writeAll(");\n"); | 4146 | try writer.writeAll(");\n"); |
| ... | @@ -4331,11 +4153,11 @@ fn airCall( | ... | @@ -4331,11 +4153,11 @@ fn airCall( |
| 4331 | try writer.writeAll("memcpy("); | 4153 | try writer.writeAll("memcpy("); |
| 4332 | try f.writeCValue(writer, array_local, .FunctionArgument); | 4154 | try f.writeCValue(writer, array_local, .FunctionArgument); |
| 4333 | try writer.writeAll(", "); | 4155 | try writer.writeAll(", "); |
| 4334 | try f.writeCValueMember(writer, result_local, .{ .field = 0 }); | 4156 | try f.writeCValueMember(writer, result_local, .{ .identifier = "array" }); |
| 4335 | try writer.writeAll(", sizeof("); | 4157 | try writer.writeAll(", sizeof("); |
| 4336 | try f.renderTypecast(writer, ret_ty); | 4158 | try f.renderType(writer, ret_ty); |
| 4337 | try writer.writeAll("));\n"); | 4159 | try writer.writeAll("));\n"); |
| 4338 | try freeLocal(f, inst, result_local.local, 0); | 4160 | try freeLocal(f, inst, result_local.new_local, 0); |
| 4339 | break :r array_local; | 4161 | break :r array_local; |
| 4340 | }; | 4162 | }; |
| 4341 | 4163 | ||
| ... | @@ -4354,7 +4176,7 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4354,7 +4176,7 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4354 | // Perhaps an additional compilation option is in order? | 4176 | // Perhaps an additional compilation option is in order? |
| 4355 | //try writer.print("#line {d}\n", .{dbg_stmt.line + 1}); | 4177 | //try writer.print("#line {d}\n", .{dbg_stmt.line + 1}); |
| 4356 | try writer.print("/* file:{d}:{d} */\n", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); | 4178 | try writer.print("/* file:{d}:{d} */\n", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); |
| 4357 | return CValue.none; | 4179 | return .none; |
| 4358 | } | 4180 | } |
| 4359 | 4181 | ||
| 4360 | fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { | 4182 | fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -4363,7 +4185,7 @@ fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4363,7 +4185,7 @@ fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4363 | const function = f.air.values[ty_pl.payload].castTag(.function).?.data; | 4185 | const function = f.air.values[ty_pl.payload].castTag(.function).?.data; |
| 4364 | const mod = f.object.dg.module; | 4186 | const mod = f.object.dg.module; |
| 4365 | try writer.print("/* dbg func:{s} */\n", .{mod.declPtr(function.owner_decl).name}); | 4187 | try writer.print("/* dbg func:{s} */\n", .{mod.declPtr(function.owner_decl).name}); |
| 4366 | return CValue.none; | 4188 | return .none; |
| 4367 | } | 4189 | } |
| 4368 | 4190 | ||
| 4369 | fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { | 4191 | fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -4375,7 +4197,7 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4375,7 +4197,7 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4375 | try reap(f, inst, &.{pl_op.operand}); | 4197 | try reap(f, inst, &.{pl_op.operand}); |
| 4376 | const writer = f.object.writer(); | 4198 | const writer = f.object.writer(); |
| 4377 | try writer.print("/* var:{s} */\n", .{name}); | 4199 | try writer.print("/* var:{s} */\n", .{name}); |
| 4378 | return CValue.none; | 4200 | return .none; |
| 4379 | } | 4201 | } |
| 4380 | 4202 | ||
| 4381 | fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { | 4203 | fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -4391,7 +4213,7 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4391,7 +4213,7 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4391 | const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) | 4213 | const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) |
| 4392 | try f.allocLocal(inst, inst_ty) | 4214 | try f.allocLocal(inst, inst_ty) |
| 4393 | else | 4215 | else |
| 4394 | CValue{ .none = {} }; | 4216 | .none; |
| 4395 | 4217 | ||
| 4396 | try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{ | 4218 | try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{ |
| 4397 | .block_id = block_id, | 4219 | .block_id = block_id, |
| ... | @@ -4400,8 +4222,9 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4400,8 +4222,9 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4400 | 4222 | ||
| 4401 | try genBodyInner(f, body); | 4223 | try genBodyInner(f, body); |
| 4402 | try f.object.indent_writer.insertNewline(); | 4224 | try f.object.indent_writer.insertNewline(); |
| 4225 | // label might be unused, add a dummy goto | ||
| 4403 | // label must be followed by an expression, add an empty one. | 4226 | // label must be followed by an expression, add an empty one. |
| 4404 | try writer.print("zig_block_{d}:;\n", .{block_id}); | 4227 | try writer.print("goto zig_block_{d};\nzig_block_{d}: (void)0;\n", .{ block_id, block_id }); |
| 4405 | return result; | 4228 | return result; |
| 4406 | } | 4229 | } |
| 4407 | 4230 | ||
| ... | @@ -4460,7 +4283,7 @@ fn lowerTry( | ... | @@ -4460,7 +4283,7 @@ fn lowerTry( |
| 4460 | 4283 | ||
| 4461 | if (!payload_has_bits) { | 4284 | if (!payload_has_bits) { |
| 4462 | if (!operand_is_ptr) { | 4285 | if (!operand_is_ptr) { |
| 4463 | return CValue.none; | 4286 | return .none; |
| 4464 | } else { | 4287 | } else { |
| 4465 | return err_union; | 4288 | return err_union; |
| 4466 | } | 4289 | } |
| ... | @@ -4469,7 +4292,7 @@ fn lowerTry( | ... | @@ -4469,7 +4292,7 @@ fn lowerTry( |
| 4469 | try reap(f, inst, &.{operand}); | 4292 | try reap(f, inst, &.{operand}); |
| 4470 | 4293 | ||
| 4471 | if (f.liveness.isUnused(inst)) { | 4294 | if (f.liveness.isUnused(inst)) { |
| 4472 | return CValue.none; | 4295 | return .none; |
| 4473 | } | 4296 | } |
| 4474 | 4297 | ||
| 4475 | const target = f.object.dg.module.getTarget(); | 4298 | const target = f.object.dg.module.getTarget(); |
| ... | @@ -4481,7 +4304,7 @@ fn lowerTry( | ... | @@ -4481,7 +4304,7 @@ fn lowerTry( |
| 4481 | try writer.writeAll(", "); | 4304 | try writer.writeAll(", "); |
| 4482 | try f.writeCValueMember(writer, err_union, .{ .identifier = "payload" }); | 4305 | try f.writeCValueMember(writer, err_union, .{ .identifier = "payload" }); |
| 4483 | try writer.writeAll(", sizeof("); | 4306 | try writer.writeAll(", sizeof("); |
| 4484 | try f.renderTypecast(writer, payload_ty); | 4307 | try f.renderType(writer, payload_ty); |
| 4485 | try writer.writeAll("));\n"); | 4308 | try writer.writeAll("));\n"); |
| 4486 | } else { | 4309 | } else { |
| 4487 | try f.writeCValue(writer, local, .Other); | 4310 | try f.writeCValue(writer, local, .Other); |
| ... | @@ -4514,7 +4337,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4514,7 +4337,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4514 | try writer.writeAll(", "); | 4337 | try writer.writeAll(", "); |
| 4515 | try f.writeCValue(writer, operand, .FunctionArgument); | 4338 | try f.writeCValue(writer, operand, .FunctionArgument); |
| 4516 | try writer.writeAll(", sizeof("); | 4339 | try writer.writeAll(", sizeof("); |
| 4517 | try f.renderTypecast(writer, operand_ty); | 4340 | try f.renderType(writer, operand_ty); |
| 4518 | try writer.writeAll("))"); | 4341 | try writer.writeAll("))"); |
| 4519 | } else { | 4342 | } else { |
| 4520 | try f.writeCValue(writer, result, .Other); | 4343 | try f.writeCValue(writer, result, .Other); |
| ... | @@ -4525,7 +4348,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4525,7 +4348,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4525 | } | 4348 | } |
| 4526 | 4349 | ||
| 4527 | try writer.print("goto zig_block_{d};\n", .{block.block_id}); | 4350 | try writer.print("goto zig_block_{d};\n", .{block.block_id}); |
| 4528 | return CValue.none; | 4351 | return .none; |
| 4529 | } | 4352 | } |
| 4530 | 4353 | ||
| 4531 | fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { | 4354 | fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -4535,7 +4358,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4535,7 +4358,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4535 | // https://github.com/ziglang/zig/issues/13410 | 4358 | // https://github.com/ziglang/zig/issues/13410 |
| 4536 | if (f.liveness.isUnused(inst) or !dest_ty.hasRuntimeBits()) { | 4359 | if (f.liveness.isUnused(inst) or !dest_ty.hasRuntimeBits()) { |
| 4537 | try reap(f, inst, &.{ty_op.operand}); | 4360 | try reap(f, inst, &.{ty_op.operand}); |
| 4538 | return CValue.none; | 4361 | return .none; |
| 4539 | } | 4362 | } |
| 4540 | 4363 | ||
| 4541 | const operand = try f.resolveInst(ty_op.operand); | 4364 | const operand = try f.resolveInst(ty_op.operand); |
| ... | @@ -4563,7 +4386,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4563,7 +4386,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4563 | if (dest_ty.isPtrAtRuntime() and operand_ty.isPtrAtRuntime()) { | 4386 | if (dest_ty.isPtrAtRuntime() and operand_ty.isPtrAtRuntime()) { |
| 4564 | try f.writeCValue(writer, local, .Other); | 4387 | try f.writeCValue(writer, local, .Other); |
| 4565 | try writer.writeAll(" = ("); | 4388 | try writer.writeAll(" = ("); |
| 4566 | try f.renderTypecast(writer, dest_ty); | 4389 | try f.renderType(writer, dest_ty); |
| 4567 | try writer.writeByte(')'); | 4390 | try writer.writeByte(')'); |
| 4568 | try f.writeCValue(writer, operand, .Other); | 4391 | try f.writeCValue(writer, operand, .Other); |
| 4569 | try writer.writeAll(";\n"); | 4392 | try writer.writeAll(";\n"); |
| ... | @@ -4584,7 +4407,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4584,7 +4407,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4584 | try writer.writeAll(", &"); | 4407 | try writer.writeAll(", &"); |
| 4585 | try f.writeCValue(writer, operand_lval, .Other); | 4408 | try f.writeCValue(writer, operand_lval, .Other); |
| 4586 | try writer.writeAll(", sizeof("); | 4409 | try writer.writeAll(", sizeof("); |
| 4587 | try f.renderTypecast(writer, dest_ty); | 4410 | try f.renderType(writer, dest_ty); |
| 4588 | try writer.writeAll("));\n"); | 4411 | try writer.writeAll("));\n"); |
| 4589 | 4412 | ||
| 4590 | // Ensure padding bits have the expected value. | 4413 | // Ensure padding bits have the expected value. |
| ... | @@ -4599,7 +4422,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4599,7 +4422,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4599 | } | 4422 | } |
| 4600 | 4423 | ||
| 4601 | if (operand == .constant) { | 4424 | if (operand == .constant) { |
| 4602 | try freeLocal(f, inst, operand_lval.local, 0); | 4425 | try freeLocal(f, inst, operand_lval.new_local, 0); |
| 4603 | } | 4426 | } |
| 4604 | 4427 | ||
| 4605 | return local; | 4428 | return local; |
| ... | @@ -4607,27 +4430,27 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4607,27 +4430,27 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4607 | 4430 | ||
| 4608 | fn airBreakpoint(writer: anytype) !CValue { | 4431 | fn airBreakpoint(writer: anytype) !CValue { |
| 4609 | try writer.writeAll("zig_breakpoint();\n"); | 4432 | try writer.writeAll("zig_breakpoint();\n"); |
| 4610 | return CValue.none; | 4433 | return .none; |
| 4611 | } | 4434 | } |
| 4612 | 4435 | ||
| 4613 | fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { | 4436 | fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4614 | if (f.liveness.isUnused(inst)) return CValue.none; | 4437 | if (f.liveness.isUnused(inst)) return .none; |
| 4615 | const writer = f.object.writer(); | 4438 | const writer = f.object.writer(); |
| 4616 | const local = try f.allocLocal(inst, Type.usize); | 4439 | const local = try f.allocLocal(inst, Type.usize); |
| 4617 | try f.writeCValue(writer, local, .Other); | 4440 | try f.writeCValue(writer, local, .Other); |
| 4618 | try writer.writeAll(" = ("); | 4441 | try writer.writeAll(" = ("); |
| 4619 | try f.renderTypecast(writer, Type.usize); | 4442 | try f.renderType(writer, Type.usize); |
| 4620 | try writer.writeAll(")zig_return_address();\n"); | 4443 | try writer.writeAll(")zig_return_address();\n"); |
| 4621 | return local; | 4444 | return local; |
| 4622 | } | 4445 | } |
| 4623 | 4446 | ||
| 4624 | fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { | 4447 | fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4625 | if (f.liveness.isUnused(inst)) return CValue.none; | 4448 | if (f.liveness.isUnused(inst)) return .none; |
| 4626 | const writer = f.object.writer(); | 4449 | const writer = f.object.writer(); |
| 4627 | const local = try f.allocLocal(inst, Type.usize); | 4450 | const local = try f.allocLocal(inst, Type.usize); |
| 4628 | try f.writeCValue(writer, local, .Other); | 4451 | try f.writeCValue(writer, local, .Other); |
| 4629 | try writer.writeAll(" = ("); | 4452 | try writer.writeAll(" = ("); |
| 4630 | try f.renderTypecast(writer, Type.usize); | 4453 | try f.renderType(writer, Type.usize); |
| 4631 | try writer.writeAll(")zig_frame_address();\n"); | 4454 | try writer.writeAll(")zig_frame_address();\n"); |
| 4632 | return local; | 4455 | return local; |
| 4633 | } | 4456 | } |
| ... | @@ -4640,15 +4463,15 @@ fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4640,15 +4463,15 @@ fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4640 | try writeMemoryOrder(writer, atomic_order); | 4463 | try writeMemoryOrder(writer, atomic_order); |
| 4641 | try writer.writeAll(");\n"); | 4464 | try writer.writeAll(");\n"); |
| 4642 | 4465 | ||
| 4643 | return CValue.none; | 4466 | return .none; |
| 4644 | } | 4467 | } |
| 4645 | 4468 | ||
| 4646 | fn airUnreach(f: *Function) !CValue { | 4469 | fn airUnreach(f: *Function) !CValue { |
| 4647 | // Not even allowed to call unreachable in a naked function. | 4470 | // Not even allowed to call unreachable in a naked function. |
| 4648 | if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none; | 4471 | if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() == .Naked) return .none; |
| 4649 | 4472 | ||
| 4650 | try f.object.writer().writeAll("zig_unreachable();\n"); | 4473 | try f.object.writer().writeAll("zig_unreachable();\n"); |
| 4651 | return CValue.none; | 4474 | return .none; |
| 4652 | } | 4475 | } |
| 4653 | 4476 | ||
| 4654 | fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { | 4477 | fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -4669,18 +4492,18 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4669,18 +4492,18 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4669 | const new_free_locals = f.getFreeLocals(); | 4492 | const new_free_locals = f.getFreeLocals(); |
| 4670 | var it = new_free_locals.iterator(); | 4493 | var it = new_free_locals.iterator(); |
| 4671 | while (it.next()) |entry| { | 4494 | while (it.next()) |entry| { |
| 4672 | const gop = try old_free_locals.getOrPutContext(gpa, entry.key_ptr.*, f.tyHashCtx()); | 4495 | const gop = try old_free_locals.getOrPut(gpa, entry.key_ptr.*); |
| 4673 | if (gop.found_existing) { | 4496 | if (gop.found_existing) { |
| 4674 | try gop.value_ptr.appendSlice(gpa, entry.value_ptr.items); | 4497 | try gop.value_ptr.ensureUnusedCapacity(gpa, entry.value_ptr.count()); |
| 4675 | } else { | 4498 | for (entry.value_ptr.keys()) |local_index| { |
| 4676 | gop.value_ptr.* = entry.value_ptr.*; | 4499 | gop.value_ptr.putAssumeCapacityNoClobber(local_index, {}); |
| 4677 | entry.value_ptr.* = .{}; | 4500 | } |
| 4678 | } | 4501 | } else gop.value_ptr.* = entry.value_ptr.move(); |
| 4679 | } | 4502 | } |
| 4680 | deinitFreeLocalsMap(gpa, new_free_locals); | 4503 | deinitFreeLocalsMap(gpa, new_free_locals); |
| 4681 | new_free_locals.* = old_free_locals.move(); | 4504 | new_free_locals.* = old_free_locals.move(); |
| 4682 | 4505 | ||
| 4683 | return CValue.none; | 4506 | return .none; |
| 4684 | } | 4507 | } |
| 4685 | 4508 | ||
| 4686 | fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { | 4509 | fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -4705,6 +4528,10 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4705,6 +4528,10 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4705 | // that we can notice and use them in the else branch. Any new locals must | 4528 | // that we can notice and use them in the else branch. Any new locals must |
| 4706 | // necessarily be free already after the then branch is complete. | 4529 | // necessarily be free already after the then branch is complete. |
| 4707 | const pre_locals_len = @intCast(LocalIndex, f.locals.items.len); | 4530 | const pre_locals_len = @intCast(LocalIndex, f.locals.items.len); |
| 4531 | // Remember how many allocs there were before entering the then branch so | ||
| 4532 | // that we can notice and make sure not to use them in the else branch. | ||
| 4533 | // Any new allocs must be removed from the free list. | ||
| 4534 | const pre_allocs_len = @intCast(LocalIndex, f.allocs.count()); | ||
| 4708 | const pre_clone_depth = f.free_locals_clone_depth; | 4535 | const pre_clone_depth = f.free_locals_clone_depth; |
| 4709 | f.free_locals_clone_depth = @intCast(LoopDepth, f.free_locals_stack.items.len); | 4536 | f.free_locals_clone_depth = @intCast(LoopDepth, f.free_locals_stack.items.len); |
| 4710 | 4537 | ||
| ... | @@ -4735,7 +4562,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4735,7 +4562,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4735 | try die(f, inst, Air.indexToRef(operand)); | 4562 | try die(f, inst, Air.indexToRef(operand)); |
| 4736 | } | 4563 | } |
| 4737 | 4564 | ||
| 4738 | try noticeBranchFrees(f, pre_locals_len, inst); | 4565 | try noticeBranchFrees(f, pre_locals_len, pre_allocs_len, inst); |
| 4739 | 4566 | ||
| 4740 | if (needs_else) { | 4567 | if (needs_else) { |
| 4741 | try genBody(f, else_body); | 4568 | try genBody(f, else_body); |
| ... | @@ -4745,7 +4572,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4745,7 +4572,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4745 | 4572 | ||
| 4746 | try f.object.indent_writer.insertNewline(); | 4573 | try f.object.indent_writer.insertNewline(); |
| 4747 | 4574 | ||
| 4748 | return CValue.none; | 4575 | return .none; |
| 4749 | } | 4576 | } |
| 4750 | 4577 | ||
| 4751 | fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { | 4578 | fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -4759,11 +4586,11 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4759,11 +4586,11 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4759 | try writer.writeAll("switch ("); | 4586 | try writer.writeAll("switch ("); |
| 4760 | if (condition_ty.zigTypeTag() == .Bool) { | 4587 | if (condition_ty.zigTypeTag() == .Bool) { |
| 4761 | try writer.writeByte('('); | 4588 | try writer.writeByte('('); |
| 4762 | try f.renderTypecast(writer, Type.u1); | 4589 | try f.renderType(writer, Type.u1); |
| 4763 | try writer.writeByte(')'); | 4590 | try writer.writeByte(')'); |
| 4764 | } else if (condition_ty.isPtrAtRuntime()) { | 4591 | } else if (condition_ty.isPtrAtRuntime()) { |
| 4765 | try writer.writeByte('('); | 4592 | try writer.writeByte('('); |
| 4766 | try f.renderTypecast(writer, Type.usize); | 4593 | try f.renderType(writer, Type.usize); |
| 4767 | try writer.writeByte(')'); | 4594 | try writer.writeByte(')'); |
| 4768 | } | 4595 | } |
| 4769 | try f.writeCValue(writer, condition, .Other); | 4596 | try f.writeCValue(writer, condition, .Other); |
| ... | @@ -4780,8 +4607,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4780,8 +4607,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4780 | const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0); | 4607 | const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0); |
| 4781 | 4608 | ||
| 4782 | var extra_index: usize = switch_br.end; | 4609 | var extra_index: usize = switch_br.end; |
| 4783 | var case_i: u32 = 0; | 4610 | for (0..switch_br.data.cases_len) |case_i| { |
| 4784 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { | ||
| 4785 | const case = f.air.extraData(Air.SwitchBr.Case, extra_index); | 4611 | const case = f.air.extraData(Air.SwitchBr.Case, extra_index); |
| 4786 | const items = @ptrCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]); | 4612 | const items = @ptrCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]); |
| 4787 | const case_body = f.air.extra[case.end + items.len ..][0..case.data.body_len]; | 4613 | const case_body = f.air.extra[case.end + items.len ..][0..case.data.body_len]; |
| ... | @@ -4792,7 +4618,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4792,7 +4618,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4792 | try writer.writeAll("case "); | 4618 | try writer.writeAll("case "); |
| 4793 | if (condition_ty.isPtrAtRuntime()) { | 4619 | if (condition_ty.isPtrAtRuntime()) { |
| 4794 | try writer.writeByte('('); | 4620 | try writer.writeByte('('); |
| 4795 | try f.renderTypecast(writer, Type.usize); | 4621 | try f.renderType(writer, Type.usize); |
| 4796 | try writer.writeByte(')'); | 4622 | try writer.writeByte(')'); |
| 4797 | } | 4623 | } |
| 4798 | try f.object.dg.renderValue(writer, condition_ty, f.air.value(item).?, .Other); | 4624 | try f.object.dg.renderValue(writer, condition_ty, f.air.value(item).?, .Other); |
| ... | @@ -4811,6 +4637,10 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4811,6 +4637,10 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4811 | // we can notice and use them in subsequent branches. Any new locals must | 4637 | // we can notice and use them in subsequent branches. Any new locals must |
| 4812 | // necessarily be free already after the previous branch is complete. | 4638 | // necessarily be free already after the previous branch is complete. |
| 4813 | const pre_locals_len = @intCast(LocalIndex, f.locals.items.len); | 4639 | const pre_locals_len = @intCast(LocalIndex, f.locals.items.len); |
| 4640 | // Remember how many allocs there were before entering each branch so that | ||
| 4641 | // we can notice and make sure not to use them in subsequent branches. | ||
| 4642 | // Any new allocs must be removed from the free list. | ||
| 4643 | const pre_allocs_len = @intCast(LocalIndex, f.allocs.count()); | ||
| 4814 | const pre_clone_depth = f.free_locals_clone_depth; | 4644 | const pre_clone_depth = f.free_locals_clone_depth; |
| 4815 | f.free_locals_clone_depth = @intCast(LoopDepth, f.free_locals_stack.items.len); | 4645 | f.free_locals_clone_depth = @intCast(LoopDepth, f.free_locals_stack.items.len); |
| 4816 | 4646 | ||
| ... | @@ -4831,7 +4661,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4831,7 +4661,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4831 | try genBody(f, case_body); | 4661 | try genBody(f, case_body); |
| 4832 | } | 4662 | } |
| 4833 | 4663 | ||
| 4834 | try noticeBranchFrees(f, pre_locals_len, inst); | 4664 | try noticeBranchFrees(f, pre_locals_len, pre_allocs_len, inst); |
| 4835 | } else { | 4665 | } else { |
| 4836 | for (liveness.deaths[case_i]) |operand| { | 4666 | for (liveness.deaths[case_i]) |operand| { |
| 4837 | try die(f, inst, Air.indexToRef(operand)); | 4667 | try die(f, inst, Air.indexToRef(operand)); |
| ... | @@ -4858,7 +4688,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4858,7 +4688,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4858 | 4688 | ||
| 4859 | f.object.indent_writer.popIndent(); | 4689 | f.object.indent_writer.popIndent(); |
| 4860 | try writer.writeAll("}\n"); | 4690 | try writer.writeAll("}\n"); |
| 4861 | return CValue.none; | 4691 | return .none; |
| 4862 | } | 4692 | } |
| 4863 | 4693 | ||
| 4864 | fn asmInputNeedsLocal(constraint: []const u8, value: CValue) bool { | 4694 | fn asmInputNeedsLocal(constraint: []const u8, value: CValue) bool { |
| ... | @@ -4880,8 +4710,8 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4880,8 +4710,8 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4880 | const inputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); | 4710 | const inputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 4881 | extra_i += inputs.len; | 4711 | extra_i += inputs.len; |
| 4882 | 4712 | ||
| 4883 | const result: CValue = r: { | 4713 | const result = r: { |
| 4884 | if (!is_volatile and f.liveness.isUnused(inst)) break :r CValue.none; | 4714 | if (!is_volatile and f.liveness.isUnused(inst)) break :r .none; |
| 4885 | 4715 | ||
| 4886 | const writer = f.object.writer(); | 4716 | const writer = f.object.writer(); |
| 4887 | const inst_ty = f.air.typeOfIndex(inst); | 4717 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -4918,14 +4748,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4918,14 +4748,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4918 | try writer.writeAll("register "); | 4748 | try writer.writeAll("register "); |
| 4919 | const alignment = 0; | 4749 | const alignment = 0; |
| 4920 | const local_value = try f.allocLocalValue(output_ty, alignment); | 4750 | const local_value = try f.allocLocalValue(output_ty, alignment); |
| 4921 | try f.object.dg.renderTypeAndName( | 4751 | try f.object.dg.renderTypeAndName(writer, output_ty, local_value, .{}, alignment, .complete); |
| 4922 | writer, | ||
| 4923 | output_ty, | ||
| 4924 | local_value, | ||
| 4925 | .Mut, | ||
| 4926 | alignment, | ||
| 4927 | .Complete, | ||
| 4928 | ); | ||
| 4929 | try writer.writeAll(" __asm(\""); | 4752 | try writer.writeAll(" __asm(\""); |
| 4930 | try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); | 4753 | try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); |
| 4931 | try writer.writeAll("\")"); | 4754 | try writer.writeAll("\")"); |
| ... | @@ -4957,14 +4780,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4957,14 +4780,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4957 | if (is_reg) try writer.writeAll("register "); | 4780 | if (is_reg) try writer.writeAll("register "); |
| 4958 | const alignment = 0; | 4781 | const alignment = 0; |
| 4959 | const local_value = try f.allocLocalValue(input_ty, alignment); | 4782 | const local_value = try f.allocLocalValue(input_ty, alignment); |
| 4960 | try f.object.dg.renderTypeAndName( | 4783 | try f.object.dg.renderTypeAndName(writer, input_ty, local_value, Const, alignment, .complete); |
| 4961 | writer, | ||
| 4962 | input_ty, | ||
| 4963 | local_value, | ||
| 4964 | .Const, | ||
| 4965 | alignment, | ||
| 4966 | .Complete, | ||
| 4967 | ); | ||
| 4968 | if (is_reg) { | 4784 | if (is_reg) { |
| 4969 | try writer.writeAll(" __asm(\""); | 4785 | try writer.writeAll(" __asm(\""); |
| 4970 | try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); | 4786 | try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); |
| ... | @@ -4975,14 +4791,11 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -4975,14 +4791,11 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4975 | try writer.writeAll(";\n"); | 4791 | try writer.writeAll(";\n"); |
| 4976 | } | 4792 | } |
| 4977 | } | 4793 | } |
| 4978 | { | 4794 | for (0..clobbers_len) |_| { |
| 4979 | var clobber_i: u32 = 0; | 4795 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); |
| 4980 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | 4796 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 4981 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); | 4797 | // for the string, we still use the next u32 for the null terminator. |
| 4982 | // This equation accounts for the fact that even if we have exactly 4 bytes | 4798 | extra_i += clobber.len / 4 + 1; |
| 4983 | // for the string, we still use the next u32 for the null terminator. | ||
| 4984 | extra_i += clobber.len / 4 + 1; | ||
| 4985 | } | ||
| 4986 | } | 4799 | } |
| 4987 | 4800 | ||
| 4988 | { | 4801 | { |
| ... | @@ -5037,7 +4850,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5037,7 +4850,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5037 | 4850 | ||
| 5038 | try writer.writeAll("__asm"); | 4851 | try writer.writeAll("__asm"); |
| 5039 | if (is_volatile) try writer.writeAll(" volatile"); | 4852 | if (is_volatile) try writer.writeAll(" volatile"); |
| 5040 | try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i])}); | 4853 | try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i], null)}); |
| 5041 | } | 4854 | } |
| 5042 | 4855 | ||
| 5043 | extra_i = constraints_extra_begin; | 4856 | extra_i = constraints_extra_begin; |
| ... | @@ -5055,7 +4868,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5055,7 +4868,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5055 | try writer.writeByte(' '); | 4868 | try writer.writeByte(' '); |
| 5056 | if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); | 4869 | if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); |
| 5057 | const is_reg = constraint[1] == '{'; | 4870 | const is_reg = constraint[1] == '{'; |
| 5058 | try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint)}); | 4871 | try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)}); |
| 5059 | if (is_reg) { | 4872 | if (is_reg) { |
| 5060 | try f.writeCValue(writer, .{ .local = locals_index }, .Other); | 4873 | try f.writeCValue(writer, .{ .local = locals_index }, .Other); |
| 5061 | locals_index += 1; | 4874 | locals_index += 1; |
| ... | @@ -5081,28 +4894,25 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5081,28 +4894,25 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5081 | 4894 | ||
| 5082 | const is_reg = constraint[0] == '{'; | 4895 | const is_reg = constraint[0] == '{'; |
| 5083 | const input_val = try f.resolveInst(input); | 4896 | const input_val = try f.resolveInst(input); |
| 5084 | try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint)}); | 4897 | try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)}); |
| 5085 | try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: { | 4898 | try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: { |
| 5086 | const input_local = CValue{ .local = locals_index }; | 4899 | const input_local = .{ .local = locals_index }; |
| 5087 | locals_index += 1; | 4900 | locals_index += 1; |
| 5088 | break :local input_local; | 4901 | break :local input_local; |
| 5089 | } else input_val, .Other); | 4902 | } else input_val, .Other); |
| 5090 | try writer.writeByte(')'); | 4903 | try writer.writeByte(')'); |
| 5091 | } | 4904 | } |
| 5092 | try writer.writeByte(':'); | 4905 | try writer.writeByte(':'); |
| 5093 | { | 4906 | for (0..clobbers_len) |clobber_i| { |
| 5094 | var clobber_i: u32 = 0; | 4907 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); |
| 5095 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | 4908 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 5096 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); | 4909 | // for the string, we still use the next u32 for the null terminator. |
| 5097 | // This equation accounts for the fact that even if we have exactly 4 bytes | 4910 | extra_i += clobber.len / 4 + 1; |
| 5098 | // for the string, we still use the next u32 for the null terminator. | ||
| 5099 | extra_i += clobber.len / 4 + 1; | ||
| 5100 | 4911 | ||
| 5101 | if (clobber.len == 0) continue; | 4912 | if (clobber.len == 0) continue; |
| 5102 | 4913 | ||
| 5103 | if (clobber_i > 0) try writer.writeByte(','); | 4914 | if (clobber_i > 0) try writer.writeByte(','); |
| 5104 | try writer.print(" {s}", .{fmtStringLiteral(clobber)}); | 4915 | try writer.print(" {s}", .{fmtStringLiteral(clobber, null)}); |
| 5105 | } | ||
| 5106 | } | 4916 | } |
| 5107 | try writer.writeAll(");\n"); | 4917 | try writer.writeAll(");\n"); |
| 5108 | 4918 | ||
| ... | @@ -5119,7 +4929,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5119,7 +4929,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5119 | const is_reg = constraint[1] == '{'; | 4929 | const is_reg = constraint[1] == '{'; |
| 5120 | if (is_reg) { | 4930 | if (is_reg) { |
| 5121 | try f.writeCValueDeref(writer, if (output == .none) | 4931 | try f.writeCValueDeref(writer, if (output == .none) |
| 5122 | CValue{ .local_ref = local.local } | 4932 | .{ .local_ref = local.new_local } |
| 5123 | else | 4933 | else |
| 5124 | try f.resolveInst(output)); | 4934 | try f.resolveInst(output)); |
| 5125 | try writer.writeAll(" = "); | 4935 | try writer.writeAll(" = "); |
| ... | @@ -5154,7 +4964,7 @@ fn airIsNull( | ... | @@ -5154,7 +4964,7 @@ fn airIsNull( |
| 5154 | 4964 | ||
| 5155 | if (f.liveness.isUnused(inst)) { | 4965 | if (f.liveness.isUnused(inst)) { |
| 5156 | try reap(f, inst, &.{un_op}); | 4966 | try reap(f, inst, &.{un_op}); |
| 5157 | return CValue.none; | 4967 | return .none; |
| 5158 | } | 4968 | } |
| 5159 | 4969 | ||
| 5160 | const writer = f.object.writer(); | 4970 | const writer = f.object.writer(); |
| ... | @@ -5204,7 +5014,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5204,7 +5014,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5204 | 5014 | ||
| 5205 | if (f.liveness.isUnused(inst)) { | 5015 | if (f.liveness.isUnused(inst)) { |
| 5206 | try reap(f, inst, &.{ty_op.operand}); | 5016 | try reap(f, inst, &.{ty_op.operand}); |
| 5207 | return CValue.none; | 5017 | return .none; |
| 5208 | } | 5018 | } |
| 5209 | 5019 | ||
| 5210 | const operand = try f.resolveInst(ty_op.operand); | 5020 | const operand = try f.resolveInst(ty_op.operand); |
| ... | @@ -5215,7 +5025,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5215,7 +5025,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5215 | const payload_ty = opt_ty.optionalChild(&buf); | 5025 | const payload_ty = opt_ty.optionalChild(&buf); |
| 5216 | 5026 | ||
| 5217 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 5027 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 5218 | return CValue.none; | 5028 | return .none; |
| 5219 | } | 5029 | } |
| 5220 | 5030 | ||
| 5221 | const inst_ty = f.air.typeOfIndex(inst); | 5031 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -5244,7 +5054,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5244,7 +5054,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5244 | try f.writeCValueMember(writer, operand, .{ .identifier = "payload" }); | 5054 | try f.writeCValueMember(writer, operand, .{ .identifier = "payload" }); |
| 5245 | if (is_array) { | 5055 | if (is_array) { |
| 5246 | try writer.writeAll(", sizeof("); | 5056 | try writer.writeAll(", sizeof("); |
| 5247 | try f.renderTypecast(writer, inst_ty); | 5057 | try f.renderType(writer, inst_ty); |
| 5248 | try writer.writeAll("))"); | 5058 | try writer.writeAll("))"); |
| 5249 | } | 5059 | } |
| 5250 | try writer.writeAll(";\n"); | 5060 | try writer.writeAll(";\n"); |
| ... | @@ -5256,7 +5066,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5256,7 +5066,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5256 | 5066 | ||
| 5257 | if (f.liveness.isUnused(inst)) { | 5067 | if (f.liveness.isUnused(inst)) { |
| 5258 | try reap(f, inst, &.{ty_op.operand}); | 5068 | try reap(f, inst, &.{ty_op.operand}); |
| 5259 | return CValue.none; | 5069 | return .none; |
| 5260 | } | 5070 | } |
| 5261 | 5071 | ||
| 5262 | const writer = f.object.writer(); | 5072 | const writer = f.object.writer(); |
| ... | @@ -5267,7 +5077,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5267,7 +5077,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5267 | const inst_ty = f.air.typeOfIndex(inst); | 5077 | const inst_ty = f.air.typeOfIndex(inst); |
| 5268 | 5078 | ||
| 5269 | if (!inst_ty.childType().hasRuntimeBitsIgnoreComptime()) { | 5079 | if (!inst_ty.childType().hasRuntimeBitsIgnoreComptime()) { |
| 5270 | return CValue{ .undef = inst_ty }; | 5080 | return .{ .undef = inst_ty }; |
| 5271 | } | 5081 | } |
| 5272 | 5082 | ||
| 5273 | const local = try f.allocLocal(inst, inst_ty); | 5083 | const local = try f.allocLocal(inst, inst_ty); |
| ... | @@ -5299,7 +5109,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5299,7 +5109,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5299 | 5109 | ||
| 5300 | if (opt_ty.optionalReprIsPayload()) { | 5110 | if (opt_ty.optionalReprIsPayload()) { |
| 5301 | if (f.liveness.isUnused(inst)) { | 5111 | if (f.liveness.isUnused(inst)) { |
| 5302 | return CValue.none; | 5112 | return .none; |
| 5303 | } | 5113 | } |
| 5304 | const local = try f.allocLocal(inst, inst_ty); | 5114 | const local = try f.allocLocal(inst, inst_ty); |
| 5305 | // The payload and the optional are the same value. | 5115 | // The payload and the optional are the same value. |
| ... | @@ -5316,7 +5126,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5316,7 +5126,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5316 | try writer.writeAll(";\n"); | 5126 | try writer.writeAll(";\n"); |
| 5317 | 5127 | ||
| 5318 | if (f.liveness.isUnused(inst)) { | 5128 | if (f.liveness.isUnused(inst)) { |
| 5319 | return CValue.none; | 5129 | return .none; |
| 5320 | } | 5130 | } |
| 5321 | 5131 | ||
| 5322 | const local = try f.allocLocal(inst, inst_ty); | 5132 | const local = try f.allocLocal(inst, inst_ty); |
| ... | @@ -5328,6 +5138,62 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5328,6 +5138,62 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5328 | } | 5138 | } |
| 5329 | } | 5139 | } |
| 5330 | 5140 | ||
| 5141 | fn fieldLocation( | ||
| 5142 | container_ty: Type, | ||
| 5143 | field_ptr_ty: Type, | ||
| 5144 | field_index: u32, | ||
| 5145 | target: std.Target, | ||
| 5146 | ) union(enum) { | ||
| 5147 | begin: void, | ||
| 5148 | field: CValue, | ||
| 5149 | byte_offset: u32, | ||
| 5150 | end: void, | ||
| 5151 | } { | ||
| 5152 | return switch (container_ty.zigTypeTag()) { | ||
| 5153 | .Struct => switch (container_ty.containerLayout()) { | ||
| 5154 | .Auto, .Extern => for (field_index..container_ty.structFieldCount()) |next_field_index| { | ||
| 5155 | if (container_ty.structFieldIsComptime(next_field_index)) continue; | ||
| 5156 | const field_ty = container_ty.structFieldType(next_field_index); | ||
| 5157 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 5158 | break .{ .field = if (container_ty.isSimpleTuple()) | ||
| 5159 | .{ .field = next_field_index } | ||
| 5160 | else | ||
| 5161 | .{ .identifier = container_ty.structFieldName(next_field_index) } }; | ||
| 5162 | } else if (container_ty.hasRuntimeBitsIgnoreComptime()) .end else .begin, | ||
| 5163 | .Packed => if (field_ptr_ty.ptrInfo().data.host_size == 0) | ||
| 5164 | .{ .byte_offset = container_ty.packedStructFieldByteOffset(field_index, target) } | ||
| 5165 | else | ||
| 5166 | .begin, | ||
| 5167 | }, | ||
| 5168 | .Union => switch (container_ty.containerLayout()) { | ||
| 5169 | .Auto, .Extern => { | ||
| 5170 | const field_ty = container_ty.structFieldType(field_index); | ||
| 5171 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) | ||
| 5172 | return if (container_ty.unionTagTypeSafety() != null and | ||
| 5173 | !container_ty.unionHasAllZeroBitFieldTypes()) | ||
| 5174 | .{ .field = .{ .identifier = "payload" } } | ||
| 5175 | else | ||
| 5176 | .begin; | ||
| 5177 | const field_name = container_ty.unionFields().keys()[field_index]; | ||
| 5178 | return .{ .field = if (container_ty.unionTagTypeSafety()) |_| | ||
| 5179 | .{ .payload_identifier = field_name } | ||
| 5180 | else | ||
| 5181 | .{ .identifier = field_name } }; | ||
| 5182 | }, | ||
| 5183 | .Packed => .begin, | ||
| 5184 | }, | ||
| 5185 | .Pointer => switch (container_ty.ptrSize()) { | ||
| 5186 | .Slice => switch (field_index) { | ||
| 5187 | 0 => .{ .field = .{ .identifier = "ptr" } }, | ||
| 5188 | 1 => .{ .field = .{ .identifier = "len" } }, | ||
| 5189 | else => unreachable, | ||
| 5190 | }, | ||
| 5191 | .One, .Many, .C => unreachable, | ||
| 5192 | }, | ||
| 5193 | else => unreachable, | ||
| 5194 | }; | ||
| 5195 | } | ||
| 5196 | |||
| 5331 | fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { | 5197 | fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5332 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; | 5198 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; |
| 5333 | const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; | 5199 | const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; |
| ... | @@ -5337,10 +5203,10 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5337,10 +5203,10 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5337 | return .none; | 5203 | return .none; |
| 5338 | } | 5204 | } |
| 5339 | 5205 | ||
| 5340 | const struct_ptr = try f.resolveInst(extra.struct_operand); | 5206 | const container_ptr_val = try f.resolveInst(extra.struct_operand); |
| 5341 | try reap(f, inst, &.{extra.struct_operand}); | 5207 | try reap(f, inst, &.{extra.struct_operand}); |
| 5342 | const struct_ptr_ty = f.air.typeOf(extra.struct_operand); | 5208 | const container_ptr_ty = f.air.typeOf(extra.struct_operand); |
| 5343 | return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, extra.field_index); | 5209 | return fieldPtr(f, inst, container_ptr_ty, container_ptr_val, extra.field_index); |
| 5344 | } | 5210 | } |
| 5345 | 5211 | ||
| 5346 | fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue { | 5212 | fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue { |
| ... | @@ -5351,10 +5217,10 @@ fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue | ... | @@ -5351,10 +5217,10 @@ fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue |
| 5351 | return .none; | 5217 | return .none; |
| 5352 | } | 5218 | } |
| 5353 | 5219 | ||
| 5354 | const struct_ptr = try f.resolveInst(ty_op.operand); | 5220 | const container_ptr_val = try f.resolveInst(ty_op.operand); |
| 5355 | try reap(f, inst, &.{ty_op.operand}); | 5221 | try reap(f, inst, &.{ty_op.operand}); |
| 5356 | const struct_ptr_ty = f.air.typeOf(ty_op.operand); | 5222 | const container_ptr_ty = f.air.typeOf(ty_op.operand); |
| 5357 | return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, index); | 5223 | return fieldPtr(f, inst, container_ptr_ty, container_ptr_val, index); |
| 5358 | } | 5224 | } |
| 5359 | 5225 | ||
| 5360 | fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { | 5226 | fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -5363,137 +5229,119 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5363,137 +5229,119 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5363 | 5229 | ||
| 5364 | if (f.liveness.isUnused(inst)) { | 5230 | if (f.liveness.isUnused(inst)) { |
| 5365 | try reap(f, inst, &.{extra.field_ptr}); | 5231 | try reap(f, inst, &.{extra.field_ptr}); |
| 5366 | return CValue.none; | 5232 | return .none; |
| 5367 | } | 5233 | } |
| 5368 | 5234 | ||
| 5369 | const struct_ptr_ty = f.air.typeOfIndex(inst); | 5235 | const target = f.object.dg.module.getTarget(); |
| 5236 | const container_ptr_ty = f.air.typeOfIndex(inst); | ||
| 5237 | const container_ty = container_ptr_ty.childType(); | ||
| 5238 | |||
| 5370 | const field_ptr_ty = f.air.typeOf(extra.field_ptr); | 5239 | const field_ptr_ty = f.air.typeOf(extra.field_ptr); |
| 5371 | const field_ptr_val = try f.resolveInst(extra.field_ptr); | 5240 | const field_ptr_val = try f.resolveInst(extra.field_ptr); |
| 5372 | try reap(f, inst, &.{extra.field_ptr}); | 5241 | try reap(f, inst, &.{extra.field_ptr}); |
| 5373 | 5242 | ||
| 5374 | const target = f.object.dg.module.getTarget(); | ||
| 5375 | const struct_ty = struct_ptr_ty.childType(); | ||
| 5376 | const field_offset = struct_ty.structFieldOffset(extra.field_index, target); | ||
| 5377 | |||
| 5378 | var field_offset_pl = Value.Payload.I64{ | ||
| 5379 | .base = .{ .tag = .int_i64 }, | ||
| 5380 | .data = -@intCast(i64, field_offset), | ||
| 5381 | }; | ||
| 5382 | const field_offset_val = Value.initPayload(&field_offset_pl.base); | ||
| 5383 | |||
| 5384 | var u8_ptr_pl = field_ptr_ty.ptrInfo(); | ||
| 5385 | u8_ptr_pl.data.pointee_type = Type.u8; | ||
| 5386 | const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); | ||
| 5387 | |||
| 5388 | const writer = f.object.writer(); | 5243 | const writer = f.object.writer(); |
| 5389 | const local = try f.allocLocal(inst, struct_ptr_ty); | 5244 | const local = try f.allocLocal(inst, container_ptr_ty); |
| 5390 | try f.writeCValue(writer, local, .Other); | 5245 | try f.writeCValue(writer, local, .Other); |
| 5391 | try writer.writeAll(" = ("); | 5246 | try writer.writeAll(" = ("); |
| 5392 | try f.renderTypecast(writer, struct_ptr_ty); | 5247 | try f.renderType(writer, container_ptr_ty); |
| 5393 | try writer.writeAll(")&(("); | ||
| 5394 | try f.renderTypecast(writer, u8_ptr_ty); | ||
| 5395 | try writer.writeByte(')'); | 5248 | try writer.writeByte(')'); |
| 5396 | try f.writeCValue(writer, field_ptr_val, .Other); | 5249 | |
| 5397 | try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.isize, field_offset_val)}); | 5250 | switch (fieldLocation(container_ty, field_ptr_ty, extra.field_index, target)) { |
| 5251 | .begin => try f.writeCValue(writer, field_ptr_val, .Initializer), | ||
| 5252 | .field => |field| { | ||
| 5253 | var u8_ptr_pl = field_ptr_ty.ptrInfo(); | ||
| 5254 | u8_ptr_pl.data.pointee_type = Type.u8; | ||
| 5255 | const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); | ||
| 5256 | |||
| 5257 | try writer.writeAll("(("); | ||
| 5258 | try f.renderType(writer, u8_ptr_ty); | ||
| 5259 | try writer.writeByte(')'); | ||
| 5260 | try f.writeCValue(writer, field_ptr_val, .Other); | ||
| 5261 | try writer.writeAll(" - offsetof("); | ||
| 5262 | try f.renderType(writer, container_ty); | ||
| 5263 | try writer.writeAll(", "); | ||
| 5264 | try f.writeCValue(writer, field, .Other); | ||
| 5265 | try writer.writeAll("))"); | ||
| 5266 | }, | ||
| 5267 | .byte_offset => |byte_offset| { | ||
| 5268 | var u8_ptr_pl = field_ptr_ty.ptrInfo(); | ||
| 5269 | u8_ptr_pl.data.pointee_type = Type.u8; | ||
| 5270 | const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); | ||
| 5271 | |||
| 5272 | var byte_offset_pl = Value.Payload.U64{ | ||
| 5273 | .base = .{ .tag = .int_u64 }, | ||
| 5274 | .data = byte_offset, | ||
| 5275 | }; | ||
| 5276 | const byte_offset_val = Value.initPayload(&byte_offset_pl.base); | ||
| 5277 | |||
| 5278 | try writer.writeAll("(("); | ||
| 5279 | try f.renderType(writer, u8_ptr_ty); | ||
| 5280 | try writer.writeByte(')'); | ||
| 5281 | try f.writeCValue(writer, field_ptr_val, .Other); | ||
| 5282 | try writer.print(" - {})", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); | ||
| 5283 | }, | ||
| 5284 | .end => { | ||
| 5285 | try f.writeCValue(writer, field_ptr_val, .Other); | ||
| 5286 | try writer.print(" - {}", .{try f.fmtIntLiteral(Type.usize, Value.one)}); | ||
| 5287 | }, | ||
| 5288 | } | ||
| 5289 | |||
| 5290 | try writer.writeAll(";\n"); | ||
| 5398 | return local; | 5291 | return local; |
| 5399 | } | 5292 | } |
| 5400 | 5293 | ||
| 5401 | fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue { | 5294 | fn fieldPtr( |
| 5402 | const writer = f.object.writer(); | 5295 | f: *Function, |
| 5296 | inst: Air.Inst.Index, | ||
| 5297 | container_ptr_ty: Type, | ||
| 5298 | container_ptr_val: CValue, | ||
| 5299 | field_index: u32, | ||
| 5300 | ) !CValue { | ||
| 5301 | const target = f.object.dg.module.getTarget(); | ||
| 5302 | const container_ty = container_ptr_ty.elemType(); | ||
| 5403 | const field_ptr_ty = f.air.typeOfIndex(inst); | 5303 | const field_ptr_ty = f.air.typeOfIndex(inst); |
| 5404 | const field_ptr_info = field_ptr_ty.ptrInfo(); | ||
| 5405 | const struct_ty = struct_ptr_ty.elemType(); | ||
| 5406 | const field_ty = struct_ty.structFieldType(index); | ||
| 5407 | 5304 | ||
| 5408 | // Ensure complete type definition is visible before accessing fields. | 5305 | // Ensure complete type definition is visible before accessing fields. |
| 5409 | try f.renderType(std.io.null_writer, struct_ty); | 5306 | _ = try f.typeToIndex(container_ty, .complete); |
| 5410 | 5307 | ||
| 5308 | const writer = f.object.writer(); | ||
| 5411 | const local = try f.allocLocal(inst, field_ptr_ty); | 5309 | const local = try f.allocLocal(inst, field_ptr_ty); |
| 5412 | try f.writeCValue(writer, local, .Other); | 5310 | try f.writeCValue(writer, local, .Other); |
| 5413 | try writer.writeAll(" = ("); | 5311 | try writer.writeAll(" = ("); |
| 5414 | try f.renderTypecast(writer, field_ptr_ty); | 5312 | try f.renderType(writer, field_ptr_ty); |
| 5415 | try writer.writeByte(')'); | 5313 | try writer.writeByte(')'); |
| 5416 | 5314 | ||
| 5417 | const extra_name: CValue = switch (struct_ty.tag()) { | 5315 | switch (fieldLocation(container_ty, field_ptr_ty, field_index, target)) { |
| 5418 | .union_tagged, .union_safety_tagged => .{ .identifier = "payload" }, | 5316 | .begin => try f.writeCValue(writer, container_ptr_val, .Initializer), |
| 5419 | else => .none, | 5317 | .field => |field| { |
| 5420 | }; | 5318 | try writer.writeByte('&'); |
| 5421 | 5319 | try f.writeCValueDerefMember(writer, container_ptr_val, field); | |
| 5422 | const FieldLoc = union(enum) { | 5320 | }, |
| 5423 | begin: void, | 5321 | .byte_offset => |byte_offset| { |
| 5424 | field: CValue, | 5322 | var u8_ptr_pl = field_ptr_ty.ptrInfo(); |
| 5425 | end: void, | 5323 | u8_ptr_pl.data.pointee_type = Type.u8; |
| 5426 | }; | 5324 | const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); |
| 5427 | const field_loc = switch (struct_ty.tag()) { | ||
| 5428 | .@"struct" => switch (struct_ty.containerLayout()) { | ||
| 5429 | .Auto, .Extern => for (struct_ty.structFields().values()[index..], 0..) |field, offset| { | ||
| 5430 | if (field.ty.hasRuntimeBitsIgnoreComptime()) break FieldLoc{ .field = .{ | ||
| 5431 | .identifier = struct_ty.structFieldName(index + offset), | ||
| 5432 | } }; | ||
| 5433 | } else @as(FieldLoc, .end), | ||
| 5434 | .Packed => if (field_ptr_info.data.host_size == 0) { | ||
| 5435 | const target = f.object.dg.module.getTarget(); | ||
| 5436 | |||
| 5437 | const byte_offset = struct_ty.packedStructFieldByteOffset(index, target); | ||
| 5438 | var byte_offset_pl = Value.Payload.U64{ | ||
| 5439 | .base = .{ .tag = .int_u64 }, | ||
| 5440 | .data = byte_offset, | ||
| 5441 | }; | ||
| 5442 | const byte_offset_val = Value.initPayload(&byte_offset_pl.base); | ||
| 5443 | |||
| 5444 | var u8_ptr_pl = field_ptr_info; | ||
| 5445 | u8_ptr_pl.data.pointee_type = Type.u8; | ||
| 5446 | const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); | ||
| 5447 | 5325 | ||
| 5448 | if (!std.mem.isAligned(byte_offset, field_ptr_ty.ptrAlignment(target))) { | 5326 | var byte_offset_pl = Value.Payload.U64{ |
| 5449 | return f.fail("TODO: CBE: unaligned packed struct field pointer", .{}); | 5327 | .base = .{ .tag = .int_u64 }, |
| 5450 | } | 5328 | .data = byte_offset, |
| 5329 | }; | ||
| 5330 | const byte_offset_val = Value.initPayload(&byte_offset_pl.base); | ||
| 5451 | 5331 | ||
| 5452 | try writer.writeAll("&(("); | 5332 | try writer.writeAll("(("); |
| 5453 | try f.renderTypecast(writer, u8_ptr_ty); | 5333 | try f.renderType(writer, u8_ptr_ty); |
| 5454 | try writer.writeByte(')'); | 5334 | try writer.writeByte(')'); |
| 5455 | try f.writeCValue(writer, struct_ptr, .Other); | 5335 | try f.writeCValue(writer, container_ptr_val, .Other); |
| 5456 | try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); | 5336 | try writer.print(" + {})", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); |
| 5457 | return local; | ||
| 5458 | } else @as(FieldLoc, .begin), | ||
| 5459 | }, | ||
| 5460 | .@"union", .union_safety_tagged, .union_tagged => if (struct_ty.containerLayout() == .Packed) { | ||
| 5461 | try f.writeCValue(writer, struct_ptr, .Other); | ||
| 5462 | try writer.writeAll(";\n"); | ||
| 5463 | return local; | ||
| 5464 | } else if (field_ty.hasRuntimeBitsIgnoreComptime()) FieldLoc{ .field = .{ | ||
| 5465 | .identifier = struct_ty.unionFields().keys()[index], | ||
| 5466 | } } else @as(FieldLoc, .end), | ||
| 5467 | .tuple, .anon_struct => field_name: { | ||
| 5468 | const tuple = struct_ty.tupleFields(); | ||
| 5469 | if (tuple.values[index].tag() != .unreachable_value) return CValue.none; | ||
| 5470 | |||
| 5471 | var id: usize = 0; | ||
| 5472 | break :field_name for (tuple.values, 0..) |value, i| { | ||
| 5473 | if (value.tag() != .unreachable_value) continue; | ||
| 5474 | if (!tuple.types[i].hasRuntimeBitsIgnoreComptime()) continue; | ||
| 5475 | if (i >= index) break FieldLoc{ .field = .{ .field = id } }; | ||
| 5476 | id += 1; | ||
| 5477 | } else @as(FieldLoc, .end); | ||
| 5478 | }, | 5337 | }, |
| 5479 | else => unreachable, | 5338 | .end => { |
| 5480 | }; | ||
| 5481 | |||
| 5482 | try writer.writeByte('&'); | ||
| 5483 | switch (field_loc) { | ||
| 5484 | .begin, .end => { | ||
| 5485 | try writer.writeByte('('); | 5339 | try writer.writeByte('('); |
| 5486 | try f.writeCValue(writer, struct_ptr, .Other); | 5340 | try f.writeCValue(writer, container_ptr_val, .Other); |
| 5487 | try writer.print(")[{}]", .{ | 5341 | try writer.print(" + {})", .{try f.fmtIntLiteral(Type.usize, Value.one)}); |
| 5488 | @boolToInt(field_loc == .end and struct_ty.hasRuntimeBitsIgnoreComptime()), | ||
| 5489 | }); | ||
| 5490 | }, | 5342 | }, |
| 5491 | .field => |field| if (extra_name != .none) { | ||
| 5492 | try f.writeCValueDerefMember(writer, struct_ptr, extra_name); | ||
| 5493 | try writer.writeByte('.'); | ||
| 5494 | try f.writeCValue(writer, field, .Other); | ||
| 5495 | } else try f.writeCValueDerefMember(writer, struct_ptr, field), | ||
| 5496 | } | 5343 | } |
| 5344 | |||
| 5497 | try writer.writeAll(";\n"); | 5345 | try writer.writeAll(";\n"); |
| 5498 | return local; | 5346 | return local; |
| 5499 | } | 5347 | } |
| ... | @@ -5504,13 +5352,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5504,13 +5352,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5504 | 5352 | ||
| 5505 | if (f.liveness.isUnused(inst)) { | 5353 | if (f.liveness.isUnused(inst)) { |
| 5506 | try reap(f, inst, &.{extra.struct_operand}); | 5354 | try reap(f, inst, &.{extra.struct_operand}); |
| 5507 | return CValue.none; | 5355 | return .none; |
| 5508 | } | 5356 | } |
| 5509 | 5357 | ||
| 5510 | const inst_ty = f.air.typeOfIndex(inst); | 5358 | const inst_ty = f.air.typeOfIndex(inst); |
| 5511 | if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { | 5359 | if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { |
| 5512 | try reap(f, inst, &.{extra.struct_operand}); | 5360 | try reap(f, inst, &.{extra.struct_operand}); |
| 5513 | return CValue.none; | 5361 | return .none; |
| 5514 | } | 5362 | } |
| 5515 | 5363 | ||
| 5516 | const target = f.object.dg.module.getTarget(); | 5364 | const target = f.object.dg.module.getTarget(); |
| ... | @@ -5520,16 +5368,14 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5520,16 +5368,14 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5520 | const writer = f.object.writer(); | 5368 | const writer = f.object.writer(); |
| 5521 | 5369 | ||
| 5522 | // Ensure complete type definition is visible before accessing fields. | 5370 | // Ensure complete type definition is visible before accessing fields. |
| 5523 | try f.renderType(std.io.null_writer, struct_ty); | 5371 | _ = try f.typeToIndex(struct_ty, .complete); |
| 5524 | |||
| 5525 | const extra_name: CValue = switch (struct_ty.tag()) { | ||
| 5526 | .union_tagged, .union_safety_tagged => .{ .identifier = "payload" }, | ||
| 5527 | else => .none, | ||
| 5528 | }; | ||
| 5529 | 5372 | ||
| 5530 | const field_name: CValue = switch (struct_ty.tag()) { | 5373 | const field_name: CValue = switch (struct_ty.tag()) { |
| 5531 | .@"struct" => switch (struct_ty.containerLayout()) { | 5374 | .tuple, .anon_struct, .@"struct" => switch (struct_ty.containerLayout()) { |
| 5532 | .Auto, .Extern => .{ .identifier = struct_ty.structFieldName(extra.field_index) }, | 5375 | .Auto, .Extern => if (struct_ty.isSimpleTuple()) |
| 5376 | .{ .field = extra.field_index } | ||
| 5377 | else | ||
| 5378 | .{ .identifier = struct_ty.structFieldName(extra.field_index) }, | ||
| 5533 | .Packed => { | 5379 | .Packed => { |
| 5534 | const struct_obj = struct_ty.castTag(.@"struct").?.data; | 5380 | const struct_obj = struct_ty.castTag(.@"struct").?.data; |
| 5535 | const int_info = struct_ty.intInfo(target); | 5381 | const int_info = struct_ty.intInfo(target); |
| ... | @@ -5564,7 +5410,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5564,7 +5410,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5564 | try writer.writeAll(" = zig_wrap_"); | 5410 | try writer.writeAll(" = zig_wrap_"); |
| 5565 | try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty); | 5411 | try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty); |
| 5566 | try writer.writeAll("(("); | 5412 | try writer.writeAll("(("); |
| 5567 | try f.renderTypecast(writer, field_int_ty); | 5413 | try f.renderType(writer, field_int_ty); |
| 5568 | try writer.writeByte(')'); | 5414 | try writer.writeByte(')'); |
| 5569 | const cant_cast = int_info.bits > 64; | 5415 | const cant_cast = int_info.bits > 64; |
| 5570 | if (cant_cast) { | 5416 | if (cant_cast) { |
| ... | @@ -5587,13 +5433,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5587,13 +5433,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5587 | 5433 | ||
| 5588 | const local = try f.allocLocal(inst, inst_ty); | 5434 | const local = try f.allocLocal(inst, inst_ty); |
| 5589 | try writer.writeAll("memcpy("); | 5435 | try writer.writeAll("memcpy("); |
| 5590 | try f.writeCValue(writer, .{ .local_ref = local.local }, .FunctionArgument); | 5436 | try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument); |
| 5591 | try writer.writeAll(", "); | 5437 | try writer.writeAll(", "); |
| 5592 | try f.writeCValue(writer, .{ .local_ref = temp_local.local }, .FunctionArgument); | 5438 | try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument); |
| 5593 | try writer.writeAll(", sizeof("); | 5439 | try writer.writeAll(", sizeof("); |
| 5594 | try f.renderTypecast(writer, inst_ty); | 5440 | try f.renderType(writer, inst_ty); |
| 5595 | try writer.writeAll("));\n"); | 5441 | try writer.writeAll("));\n"); |
| 5596 | try freeLocal(f, inst, temp_local.local, 0); | 5442 | try freeLocal(f, inst, temp_local.new_local, 0); |
| 5597 | return local; | 5443 | return local; |
| 5598 | }, | 5444 | }, |
| 5599 | }, | 5445 | }, |
| ... | @@ -5613,48 +5459,37 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5613,48 +5459,37 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5613 | try writer.writeAll(", &"); | 5459 | try writer.writeAll(", &"); |
| 5614 | try f.writeCValue(writer, operand_lval, .FunctionArgument); | 5460 | try f.writeCValue(writer, operand_lval, .FunctionArgument); |
| 5615 | try writer.writeAll(", sizeof("); | 5461 | try writer.writeAll(", sizeof("); |
| 5616 | try f.renderTypecast(writer, inst_ty); | 5462 | try f.renderType(writer, inst_ty); |
| 5617 | try writer.writeAll("));\n"); | 5463 | try writer.writeAll("));\n"); |
| 5618 | 5464 | ||
| 5619 | if (struct_byval == .constant) { | 5465 | if (struct_byval == .constant) { |
| 5620 | try freeLocal(f, inst, operand_lval.local, 0); | 5466 | try freeLocal(f, inst, operand_lval.new_local, 0); |
| 5621 | } | 5467 | } |
| 5622 | 5468 | ||
| 5623 | return local; | 5469 | return local; |
| 5624 | } else .{ | 5470 | } else field_name: { |
| 5625 | .identifier = struct_ty.unionFields().keys()[extra.field_index], | 5471 | const name = struct_ty.unionFields().keys()[extra.field_index]; |
| 5626 | }, | 5472 | break :field_name if (struct_ty.unionTagTypeSafety()) |_| |
| 5627 | .tuple, .anon_struct => blk: { | 5473 | .{ .payload_identifier = name } |
| 5628 | const tuple = struct_ty.tupleFields(); | 5474 | else |
| 5629 | if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none; | 5475 | .{ .identifier = name }; |
| 5630 | |||
| 5631 | var id: usize = 0; | ||
| 5632 | for (tuple.values[0..extra.field_index]) |value| | ||
| 5633 | id += @boolToInt(value.tag() == .unreachable_value); | ||
| 5634 | break :blk .{ .field = id }; | ||
| 5635 | }, | 5476 | }, |
| 5636 | else => unreachable, | 5477 | else => unreachable, |
| 5637 | }; | 5478 | }; |
| 5638 | 5479 | ||
| 5639 | const is_array = lowersToArray(inst_ty, target); | ||
| 5640 | const local = try f.allocLocal(inst, inst_ty); | 5480 | const local = try f.allocLocal(inst, inst_ty); |
| 5641 | if (is_array) { | 5481 | if (lowersToArray(inst_ty, target)) { |
| 5642 | try writer.writeAll("memcpy("); | 5482 | try writer.writeAll("memcpy("); |
| 5643 | try f.writeCValue(writer, local, .FunctionArgument); | 5483 | try f.writeCValue(writer, local, .FunctionArgument); |
| 5644 | try writer.writeAll(", "); | 5484 | try writer.writeAll(", "); |
| 5485 | try f.writeCValueMember(writer, struct_byval, field_name); | ||
| 5486 | try writer.writeAll(", sizeof("); | ||
| 5487 | try f.renderType(writer, inst_ty); | ||
| 5488 | try writer.writeAll("))"); | ||
| 5645 | } else { | 5489 | } else { |
| 5646 | try f.writeCValue(writer, local, .Other); | 5490 | try f.writeCValue(writer, local, .Other); |
| 5647 | try writer.writeAll(" = "); | 5491 | try writer.writeAll(" = "); |
| 5648 | } | 5492 | try f.writeCValueMember(writer, struct_byval, field_name); |
| 5649 | if (extra_name != .none) { | ||
| 5650 | try f.writeCValueMember(writer, struct_byval, extra_name); | ||
| 5651 | try writer.writeByte('.'); | ||
| 5652 | try f.writeCValue(writer, field_name, .Other); | ||
| 5653 | } else try f.writeCValueMember(writer, struct_byval, field_name); | ||
| 5654 | if (is_array) { | ||
| 5655 | try writer.writeAll(", sizeof("); | ||
| 5656 | try f.renderTypecast(writer, inst_ty); | ||
| 5657 | try writer.writeAll("))"); | ||
| 5658 | } | 5493 | } |
| 5659 | try writer.writeAll(";\n"); | 5494 | try writer.writeAll(";\n"); |
| 5660 | return local; | 5495 | return local; |
| ... | @@ -5667,7 +5502,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5667,7 +5502,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5667 | 5502 | ||
| 5668 | if (f.liveness.isUnused(inst)) { | 5503 | if (f.liveness.isUnused(inst)) { |
| 5669 | try reap(f, inst, &.{ty_op.operand}); | 5504 | try reap(f, inst, &.{ty_op.operand}); |
| 5670 | return CValue.none; | 5505 | return .none; |
| 5671 | } | 5506 | } |
| 5672 | 5507 | ||
| 5673 | const inst_ty = f.air.typeOfIndex(inst); | 5508 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -5704,7 +5539,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu | ... | @@ -5704,7 +5539,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu |
| 5704 | 5539 | ||
| 5705 | if (f.liveness.isUnused(inst)) { | 5540 | if (f.liveness.isUnused(inst)) { |
| 5706 | try reap(f, inst, &.{ty_op.operand}); | 5541 | try reap(f, inst, &.{ty_op.operand}); |
| 5707 | return CValue.none; | 5542 | return .none; |
| 5708 | } | 5543 | } |
| 5709 | 5544 | ||
| 5710 | const inst_ty = f.air.typeOfIndex(inst); | 5545 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -5715,13 +5550,13 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu | ... | @@ -5715,13 +5550,13 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu |
| 5715 | const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; | 5550 | const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; |
| 5716 | 5551 | ||
| 5717 | if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) { | 5552 | if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) { |
| 5718 | if (!is_ptr) return CValue.none; | 5553 | if (!is_ptr) return .none; |
| 5719 | 5554 | ||
| 5720 | const w = f.object.writer(); | 5555 | const w = f.object.writer(); |
| 5721 | const local = try f.allocLocal(inst, inst_ty); | 5556 | const local = try f.allocLocal(inst, inst_ty); |
| 5722 | try f.writeCValue(w, local, .Other); | 5557 | try f.writeCValue(w, local, .Other); |
| 5723 | try w.writeAll(" = ("); | 5558 | try w.writeAll(" = ("); |
| 5724 | try f.renderTypecast(w, inst_ty); | 5559 | try f.renderType(w, inst_ty); |
| 5725 | try w.writeByte(')'); | 5560 | try w.writeByte(')'); |
| 5726 | try f.writeCValue(w, operand, .Initializer); | 5561 | try f.writeCValue(w, operand, .Initializer); |
| 5727 | try w.writeAll(";\n"); | 5562 | try w.writeAll(";\n"); |
| ... | @@ -5746,7 +5581,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5746,7 +5581,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5746 | 5581 | ||
| 5747 | if (f.liveness.isUnused(inst)) { | 5582 | if (f.liveness.isUnused(inst)) { |
| 5748 | try reap(f, inst, &.{ty_op.operand}); | 5583 | try reap(f, inst, &.{ty_op.operand}); |
| 5749 | return CValue.none; | 5584 | return .none; |
| 5750 | } | 5585 | } |
| 5751 | 5586 | ||
| 5752 | const inst_ty = f.air.typeOfIndex(inst); | 5587 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -5782,7 +5617,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5782,7 +5617,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5782 | try writer.writeAll(", "); | 5617 | try writer.writeAll(", "); |
| 5783 | try f.writeCValue(writer, payload, .FunctionArgument); | 5618 | try f.writeCValue(writer, payload, .FunctionArgument); |
| 5784 | try writer.writeAll(", sizeof("); | 5619 | try writer.writeAll(", sizeof("); |
| 5785 | try f.renderTypecast(writer, payload_ty); | 5620 | try f.renderType(writer, payload_ty); |
| 5786 | try writer.writeAll("));\n"); | 5621 | try writer.writeAll("));\n"); |
| 5787 | } | 5622 | } |
| 5788 | return local; | 5623 | return local; |
| ... | @@ -5792,7 +5627,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5792,7 +5627,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5792 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | 5627 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 5793 | if (f.liveness.isUnused(inst)) { | 5628 | if (f.liveness.isUnused(inst)) { |
| 5794 | try reap(f, inst, &.{ty_op.operand}); | 5629 | try reap(f, inst, &.{ty_op.operand}); |
| 5795 | return CValue.none; | 5630 | return .none; |
| 5796 | } | 5631 | } |
| 5797 | 5632 | ||
| 5798 | const writer = f.object.writer(); | 5633 | const writer = f.object.writer(); |
| ... | @@ -5846,7 +5681,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5846,7 +5681,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5846 | try writer.writeAll(";\n"); | 5681 | try writer.writeAll(";\n"); |
| 5847 | 5682 | ||
| 5848 | // Then return the payload pointer (only if it is used) | 5683 | // Then return the payload pointer (only if it is used) |
| 5849 | if (f.liveness.isUnused(inst)) return CValue.none; | 5684 | if (f.liveness.isUnused(inst)) return .none; |
| 5850 | 5685 | ||
| 5851 | const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); | 5686 | const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); |
| 5852 | try f.writeCValue(writer, local, .Other); | 5687 | try f.writeCValue(writer, local, .Other); |
| ... | @@ -5857,7 +5692,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5857,7 +5692,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5857 | } | 5692 | } |
| 5858 | 5693 | ||
| 5859 | fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { | 5694 | fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5860 | if (f.liveness.isUnused(inst)) return CValue.none; | 5695 | if (f.liveness.isUnused(inst)) return .none; |
| 5861 | return f.fail("TODO: C backend: implement airErrReturnTrace", .{}); | 5696 | return f.fail("TODO: C backend: implement airErrReturnTrace", .{}); |
| 5862 | } | 5697 | } |
| 5863 | 5698 | ||
| ... | @@ -5875,7 +5710,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5875,7 +5710,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5875 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | 5710 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 5876 | if (f.liveness.isUnused(inst)) { | 5711 | if (f.liveness.isUnused(inst)) { |
| 5877 | try reap(f, inst, &.{ty_op.operand}); | 5712 | try reap(f, inst, &.{ty_op.operand}); |
| 5878 | return CValue.none; | 5713 | return .none; |
| 5879 | } | 5714 | } |
| 5880 | 5715 | ||
| 5881 | const inst_ty = f.air.typeOfIndex(inst); | 5716 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -5902,7 +5737,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5902,7 +5737,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5902 | try writer.writeAll(", "); | 5737 | try writer.writeAll(", "); |
| 5903 | try f.writeCValue(writer, payload, .FunctionArgument); | 5738 | try f.writeCValue(writer, payload, .FunctionArgument); |
| 5904 | try writer.writeAll(", sizeof("); | 5739 | try writer.writeAll(", sizeof("); |
| 5905 | try f.renderTypecast(writer, payload_ty); | 5740 | try f.renderType(writer, payload_ty); |
| 5906 | try writer.writeAll("));\n"); | 5741 | try writer.writeAll("));\n"); |
| 5907 | } | 5742 | } |
| 5908 | return local; | 5743 | return local; |
| ... | @@ -5913,7 +5748,7 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const | ... | @@ -5913,7 +5748,7 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const |
| 5913 | 5748 | ||
| 5914 | if (f.liveness.isUnused(inst)) { | 5749 | if (f.liveness.isUnused(inst)) { |
| 5915 | try reap(f, inst, &.{un_op}); | 5750 | try reap(f, inst, &.{un_op}); |
| 5916 | return CValue.none; | 5751 | return .none; |
| 5917 | } | 5752 | } |
| 5918 | 5753 | ||
| 5919 | const writer = f.object.writer(); | 5754 | const writer = f.object.writer(); |
| ... | @@ -5951,7 +5786,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5951,7 +5786,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5951 | 5786 | ||
| 5952 | if (f.liveness.isUnused(inst)) { | 5787 | if (f.liveness.isUnused(inst)) { |
| 5953 | try reap(f, inst, &.{ty_op.operand}); | 5788 | try reap(f, inst, &.{ty_op.operand}); |
| 5954 | return CValue.none; | 5789 | return .none; |
| 5955 | } | 5790 | } |
| 5956 | 5791 | ||
| 5957 | const operand = try f.resolveInst(ty_op.operand); | 5792 | const operand = try f.resolveInst(ty_op.operand); |
| ... | @@ -5959,26 +5794,28 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5959,26 +5794,28 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5959 | const inst_ty = f.air.typeOfIndex(inst); | 5794 | const inst_ty = f.air.typeOfIndex(inst); |
| 5960 | const writer = f.object.writer(); | 5795 | const writer = f.object.writer(); |
| 5961 | const local = try f.allocLocal(inst, inst_ty); | 5796 | const local = try f.allocLocal(inst, inst_ty); |
| 5962 | try f.writeCValue(writer, local, .Other); | 5797 | const array_ty = f.air.typeOf(ty_op.operand).childType(); |
| 5963 | const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen(); | ||
| 5964 | 5798 | ||
| 5965 | try writer.writeAll(".ptr = "); | 5799 | try f.writeCValueMember(writer, local, .{ .identifier = "ptr" }); |
| 5800 | try writer.writeAll(" = "); | ||
| 5801 | // Unfortunately, C does not support any equivalent to | ||
| 5802 | // &(*(void *)p)[0], although LLVM does via GetElementPtr | ||
| 5966 | if (operand == .undef) { | 5803 | if (operand == .undef) { |
| 5967 | // Unfortunately, C does not support any equivalent to | ||
| 5968 | // &(*(void *)p)[0], although LLVM does via GetElementPtr | ||
| 5969 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 5804 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 5970 | try f.writeCValue(writer, CValue{ .undef = inst_ty.slicePtrFieldType(&buf) }, .Initializer); | 5805 | try f.writeCValue(writer, .{ .undef = inst_ty.slicePtrFieldType(&buf) }, .Initializer); |
| 5971 | } else { | 5806 | } else if (array_ty.hasRuntimeBitsIgnoreComptime()) { |
| 5972 | try writer.writeAll("&("); | 5807 | try writer.writeAll("&("); |
| 5973 | try f.writeCValueDeref(writer, operand); | 5808 | try f.writeCValueDeref(writer, operand); |
| 5974 | try writer.print(")[{}]", .{try f.fmtIntLiteral(Type.usize, Value.zero)}); | 5809 | try writer.print(")[{}]", .{try f.fmtIntLiteral(Type.usize, Value.zero)}); |
| 5975 | } | 5810 | } else try f.writeCValue(writer, operand, .Initializer); |
| 5811 | try writer.writeAll("; "); | ||
| 5976 | 5812 | ||
| 5813 | const array_len = array_ty.arrayLen(); | ||
| 5977 | var len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = array_len }; | 5814 | var len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = array_len }; |
| 5978 | const len_val = Value.initPayload(&len_pl.base); | 5815 | const len_val = Value.initPayload(&len_pl.base); |
| 5979 | try writer.writeAll("; "); | 5816 | try f.writeCValueMember(writer, local, .{ .identifier = "len" }); |
| 5980 | try f.writeCValue(writer, local, .Other); | 5817 | try writer.print(" = {};\n", .{try f.fmtIntLiteral(Type.usize, len_val)}); |
| 5981 | try writer.print(".len = {};\n", .{try f.fmtIntLiteral(Type.usize, len_val)}); | 5818 | |
| 5982 | return local; | 5819 | return local; |
| 5983 | } | 5820 | } |
| 5984 | 5821 | ||
| ... | @@ -5987,7 +5824,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -5987,7 +5824,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5987 | 5824 | ||
| 5988 | if (f.liveness.isUnused(inst)) { | 5825 | if (f.liveness.isUnused(inst)) { |
| 5989 | try reap(f, inst, &.{ty_op.operand}); | 5826 | try reap(f, inst, &.{ty_op.operand}); |
| 5990 | return CValue.none; | 5827 | return .none; |
| 5991 | } | 5828 | } |
| 5992 | 5829 | ||
| 5993 | const inst_ty = f.air.typeOfIndex(inst); | 5830 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -6035,7 +5872,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6035,7 +5872,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6035 | 5872 | ||
| 6036 | if (f.liveness.isUnused(inst)) { | 5873 | if (f.liveness.isUnused(inst)) { |
| 6037 | try reap(f, inst, &.{un_op}); | 5874 | try reap(f, inst, &.{un_op}); |
| 6038 | return CValue.none; | 5875 | return .none; |
| 6039 | } | 5876 | } |
| 6040 | 5877 | ||
| 6041 | const operand = try f.resolveInst(un_op); | 5878 | const operand = try f.resolveInst(un_op); |
| ... | @@ -6046,7 +5883,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6046,7 +5883,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6046 | try f.writeCValue(writer, local, .Other); | 5883 | try f.writeCValue(writer, local, .Other); |
| 6047 | 5884 | ||
| 6048 | try writer.writeAll(" = ("); | 5885 | try writer.writeAll(" = ("); |
| 6049 | try f.renderTypecast(writer, inst_ty); | 5886 | try f.renderType(writer, inst_ty); |
| 6050 | try writer.writeByte(')'); | 5887 | try writer.writeByte(')'); |
| 6051 | try f.writeCValue(writer, operand, .Other); | 5888 | try f.writeCValue(writer, operand, .Other); |
| 6052 | try writer.writeAll(";\n"); | 5889 | try writer.writeAll(";\n"); |
| ... | @@ -6063,7 +5900,7 @@ fn airUnBuiltinCall( | ... | @@ -6063,7 +5900,7 @@ fn airUnBuiltinCall( |
| 6063 | 5900 | ||
| 6064 | if (f.liveness.isUnused(inst)) { | 5901 | if (f.liveness.isUnused(inst)) { |
| 6065 | try reap(f, inst, &.{ty_op.operand}); | 5902 | try reap(f, inst, &.{ty_op.operand}); |
| 6066 | return CValue.none; | 5903 | return .none; |
| 6067 | } | 5904 | } |
| 6068 | 5905 | ||
| 6069 | const operand = try f.resolveInst(ty_op.operand); | 5906 | const operand = try f.resolveInst(ty_op.operand); |
| ... | @@ -6095,7 +5932,7 @@ fn airBinBuiltinCall( | ... | @@ -6095,7 +5932,7 @@ fn airBinBuiltinCall( |
| 6095 | 5932 | ||
| 6096 | if (f.liveness.isUnused(inst)) { | 5933 | if (f.liveness.isUnused(inst)) { |
| 6097 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 5934 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 6098 | return CValue.none; | 5935 | return .none; |
| 6099 | } | 5936 | } |
| 6100 | 5937 | ||
| 6101 | const lhs = try f.resolveInst(bin_op.lhs); | 5938 | const lhs = try f.resolveInst(bin_op.lhs); |
| ... | @@ -6168,7 +6005,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue | ... | @@ -6168,7 +6005,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue |
| 6168 | try writer.writeAll(";\n"); | 6005 | try writer.writeAll(";\n"); |
| 6169 | try writer.writeAll("if ("); | 6006 | try writer.writeAll("if ("); |
| 6170 | try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); | 6007 | try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); |
| 6171 | try f.renderTypecast(writer, ptr_ty.childType()); | 6008 | try f.renderType(writer, ptr_ty.childType()); |
| 6172 | try writer.writeByte(')'); | 6009 | try writer.writeByte(')'); |
| 6173 | if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); | 6010 | if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); |
| 6174 | try writer.writeAll(" *)"); | 6011 | try writer.writeAll(" *)"); |
| ... | @@ -6197,7 +6034,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue | ... | @@ -6197,7 +6034,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue |
| 6197 | try writer.writeAll(";\n"); | 6034 | try writer.writeAll(";\n"); |
| 6198 | try f.writeCValue(writer, local, .Other); | 6035 | try f.writeCValue(writer, local, .Other); |
| 6199 | try writer.print(".is_null = zig_cmpxchg_{s}((zig_atomic(", .{flavor}); | 6036 | try writer.print(".is_null = zig_cmpxchg_{s}((zig_atomic(", .{flavor}); |
| 6200 | try f.renderTypecast(writer, ptr_ty.childType()); | 6037 | try f.renderType(writer, ptr_ty.childType()); |
| 6201 | try writer.writeByte(')'); | 6038 | try writer.writeByte(')'); |
| 6202 | if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); | 6039 | if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); |
| 6203 | try writer.writeAll(" *)"); | 6040 | try writer.writeAll(" *)"); |
| ... | @@ -6217,8 +6054,8 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue | ... | @@ -6217,8 +6054,8 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue |
| 6217 | } | 6054 | } |
| 6218 | 6055 | ||
| 6219 | if (f.liveness.isUnused(inst)) { | 6056 | if (f.liveness.isUnused(inst)) { |
| 6220 | try freeLocal(f, inst, local.local, 0); | 6057 | try freeLocal(f, inst, local.new_local, 0); |
| 6221 | return CValue.none; | 6058 | return .none; |
| 6222 | } | 6059 | } |
| 6223 | 6060 | ||
| 6224 | return local; | 6061 | return local; |
| ... | @@ -6240,12 +6077,12 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6240,12 +6077,12 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6240 | switch (extra.op()) { | 6077 | switch (extra.op()) { |
| 6241 | else => { | 6078 | else => { |
| 6242 | try writer.writeAll("zig_atomic("); | 6079 | try writer.writeAll("zig_atomic("); |
| 6243 | try f.renderTypecast(writer, ptr_ty.elemType()); | 6080 | try f.renderType(writer, ptr_ty.elemType()); |
| 6244 | try writer.writeByte(')'); | 6081 | try writer.writeByte(')'); |
| 6245 | }, | 6082 | }, |
| 6246 | .Nand, .Min, .Max => { | 6083 | .Nand, .Min, .Max => { |
| 6247 | // These are missing from stdatomic.h, so no atomic types for now. | 6084 | // These are missing from stdatomic.h, so no atomic types for now. |
| 6248 | try f.renderTypecast(writer, ptr_ty.elemType()); | 6085 | try f.renderType(writer, ptr_ty.elemType()); |
| 6249 | }, | 6086 | }, |
| 6250 | } | 6087 | } |
| 6251 | if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); | 6088 | if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); |
| ... | @@ -6260,8 +6097,8 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6260,8 +6097,8 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6260 | try writer.writeAll(");\n"); | 6097 | try writer.writeAll(");\n"); |
| 6261 | 6098 | ||
| 6262 | if (f.liveness.isUnused(inst)) { | 6099 | if (f.liveness.isUnused(inst)) { |
| 6263 | try freeLocal(f, inst, local.local, 0); | 6100 | try freeLocal(f, inst, local.new_local, 0); |
| 6264 | return CValue.none; | 6101 | return .none; |
| 6265 | } | 6102 | } |
| 6266 | 6103 | ||
| 6267 | return local; | 6104 | return local; |
| ... | @@ -6273,7 +6110,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6273,7 +6110,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6273 | try reap(f, inst, &.{atomic_load.ptr}); | 6110 | try reap(f, inst, &.{atomic_load.ptr}); |
| 6274 | const ptr_ty = f.air.typeOf(atomic_load.ptr); | 6111 | const ptr_ty = f.air.typeOf(atomic_load.ptr); |
| 6275 | if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) { | 6112 | if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) { |
| 6276 | return CValue.none; | 6113 | return .none; |
| 6277 | } | 6114 | } |
| 6278 | 6115 | ||
| 6279 | const inst_ty = f.air.typeOfIndex(inst); | 6116 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -6282,7 +6119,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6282,7 +6119,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6282 | try f.writeCValue(writer, local, .Other); | 6119 | try f.writeCValue(writer, local, .Other); |
| 6283 | 6120 | ||
| 6284 | try writer.writeAll(" = zig_atomic_load((zig_atomic("); | 6121 | try writer.writeAll(" = zig_atomic_load((zig_atomic("); |
| 6285 | try f.renderTypecast(writer, ptr_ty.elemType()); | 6122 | try f.renderType(writer, ptr_ty.elemType()); |
| 6286 | try writer.writeByte(')'); | 6123 | try writer.writeByte(')'); |
| 6287 | if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); | 6124 | if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); |
| 6288 | try writer.writeAll(" *)"); | 6125 | try writer.writeAll(" *)"); |
| ... | @@ -6305,7 +6142,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa | ... | @@ -6305,7 +6142,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa |
| 6305 | const writer = f.object.writer(); | 6142 | const writer = f.object.writer(); |
| 6306 | 6143 | ||
| 6307 | try writer.writeAll("zig_atomic_store((zig_atomic("); | 6144 | try writer.writeAll("zig_atomic_store((zig_atomic("); |
| 6308 | try f.renderTypecast(writer, ptr_ty.elemType()); | 6145 | try f.renderType(writer, ptr_ty.elemType()); |
| 6309 | try writer.writeByte(')'); | 6146 | try writer.writeByte(')'); |
| 6310 | if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); | 6147 | if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); |
| 6311 | try writer.writeAll(" *)"); | 6148 | try writer.writeAll(" *)"); |
| ... | @@ -6316,7 +6153,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa | ... | @@ -6316,7 +6153,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa |
| 6316 | try f.object.dg.renderTypeForBuiltinFnName(writer, ptr_ty.childType()); | 6153 | try f.object.dg.renderTypeForBuiltinFnName(writer, ptr_ty.childType()); |
| 6317 | try writer.writeAll(");\n"); | 6154 | try writer.writeAll(");\n"); |
| 6318 | 6155 | ||
| 6319 | return CValue.none; | 6156 | return .none; |
| 6320 | } | 6157 | } |
| 6321 | 6158 | ||
| 6322 | fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { | 6159 | fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -6347,7 +6184,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6347,7 +6184,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6347 | try writer.writeAll(" += "); | 6184 | try writer.writeAll(" += "); |
| 6348 | try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other); | 6185 | try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other); |
| 6349 | try writer.writeAll(") (("); | 6186 | try writer.writeAll(") (("); |
| 6350 | try f.renderTypecast(writer, u8_ptr_ty); | 6187 | try f.renderType(writer, u8_ptr_ty); |
| 6351 | try writer.writeByte(')'); | 6188 | try writer.writeByte(')'); |
| 6352 | try f.writeCValue(writer, dest_ptr, .FunctionArgument); | 6189 | try f.writeCValue(writer, dest_ptr, .FunctionArgument); |
| 6353 | try writer.writeAll(")["); | 6190 | try writer.writeAll(")["); |
| ... | @@ -6357,9 +6194,9 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6357,9 +6194,9 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6357 | try writer.writeAll(";\n"); | 6194 | try writer.writeAll(";\n"); |
| 6358 | 6195 | ||
| 6359 | try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); | 6196 | try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); |
| 6360 | try freeLocal(f, inst, index.local, 0); | 6197 | try freeLocal(f, inst, index.new_local, 0); |
| 6361 | 6198 | ||
| 6362 | return CValue.none; | 6199 | return .none; |
| 6363 | } | 6200 | } |
| 6364 | 6201 | ||
| 6365 | try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); | 6202 | try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); |
| ... | @@ -6371,7 +6208,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6371,7 +6208,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6371 | try f.writeCValue(writer, len, .FunctionArgument); | 6208 | try f.writeCValue(writer, len, .FunctionArgument); |
| 6372 | try writer.writeAll(");\n"); | 6209 | try writer.writeAll(");\n"); |
| 6373 | 6210 | ||
| 6374 | return CValue.none; | 6211 | return .none; |
| 6375 | } | 6212 | } |
| 6376 | 6213 | ||
| 6377 | fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { | 6214 | fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -6391,7 +6228,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6391,7 +6228,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6391 | try f.writeCValue(writer, len, .FunctionArgument); | 6228 | try f.writeCValue(writer, len, .FunctionArgument); |
| 6392 | try writer.writeAll(");\n"); | 6229 | try writer.writeAll(");\n"); |
| 6393 | 6230 | ||
| 6394 | return CValue.none; | 6231 | return .none; |
| 6395 | } | 6232 | } |
| 6396 | 6233 | ||
| 6397 | fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { | 6234 | fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -6404,7 +6241,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6404,7 +6241,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6404 | const union_ty = f.air.typeOf(bin_op.lhs).childType(); | 6241 | const union_ty = f.air.typeOf(bin_op.lhs).childType(); |
| 6405 | const target = f.object.dg.module.getTarget(); | 6242 | const target = f.object.dg.module.getTarget(); |
| 6406 | const layout = union_ty.unionGetLayout(target); | 6243 | const layout = union_ty.unionGetLayout(target); |
| 6407 | if (layout.tag_size == 0) return CValue.none; | 6244 | if (layout.tag_size == 0) return .none; |
| 6408 | 6245 | ||
| 6409 | try writer.writeByte('('); | 6246 | try writer.writeByte('('); |
| 6410 | try f.writeCValue(writer, union_ptr, .Other); | 6247 | try f.writeCValue(writer, union_ptr, .Other); |
| ... | @@ -6412,7 +6249,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6412,7 +6249,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6412 | try f.writeCValue(writer, new_tag, .Other); | 6249 | try f.writeCValue(writer, new_tag, .Other); |
| 6413 | try writer.writeAll(";\n"); | 6250 | try writer.writeAll(";\n"); |
| 6414 | 6251 | ||
| 6415 | return CValue.none; | 6252 | return .none; |
| 6416 | } | 6253 | } |
| 6417 | 6254 | ||
| 6418 | fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { | 6255 | fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -6420,7 +6257,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6420,7 +6257,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6420 | 6257 | ||
| 6421 | if (f.liveness.isUnused(inst)) { | 6258 | if (f.liveness.isUnused(inst)) { |
| 6422 | try reap(f, inst, &.{ty_op.operand}); | 6259 | try reap(f, inst, &.{ty_op.operand}); |
| 6423 | return CValue.none; | 6260 | return .none; |
| 6424 | } | 6261 | } |
| 6425 | 6262 | ||
| 6426 | const operand = try f.resolveInst(ty_op.operand); | 6263 | const operand = try f.resolveInst(ty_op.operand); |
| ... | @@ -6430,7 +6267,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6430,7 +6267,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6430 | 6267 | ||
| 6431 | const target = f.object.dg.module.getTarget(); | 6268 | const target = f.object.dg.module.getTarget(); |
| 6432 | const layout = un_ty.unionGetLayout(target); | 6269 | const layout = un_ty.unionGetLayout(target); |
| 6433 | if (layout.tag_size == 0) return CValue.none; | 6270 | if (layout.tag_size == 0) return .none; |
| 6434 | 6271 | ||
| 6435 | const inst_ty = f.air.typeOfIndex(inst); | 6272 | const inst_ty = f.air.typeOfIndex(inst); |
| 6436 | const writer = f.object.writer(); | 6273 | const writer = f.object.writer(); |
| ... | @@ -6448,7 +6285,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6448,7 +6285,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6448 | 6285 | ||
| 6449 | if (f.liveness.isUnused(inst)) { | 6286 | if (f.liveness.isUnused(inst)) { |
| 6450 | try reap(f, inst, &.{un_op}); | 6287 | try reap(f, inst, &.{un_op}); |
| 6451 | return CValue.none; | 6288 | return .none; |
| 6452 | } | 6289 | } |
| 6453 | 6290 | ||
| 6454 | const inst_ty = f.air.typeOfIndex(inst); | 6291 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -6459,7 +6296,9 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6459,7 +6296,9 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6459 | const writer = f.object.writer(); | 6296 | const writer = f.object.writer(); |
| 6460 | const local = try f.allocLocal(inst, inst_ty); | 6297 | const local = try f.allocLocal(inst, inst_ty); |
| 6461 | try f.writeCValue(writer, local, .Other); | 6298 | try f.writeCValue(writer, local, .Other); |
| 6462 | try writer.print(" = {s}(", .{try f.object.dg.getTagNameFn(enum_ty)}); | 6299 | try writer.print(" = {s}(", .{ |
| 6300 | try f.getLazyFnName(.{ .tag_name = enum_ty.getOwnerDecl() }, .{ .tag_name = enum_ty }), | ||
| 6301 | }); | ||
| 6463 | try f.writeCValue(writer, operand, .Other); | 6302 | try f.writeCValue(writer, operand, .Other); |
| 6464 | try writer.writeAll(");\n"); | 6303 | try writer.writeAll(");\n"); |
| 6465 | 6304 | ||
| ... | @@ -6471,7 +6310,7 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6471,7 +6310,7 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6471 | 6310 | ||
| 6472 | if (f.liveness.isUnused(inst)) { | 6311 | if (f.liveness.isUnused(inst)) { |
| 6473 | try reap(f, inst, &.{un_op}); | 6312 | try reap(f, inst, &.{un_op}); |
| 6474 | return CValue.none; | 6313 | return .none; |
| 6475 | } | 6314 | } |
| 6476 | 6315 | ||
| 6477 | const writer = f.object.writer(); | 6316 | const writer = f.object.writer(); |
| ... | @@ -6491,7 +6330,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6491,7 +6330,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6491 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | 6330 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 6492 | if (f.liveness.isUnused(inst)) { | 6331 | if (f.liveness.isUnused(inst)) { |
| 6493 | try reap(f, inst, &.{ty_op.operand}); | 6332 | try reap(f, inst, &.{ty_op.operand}); |
| 6494 | return CValue.none; | 6333 | return .none; |
| 6495 | } | 6334 | } |
| 6496 | 6335 | ||
| 6497 | const inst_ty = f.air.typeOfIndex(inst); | 6336 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | @@ -6507,13 +6346,13 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6507,13 +6346,13 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6507 | } | 6346 | } |
| 6508 | 6347 | ||
| 6509 | fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { | 6348 | fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6510 | if (f.liveness.isUnused(inst)) return CValue.none; | 6349 | if (f.liveness.isUnused(inst)) return .none; |
| 6511 | 6350 | ||
| 6512 | return f.fail("TODO: C backend: implement airSelect", .{}); | 6351 | return f.fail("TODO: C backend: implement airSelect", .{}); |
| 6513 | } | 6352 | } |
| 6514 | 6353 | ||
| 6515 | fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { | 6354 | fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6516 | if (f.liveness.isUnused(inst)) return CValue.none; | 6355 | if (f.liveness.isUnused(inst)) return .none; |
| 6517 | 6356 | ||
| 6518 | return f.fail("TODO: C backend: implement airShuffle", .{}); | 6357 | return f.fail("TODO: C backend: implement airShuffle", .{}); |
| 6519 | } | 6358 | } |
| ... | @@ -6523,7 +6362,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6523,7 +6362,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6523 | 6362 | ||
| 6524 | if (f.liveness.isUnused(inst)) { | 6363 | if (f.liveness.isUnused(inst)) { |
| 6525 | try reap(f, inst, &.{reduce.operand}); | 6364 | try reap(f, inst, &.{reduce.operand}); |
| 6526 | return CValue.none; | 6365 | return .none; |
| 6527 | } | 6366 | } |
| 6528 | 6367 | ||
| 6529 | const target = f.object.dg.module.getTarget(); | 6368 | const target = f.object.dg.module.getTarget(); |
| ... | @@ -6599,10 +6438,9 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6599,10 +6438,9 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6599 | // | 6438 | // |
| 6600 | // Equivalent to: | 6439 | // Equivalent to: |
| 6601 | // reduce: { | 6440 | // reduce: { |
| 6602 | // var i: usize = 0; | ||
| 6603 | // var accum: T = init; | 6441 | // var accum: T = init; |
| 6604 | // while (i < vec.len) : (i += 1) { | 6442 | // for (vec) : (elem) { |
| 6605 | // accum = func(accum, vec[i]); | 6443 | // accum = func(accum, elem); |
| 6606 | // } | 6444 | // } |
| 6607 | // break :reduce accum; | 6445 | // break :reduce accum; |
| 6608 | // } | 6446 | // } |
| ... | @@ -6674,7 +6512,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6674,7 +6512,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6674 | 6512 | ||
| 6675 | try writer.writeAll(";\n"); | 6513 | try writer.writeAll(";\n"); |
| 6676 | 6514 | ||
| 6677 | try freeLocal(f, inst, it.local, 0); | 6515 | try freeLocal(f, inst, it.new_local, 0); |
| 6678 | 6516 | ||
| 6679 | return accum; | 6517 | return accum; |
| 6680 | } | 6518 | } |
| ... | @@ -6687,8 +6525,8 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6687,8 +6525,8 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6687 | const gpa = f.object.dg.gpa; | 6525 | const gpa = f.object.dg.gpa; |
| 6688 | const resolved_elements = try gpa.alloc(CValue, elements.len); | 6526 | const resolved_elements = try gpa.alloc(CValue, elements.len); |
| 6689 | defer gpa.free(resolved_elements); | 6527 | defer gpa.free(resolved_elements); |
| 6690 | for (elements, 0..) |element, i| { | 6528 | for (resolved_elements, elements) |*resolved_element, element| { |
| 6691 | resolved_elements[i] = try f.resolveInst(element); | 6529 | resolved_element.* = try f.resolveInst(element); |
| 6692 | } | 6530 | } |
| 6693 | { | 6531 | { |
| 6694 | var bt = iterateBigTomb(f, inst); | 6532 | var bt = iterateBigTomb(f, inst); |
| ... | @@ -6697,7 +6535,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6697,7 +6535,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6697 | } | 6535 | } |
| 6698 | } | 6536 | } |
| 6699 | 6537 | ||
| 6700 | if (f.liveness.isUnused(inst)) return CValue.none; | 6538 | if (f.liveness.isUnused(inst)) return .none; |
| 6701 | 6539 | ||
| 6702 | const target = f.object.dg.module.getTarget(); | 6540 | const target = f.object.dg.module.getTarget(); |
| 6703 | 6541 | ||
| ... | @@ -6723,50 +6561,51 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6723,50 +6561,51 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6723 | .Auto, .Extern => { | 6561 | .Auto, .Extern => { |
| 6724 | try f.writeCValue(writer, local, .Other); | 6562 | try f.writeCValue(writer, local, .Other); |
| 6725 | try writer.writeAll(" = ("); | 6563 | try writer.writeAll(" = ("); |
| 6726 | try f.renderTypecast(writer, inst_ty); | 6564 | try f.renderType(writer, inst_ty); |
| 6727 | try writer.writeAll(")"); | 6565 | try writer.writeAll(")"); |
| 6728 | try writer.writeByte('{'); | 6566 | try writer.writeByte('{'); |
| 6729 | var empty = true; | 6567 | var empty = true; |
| 6730 | for (elements, 0..) |element, index| { | 6568 | for (elements, resolved_elements, 0..) |element, resolved_element, field_i| { |
| 6731 | if (inst_ty.structFieldValueComptime(index)) |_| continue; | 6569 | if (inst_ty.structFieldValueComptime(field_i)) |_| continue; |
| 6732 | 6570 | ||
| 6733 | if (!empty) try writer.writeAll(", "); | 6571 | if (!empty) try writer.writeAll(", "); |
| 6734 | if (!inst_ty.isTupleOrAnonStruct()) { | 6572 | |
| 6735 | try writer.print(".{ } = ", .{fmtIdent(inst_ty.structFieldName(index))}); | 6573 | const field_name: CValue = if (inst_ty.isSimpleTuple()) |
| 6736 | } | 6574 | .{ .field = field_i } |
| 6575 | else | ||
| 6576 | .{ .identifier = inst_ty.structFieldName(field_i) }; | ||
| 6577 | try writer.writeByte('.'); | ||
| 6578 | try f.object.dg.writeCValue(writer, field_name); | ||
| 6579 | try writer.writeAll(" = "); | ||
| 6737 | 6580 | ||
| 6738 | const element_ty = f.air.typeOf(element); | 6581 | const element_ty = f.air.typeOf(element); |
| 6739 | try f.writeCValue(writer, switch (element_ty.zigTypeTag()) { | 6582 | try f.writeCValue(writer, switch (element_ty.zigTypeTag()) { |
| 6740 | .Array => CValue{ .undef = element_ty }, | 6583 | .Array => .{ .undef = element_ty }, |
| 6741 | else => resolved_elements[index], | 6584 | else => resolved_element, |
| 6742 | }, .Initializer); | 6585 | }, .Initializer); |
| 6743 | empty = false; | 6586 | empty = false; |
| 6744 | } | 6587 | } |
| 6745 | if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); | ||
| 6746 | try writer.writeAll("};\n"); | 6588 | try writer.writeAll("};\n"); |
| 6747 | 6589 | ||
| 6748 | var field_id: usize = 0; | 6590 | for (elements, resolved_elements, 0..) |element, resolved_element, field_i| { |
| 6749 | for (elements, 0..) |element, index| { | 6591 | if (inst_ty.structFieldValueComptime(field_i)) |_| continue; |
| 6750 | if (inst_ty.structFieldValueComptime(index)) |_| continue; | ||
| 6751 | 6592 | ||
| 6752 | const element_ty = f.air.typeOf(element); | 6593 | const element_ty = f.air.typeOf(element); |
| 6753 | if (element_ty.zigTypeTag() != .Array) continue; | 6594 | if (element_ty.zigTypeTag() != .Array) continue; |
| 6754 | 6595 | ||
| 6755 | const field_name = if (inst_ty.isTupleOrAnonStruct()) | 6596 | const field_name: CValue = if (inst_ty.isSimpleTuple()) |
| 6756 | CValue{ .field = field_id } | 6597 | .{ .field = field_i } |
| 6757 | else | 6598 | else |
| 6758 | CValue{ .identifier = inst_ty.structFieldName(index) }; | 6599 | .{ .identifier = inst_ty.structFieldName(field_i) }; |
| 6759 | 6600 | ||
| 6760 | try writer.writeAll(";\n"); | 6601 | try writer.writeAll(";\n"); |
| 6761 | try writer.writeAll("memcpy("); | 6602 | try writer.writeAll("memcpy("); |
| 6762 | try f.writeCValueMember(writer, local, field_name); | 6603 | try f.writeCValueMember(writer, local, field_name); |
| 6763 | try writer.writeAll(", "); | 6604 | try writer.writeAll(", "); |
| 6764 | try f.writeCValue(writer, resolved_elements[index], .FunctionArgument); | 6605 | try f.writeCValue(writer, resolved_element, .FunctionArgument); |
| 6765 | try writer.writeAll(", sizeof("); | 6606 | try writer.writeAll(", sizeof("); |
| 6766 | try f.renderTypecast(writer, element_ty); | 6607 | try f.renderType(writer, element_ty); |
| 6767 | try writer.writeAll("));\n"); | 6608 | try writer.writeAll("));\n"); |
| 6768 | |||
| 6769 | field_id += 1; | ||
| 6770 | } | 6609 | } |
| 6771 | }, | 6610 | }, |
| 6772 | .Packed => { | 6611 | .Packed => { |
| ... | @@ -6784,7 +6623,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6784,7 +6623,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6784 | const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); | 6623 | const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); |
| 6785 | 6624 | ||
| 6786 | var empty = true; | 6625 | var empty = true; |
| 6787 | for (elements, 0..) |_, index| { | 6626 | for (0..elements.len) |index| { |
| 6788 | const field_ty = inst_ty.structFieldType(index); | 6627 | const field_ty = inst_ty.structFieldType(index); |
| 6789 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; | 6628 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 6790 | 6629 | ||
| ... | @@ -6810,11 +6649,11 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6810,11 +6649,11 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6810 | try f.renderIntCast(writer, inst_ty, element, field_ty, .FunctionArgument); | 6649 | try f.renderIntCast(writer, inst_ty, element, field_ty, .FunctionArgument); |
| 6811 | } else { | 6650 | } else { |
| 6812 | try writer.writeByte('('); | 6651 | try writer.writeByte('('); |
| 6813 | try f.renderTypecast(writer, inst_ty); | 6652 | try f.renderType(writer, inst_ty); |
| 6814 | try writer.writeByte(')'); | 6653 | try writer.writeByte(')'); |
| 6815 | if (field_ty.isPtrAtRuntime()) { | 6654 | if (field_ty.isPtrAtRuntime()) { |
| 6816 | try writer.writeByte('('); | 6655 | try writer.writeByte('('); |
| 6817 | try f.renderTypecast(writer, switch (int_info.signedness) { | 6656 | try f.renderType(writer, switch (int_info.signedness) { |
| 6818 | .unsigned => Type.usize, | 6657 | .unsigned => Type.usize, |
| 6819 | .signed => Type.isize, | 6658 | .signed => Type.isize, |
| 6820 | }); | 6659 | }); |
| ... | @@ -6833,13 +6672,6 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6833,13 +6672,6 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6833 | empty = false; | 6672 | empty = false; |
| 6834 | } | 6673 | } |
| 6835 | 6674 | ||
| 6836 | if (empty) { | ||
| 6837 | try writer.writeByte('('); | ||
| 6838 | try f.renderTypecast(writer, inst_ty); | ||
| 6839 | try writer.writeByte(')'); | ||
| 6840 | try f.writeCValue(writer, .{ .undef = inst_ty }, .Initializer); | ||
| 6841 | } | ||
| 6842 | |||
| 6843 | try writer.writeAll(";\n"); | 6675 | try writer.writeAll(";\n"); |
| 6844 | }, | 6676 | }, |
| 6845 | }, | 6677 | }, |
| ... | @@ -6855,7 +6687,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6855,7 +6687,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6855 | 6687 | ||
| 6856 | if (f.liveness.isUnused(inst)) { | 6688 | if (f.liveness.isUnused(inst)) { |
| 6857 | try reap(f, inst, &.{extra.init}); | 6689 | try reap(f, inst, &.{extra.init}); |
| 6858 | return CValue.none; | 6690 | return .none; |
| 6859 | } | 6691 | } |
| 6860 | 6692 | ||
| 6861 | const union_ty = f.air.typeOfIndex(inst); | 6693 | const union_ty = f.air.typeOfIndex(inst); |
| ... | @@ -6875,7 +6707,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6875,7 +6707,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6875 | return local; | 6707 | return local; |
| 6876 | } | 6708 | } |
| 6877 | 6709 | ||
| 6878 | if (union_ty.unionTagTypeSafety()) |tag_ty| { | 6710 | const field: CValue = if (union_ty.unionTagTypeSafety()) |tag_ty| field: { |
| 6879 | const layout = union_ty.unionGetLayout(target); | 6711 | const layout = union_ty.unionGetLayout(target); |
| 6880 | if (layout.tag_size != 0) { | 6712 | if (layout.tag_size != 0) { |
| 6881 | const field_index = tag_ty.enumFieldIndex(field_name).?; | 6713 | const field_index = tag_ty.enumFieldIndex(field_name).?; |
| ... | @@ -6892,18 +6724,13 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6892,18 +6724,13 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6892 | try f.writeCValue(writer, local, .Other); | 6724 | try f.writeCValue(writer, local, .Other); |
| 6893 | try writer.print(".tag = {}; ", .{try f.fmtIntLiteral(tag_ty, int_val)}); | 6725 | try writer.print(".tag = {}; ", .{try f.fmtIntLiteral(tag_ty, int_val)}); |
| 6894 | } | 6726 | } |
| 6895 | try f.writeCValue(writer, local, .Other); | 6727 | break :field .{ .payload_identifier = field_name }; |
| 6896 | try writer.print(".payload.{ } = ", .{fmtIdent(field_name)}); | 6728 | } else .{ .identifier = field_name }; |
| 6897 | try f.writeCValue(writer, payload, .Other); | ||
| 6898 | try writer.writeAll(";\n"); | ||
| 6899 | return local; | ||
| 6900 | } | ||
| 6901 | 6729 | ||
| 6902 | try f.writeCValue(writer, local, .Other); | 6730 | try f.writeCValueMember(writer, local, field); |
| 6903 | try writer.print(".{ } = ", .{fmtIdent(field_name)}); | 6731 | try writer.writeAll(" = "); |
| 6904 | try f.writeCValue(writer, payload, .Other); | 6732 | try f.writeCValue(writer, payload, .Other); |
| 6905 | try writer.writeAll(";\n"); | 6733 | try writer.writeAll(";\n"); |
| 6906 | |||
| 6907 | return local; | 6734 | return local; |
| 6908 | } | 6735 | } |
| 6909 | 6736 | ||
| ... | @@ -6914,7 +6741,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6914,7 +6741,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6914 | // The available prefetch intrinsics do not accept a cache argument; only | 6741 | // The available prefetch intrinsics do not accept a cache argument; only |
| 6915 | // address, rw, and locality. So unless the cache is data, we do not lower | 6742 | // address, rw, and locality. So unless the cache is data, we do not lower |
| 6916 | // this instruction. | 6743 | // this instruction. |
| 6917 | .instruction => return CValue.none, | 6744 | .instruction => return .none, |
| 6918 | } | 6745 | } |
| 6919 | const ptr = try f.resolveInst(prefetch.ptr); | 6746 | const ptr = try f.resolveInst(prefetch.ptr); |
| 6920 | try reap(f, inst, &.{prefetch.ptr}); | 6747 | try reap(f, inst, &.{prefetch.ptr}); |
| ... | @@ -6924,11 +6751,11 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6924,11 +6751,11 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6924 | try writer.print(", {d}, {d});\n", .{ | 6751 | try writer.print(", {d}, {d});\n", .{ |
| 6925 | @enumToInt(prefetch.rw), prefetch.locality, | 6752 | @enumToInt(prefetch.rw), prefetch.locality, |
| 6926 | }); | 6753 | }); |
| 6927 | return CValue.none; | 6754 | return .none; |
| 6928 | } | 6755 | } |
| 6929 | 6756 | ||
| 6930 | fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { | 6757 | fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6931 | if (f.liveness.isUnused(inst)) return CValue.none; | 6758 | if (f.liveness.isUnused(inst)) return .none; |
| 6932 | 6759 | ||
| 6933 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | 6760 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; |
| 6934 | 6761 | ||
| ... | @@ -6965,7 +6792,7 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6965,7 +6792,7 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6965 | const un_op = f.air.instructions.items(.data)[inst].un_op; | 6792 | const un_op = f.air.instructions.items(.data)[inst].un_op; |
| 6966 | if (f.liveness.isUnused(inst)) { | 6793 | if (f.liveness.isUnused(inst)) { |
| 6967 | try reap(f, inst, &.{un_op}); | 6794 | try reap(f, inst, &.{un_op}); |
| 6968 | return CValue.none; | 6795 | return .none; |
| 6969 | } | 6796 | } |
| 6970 | 6797 | ||
| 6971 | const operand = try f.resolveInst(un_op); | 6798 | const operand = try f.resolveInst(un_op); |
| ... | @@ -6987,7 +6814,7 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal | ... | @@ -6987,7 +6814,7 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal |
| 6987 | const un_op = f.air.instructions.items(.data)[inst].un_op; | 6814 | const un_op = f.air.instructions.items(.data)[inst].un_op; |
| 6988 | if (f.liveness.isUnused(inst)) { | 6815 | if (f.liveness.isUnused(inst)) { |
| 6989 | try reap(f, inst, &.{un_op}); | 6816 | try reap(f, inst, &.{un_op}); |
| 6990 | return CValue.none; | 6817 | return .none; |
| 6991 | } | 6818 | } |
| 6992 | const operand = try f.resolveInst(un_op); | 6819 | const operand = try f.resolveInst(un_op); |
| 6993 | try reap(f, inst, &.{un_op}); | 6820 | try reap(f, inst, &.{un_op}); |
| ... | @@ -7009,7 +6836,7 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa | ... | @@ -7009,7 +6836,7 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa |
| 7009 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | 6836 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 7010 | if (f.liveness.isUnused(inst)) { | 6837 | if (f.liveness.isUnused(inst)) { |
| 7011 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | 6838 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); |
| 7012 | return CValue.none; | 6839 | return .none; |
| 7013 | } | 6840 | } |
| 7014 | const lhs = try f.resolveInst(bin_op.lhs); | 6841 | const lhs = try f.resolveInst(bin_op.lhs); |
| 7015 | const rhs = try f.resolveInst(bin_op.rhs); | 6842 | const rhs = try f.resolveInst(bin_op.rhs); |
| ... | @@ -7036,7 +6863,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -7036,7 +6863,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7036 | const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data; | 6863 | const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data; |
| 7037 | if (f.liveness.isUnused(inst)) { | 6864 | if (f.liveness.isUnused(inst)) { |
| 7038 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); | 6865 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); |
| 7039 | return CValue.none; | 6866 | return .none; |
| 7040 | } | 6867 | } |
| 7041 | const inst_ty = f.air.typeOfIndex(inst); | 6868 | const inst_ty = f.air.typeOfIndex(inst); |
| 7042 | const mulend1 = try f.resolveInst(bin_op.lhs); | 6869 | const mulend1 = try f.resolveInst(bin_op.lhs); |
| ... | @@ -7058,6 +6885,81 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -7058,6 +6885,81 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { |
| 7058 | return local; | 6885 | return local; |
| 7059 | } | 6886 | } |
| 7060 | 6887 | ||
| 6888 | fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 6889 | if (f.liveness.isUnused(inst)) return .none; | ||
| 6890 | |||
| 6891 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 6892 | const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete); | ||
| 6893 | const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len; | ||
| 6894 | |||
| 6895 | const writer = f.object.writer(); | ||
| 6896 | const local = try f.allocLocal(inst, inst_ty); | ||
| 6897 | try writer.writeAll("va_start(*(va_list *)&"); | ||
| 6898 | try f.writeCValue(writer, local, .Other); | ||
| 6899 | if (param_len > 0) { | ||
| 6900 | try writer.writeAll(", "); | ||
| 6901 | try f.writeCValue(writer, .{ .arg = param_len - 1 }, .FunctionArgument); | ||
| 6902 | } | ||
| 6903 | try writer.writeAll(");\n"); | ||
| 6904 | return local; | ||
| 6905 | } | ||
| 6906 | |||
| 6907 | fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 6908 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | ||
| 6909 | if (f.liveness.isUnused(inst)) { | ||
| 6910 | try reap(f, inst, &.{ty_op.operand}); | ||
| 6911 | return .none; | ||
| 6912 | } | ||
| 6913 | |||
| 6914 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 6915 | const va_list = try f.resolveInst(ty_op.operand); | ||
| 6916 | try reap(f, inst, &.{ty_op.operand}); | ||
| 6917 | |||
| 6918 | const writer = f.object.writer(); | ||
| 6919 | const local = try f.allocLocal(inst, inst_ty); | ||
| 6920 | try f.writeCValue(writer, local, .Other); | ||
| 6921 | try writer.writeAll(" = va_arg(*(va_list *)"); | ||
| 6922 | try f.writeCValue(writer, va_list, .Other); | ||
| 6923 | try writer.writeAll(", "); | ||
| 6924 | try f.renderType(writer, f.air.getRefType(ty_op.ty)); | ||
| 6925 | try writer.writeAll(");\n"); | ||
| 6926 | return local; | ||
| 6927 | } | ||
| 6928 | |||
| 6929 | fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 6930 | const un_op = f.air.instructions.items(.data)[inst].un_op; | ||
| 6931 | |||
| 6932 | const va_list = try f.resolveInst(un_op); | ||
| 6933 | try reap(f, inst, &.{un_op}); | ||
| 6934 | |||
| 6935 | const writer = f.object.writer(); | ||
| 6936 | try writer.writeAll("va_end(*(va_list *)"); | ||
| 6937 | try f.writeCValue(writer, va_list, .Other); | ||
| 6938 | try writer.writeAll(");\n"); | ||
| 6939 | return .none; | ||
| 6940 | } | ||
| 6941 | |||
| 6942 | fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 6943 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | ||
| 6944 | if (f.liveness.isUnused(inst)) { | ||
| 6945 | try reap(f, inst, &.{ty_op.operand}); | ||
| 6946 | return .none; | ||
| 6947 | } | ||
| 6948 | |||
| 6949 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 6950 | const va_list = try f.resolveInst(ty_op.operand); | ||
| 6951 | try reap(f, inst, &.{ty_op.operand}); | ||
| 6952 | |||
| 6953 | const writer = f.object.writer(); | ||
| 6954 | const local = try f.allocLocal(inst, inst_ty); | ||
| 6955 | try writer.writeAll("va_copy(*(va_list *)&"); | ||
| 6956 | try f.writeCValue(writer, local, .Other); | ||
| 6957 | try writer.writeAll(", *(va_list *)"); | ||
| 6958 | try f.writeCValue(writer, va_list, .Other); | ||
| 6959 | try writer.writeAll(");\n"); | ||
| 6960 | return local; | ||
| 6961 | } | ||
| 6962 | |||
| 7061 | fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { | 6963 | fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { |
| 7062 | return switch (order) { | 6964 | return switch (order) { |
| 7063 | // Note: unordered is actually even less atomic than relaxed | 6965 | // Note: unordered is actually even less atomic than relaxed |
| ... | @@ -7243,8 +7145,9 @@ fn stringLiteral(child_stream: anytype) StringLiteral(@TypeOf(child_stream)) { | ... | @@ -7243,8 +7145,9 @@ fn stringLiteral(child_stream: anytype) StringLiteral(@TypeOf(child_stream)) { |
| 7243 | return .{ .counting_writer = std.io.countingWriter(child_stream) }; | 7145 | return .{ .counting_writer = std.io.countingWriter(child_stream) }; |
| 7244 | } | 7146 | } |
| 7245 | 7147 | ||
| 7148 | const FormatStringContext = struct { str: []const u8, sentinel: ?u8 }; | ||
| 7246 | fn formatStringLiteral( | 7149 | fn formatStringLiteral( |
| 7247 | str: []const u8, | 7150 | data: FormatStringContext, |
| 7248 | comptime fmt: []const u8, | 7151 | comptime fmt: []const u8, |
| 7249 | _: std.fmt.FormatOptions, | 7152 | _: std.fmt.FormatOptions, |
| 7250 | writer: anytype, | 7153 | writer: anytype, |
| ... | @@ -7253,13 +7156,13 @@ fn formatStringLiteral( | ... | @@ -7253,13 +7156,13 @@ fn formatStringLiteral( |
| 7253 | 7156 | ||
| 7254 | var literal = stringLiteral(writer); | 7157 | var literal = stringLiteral(writer); |
| 7255 | try literal.start(); | 7158 | try literal.start(); |
| 7256 | for (str) |c| | 7159 | for (data.str) |c| try literal.writeChar(c); |
| 7257 | try literal.writeChar(c); | 7160 | if (data.sentinel) |sentinel| if (sentinel != 0) try literal.writeChar(sentinel); |
| 7258 | try literal.end(); | 7161 | try literal.end(); |
| 7259 | } | 7162 | } |
| 7260 | 7163 | ||
| 7261 | fn fmtStringLiteral(str: []const u8) std.fmt.Formatter(formatStringLiteral) { | 7164 | fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(formatStringLiteral) { |
| 7262 | return .{ .data = str }; | 7165 | return .{ .data = .{ .str = str, .sentinel = sentinel } }; |
| 7263 | } | 7166 | } |
| 7264 | 7167 | ||
| 7265 | fn undefPattern(comptime IntType: type) IntType { | 7168 | fn undefPattern(comptime IntType: type) IntType { |
| ... | @@ -7344,7 +7247,7 @@ fn formatIntLiteral( | ... | @@ -7344,7 +7247,7 @@ fn formatIntLiteral( |
| 7344 | use_twos_comp = true; | 7247 | use_twos_comp = true; |
| 7345 | } else { | 7248 | } else { |
| 7346 | // TODO: Use fmtIntLiteral for 0? | 7249 | // TODO: Use fmtIntLiteral for 0? |
| 7347 | try writer.print("zig_sub_{c}{d}(zig_as_{c}{d}(0, 0), ", .{ signAbbrev(int_info.signedness), c_bits, signAbbrev(int_info.signedness), c_bits }); | 7250 | try writer.print("zig_sub_{c}{d}(zig_make_{c}{d}(0, 0), ", .{ signAbbrev(int_info.signedness), c_bits, signAbbrev(int_info.signedness), c_bits }); |
| 7348 | } | 7251 | } |
| 7349 | } else { | 7252 | } else { |
| 7350 | try writer.writeByte('-'); | 7253 | try writer.writeByte('-'); |
| ... | @@ -7354,11 +7257,16 @@ fn formatIntLiteral( | ... | @@ -7354,11 +7257,16 @@ fn formatIntLiteral( |
| 7354 | switch (data.ty.tag()) { | 7257 | switch (data.ty.tag()) { |
| 7355 | .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {}, | 7258 | .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {}, |
| 7356 | else => { | 7259 | else => { |
| 7357 | if (int_info.bits > 64 and data.location != null and data.location.? == .StaticInitializer) { | 7260 | if (int_info.bits <= 64) { |
| 7261 | try writer.print("{s}INT{d}_C(", .{ switch (int_info.signedness) { | ||
| 7262 | .signed => "", | ||
| 7263 | .unsigned => "U", | ||
| 7264 | }, c_bits }); | ||
| 7265 | } else if (data.location != null and data.location.? == .StaticInitializer) { | ||
| 7358 | // MSVC treats casting the struct initializer as not constant (C2099), so an alternate form is used in global initializers | 7266 | // MSVC treats casting the struct initializer as not constant (C2099), so an alternate form is used in global initializers |
| 7359 | try writer.print("zig_as_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); | 7267 | try writer.print("zig_make_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); |
| 7360 | } else { | 7268 | } else { |
| 7361 | try writer.print("zig_as_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); | 7269 | try writer.print("zig_make_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); |
| 7362 | } | 7270 | } |
| 7363 | }, | 7271 | }, |
| 7364 | } | 7272 | } |
| ... | @@ -7467,17 +7375,20 @@ fn isByRef(ty: Type) bool { | ... | @@ -7467,17 +7375,20 @@ fn isByRef(ty: Type) bool { |
| 7467 | } | 7375 | } |
| 7468 | 7376 | ||
| 7469 | const LowerFnRetTyBuffer = struct { | 7377 | const LowerFnRetTyBuffer = struct { |
| 7378 | names: [1][]const u8, | ||
| 7470 | types: [1]Type, | 7379 | types: [1]Type, |
| 7471 | values: [1]Value, | 7380 | values: [1]Value, |
| 7472 | payload: Type.Payload.Tuple, | 7381 | payload: Type.Payload.AnonStruct, |
| 7473 | }; | 7382 | }; |
| 7474 | fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, target: std.Target) Type { | 7383 | fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, target: std.Target) Type { |
| 7475 | if (ret_ty.zigTypeTag() == .NoReturn) return Type.initTag(.noreturn); | 7384 | if (ret_ty.zigTypeTag() == .NoReturn) return Type.initTag(.noreturn); |
| 7476 | 7385 | ||
| 7477 | if (lowersToArray(ret_ty, target)) { | 7386 | if (lowersToArray(ret_ty, target)) { |
| 7387 | buffer.names = [1][]const u8{"array"}; | ||
| 7478 | buffer.types = [1]Type{ret_ty}; | 7388 | buffer.types = [1]Type{ret_ty}; |
| 7479 | buffer.values = [1]Value{Value.initTag(.unreachable_value)}; | 7389 | buffer.values = [1]Value{Value.initTag(.unreachable_value)}; |
| 7480 | buffer.payload = .{ .data = .{ | 7390 | buffer.payload = .{ .data = .{ |
| 7391 | .names = &buffer.names, | ||
| 7481 | .types = &buffer.types, | 7392 | .types = &buffer.types, |
| 7482 | .values = &buffer.values, | 7393 | .values = &buffer.values, |
| 7483 | } }; | 7394 | } }; |
| ... | @@ -7533,7 +7444,7 @@ fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void { | ... | @@ -7533,7 +7444,7 @@ fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void { |
| 7533 | if (f.air.instructions.items(.tag)[ref_inst] == .constant) return; | 7444 | if (f.air.instructions.items(.tag)[ref_inst] == .constant) return; |
| 7534 | const c_value = (f.value_map.fetchRemove(ref) orelse return).value; | 7445 | const c_value = (f.value_map.fetchRemove(ref) orelse return).value; |
| 7535 | const local_index = switch (c_value) { | 7446 | const local_index = switch (c_value) { |
| 7536 | .local => |l| l, | 7447 | .local, .new_local => |l| l, |
| 7537 | else => return, | 7448 | else => return, |
| 7538 | }; | 7449 | }; |
| 7539 | try freeLocal(f, inst, local_index, ref_inst); | 7450 | try freeLocal(f, inst, local_index, ref_inst); |
| ... | @@ -7544,21 +7455,16 @@ fn freeLocal(f: *Function, inst: Air.Inst.Index, local_index: LocalIndex, ref_in | ... | @@ -7544,21 +7455,16 @@ fn freeLocal(f: *Function, inst: Air.Inst.Index, local_index: LocalIndex, ref_in |
| 7544 | const local = &f.locals.items[local_index]; | 7455 | const local = &f.locals.items[local_index]; |
| 7545 | log.debug("%{d}: freeing t{d} (operand %{d})", .{ inst, local_index, ref_inst }); | 7456 | log.debug("%{d}: freeing t{d} (operand %{d})", .{ inst, local_index, ref_inst }); |
| 7546 | if (local.loop_depth < f.free_locals_clone_depth) return; | 7457 | if (local.loop_depth < f.free_locals_clone_depth) return; |
| 7547 | const gop = try f.free_locals_stack.items[local.loop_depth].getOrPutContext( | 7458 | const gop = try f.free_locals_stack.items[local.loop_depth].getOrPut(gpa, local.getType()); |
| 7548 | gpa, | ||
| 7549 | local.ty, | ||
| 7550 | f.tyHashCtx(), | ||
| 7551 | ); | ||
| 7552 | if (!gop.found_existing) gop.value_ptr.* = .{}; | 7459 | if (!gop.found_existing) gop.value_ptr.* = .{}; |
| 7553 | if (std.debug.runtime_safety) { | 7460 | if (std.debug.runtime_safety) { |
| 7554 | // If this trips, it means a local is being inserted into the | ||
| 7555 | // free_locals map while it already exists in the map, which is not | ||
| 7556 | // allowed. | ||
| 7557 | assert(mem.indexOfScalar(LocalIndex, gop.value_ptr.items, local_index) == null); | ||
| 7558 | // If this trips, an unfreeable allocation was attempted to be freed. | 7461 | // If this trips, an unfreeable allocation was attempted to be freed. |
| 7559 | assert(!f.allocs.contains(local_index)); | 7462 | assert(!f.allocs.contains(local_index)); |
| 7560 | } | 7463 | } |
| 7561 | try gop.value_ptr.append(gpa, local_index); | 7464 | // If this trips, it means a local is being inserted into the |
| 7465 | // free_locals map while it already exists in the map, which is not | ||
| 7466 | // allowed. | ||
| 7467 | try gop.value_ptr.putNoClobber(gpa, local_index, {}); | ||
| 7562 | } | 7468 | } |
| 7563 | 7469 | ||
| 7564 | const BigTomb = struct { | 7470 | const BigTomb = struct { |
| ... | @@ -7607,14 +7513,36 @@ fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void { | ... | @@ -7607,14 +7513,36 @@ fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void { |
| 7607 | map.deinit(gpa); | 7513 | map.deinit(gpa); |
| 7608 | } | 7514 | } |
| 7609 | 7515 | ||
| 7610 | fn noticeBranchFrees(f: *Function, pre_locals_len: LocalIndex, inst: Air.Inst.Index) !void { | 7516 | fn noticeBranchFrees( |
| 7611 | for (f.locals.items[pre_locals_len..], 0..) |*local, local_offset| { | 7517 | f: *Function, |
| 7612 | const local_index = pre_locals_len + @intCast(LocalIndex, local_offset); | 7518 | pre_locals_len: LocalIndex, |
| 7613 | if (f.allocs.contains(local_index)) continue; // allocs are not freeable | 7519 | pre_allocs_len: LocalIndex, |
| 7520 | inst: Air.Inst.Index, | ||
| 7521 | ) !void { | ||
| 7522 | const free_locals = f.getFreeLocals(); | ||
| 7523 | |||
| 7524 | for (f.locals.items[pre_locals_len..], pre_locals_len..) |*local, local_i| { | ||
| 7525 | const local_index = @intCast(LocalIndex, local_i); | ||
| 7526 | if (f.allocs.contains(local_index)) { | ||
| 7527 | if (std.debug.runtime_safety) { | ||
| 7528 | // new allocs are no longer freeable, so make sure they aren't in the free list | ||
| 7529 | if (free_locals.getPtr(local.getType())) |locals_list| { | ||
| 7530 | assert(!locals_list.contains(local_index)); | ||
| 7531 | } | ||
| 7532 | } | ||
| 7533 | continue; | ||
| 7534 | } | ||
| 7614 | 7535 | ||
| 7615 | // free more deeply nested locals from other branches at current depth | 7536 | // free more deeply nested locals from other branches at current depth |
| 7616 | assert(local.loop_depth >= f.free_locals_stack.items.len - 1); | 7537 | assert(local.loop_depth >= f.free_locals_stack.items.len - 1); |
| 7617 | local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); | 7538 | local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); |
| 7618 | try freeLocal(f, inst, local_index, 0); | 7539 | try freeLocal(f, inst, local_index, 0); |
| 7619 | } | 7540 | } |
| 7541 | |||
| 7542 | for (f.allocs.keys()[pre_allocs_len..]) |local_i| { | ||
| 7543 | const local_index = @intCast(LocalIndex, local_i); | ||
| 7544 | const local = &f.locals.items[local_index]; | ||
| 7545 | // new allocs are no longer freeable, so remove them from the free list | ||
| 7546 | if (free_locals.getPtr(local.getType())) |locals_list| _ = locals_list.swapRemove(local_index); | ||
| 7547 | } | ||
| 7620 | } | 7548 | } |
src/codegen/c/type.zig created+1896| ... | @@ -0,0 +1,1896 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const cstr = std.cstr; | ||
| 3 | const mem = std.mem; | ||
| 4 | const Allocator = mem.Allocator; | ||
| 5 | const assert = std.debug.assert; | ||
| 6 | const autoHash = std.hash.autoHash; | ||
| 7 | const Target = std.Target; | ||
| 8 | |||
| 9 | const Module = @import("../../Module.zig"); | ||
| 10 | const Type = @import("../../type.zig").Type; | ||
| 11 | |||
| 12 | pub const CType = extern union { | ||
| 13 | /// If the tag value is less than Tag.no_payload_count, then no pointer | ||
| 14 | /// dereference is needed. | ||
| 15 | tag_if_small_enough: Tag, | ||
| 16 | ptr_otherwise: *const Payload, | ||
| 17 | |||
| 18 | pub fn initTag(small_tag: Tag) CType { | ||
| 19 | assert(!small_tag.hasPayload()); | ||
| 20 | return .{ .tag_if_small_enough = small_tag }; | ||
| 21 | } | ||
| 22 | |||
| 23 | pub fn initPayload(pl: anytype) CType { | ||
| 24 | const T = @typeInfo(@TypeOf(pl)).Pointer.child; | ||
| 25 | return switch (pl.base.tag) { | ||
| 26 | inline else => |t| if (comptime t.hasPayload() and t.Type() == T) .{ | ||
| 27 | .ptr_otherwise = &pl.base, | ||
| 28 | } else unreachable, | ||
| 29 | }; | ||
| 30 | } | ||
| 31 | |||
| 32 | pub fn hasPayload(self: CType) bool { | ||
| 33 | return self.tag_if_small_enough.hasPayload(); | ||
| 34 | } | ||
| 35 | |||
| 36 | pub fn tag(self: CType) Tag { | ||
| 37 | return if (self.hasPayload()) self.ptr_otherwise.tag else self.tag_if_small_enough; | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn cast(self: CType, comptime T: type) ?*const T { | ||
| 41 | if (!self.hasPayload()) return null; | ||
| 42 | const pl = self.ptr_otherwise; | ||
| 43 | return switch (pl.tag) { | ||
| 44 | inline else => |t| if (comptime t.hasPayload() and t.Type() == T) | ||
| 45 | @fieldParentPtr(T, "base", pl) | ||
| 46 | else | ||
| 47 | null, | ||
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn castTag(self: CType, comptime t: Tag) ?*const t.Type() { | ||
| 52 | return if (self.tag() == t) @fieldParentPtr(t.Type(), "base", self.ptr_otherwise) else null; | ||
| 53 | } | ||
| 54 | |||
| 55 | pub const Tag = enum(usize) { | ||
| 56 | // The first section of this enum are tags that require no payload. | ||
| 57 | void, | ||
| 58 | |||
| 59 | // C basic types | ||
| 60 | char, | ||
| 61 | |||
| 62 | @"signed char", | ||
| 63 | short, | ||
| 64 | int, | ||
| 65 | long, | ||
| 66 | @"long long", | ||
| 67 | |||
| 68 | _Bool, | ||
| 69 | @"unsigned char", | ||
| 70 | @"unsigned short", | ||
| 71 | @"unsigned int", | ||
| 72 | @"unsigned long", | ||
| 73 | @"unsigned long long", | ||
| 74 | |||
| 75 | float, | ||
| 76 | double, | ||
| 77 | @"long double", | ||
| 78 | |||
| 79 | // C header types | ||
| 80 | // - stdbool.h | ||
| 81 | bool, | ||
| 82 | // - stddef.h | ||
| 83 | size_t, | ||
| 84 | ptrdiff_t, | ||
| 85 | // - stdint.h | ||
| 86 | uint8_t, | ||
| 87 | int8_t, | ||
| 88 | uint16_t, | ||
| 89 | int16_t, | ||
| 90 | uint32_t, | ||
| 91 | int32_t, | ||
| 92 | uint64_t, | ||
| 93 | int64_t, | ||
| 94 | uintptr_t, | ||
| 95 | intptr_t, | ||
| 96 | |||
| 97 | // zig.h types | ||
| 98 | zig_u128, | ||
| 99 | zig_i128, | ||
| 100 | zig_f16, | ||
| 101 | zig_f32, | ||
| 102 | zig_f64, | ||
| 103 | zig_f80, | ||
| 104 | zig_f128, | ||
| 105 | zig_c_longdouble, // Keep last_no_payload_tag updated! | ||
| 106 | |||
| 107 | // After this, the tag requires a payload. | ||
| 108 | pointer, | ||
| 109 | pointer_const, | ||
| 110 | pointer_volatile, | ||
| 111 | pointer_const_volatile, | ||
| 112 | array, | ||
| 113 | vector, | ||
| 114 | fwd_anon_struct, | ||
| 115 | fwd_anon_union, | ||
| 116 | fwd_struct, | ||
| 117 | fwd_union, | ||
| 118 | unnamed_struct, | ||
| 119 | unnamed_union, | ||
| 120 | packed_unnamed_struct, | ||
| 121 | packed_unnamed_union, | ||
| 122 | anon_struct, | ||
| 123 | anon_union, | ||
| 124 | @"struct", | ||
| 125 | @"union", | ||
| 126 | packed_struct, | ||
| 127 | packed_union, | ||
| 128 | function, | ||
| 129 | varargs_function, | ||
| 130 | |||
| 131 | pub const last_no_payload_tag = Tag.zig_c_longdouble; | ||
| 132 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | ||
| 133 | |||
| 134 | pub fn hasPayload(self: Tag) bool { | ||
| 135 | return @enumToInt(self) >= no_payload_count; | ||
| 136 | } | ||
| 137 | |||
| 138 | pub fn toIndex(self: Tag) Index { | ||
| 139 | assert(!self.hasPayload()); | ||
| 140 | return @intCast(Index, @enumToInt(self)); | ||
| 141 | } | ||
| 142 | |||
| 143 | pub fn Type(comptime self: Tag) type { | ||
| 144 | return switch (self) { | ||
| 145 | .void, | ||
| 146 | .char, | ||
| 147 | .@"signed char", | ||
| 148 | .short, | ||
| 149 | .int, | ||
| 150 | .long, | ||
| 151 | .@"long long", | ||
| 152 | ._Bool, | ||
| 153 | .@"unsigned char", | ||
| 154 | .@"unsigned short", | ||
| 155 | .@"unsigned int", | ||
| 156 | .@"unsigned long", | ||
| 157 | .@"unsigned long long", | ||
| 158 | .float, | ||
| 159 | .double, | ||
| 160 | .@"long double", | ||
| 161 | .bool, | ||
| 162 | .size_t, | ||
| 163 | .ptrdiff_t, | ||
| 164 | .uint8_t, | ||
| 165 | .int8_t, | ||
| 166 | .uint16_t, | ||
| 167 | .int16_t, | ||
| 168 | .uint32_t, | ||
| 169 | .int32_t, | ||
| 170 | .uint64_t, | ||
| 171 | .int64_t, | ||
| 172 | .uintptr_t, | ||
| 173 | .intptr_t, | ||
| 174 | .zig_u128, | ||
| 175 | .zig_i128, | ||
| 176 | .zig_f16, | ||
| 177 | .zig_f32, | ||
| 178 | .zig_f64, | ||
| 179 | .zig_f80, | ||
| 180 | .zig_f128, | ||
| 181 | .zig_c_longdouble, | ||
| 182 | => @compileError("Type Tag " ++ @tagName(self) ++ " has no payload"), | ||
| 183 | |||
| 184 | .pointer, | ||
| 185 | .pointer_const, | ||
| 186 | .pointer_volatile, | ||
| 187 | .pointer_const_volatile, | ||
| 188 | => Payload.Child, | ||
| 189 | |||
| 190 | .array, | ||
| 191 | .vector, | ||
| 192 | => Payload.Sequence, | ||
| 193 | |||
| 194 | .fwd_anon_struct, | ||
| 195 | .fwd_anon_union, | ||
| 196 | => Payload.Fields, | ||
| 197 | |||
| 198 | .fwd_struct, | ||
| 199 | .fwd_union, | ||
| 200 | => Payload.FwdDecl, | ||
| 201 | |||
| 202 | .unnamed_struct, | ||
| 203 | .unnamed_union, | ||
| 204 | .packed_unnamed_struct, | ||
| 205 | .packed_unnamed_union, | ||
| 206 | => Payload.Unnamed, | ||
| 207 | |||
| 208 | .anon_struct, | ||
| 209 | .anon_union, | ||
| 210 | .@"struct", | ||
| 211 | .@"union", | ||
| 212 | .packed_struct, | ||
| 213 | .packed_union, | ||
| 214 | => Payload.Aggregate, | ||
| 215 | |||
| 216 | .function, | ||
| 217 | .varargs_function, | ||
| 218 | => Payload.Function, | ||
| 219 | }; | ||
| 220 | } | ||
| 221 | }; | ||
| 222 | |||
| 223 | pub const Payload = struct { | ||
| 224 | tag: Tag, | ||
| 225 | |||
| 226 | pub const Child = struct { | ||
| 227 | base: Payload, | ||
| 228 | data: Index, | ||
| 229 | }; | ||
| 230 | |||
| 231 | pub const Sequence = struct { | ||
| 232 | base: Payload, | ||
| 233 | data: struct { | ||
| 234 | len: u64, | ||
| 235 | elem_type: Index, | ||
| 236 | }, | ||
| 237 | }; | ||
| 238 | |||
| 239 | pub const FwdDecl = struct { | ||
| 240 | base: Payload, | ||
| 241 | data: Module.Decl.Index, | ||
| 242 | }; | ||
| 243 | |||
| 244 | pub const Fields = struct { | ||
| 245 | base: Payload, | ||
| 246 | data: Data, | ||
| 247 | |||
| 248 | pub const Data = []const Field; | ||
| 249 | pub const Field = struct { | ||
| 250 | name: [*:0]const u8, | ||
| 251 | type: Index, | ||
| 252 | alignas: AlignAs, | ||
| 253 | }; | ||
| 254 | }; | ||
| 255 | |||
| 256 | pub const Unnamed = struct { | ||
| 257 | base: Payload, | ||
| 258 | data: struct { | ||
| 259 | fields: Fields.Data, | ||
| 260 | owner_decl: Module.Decl.Index, | ||
| 261 | id: u32, | ||
| 262 | }, | ||
| 263 | }; | ||
| 264 | |||
| 265 | pub const Aggregate = struct { | ||
| 266 | base: Payload, | ||
| 267 | data: struct { | ||
| 268 | fields: Fields.Data, | ||
| 269 | fwd_decl: Index, | ||
| 270 | }, | ||
| 271 | }; | ||
| 272 | |||
| 273 | pub const Function = struct { | ||
| 274 | base: Payload, | ||
| 275 | data: struct { | ||
| 276 | return_type: Index, | ||
| 277 | param_types: []const Index, | ||
| 278 | }, | ||
| 279 | }; | ||
| 280 | }; | ||
| 281 | |||
| 282 | pub const AlignAs = struct { | ||
| 283 | @"align": std.math.Log2Int(u32), | ||
| 284 | abi: std.math.Log2Int(u32), | ||
| 285 | |||
| 286 | pub fn init(alignment: u32, abi_alignment: u32) AlignAs { | ||
| 287 | const actual_align = if (alignment != 0) alignment else abi_alignment; | ||
| 288 | assert(std.math.isPowerOfTwo(actual_align)); | ||
| 289 | assert(std.math.isPowerOfTwo(abi_alignment)); | ||
| 290 | return .{ | ||
| 291 | .@"align" = std.math.log2_int(u32, actual_align), | ||
| 292 | .abi = std.math.log2_int(u32, abi_alignment), | ||
| 293 | }; | ||
| 294 | } | ||
| 295 | pub fn abiAlign(ty: Type, target: Target) AlignAs { | ||
| 296 | const abi_align = ty.abiAlignment(target); | ||
| 297 | return init(abi_align, abi_align); | ||
| 298 | } | ||
| 299 | pub fn fieldAlign(struct_ty: Type, field_i: usize, target: Target) AlignAs { | ||
| 300 | return init( | ||
| 301 | struct_ty.structFieldAlign(field_i, target), | ||
| 302 | struct_ty.structFieldType(field_i).abiAlignment(target), | ||
| 303 | ); | ||
| 304 | } | ||
| 305 | pub fn unionPayloadAlign(union_ty: Type, target: Target) AlignAs { | ||
| 306 | const union_obj = union_ty.cast(Type.Payload.Union).?.data; | ||
| 307 | const union_payload_align = union_obj.abiAlignment(target, false); | ||
| 308 | return init(union_payload_align, union_payload_align); | ||
| 309 | } | ||
| 310 | |||
| 311 | pub fn getAlign(self: AlignAs) u32 { | ||
| 312 | return @as(u32, 1) << self.@"align"; | ||
| 313 | } | ||
| 314 | }; | ||
| 315 | |||
| 316 | pub const Index = u32; | ||
| 317 | pub const Store = struct { | ||
| 318 | arena: std.heap.ArenaAllocator.State = .{}, | ||
| 319 | set: Set = .{}, | ||
| 320 | |||
| 321 | pub const Set = struct { | ||
| 322 | pub const Map = std.ArrayHashMapUnmanaged(CType, void, HashContext, true); | ||
| 323 | const HashContext = struct { | ||
| 324 | store: *const Set, | ||
| 325 | |||
| 326 | pub fn hash(self: @This(), cty: CType) Map.Hash { | ||
| 327 | return @truncate(Map.Hash, cty.hash(self.store.*)); | ||
| 328 | } | ||
| 329 | pub fn eql(_: @This(), lhs: CType, rhs: CType, _: usize) bool { | ||
| 330 | return lhs.eql(rhs); | ||
| 331 | } | ||
| 332 | }; | ||
| 333 | |||
| 334 | map: Map = .{}, | ||
| 335 | |||
| 336 | pub fn indexToCType(self: Set, index: Index) CType { | ||
| 337 | if (index < Tag.no_payload_count) return initTag(@intToEnum(Tag, index)); | ||
| 338 | return self.map.keys()[index - Tag.no_payload_count]; | ||
| 339 | } | ||
| 340 | |||
| 341 | pub fn indexToHash(self: Set, index: Index) Map.Hash { | ||
| 342 | if (index < Tag.no_payload_count) | ||
| 343 | return (HashContext{ .store = &self }).hash(self.indexToCType(index)); | ||
| 344 | return self.map.entries.items(.hash)[index - Tag.no_payload_count]; | ||
| 345 | } | ||
| 346 | |||
| 347 | pub fn typeToIndex(self: Set, ty: Type, target: Target, kind: Kind) ?Index { | ||
| 348 | const lookup = Convert.Lookup{ .imm = .{ .set = &self, .target = target } }; | ||
| 349 | |||
| 350 | var convert: Convert = undefined; | ||
| 351 | convert.initType(ty, kind, lookup) catch unreachable; | ||
| 352 | |||
| 353 | const t = convert.tag(); | ||
| 354 | if (!t.hasPayload()) return t.toIndex(); | ||
| 355 | |||
| 356 | return if (self.map.getIndexAdapted( | ||
| 357 | ty, | ||
| 358 | TypeAdapter32{ .kind = kind, .lookup = lookup, .convert = &convert }, | ||
| 359 | )) |idx| @intCast(Index, Tag.no_payload_count + idx) else null; | ||
| 360 | } | ||
| 361 | }; | ||
| 362 | |||
| 363 | pub const Promoted = struct { | ||
| 364 | arena: std.heap.ArenaAllocator, | ||
| 365 | set: Set, | ||
| 366 | |||
| 367 | pub fn gpa(self: *Promoted) Allocator { | ||
| 368 | return self.arena.child_allocator; | ||
| 369 | } | ||
| 370 | |||
| 371 | pub fn cTypeToIndex(self: *Promoted, cty: CType) Allocator.Error!Index { | ||
| 372 | const t = cty.tag(); | ||
| 373 | if (@enumToInt(t) < Tag.no_payload_count) return @intCast(Index, @enumToInt(t)); | ||
| 374 | |||
| 375 | const gop = try self.set.map.getOrPutContext(self.gpa(), cty, .{ .store = &self.set }); | ||
| 376 | if (!gop.found_existing) gop.key_ptr.* = cty; | ||
| 377 | if (std.debug.runtime_safety) { | ||
| 378 | const key = &self.set.map.entries.items(.key)[gop.index]; | ||
| 379 | assert(key == gop.key_ptr); | ||
| 380 | assert(cty.eql(key.*)); | ||
| 381 | assert(cty.hash(self.set) == key.hash(self.set)); | ||
| 382 | } | ||
| 383 | return @intCast(Index, Tag.no_payload_count + gop.index); | ||
| 384 | } | ||
| 385 | |||
| 386 | pub fn typeToIndex( | ||
| 387 | self: *Promoted, | ||
| 388 | ty: Type, | ||
| 389 | mod: *Module, | ||
| 390 | kind: Kind, | ||
| 391 | ) Allocator.Error!Index { | ||
| 392 | const lookup = Convert.Lookup{ .mut = .{ .promoted = self, .mod = mod } }; | ||
| 393 | |||
| 394 | var convert: Convert = undefined; | ||
| 395 | try convert.initType(ty, kind, lookup); | ||
| 396 | |||
| 397 | const t = convert.tag(); | ||
| 398 | if (!t.hasPayload()) return t.toIndex(); | ||
| 399 | |||
| 400 | const gop = try self.set.map.getOrPutContextAdapted( | ||
| 401 | self.gpa(), | ||
| 402 | ty, | ||
| 403 | TypeAdapter32{ .kind = kind, .lookup = lookup.freeze(), .convert = &convert }, | ||
| 404 | .{ .store = &self.set }, | ||
| 405 | ); | ||
| 406 | if (!gop.found_existing) { | ||
| 407 | errdefer _ = self.set.map.pop(); | ||
| 408 | gop.key_ptr.* = try createFromConvert(self, ty, lookup.getTarget(), kind, convert); | ||
| 409 | } | ||
| 410 | if (std.debug.runtime_safety) { | ||
| 411 | const adapter = TypeAdapter64{ | ||
| 412 | .kind = kind, | ||
| 413 | .lookup = lookup.freeze(), | ||
| 414 | .convert = &convert, | ||
| 415 | }; | ||
| 416 | const cty = &self.set.map.entries.items(.key)[gop.index]; | ||
| 417 | assert(cty == gop.key_ptr); | ||
| 418 | assert(adapter.eql(ty, cty.*)); | ||
| 419 | assert(adapter.hash(ty) == cty.hash(self.set)); | ||
| 420 | } | ||
| 421 | return @intCast(Index, Tag.no_payload_count + gop.index); | ||
| 422 | } | ||
| 423 | }; | ||
| 424 | |||
| 425 | pub fn promote(self: Store, gpa: Allocator) Promoted { | ||
| 426 | return .{ .arena = self.arena.promote(gpa), .set = self.set }; | ||
| 427 | } | ||
| 428 | |||
| 429 | pub fn demote(self: *Store, promoted: Promoted) void { | ||
| 430 | self.arena = promoted.arena.state; | ||
| 431 | self.set = promoted.set; | ||
| 432 | } | ||
| 433 | |||
| 434 | pub fn indexToCType(self: Store, index: Index) CType { | ||
| 435 | return self.set.indexToCType(index); | ||
| 436 | } | ||
| 437 | |||
| 438 | pub fn indexToHash(self: Store, index: Index) Set.Map.Hash { | ||
| 439 | return self.set.indexToHash(index); | ||
| 440 | } | ||
| 441 | |||
| 442 | pub fn cTypeToIndex(self: *Store, gpa: Allocator, cty: CType) !Index { | ||
| 443 | var promoted = self.promote(gpa); | ||
| 444 | defer self.demote(promoted); | ||
| 445 | return promoted.cTypeToIndex(cty); | ||
| 446 | } | ||
| 447 | |||
| 448 | pub fn typeToCType(self: *Store, gpa: Allocator, ty: Type, mod: *Module, kind: Kind) !CType { | ||
| 449 | const idx = try self.typeToIndex(gpa, ty, mod, kind); | ||
| 450 | return self.indexToCType(idx); | ||
| 451 | } | ||
| 452 | |||
| 453 | pub fn typeToIndex(self: *Store, gpa: Allocator, ty: Type, mod: *Module, kind: Kind) !Index { | ||
| 454 | var promoted = self.promote(gpa); | ||
| 455 | defer self.demote(promoted); | ||
| 456 | return promoted.typeToIndex(ty, mod, kind); | ||
| 457 | } | ||
| 458 | |||
| 459 | pub fn clearRetainingCapacity(self: *Store, gpa: Allocator) void { | ||
| 460 | var promoted = self.promote(gpa); | ||
| 461 | defer self.demote(promoted); | ||
| 462 | promoted.set.map.clearRetainingCapacity(); | ||
| 463 | _ = promoted.arena.reset(.retain_capacity); | ||
| 464 | } | ||
| 465 | |||
| 466 | pub fn clearAndFree(self: *Store, gpa: Allocator) void { | ||
| 467 | var promoted = self.promote(gpa); | ||
| 468 | defer self.demote(promoted); | ||
| 469 | promoted.set.map.clearAndFree(gpa); | ||
| 470 | _ = promoted.arena.reset(.free_all); | ||
| 471 | } | ||
| 472 | |||
| 473 | pub fn shrinkRetainingCapacity(self: *Store, gpa: Allocator, new_len: usize) void { | ||
| 474 | self.set.map.shrinkRetainingCapacity(gpa, new_len); | ||
| 475 | } | ||
| 476 | |||
| 477 | pub fn shrinkAndFree(self: *Store, gpa: Allocator, new_len: usize) void { | ||
| 478 | self.set.map.shrinkAndFree(gpa, new_len); | ||
| 479 | } | ||
| 480 | |||
| 481 | pub fn count(self: Store) usize { | ||
| 482 | return self.set.map.count(); | ||
| 483 | } | ||
| 484 | |||
| 485 | pub fn move(self: *Store) Store { | ||
| 486 | const moved = self.*; | ||
| 487 | self.* = .{}; | ||
| 488 | return moved; | ||
| 489 | } | ||
| 490 | |||
| 491 | pub fn deinit(self: *Store, gpa: Allocator) void { | ||
| 492 | var promoted = self.promote(gpa); | ||
| 493 | promoted.set.map.deinit(gpa); | ||
| 494 | _ = promoted.arena.deinit(); | ||
| 495 | self.* = undefined; | ||
| 496 | } | ||
| 497 | }; | ||
| 498 | |||
| 499 | pub fn isPacked(self: CType) bool { | ||
| 500 | return switch (self.tag()) { | ||
| 501 | else => false, | ||
| 502 | .packed_unnamed_struct, | ||
| 503 | .packed_unnamed_union, | ||
| 504 | .packed_struct, | ||
| 505 | .packed_union, | ||
| 506 | => true, | ||
| 507 | }; | ||
| 508 | } | ||
| 509 | |||
| 510 | pub fn fields(self: CType) Payload.Fields.Data { | ||
| 511 | return if (self.cast(Payload.Aggregate)) |pl| | ||
| 512 | pl.data.fields | ||
| 513 | else if (self.cast(Payload.Unnamed)) |pl| | ||
| 514 | pl.data.fields | ||
| 515 | else if (self.cast(Payload.Fields)) |pl| | ||
| 516 | pl.data | ||
| 517 | else | ||
| 518 | unreachable; | ||
| 519 | } | ||
| 520 | |||
| 521 | pub fn eql(lhs: CType, rhs: CType) bool { | ||
| 522 | return lhs.eqlContext(rhs, struct { | ||
| 523 | pub fn eqlIndex(_: @This(), lhs_idx: Index, rhs_idx: Index) bool { | ||
| 524 | return lhs_idx == rhs_idx; | ||
| 525 | } | ||
| 526 | }{}); | ||
| 527 | } | ||
| 528 | |||
| 529 | pub fn eqlContext(lhs: CType, rhs: CType, ctx: anytype) bool { | ||
| 530 | // As a shortcut, if the small tags / addresses match, we're done. | ||
| 531 | if (lhs.tag_if_small_enough == rhs.tag_if_small_enough) return true; | ||
| 532 | |||
| 533 | const lhs_tag = lhs.tag(); | ||
| 534 | const rhs_tag = rhs.tag(); | ||
| 535 | if (lhs_tag != rhs_tag) return false; | ||
| 536 | |||
| 537 | return switch (lhs_tag) { | ||
| 538 | .void, | ||
| 539 | .char, | ||
| 540 | .@"signed char", | ||
| 541 | .short, | ||
| 542 | .int, | ||
| 543 | .long, | ||
| 544 | .@"long long", | ||
| 545 | ._Bool, | ||
| 546 | .@"unsigned char", | ||
| 547 | .@"unsigned short", | ||
| 548 | .@"unsigned int", | ||
| 549 | .@"unsigned long", | ||
| 550 | .@"unsigned long long", | ||
| 551 | .float, | ||
| 552 | .double, | ||
| 553 | .@"long double", | ||
| 554 | .bool, | ||
| 555 | .size_t, | ||
| 556 | .ptrdiff_t, | ||
| 557 | .uint8_t, | ||
| 558 | .int8_t, | ||
| 559 | .uint16_t, | ||
| 560 | .int16_t, | ||
| 561 | .uint32_t, | ||
| 562 | .int32_t, | ||
| 563 | .uint64_t, | ||
| 564 | .int64_t, | ||
| 565 | .uintptr_t, | ||
| 566 | .intptr_t, | ||
| 567 | .zig_u128, | ||
| 568 | .zig_i128, | ||
| 569 | .zig_f16, | ||
| 570 | .zig_f32, | ||
| 571 | .zig_f64, | ||
| 572 | .zig_f80, | ||
| 573 | .zig_f128, | ||
| 574 | .zig_c_longdouble, | ||
| 575 | => false, | ||
| 576 | |||
| 577 | .pointer, | ||
| 578 | .pointer_const, | ||
| 579 | .pointer_volatile, | ||
| 580 | .pointer_const_volatile, | ||
| 581 | => ctx.eqlIndex(lhs.cast(Payload.Child).?.data, rhs.cast(Payload.Child).?.data), | ||
| 582 | |||
| 583 | .array, | ||
| 584 | .vector, | ||
| 585 | => { | ||
| 586 | const lhs_data = lhs.cast(Payload.Sequence).?.data; | ||
| 587 | const rhs_data = rhs.cast(Payload.Sequence).?.data; | ||
| 588 | return lhs_data.len == rhs_data.len and | ||
| 589 | ctx.eqlIndex(lhs_data.elem_type, rhs_data.elem_type); | ||
| 590 | }, | ||
| 591 | |||
| 592 | .fwd_anon_struct, | ||
| 593 | .fwd_anon_union, | ||
| 594 | => { | ||
| 595 | const lhs_data = lhs.cast(Payload.Fields).?.data; | ||
| 596 | const rhs_data = rhs.cast(Payload.Fields).?.data; | ||
| 597 | if (lhs_data.len != rhs_data.len) return false; | ||
| 598 | for (lhs_data, rhs_data) |lhs_field, rhs_field| { | ||
| 599 | if (!ctx.eqlIndex(lhs_field.type, rhs_field.type)) return false; | ||
| 600 | if (lhs_field.alignas.@"align" != rhs_field.alignas.@"align") return false; | ||
| 601 | if (cstr.cmp(lhs_field.name, rhs_field.name) != 0) return false; | ||
| 602 | } | ||
| 603 | return true; | ||
| 604 | }, | ||
| 605 | |||
| 606 | .fwd_struct, | ||
| 607 | .fwd_union, | ||
| 608 | => lhs.cast(Payload.FwdDecl).?.data == rhs.cast(Payload.FwdDecl).?.data, | ||
| 609 | |||
| 610 | .unnamed_struct, | ||
| 611 | .unnamed_union, | ||
| 612 | .packed_unnamed_struct, | ||
| 613 | .packed_unnamed_union, | ||
| 614 | => { | ||
| 615 | const lhs_data = lhs.cast(Payload.Unnamed).?.data; | ||
| 616 | const rhs_data = rhs.cast(Payload.Unnamed).?.data; | ||
| 617 | return lhs_data.owner_decl == rhs_data.owner_decl and lhs_data.id == rhs_data.id; | ||
| 618 | }, | ||
| 619 | |||
| 620 | .anon_struct, | ||
| 621 | .anon_union, | ||
| 622 | .@"struct", | ||
| 623 | .@"union", | ||
| 624 | .packed_struct, | ||
| 625 | .packed_union, | ||
| 626 | => ctx.eqlIndex( | ||
| 627 | lhs.cast(Payload.Aggregate).?.data.fwd_decl, | ||
| 628 | rhs.cast(Payload.Aggregate).?.data.fwd_decl, | ||
| 629 | ), | ||
| 630 | |||
| 631 | .function, | ||
| 632 | .varargs_function, | ||
| 633 | => { | ||
| 634 | const lhs_data = lhs.cast(Payload.Function).?.data; | ||
| 635 | const rhs_data = rhs.cast(Payload.Function).?.data; | ||
| 636 | if (lhs_data.param_types.len != rhs_data.param_types.len) return false; | ||
| 637 | if (!ctx.eqlIndex(lhs_data.return_type, rhs_data.return_type)) return false; | ||
| 638 | for (lhs_data.param_types, rhs_data.param_types) |lhs_param_idx, rhs_param_idx| { | ||
| 639 | if (!ctx.eqlIndex(lhs_param_idx, rhs_param_idx)) return false; | ||
| 640 | } | ||
| 641 | return true; | ||
| 642 | }, | ||
| 643 | }; | ||
| 644 | } | ||
| 645 | |||
| 646 | pub fn hash(self: CType, store: Store.Set) u64 { | ||
| 647 | var hasher = std.hash.Wyhash.init(0); | ||
| 648 | self.updateHasher(&hasher, store); | ||
| 649 | return hasher.final(); | ||
| 650 | } | ||
| 651 | |||
| 652 | pub fn updateHasher(self: CType, hasher: anytype, store: Store.Set) void { | ||
| 653 | const t = self.tag(); | ||
| 654 | autoHash(hasher, t); | ||
| 655 | switch (t) { | ||
| 656 | .void, | ||
| 657 | .char, | ||
| 658 | .@"signed char", | ||
| 659 | .short, | ||
| 660 | .int, | ||
| 661 | .long, | ||
| 662 | .@"long long", | ||
| 663 | ._Bool, | ||
| 664 | .@"unsigned char", | ||
| 665 | .@"unsigned short", | ||
| 666 | .@"unsigned int", | ||
| 667 | .@"unsigned long", | ||
| 668 | .@"unsigned long long", | ||
| 669 | .float, | ||
| 670 | .double, | ||
| 671 | .@"long double", | ||
| 672 | .bool, | ||
| 673 | .size_t, | ||
| 674 | .ptrdiff_t, | ||
| 675 | .uint8_t, | ||
| 676 | .int8_t, | ||
| 677 | .uint16_t, | ||
| 678 | .int16_t, | ||
| 679 | .uint32_t, | ||
| 680 | .int32_t, | ||
| 681 | .uint64_t, | ||
| 682 | .int64_t, | ||
| 683 | .uintptr_t, | ||
| 684 | .intptr_t, | ||
| 685 | .zig_u128, | ||
| 686 | .zig_i128, | ||
| 687 | .zig_f16, | ||
| 688 | .zig_f32, | ||
| 689 | .zig_f64, | ||
| 690 | .zig_f80, | ||
| 691 | .zig_f128, | ||
| 692 | .zig_c_longdouble, | ||
| 693 | => {}, | ||
| 694 | |||
| 695 | .pointer, | ||
| 696 | .pointer_const, | ||
| 697 | .pointer_volatile, | ||
| 698 | .pointer_const_volatile, | ||
| 699 | => store.indexToCType(self.cast(Payload.Child).?.data).updateHasher(hasher, store), | ||
| 700 | |||
| 701 | .array, | ||
| 702 | .vector, | ||
| 703 | => { | ||
| 704 | const data = self.cast(Payload.Sequence).?.data; | ||
| 705 | autoHash(hasher, data.len); | ||
| 706 | store.indexToCType(data.elem_type).updateHasher(hasher, store); | ||
| 707 | }, | ||
| 708 | |||
| 709 | .fwd_anon_struct, | ||
| 710 | .fwd_anon_union, | ||
| 711 | => for (self.cast(Payload.Fields).?.data) |field| { | ||
| 712 | store.indexToCType(field.type).updateHasher(hasher, store); | ||
| 713 | hasher.update(mem.span(field.name)); | ||
| 714 | autoHash(hasher, field.alignas.@"align"); | ||
| 715 | }, | ||
| 716 | |||
| 717 | .fwd_struct, | ||
| 718 | .fwd_union, | ||
| 719 | => autoHash(hasher, self.cast(Payload.FwdDecl).?.data), | ||
| 720 | |||
| 721 | .unnamed_struct, | ||
| 722 | .unnamed_union, | ||
| 723 | .packed_unnamed_struct, | ||
| 724 | .packed_unnamed_union, | ||
| 725 | => { | ||
| 726 | const data = self.cast(Payload.Unnamed).?.data; | ||
| 727 | autoHash(hasher, data.owner_decl); | ||
| 728 | autoHash(hasher, data.id); | ||
| 729 | }, | ||
| 730 | |||
| 731 | .anon_struct, | ||
| 732 | .anon_union, | ||
| 733 | .@"struct", | ||
| 734 | .@"union", | ||
| 735 | .packed_struct, | ||
| 736 | .packed_union, | ||
| 737 | => store.indexToCType(self.cast(Payload.Aggregate).?.data.fwd_decl) | ||
| 738 | .updateHasher(hasher, store), | ||
| 739 | |||
| 740 | .function, | ||
| 741 | .varargs_function, | ||
| 742 | => { | ||
| 743 | const data = self.cast(Payload.Function).?.data; | ||
| 744 | store.indexToCType(data.return_type).updateHasher(hasher, store); | ||
| 745 | for (data.param_types) |param_ty| { | ||
| 746 | store.indexToCType(param_ty).updateHasher(hasher, store); | ||
| 747 | } | ||
| 748 | }, | ||
| 749 | } | ||
| 750 | } | ||
| 751 | |||
| 752 | pub const Kind = enum { forward, forward_parameter, complete, global, parameter, payload }; | ||
| 753 | |||
| 754 | const Convert = struct { | ||
| 755 | storage: union { | ||
| 756 | none: void, | ||
| 757 | child: Payload.Child, | ||
| 758 | seq: Payload.Sequence, | ||
| 759 | fwd: Payload.FwdDecl, | ||
| 760 | anon: struct { | ||
| 761 | fields: [2]Payload.Fields.Field, | ||
| 762 | pl: union { | ||
| 763 | forward: Payload.Fields, | ||
| 764 | complete: Payload.Aggregate, | ||
| 765 | }, | ||
| 766 | }, | ||
| 767 | }, | ||
| 768 | value: union(enum) { | ||
| 769 | tag: Tag, | ||
| 770 | cty: CType, | ||
| 771 | }, | ||
| 772 | |||
| 773 | pub fn init(self: *@This(), t: Tag) void { | ||
| 774 | self.* = if (t.hasPayload()) .{ | ||
| 775 | .storage = .{ .none = {} }, | ||
| 776 | .value = .{ .tag = t }, | ||
| 777 | } else .{ | ||
| 778 | .storage = .{ .none = {} }, | ||
| 779 | .value = .{ .cty = initTag(t) }, | ||
| 780 | }; | ||
| 781 | } | ||
| 782 | |||
| 783 | pub fn tag(self: @This()) Tag { | ||
| 784 | return switch (self.value) { | ||
| 785 | .tag => |t| t, | ||
| 786 | .cty => |c| c.tag(), | ||
| 787 | }; | ||
| 788 | } | ||
| 789 | |||
| 790 | fn tagFromIntInfo(signedness: std.builtin.Signedness, bits: u16) Tag { | ||
| 791 | return switch (bits) { | ||
| 792 | 0 => .void, | ||
| 793 | 1...8 => switch (signedness) { | ||
| 794 | .unsigned => .uint8_t, | ||
| 795 | .signed => .int8_t, | ||
| 796 | }, | ||
| 797 | 9...16 => switch (signedness) { | ||
| 798 | .unsigned => .uint16_t, | ||
| 799 | .signed => .int16_t, | ||
| 800 | }, | ||
| 801 | 17...32 => switch (signedness) { | ||
| 802 | .unsigned => .uint32_t, | ||
| 803 | .signed => .int32_t, | ||
| 804 | }, | ||
| 805 | 33...64 => switch (signedness) { | ||
| 806 | .unsigned => .uint64_t, | ||
| 807 | .signed => .int64_t, | ||
| 808 | }, | ||
| 809 | 65...128 => switch (signedness) { | ||
| 810 | .unsigned => .zig_u128, | ||
| 811 | .signed => .zig_i128, | ||
| 812 | }, | ||
| 813 | else => .array, | ||
| 814 | }; | ||
| 815 | } | ||
| 816 | |||
| 817 | pub const Lookup = union(enum) { | ||
| 818 | fail: Target, | ||
| 819 | imm: struct { | ||
| 820 | set: *const Store.Set, | ||
| 821 | target: Target, | ||
| 822 | }, | ||
| 823 | mut: struct { | ||
| 824 | promoted: *Store.Promoted, | ||
| 825 | mod: *Module, | ||
| 826 | }, | ||
| 827 | |||
| 828 | pub fn isMutable(self: @This()) bool { | ||
| 829 | return switch (self) { | ||
| 830 | .fail, .imm => false, | ||
| 831 | .mut => true, | ||
| 832 | }; | ||
| 833 | } | ||
| 834 | |||
| 835 | pub fn getTarget(self: @This()) Target { | ||
| 836 | return switch (self) { | ||
| 837 | .fail => |target| target, | ||
| 838 | .imm => |imm| imm.target, | ||
| 839 | .mut => |mut| mut.mod.getTarget(), | ||
| 840 | }; | ||
| 841 | } | ||
| 842 | |||
| 843 | pub fn getSet(self: @This()) ?*const Store.Set { | ||
| 844 | return switch (self) { | ||
| 845 | .fail => null, | ||
| 846 | .imm => |imm| imm.set, | ||
| 847 | .mut => |mut| &mut.promoted.set, | ||
| 848 | }; | ||
| 849 | } | ||
| 850 | |||
| 851 | pub fn typeToIndex(self: @This(), ty: Type, kind: Kind) !?Index { | ||
| 852 | return switch (self) { | ||
| 853 | .fail => null, | ||
| 854 | .imm => |imm| imm.set.typeToIndex(ty, imm.target, kind), | ||
| 855 | .mut => |mut| try mut.promoted.typeToIndex(ty, mut.mod, kind), | ||
| 856 | }; | ||
| 857 | } | ||
| 858 | |||
| 859 | pub fn indexToCType(self: @This(), index: Index) ?CType { | ||
| 860 | return if (self.getSet()) |set| set.indexToCType(index) else null; | ||
| 861 | } | ||
| 862 | |||
| 863 | pub fn freeze(self: @This()) @This() { | ||
| 864 | return switch (self) { | ||
| 865 | .fail, .imm => self, | ||
| 866 | .mut => |mut| .{ .imm = .{ .set = &mut.promoted.set, .target = self.getTarget() } }, | ||
| 867 | }; | ||
| 868 | } | ||
| 869 | }; | ||
| 870 | |||
| 871 | fn sortFields(self: *@This(), fields_len: usize) []Payload.Fields.Field { | ||
| 872 | const Field = Payload.Fields.Field; | ||
| 873 | const slice = self.storage.anon.fields[0..fields_len]; | ||
| 874 | std.sort.sort(Field, slice, {}, struct { | ||
| 875 | fn before(_: void, lhs: Field, rhs: Field) bool { | ||
| 876 | return lhs.alignas.@"align" > rhs.alignas.@"align"; | ||
| 877 | } | ||
| 878 | }.before); | ||
| 879 | return slice; | ||
| 880 | } | ||
| 881 | |||
| 882 | fn initAnon(self: *@This(), kind: Kind, fwd_idx: Index, fields_len: usize) void { | ||
| 883 | switch (kind) { | ||
| 884 | .forward, .forward_parameter => { | ||
| 885 | self.storage.anon.pl = .{ .forward = .{ | ||
| 886 | .base = .{ .tag = .fwd_anon_struct }, | ||
| 887 | .data = self.sortFields(fields_len), | ||
| 888 | } }; | ||
| 889 | self.value = .{ .cty = initPayload(&self.storage.anon.pl.forward) }; | ||
| 890 | }, | ||
| 891 | .complete, .parameter, .global => { | ||
| 892 | self.storage.anon.pl = .{ .complete = .{ | ||
| 893 | .base = .{ .tag = .anon_struct }, | ||
| 894 | .data = .{ | ||
| 895 | .fields = self.sortFields(fields_len), | ||
| 896 | .fwd_decl = fwd_idx, | ||
| 897 | }, | ||
| 898 | } }; | ||
| 899 | self.value = .{ .cty = initPayload(&self.storage.anon.pl.complete) }; | ||
| 900 | }, | ||
| 901 | .payload => unreachable, | ||
| 902 | } | ||
| 903 | } | ||
| 904 | |||
| 905 | fn initArrayParameter(self: *@This(), ty: Type, kind: Kind, lookup: Lookup) !void { | ||
| 906 | if (switch (kind) { | ||
| 907 | .forward_parameter => @as(Index, undefined), | ||
| 908 | .parameter => try lookup.typeToIndex(ty, .forward_parameter), | ||
| 909 | .forward, .complete, .global, .payload => unreachable, | ||
| 910 | }) |fwd_idx| { | ||
| 911 | if (try lookup.typeToIndex(ty, switch (kind) { | ||
| 912 | .forward_parameter => .forward, | ||
| 913 | .parameter => .complete, | ||
| 914 | .forward, .complete, .global, .payload => unreachable, | ||
| 915 | })) |array_idx| { | ||
| 916 | self.storage = .{ .anon = undefined }; | ||
| 917 | self.storage.anon.fields[0] = .{ | ||
| 918 | .name = "array", | ||
| 919 | .type = array_idx, | ||
| 920 | .alignas = AlignAs.abiAlign(ty, lookup.getTarget()), | ||
| 921 | }; | ||
| 922 | self.initAnon(kind, fwd_idx, 1); | ||
| 923 | } else self.init(switch (kind) { | ||
| 924 | .forward_parameter => .fwd_anon_struct, | ||
| 925 | .parameter => .anon_struct, | ||
| 926 | .forward, .complete, .global, .payload => unreachable, | ||
| 927 | }); | ||
| 928 | } else self.init(.anon_struct); | ||
| 929 | } | ||
| 930 | |||
| 931 | pub fn initType(self: *@This(), ty: Type, kind: Kind, lookup: Lookup) !void { | ||
| 932 | const target = lookup.getTarget(); | ||
| 933 | |||
| 934 | self.* = undefined; | ||
| 935 | if (!ty.isFnOrHasRuntimeBitsIgnoreComptime()) | ||
| 936 | self.init(.void) | ||
| 937 | else if (ty.isAbiInt()) switch (ty.tag()) { | ||
| 938 | .usize => self.init(.uintptr_t), | ||
| 939 | .isize => self.init(.intptr_t), | ||
| 940 | .c_short => self.init(.short), | ||
| 941 | .c_ushort => self.init(.@"unsigned short"), | ||
| 942 | .c_int => self.init(.int), | ||
| 943 | .c_uint => self.init(.@"unsigned int"), | ||
| 944 | .c_long => self.init(.long), | ||
| 945 | .c_ulong => self.init(.@"unsigned long"), | ||
| 946 | .c_longlong => self.init(.@"long long"), | ||
| 947 | .c_ulonglong => self.init(.@"unsigned long long"), | ||
| 948 | else => { | ||
| 949 | const info = ty.intInfo(target); | ||
| 950 | const t = tagFromIntInfo(info.signedness, info.bits); | ||
| 951 | switch (t) { | ||
| 952 | .void => unreachable, | ||
| 953 | else => self.init(t), | ||
| 954 | .array => switch (kind) { | ||
| 955 | .forward, .complete, .global => { | ||
| 956 | const abi_size = ty.abiSize(target); | ||
| 957 | const abi_align = ty.abiAlignment(target); | ||
| 958 | self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{ | ||
| 959 | .len = @divExact(abi_size, abi_align), | ||
| 960 | .elem_type = tagFromIntInfo( | ||
| 961 | .unsigned, | ||
| 962 | @intCast(u16, abi_align * 8), | ||
| 963 | ).toIndex(), | ||
| 964 | } } }; | ||
| 965 | self.value = .{ .cty = initPayload(&self.storage.seq) }; | ||
| 966 | }, | ||
| 967 | .forward_parameter, | ||
| 968 | .parameter, | ||
| 969 | => try self.initArrayParameter(ty, kind, lookup), | ||
| 970 | .payload => unreachable, | ||
| 971 | }, | ||
| 972 | } | ||
| 973 | }, | ||
| 974 | } else switch (ty.zigTypeTag()) { | ||
| 975 | .Frame => unreachable, | ||
| 976 | .AnyFrame => unreachable, | ||
| 977 | |||
| 978 | .Int, | ||
| 979 | .Enum, | ||
| 980 | .ErrorSet, | ||
| 981 | .Type, | ||
| 982 | .Void, | ||
| 983 | .NoReturn, | ||
| 984 | .ComptimeFloat, | ||
| 985 | .ComptimeInt, | ||
| 986 | .Undefined, | ||
| 987 | .Null, | ||
| 988 | .EnumLiteral, | ||
| 989 | => unreachable, | ||
| 990 | |||
| 991 | .Bool => self.init(.bool), | ||
| 992 | |||
| 993 | .Float => self.init(switch (ty.tag()) { | ||
| 994 | .f16 => .zig_f16, | ||
| 995 | .f32 => .zig_f32, | ||
| 996 | .f64 => .zig_f64, | ||
| 997 | .f80 => .zig_f80, | ||
| 998 | .f128 => .zig_f128, | ||
| 999 | .c_longdouble => .zig_c_longdouble, | ||
| 1000 | else => unreachable, | ||
| 1001 | }), | ||
| 1002 | |||
| 1003 | .Pointer => { | ||
| 1004 | const info = ty.ptrInfo().data; | ||
| 1005 | switch (info.size) { | ||
| 1006 | .Slice => { | ||
| 1007 | if (switch (kind) { | ||
| 1008 | .forward, .forward_parameter => @as(Index, undefined), | ||
| 1009 | .complete, .parameter, .global => try lookup.typeToIndex(ty, .forward), | ||
| 1010 | .payload => unreachable, | ||
| 1011 | }) |fwd_idx| { | ||
| 1012 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | ||
| 1013 | const ptr_ty = ty.slicePtrFieldType(&buf); | ||
| 1014 | if (try lookup.typeToIndex(ptr_ty, kind)) |ptr_idx| { | ||
| 1015 | self.storage = .{ .anon = undefined }; | ||
| 1016 | self.storage.anon.fields[0] = .{ | ||
| 1017 | .name = "ptr", | ||
| 1018 | .type = ptr_idx, | ||
| 1019 | .alignas = AlignAs.abiAlign(ptr_ty, target), | ||
| 1020 | }; | ||
| 1021 | self.storage.anon.fields[1] = .{ | ||
| 1022 | .name = "len", | ||
| 1023 | .type = Tag.uintptr_t.toIndex(), | ||
| 1024 | .alignas = AlignAs.abiAlign(Type.usize, target), | ||
| 1025 | }; | ||
| 1026 | self.initAnon(kind, fwd_idx, 2); | ||
| 1027 | } else self.init(switch (kind) { | ||
| 1028 | .forward, .forward_parameter => .fwd_anon_struct, | ||
| 1029 | .complete, .parameter, .global => .anon_struct, | ||
| 1030 | .payload => unreachable, | ||
| 1031 | }); | ||
| 1032 | } else self.init(.anon_struct); | ||
| 1033 | }, | ||
| 1034 | |||
| 1035 | .One, .Many, .C => { | ||
| 1036 | const t: Tag = switch (info.@"volatile") { | ||
| 1037 | false => switch (info.mutable) { | ||
| 1038 | true => .pointer, | ||
| 1039 | false => .pointer_const, | ||
| 1040 | }, | ||
| 1041 | true => switch (info.mutable) { | ||
| 1042 | true => .pointer_volatile, | ||
| 1043 | false => .pointer_const_volatile, | ||
| 1044 | }, | ||
| 1045 | }; | ||
| 1046 | |||
| 1047 | var host_int_pl = Type.Payload.Bits{ | ||
| 1048 | .base = .{ .tag = .int_unsigned }, | ||
| 1049 | .data = info.host_size * 8, | ||
| 1050 | }; | ||
| 1051 | const pointee_ty = if (info.host_size > 0) | ||
| 1052 | Type.initPayload(&host_int_pl.base) | ||
| 1053 | else | ||
| 1054 | info.pointee_type; | ||
| 1055 | |||
| 1056 | if (if (info.size == .C and pointee_ty.tag() == .u8) | ||
| 1057 | Tag.char.toIndex() | ||
| 1058 | else | ||
| 1059 | try lookup.typeToIndex(pointee_ty, .forward)) |child_idx| | ||
| 1060 | { | ||
| 1061 | self.storage = .{ .child = .{ | ||
| 1062 | .base = .{ .tag = t }, | ||
| 1063 | .data = child_idx, | ||
| 1064 | } }; | ||
| 1065 | self.value = .{ .cty = initPayload(&self.storage.child) }; | ||
| 1066 | } else self.init(t); | ||
| 1067 | }, | ||
| 1068 | } | ||
| 1069 | }, | ||
| 1070 | |||
| 1071 | .Struct, .Union => |zig_ty_tag| if (ty.containerLayout() == .Packed) { | ||
| 1072 | if (ty.castTag(.@"struct")) |struct_obj| { | ||
| 1073 | try self.initType(struct_obj.data.backing_int_ty, kind, lookup); | ||
| 1074 | } else { | ||
| 1075 | var buf: Type.Payload.Bits = .{ | ||
| 1076 | .base = .{ .tag = .int_unsigned }, | ||
| 1077 | .data = @intCast(u16, ty.bitSize(target)), | ||
| 1078 | }; | ||
| 1079 | try self.initType(Type.initPayload(&buf.base), kind, lookup); | ||
| 1080 | } | ||
| 1081 | } else if (ty.isTupleOrAnonStruct()) { | ||
| 1082 | if (lookup.isMutable()) { | ||
| 1083 | for (0..switch (zig_ty_tag) { | ||
| 1084 | .Struct => ty.structFieldCount(), | ||
| 1085 | .Union => ty.unionFields().count(), | ||
| 1086 | else => unreachable, | ||
| 1087 | }) |field_i| { | ||
| 1088 | const field_ty = ty.structFieldType(field_i); | ||
| 1089 | if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or | ||
| 1090 | !field_ty.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1091 | _ = try lookup.typeToIndex(field_ty, switch (kind) { | ||
| 1092 | .forward, .forward_parameter => .forward, | ||
| 1093 | .complete, .parameter => .complete, | ||
| 1094 | .global => .global, | ||
| 1095 | .payload => unreachable, | ||
| 1096 | }); | ||
| 1097 | } | ||
| 1098 | switch (kind) { | ||
| 1099 | .forward, .forward_parameter => {}, | ||
| 1100 | .complete, .parameter, .global => _ = try lookup.typeToIndex(ty, .forward), | ||
| 1101 | .payload => unreachable, | ||
| 1102 | } | ||
| 1103 | } | ||
| 1104 | self.init(switch (kind) { | ||
| 1105 | .forward, .forward_parameter => switch (zig_ty_tag) { | ||
| 1106 | .Struct => .fwd_anon_struct, | ||
| 1107 | .Union => .fwd_anon_union, | ||
| 1108 | else => unreachable, | ||
| 1109 | }, | ||
| 1110 | .complete, .parameter, .global => switch (zig_ty_tag) { | ||
| 1111 | .Struct => .anon_struct, | ||
| 1112 | .Union => .anon_union, | ||
| 1113 | else => unreachable, | ||
| 1114 | }, | ||
| 1115 | .payload => unreachable, | ||
| 1116 | }); | ||
| 1117 | } else { | ||
| 1118 | const tag_ty = ty.unionTagTypeSafety(); | ||
| 1119 | const is_tagged_union_wrapper = kind != .payload and tag_ty != null; | ||
| 1120 | const is_struct = zig_ty_tag == .Struct or is_tagged_union_wrapper; | ||
| 1121 | switch (kind) { | ||
| 1122 | .forward, .forward_parameter => { | ||
| 1123 | self.storage = .{ .fwd = .{ | ||
| 1124 | .base = .{ .tag = if (is_struct) .fwd_struct else .fwd_union }, | ||
| 1125 | .data = ty.getOwnerDecl(), | ||
| 1126 | } }; | ||
| 1127 | self.value = .{ .cty = initPayload(&self.storage.fwd) }; | ||
| 1128 | }, | ||
| 1129 | .complete, .parameter, .global, .payload => if (is_tagged_union_wrapper) { | ||
| 1130 | const fwd_idx = try lookup.typeToIndex(ty, .forward); | ||
| 1131 | const payload_idx = try lookup.typeToIndex(ty, .payload); | ||
| 1132 | const tag_idx = try lookup.typeToIndex(tag_ty.?, kind); | ||
| 1133 | if (fwd_idx != null and payload_idx != null and tag_idx != null) { | ||
| 1134 | self.storage = .{ .anon = undefined }; | ||
| 1135 | var field_count: usize = 0; | ||
| 1136 | if (payload_idx != Tag.void.toIndex()) { | ||
| 1137 | self.storage.anon.fields[field_count] = .{ | ||
| 1138 | .name = "payload", | ||
| 1139 | .type = payload_idx.?, | ||
| 1140 | .alignas = AlignAs.unionPayloadAlign(ty, target), | ||
| 1141 | }; | ||
| 1142 | field_count += 1; | ||
| 1143 | } | ||
| 1144 | if (tag_idx != Tag.void.toIndex()) { | ||
| 1145 | self.storage.anon.fields[field_count] = .{ | ||
| 1146 | .name = "tag", | ||
| 1147 | .type = tag_idx.?, | ||
| 1148 | .alignas = AlignAs.abiAlign(tag_ty.?, target), | ||
| 1149 | }; | ||
| 1150 | field_count += 1; | ||
| 1151 | } | ||
| 1152 | self.storage.anon.pl = .{ .complete = .{ | ||
| 1153 | .base = .{ .tag = .@"struct" }, | ||
| 1154 | .data = .{ | ||
| 1155 | .fields = self.sortFields(field_count), | ||
| 1156 | .fwd_decl = fwd_idx.?, | ||
| 1157 | }, | ||
| 1158 | } }; | ||
| 1159 | self.value = .{ .cty = initPayload(&self.storage.anon.pl.complete) }; | ||
| 1160 | } else self.init(.@"struct"); | ||
| 1161 | } else if (kind == .payload and ty.unionHasAllZeroBitFieldTypes()) { | ||
| 1162 | self.init(.void); | ||
| 1163 | } else { | ||
| 1164 | var is_packed = false; | ||
| 1165 | for (0..switch (zig_ty_tag) { | ||
| 1166 | .Struct => ty.structFieldCount(), | ||
| 1167 | .Union => ty.unionFields().count(), | ||
| 1168 | else => unreachable, | ||
| 1169 | }) |field_i| { | ||
| 1170 | const field_ty = ty.structFieldType(field_i); | ||
| 1171 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1172 | |||
| 1173 | const field_align = AlignAs.fieldAlign(ty, field_i, target); | ||
| 1174 | if (field_align.@"align" < field_align.abi) { | ||
| 1175 | is_packed = true; | ||
| 1176 | if (!lookup.isMutable()) break; | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | if (lookup.isMutable()) { | ||
| 1180 | _ = try lookup.typeToIndex(field_ty, switch (kind) { | ||
| 1181 | .forward, .forward_parameter => unreachable, | ||
| 1182 | .complete, .parameter, .payload => .complete, | ||
| 1183 | .global => .global, | ||
| 1184 | }); | ||
| 1185 | } | ||
| 1186 | } | ||
| 1187 | switch (kind) { | ||
| 1188 | .forward, .forward_parameter => unreachable, | ||
| 1189 | .complete, .parameter, .global => { | ||
| 1190 | _ = try lookup.typeToIndex(ty, .forward); | ||
| 1191 | self.init(if (is_struct) | ||
| 1192 | if (is_packed) .packed_struct else .@"struct" | ||
| 1193 | else if (is_packed) .packed_union else .@"union"); | ||
| 1194 | }, | ||
| 1195 | .payload => self.init(if (is_packed) | ||
| 1196 | .packed_unnamed_union | ||
| 1197 | else | ||
| 1198 | .unnamed_union), | ||
| 1199 | } | ||
| 1200 | }, | ||
| 1201 | } | ||
| 1202 | }, | ||
| 1203 | |||
| 1204 | .Array, .Vector => |zig_ty_tag| { | ||
| 1205 | switch (kind) { | ||
| 1206 | .forward, .complete, .global => { | ||
| 1207 | const t: Tag = switch (zig_ty_tag) { | ||
| 1208 | .Array => .array, | ||
| 1209 | .Vector => .vector, | ||
| 1210 | else => unreachable, | ||
| 1211 | }; | ||
| 1212 | if (try lookup.typeToIndex(ty.childType(), kind)) |child_idx| { | ||
| 1213 | self.storage = .{ .seq = .{ .base = .{ .tag = t }, .data = .{ | ||
| 1214 | .len = ty.arrayLenIncludingSentinel(), | ||
| 1215 | .elem_type = child_idx, | ||
| 1216 | } } }; | ||
| 1217 | self.value = .{ .cty = initPayload(&self.storage.seq) }; | ||
| 1218 | } else self.init(t); | ||
| 1219 | }, | ||
| 1220 | .forward_parameter, .parameter => try self.initArrayParameter(ty, kind, lookup), | ||
| 1221 | .payload => unreachable, | ||
| 1222 | } | ||
| 1223 | }, | ||
| 1224 | |||
| 1225 | .Optional => { | ||
| 1226 | var buf: Type.Payload.ElemType = undefined; | ||
| 1227 | const payload_ty = ty.optionalChild(&buf); | ||
| 1228 | if (payload_ty.hasRuntimeBitsIgnoreComptime()) { | ||
| 1229 | if (ty.optionalReprIsPayload()) { | ||
| 1230 | try self.initType(payload_ty, kind, lookup); | ||
| 1231 | } else if (switch (kind) { | ||
| 1232 | .forward, .forward_parameter => @as(Index, undefined), | ||
| 1233 | .complete, .parameter, .global => try lookup.typeToIndex(ty, .forward), | ||
| 1234 | .payload => unreachable, | ||
| 1235 | }) |fwd_idx| { | ||
| 1236 | if (try lookup.typeToIndex(payload_ty, switch (kind) { | ||
| 1237 | .forward, .forward_parameter => .forward, | ||
| 1238 | .complete, .parameter => .complete, | ||
| 1239 | .global => .global, | ||
| 1240 | .payload => unreachable, | ||
| 1241 | })) |payload_idx| { | ||
| 1242 | self.storage = .{ .anon = undefined }; | ||
| 1243 | self.storage.anon.fields[0] = .{ | ||
| 1244 | .name = "payload", | ||
| 1245 | .type = payload_idx, | ||
| 1246 | .alignas = AlignAs.abiAlign(payload_ty, target), | ||
| 1247 | }; | ||
| 1248 | self.storage.anon.fields[1] = .{ | ||
| 1249 | .name = "is_null", | ||
| 1250 | .type = Tag.bool.toIndex(), | ||
| 1251 | .alignas = AlignAs.abiAlign(Type.bool, target), | ||
| 1252 | }; | ||
| 1253 | self.initAnon(kind, fwd_idx, 2); | ||
| 1254 | } else self.init(switch (kind) { | ||
| 1255 | .forward, .forward_parameter => .fwd_anon_struct, | ||
| 1256 | .complete, .parameter, .global => .anon_struct, | ||
| 1257 | .payload => unreachable, | ||
| 1258 | }); | ||
| 1259 | } else self.init(.anon_struct); | ||
| 1260 | } else self.init(.bool); | ||
| 1261 | }, | ||
| 1262 | |||
| 1263 | .ErrorUnion => { | ||
| 1264 | if (switch (kind) { | ||
| 1265 | .forward, .forward_parameter => @as(Index, undefined), | ||
| 1266 | .complete, .parameter, .global => try lookup.typeToIndex(ty, .forward), | ||
| 1267 | .payload => unreachable, | ||
| 1268 | }) |fwd_idx| { | ||
| 1269 | const payload_ty = ty.errorUnionPayload(); | ||
| 1270 | if (try lookup.typeToIndex(payload_ty, switch (kind) { | ||
| 1271 | .forward, .forward_parameter => .forward, | ||
| 1272 | .complete, .parameter => .complete, | ||
| 1273 | .global => .global, | ||
| 1274 | .payload => unreachable, | ||
| 1275 | })) |payload_idx| { | ||
| 1276 | const error_ty = ty.errorUnionSet(); | ||
| 1277 | if (payload_idx == Tag.void.toIndex()) { | ||
| 1278 | try self.initType(error_ty, kind, lookup); | ||
| 1279 | } else if (try lookup.typeToIndex(error_ty, kind)) |error_idx| { | ||
| 1280 | self.storage = .{ .anon = undefined }; | ||
| 1281 | self.storage.anon.fields[0] = .{ | ||
| 1282 | .name = "payload", | ||
| 1283 | .type = payload_idx, | ||
| 1284 | .alignas = AlignAs.abiAlign(payload_ty, target), | ||
| 1285 | }; | ||
| 1286 | self.storage.anon.fields[1] = .{ | ||
| 1287 | .name = "error", | ||
| 1288 | .type = error_idx, | ||
| 1289 | .alignas = AlignAs.abiAlign(error_ty, target), | ||
| 1290 | }; | ||
| 1291 | self.initAnon(kind, fwd_idx, 2); | ||
| 1292 | } else self.init(switch (kind) { | ||
| 1293 | .forward, .forward_parameter => .fwd_anon_struct, | ||
| 1294 | .complete, .parameter, .global => .anon_struct, | ||
| 1295 | .payload => unreachable, | ||
| 1296 | }); | ||
| 1297 | } else self.init(switch (kind) { | ||
| 1298 | .forward, .forward_parameter => .fwd_anon_struct, | ||
| 1299 | .complete, .parameter, .global => .anon_struct, | ||
| 1300 | .payload => unreachable, | ||
| 1301 | }); | ||
| 1302 | } else self.init(.anon_struct); | ||
| 1303 | }, | ||
| 1304 | |||
| 1305 | .Opaque => switch (ty.tag()) { | ||
| 1306 | .anyopaque => self.init(.void), | ||
| 1307 | .@"opaque" => { | ||
| 1308 | self.storage = .{ .fwd = .{ | ||
| 1309 | .base = .{ .tag = .fwd_struct }, | ||
| 1310 | .data = ty.getOwnerDecl(), | ||
| 1311 | } }; | ||
| 1312 | self.value = .{ .cty = initPayload(&self.storage.fwd) }; | ||
| 1313 | }, | ||
| 1314 | else => unreachable, | ||
| 1315 | }, | ||
| 1316 | |||
| 1317 | .Fn => { | ||
| 1318 | const info = ty.fnInfo(); | ||
| 1319 | if (!info.is_generic) { | ||
| 1320 | if (lookup.isMutable()) { | ||
| 1321 | const param_kind: Kind = switch (kind) { | ||
| 1322 | .forward, .forward_parameter => .forward_parameter, | ||
| 1323 | .complete, .parameter, .global => .parameter, | ||
| 1324 | .payload => unreachable, | ||
| 1325 | }; | ||
| 1326 | _ = try lookup.typeToIndex(info.return_type, param_kind); | ||
| 1327 | for (info.param_types) |param_type| { | ||
| 1328 | if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1329 | _ = try lookup.typeToIndex(param_type, param_kind); | ||
| 1330 | } | ||
| 1331 | } | ||
| 1332 | self.init(if (info.is_var_args) .varargs_function else .function); | ||
| 1333 | } else self.init(.void); | ||
| 1334 | }, | ||
| 1335 | } | ||
| 1336 | } | ||
| 1337 | }; | ||
| 1338 | |||
| 1339 | pub fn copy(self: CType, arena: Allocator) !CType { | ||
| 1340 | return self.copyContext(struct { | ||
| 1341 | arena: Allocator, | ||
| 1342 | pub fn copyIndex(_: @This(), idx: Index) Index { | ||
| 1343 | return idx; | ||
| 1344 | } | ||
| 1345 | }{ .arena = arena }); | ||
| 1346 | } | ||
| 1347 | |||
| 1348 | fn copyFields(ctx: anytype, old_fields: Payload.Fields.Data) !Payload.Fields.Data { | ||
| 1349 | const new_fields = try ctx.arena.alloc(Payload.Fields.Field, old_fields.len); | ||
| 1350 | for (new_fields, old_fields) |*new_field, old_field| { | ||
| 1351 | new_field.name = try ctx.arena.dupeZ(u8, mem.span(old_field.name)); | ||
| 1352 | new_field.type = ctx.copyIndex(old_field.type); | ||
| 1353 | new_field.alignas = old_field.alignas; | ||
| 1354 | } | ||
| 1355 | return new_fields; | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | fn copyParams(ctx: anytype, old_param_types: []const Index) ![]const Index { | ||
| 1359 | const new_param_types = try ctx.arena.alloc(Index, old_param_types.len); | ||
| 1360 | for (new_param_types, old_param_types) |*new_param_type, old_param_type| | ||
| 1361 | new_param_type.* = ctx.copyIndex(old_param_type); | ||
| 1362 | return new_param_types; | ||
| 1363 | } | ||
| 1364 | |||
| 1365 | pub fn copyContext(self: CType, ctx: anytype) !CType { | ||
| 1366 | switch (self.tag()) { | ||
| 1367 | .void, | ||
| 1368 | .char, | ||
| 1369 | .@"signed char", | ||
| 1370 | .short, | ||
| 1371 | .int, | ||
| 1372 | .long, | ||
| 1373 | .@"long long", | ||
| 1374 | ._Bool, | ||
| 1375 | .@"unsigned char", | ||
| 1376 | .@"unsigned short", | ||
| 1377 | .@"unsigned int", | ||
| 1378 | .@"unsigned long", | ||
| 1379 | .@"unsigned long long", | ||
| 1380 | .float, | ||
| 1381 | .double, | ||
| 1382 | .@"long double", | ||
| 1383 | .bool, | ||
| 1384 | .size_t, | ||
| 1385 | .ptrdiff_t, | ||
| 1386 | .uint8_t, | ||
| 1387 | .int8_t, | ||
| 1388 | .uint16_t, | ||
| 1389 | .int16_t, | ||
| 1390 | .uint32_t, | ||
| 1391 | .int32_t, | ||
| 1392 | .uint64_t, | ||
| 1393 | .int64_t, | ||
| 1394 | .uintptr_t, | ||
| 1395 | .intptr_t, | ||
| 1396 | .zig_u128, | ||
| 1397 | .zig_i128, | ||
| 1398 | .zig_f16, | ||
| 1399 | .zig_f32, | ||
| 1400 | .zig_f64, | ||
| 1401 | .zig_f80, | ||
| 1402 | .zig_f128, | ||
| 1403 | .zig_c_longdouble, | ||
| 1404 | => return self, | ||
| 1405 | |||
| 1406 | .pointer, | ||
| 1407 | .pointer_const, | ||
| 1408 | .pointer_volatile, | ||
| 1409 | .pointer_const_volatile, | ||
| 1410 | => { | ||
| 1411 | const pl = self.cast(Payload.Child).?; | ||
| 1412 | const new_pl = try ctx.arena.create(Payload.Child); | ||
| 1413 | new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = ctx.copyIndex(pl.data) }; | ||
| 1414 | return initPayload(new_pl); | ||
| 1415 | }, | ||
| 1416 | |||
| 1417 | .array, | ||
| 1418 | .vector, | ||
| 1419 | => { | ||
| 1420 | const pl = self.cast(Payload.Sequence).?; | ||
| 1421 | const new_pl = try ctx.arena.create(Payload.Sequence); | ||
| 1422 | new_pl.* = .{ | ||
| 1423 | .base = .{ .tag = pl.base.tag }, | ||
| 1424 | .data = .{ .len = pl.data.len, .elem_type = ctx.copyIndex(pl.data.elem_type) }, | ||
| 1425 | }; | ||
| 1426 | return initPayload(new_pl); | ||
| 1427 | }, | ||
| 1428 | |||
| 1429 | .fwd_anon_struct, | ||
| 1430 | .fwd_anon_union, | ||
| 1431 | => { | ||
| 1432 | const pl = self.cast(Payload.Fields).?; | ||
| 1433 | const new_pl = try ctx.arena.create(Payload.Fields); | ||
| 1434 | new_pl.* = .{ | ||
| 1435 | .base = .{ .tag = pl.base.tag }, | ||
| 1436 | .data = try copyFields(ctx, pl.data), | ||
| 1437 | }; | ||
| 1438 | return initPayload(new_pl); | ||
| 1439 | }, | ||
| 1440 | |||
| 1441 | .fwd_struct, | ||
| 1442 | .fwd_union, | ||
| 1443 | => { | ||
| 1444 | const pl = self.cast(Payload.FwdDecl).?; | ||
| 1445 | const new_pl = try ctx.arena.create(Payload.FwdDecl); | ||
| 1446 | new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = pl.data }; | ||
| 1447 | return initPayload(new_pl); | ||
| 1448 | }, | ||
| 1449 | |||
| 1450 | .unnamed_struct, | ||
| 1451 | .unnamed_union, | ||
| 1452 | .packed_unnamed_struct, | ||
| 1453 | .packed_unnamed_union, | ||
| 1454 | => { | ||
| 1455 | const pl = self.cast(Payload.Unnamed).?; | ||
| 1456 | const new_pl = try ctx.arena.create(Payload.Unnamed); | ||
| 1457 | new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{ | ||
| 1458 | .fields = try copyFields(ctx, pl.data.fields), | ||
| 1459 | .owner_decl = pl.data.owner_decl, | ||
| 1460 | .id = pl.data.id, | ||
| 1461 | } }; | ||
| 1462 | return initPayload(new_pl); | ||
| 1463 | }, | ||
| 1464 | |||
| 1465 | .anon_struct, | ||
| 1466 | .anon_union, | ||
| 1467 | .@"struct", | ||
| 1468 | .@"union", | ||
| 1469 | .packed_struct, | ||
| 1470 | .packed_union, | ||
| 1471 | => { | ||
| 1472 | const pl = self.cast(Payload.Aggregate).?; | ||
| 1473 | const new_pl = try ctx.arena.create(Payload.Aggregate); | ||
| 1474 | new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{ | ||
| 1475 | .fields = try copyFields(ctx, pl.data.fields), | ||
| 1476 | .fwd_decl = ctx.copyIndex(pl.data.fwd_decl), | ||
| 1477 | } }; | ||
| 1478 | return initPayload(new_pl); | ||
| 1479 | }, | ||
| 1480 | |||
| 1481 | .function, | ||
| 1482 | .varargs_function, | ||
| 1483 | => { | ||
| 1484 | const pl = self.cast(Payload.Function).?; | ||
| 1485 | const new_pl = try ctx.arena.create(Payload.Function); | ||
| 1486 | new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{ | ||
| 1487 | .return_type = ctx.copyIndex(pl.data.return_type), | ||
| 1488 | .param_types = try copyParams(ctx, pl.data.param_types), | ||
| 1489 | } }; | ||
| 1490 | return initPayload(new_pl); | ||
| 1491 | }, | ||
| 1492 | } | ||
| 1493 | } | ||
| 1494 | |||
| 1495 | fn createFromType(store: *Store.Promoted, ty: Type, target: Target, kind: Kind) !CType { | ||
| 1496 | var convert: Convert = undefined; | ||
| 1497 | try convert.initType(ty, kind, .{ .imm = .{ .set = &store.set, .target = target } }); | ||
| 1498 | return createFromConvert(store, ty, target, kind, &convert); | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | fn createFromConvert( | ||
| 1502 | store: *Store.Promoted, | ||
| 1503 | ty: Type, | ||
| 1504 | target: Target, | ||
| 1505 | kind: Kind, | ||
| 1506 | convert: Convert, | ||
| 1507 | ) !CType { | ||
| 1508 | const arena = store.arena.allocator(); | ||
| 1509 | switch (convert.value) { | ||
| 1510 | .cty => |c| return c.copy(arena), | ||
| 1511 | .tag => |t| switch (t) { | ||
| 1512 | .fwd_anon_struct, | ||
| 1513 | .fwd_anon_union, | ||
| 1514 | .unnamed_struct, | ||
| 1515 | .unnamed_union, | ||
| 1516 | .packed_unnamed_struct, | ||
| 1517 | .packed_unnamed_union, | ||
| 1518 | .anon_struct, | ||
| 1519 | .anon_union, | ||
| 1520 | .@"struct", | ||
| 1521 | .@"union", | ||
| 1522 | .packed_struct, | ||
| 1523 | .packed_union, | ||
| 1524 | => { | ||
| 1525 | const zig_ty_tag = ty.zigTypeTag(); | ||
| 1526 | const fields_len = switch (zig_ty_tag) { | ||
| 1527 | .Struct => ty.structFieldCount(), | ||
| 1528 | .Union => ty.unionFields().count(), | ||
| 1529 | else => unreachable, | ||
| 1530 | }; | ||
| 1531 | |||
| 1532 | var c_fields_len: usize = 0; | ||
| 1533 | for (0..fields_len) |field_i| { | ||
| 1534 | const field_ty = ty.structFieldType(field_i); | ||
| 1535 | if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or | ||
| 1536 | !field_ty.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1537 | c_fields_len += 1; | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); | ||
| 1541 | var c_field_i: usize = 0; | ||
| 1542 | for (0..fields_len) |field_i| { | ||
| 1543 | const field_ty = ty.structFieldType(field_i); | ||
| 1544 | if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or | ||
| 1545 | !field_ty.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1546 | |||
| 1547 | defer c_field_i += 1; | ||
| 1548 | fields_pl[c_field_i] = .{ | ||
| 1549 | .name = try if (ty.isSimpleTuple()) | ||
| 1550 | std.fmt.allocPrintZ(arena, "f{}", .{field_i}) | ||
| 1551 | else | ||
| 1552 | arena.dupeZ(u8, switch (zig_ty_tag) { | ||
| 1553 | .Struct => ty.structFieldName(field_i), | ||
| 1554 | .Union => ty.unionFields().keys()[field_i], | ||
| 1555 | else => unreachable, | ||
| 1556 | }), | ||
| 1557 | .type = store.set.typeToIndex(field_ty, target, switch (kind) { | ||
| 1558 | .forward, .forward_parameter => .forward, | ||
| 1559 | .complete, .parameter, .payload => .complete, | ||
| 1560 | .global => .global, | ||
| 1561 | }).?, | ||
| 1562 | .alignas = AlignAs.fieldAlign(ty, field_i, target), | ||
| 1563 | }; | ||
| 1564 | } | ||
| 1565 | |||
| 1566 | switch (t) { | ||
| 1567 | .fwd_anon_struct, | ||
| 1568 | .fwd_anon_union, | ||
| 1569 | => { | ||
| 1570 | const anon_pl = try arena.create(Payload.Fields); | ||
| 1571 | anon_pl.* = .{ .base = .{ .tag = t }, .data = fields_pl }; | ||
| 1572 | return initPayload(anon_pl); | ||
| 1573 | }, | ||
| 1574 | |||
| 1575 | .unnamed_struct, | ||
| 1576 | .unnamed_union, | ||
| 1577 | .packed_unnamed_struct, | ||
| 1578 | .packed_unnamed_union, | ||
| 1579 | => { | ||
| 1580 | const unnamed_pl = try arena.create(Payload.Unnamed); | ||
| 1581 | unnamed_pl.* = .{ .base = .{ .tag = t }, .data = .{ | ||
| 1582 | .fields = fields_pl, | ||
| 1583 | .owner_decl = ty.getOwnerDecl(), | ||
| 1584 | .id = if (ty.unionTagTypeSafety()) |_| 0 else unreachable, | ||
| 1585 | } }; | ||
| 1586 | return initPayload(unnamed_pl); | ||
| 1587 | }, | ||
| 1588 | |||
| 1589 | .anon_struct, | ||
| 1590 | .anon_union, | ||
| 1591 | .@"struct", | ||
| 1592 | .@"union", | ||
| 1593 | .packed_struct, | ||
| 1594 | .packed_union, | ||
| 1595 | => { | ||
| 1596 | const struct_pl = try arena.create(Payload.Aggregate); | ||
| 1597 | struct_pl.* = .{ .base = .{ .tag = t }, .data = .{ | ||
| 1598 | .fields = fields_pl, | ||
| 1599 | .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, | ||
| 1600 | } }; | ||
| 1601 | return initPayload(struct_pl); | ||
| 1602 | }, | ||
| 1603 | |||
| 1604 | else => unreachable, | ||
| 1605 | } | ||
| 1606 | }, | ||
| 1607 | |||
| 1608 | .function, | ||
| 1609 | .varargs_function, | ||
| 1610 | => { | ||
| 1611 | const info = ty.fnInfo(); | ||
| 1612 | assert(!info.is_generic); | ||
| 1613 | const param_kind: Kind = switch (kind) { | ||
| 1614 | .forward, .forward_parameter => .forward_parameter, | ||
| 1615 | .complete, .parameter, .global => .parameter, | ||
| 1616 | .payload => unreachable, | ||
| 1617 | }; | ||
| 1618 | |||
| 1619 | var c_params_len: usize = 0; | ||
| 1620 | for (info.param_types) |param_type| { | ||
| 1621 | if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1622 | c_params_len += 1; | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | const params_pl = try arena.alloc(Index, c_params_len); | ||
| 1626 | var c_param_i: usize = 0; | ||
| 1627 | for (info.param_types) |param_type| { | ||
| 1628 | if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1629 | params_pl[c_param_i] = store.set.typeToIndex(param_type, target, param_kind).?; | ||
| 1630 | c_param_i += 1; | ||
| 1631 | } | ||
| 1632 | |||
| 1633 | const fn_pl = try arena.create(Payload.Function); | ||
| 1634 | fn_pl.* = .{ .base = .{ .tag = t }, .data = .{ | ||
| 1635 | .return_type = store.set.typeToIndex(info.return_type, target, param_kind).?, | ||
| 1636 | .param_types = params_pl, | ||
| 1637 | } }; | ||
| 1638 | return initPayload(fn_pl); | ||
| 1639 | }, | ||
| 1640 | |||
| 1641 | else => unreachable, | ||
| 1642 | }, | ||
| 1643 | } | ||
| 1644 | } | ||
| 1645 | |||
| 1646 | pub const TypeAdapter64 = struct { | ||
| 1647 | kind: Kind, | ||
| 1648 | lookup: Convert.Lookup, | ||
| 1649 | convert: *const Convert, | ||
| 1650 | |||
| 1651 | fn eqlRecurse(self: @This(), ty: Type, cty: Index, kind: Kind) bool { | ||
| 1652 | assert(!self.lookup.isMutable()); | ||
| 1653 | |||
| 1654 | var convert: Convert = undefined; | ||
| 1655 | convert.initType(ty, kind, self.lookup) catch unreachable; | ||
| 1656 | |||
| 1657 | const self_recurse = @This(){ .kind = kind, .lookup = self.lookup, .convert = &convert }; | ||
| 1658 | return self_recurse.eql(ty, self.lookup.indexToCType(cty).?); | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | pub fn eql(self: @This(), ty: Type, cty: CType) bool { | ||
| 1662 | switch (self.convert.value) { | ||
| 1663 | .cty => |c| return c.eql(cty), | ||
| 1664 | .tag => |t| { | ||
| 1665 | if (t != cty.tag()) return false; | ||
| 1666 | |||
| 1667 | const target = self.lookup.getTarget(); | ||
| 1668 | switch (t) { | ||
| 1669 | .fwd_anon_struct, | ||
| 1670 | .fwd_anon_union, | ||
| 1671 | => { | ||
| 1672 | if (!ty.isTupleOrAnonStruct()) return false; | ||
| 1673 | |||
| 1674 | var name_buf: [ | ||
| 1675 | std.fmt.count("f{}", .{std.math.maxInt(usize)}) | ||
| 1676 | ]u8 = undefined; | ||
| 1677 | const c_fields = cty.cast(Payload.Fields).?.data; | ||
| 1678 | |||
| 1679 | const zig_ty_tag = ty.zigTypeTag(); | ||
| 1680 | var c_field_i: usize = 0; | ||
| 1681 | for (0..switch (zig_ty_tag) { | ||
| 1682 | .Struct => ty.structFieldCount(), | ||
| 1683 | .Union => ty.unionFields().count(), | ||
| 1684 | else => unreachable, | ||
| 1685 | }) |field_i| { | ||
| 1686 | const field_ty = ty.structFieldType(field_i); | ||
| 1687 | if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or | ||
| 1688 | !field_ty.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1689 | |||
| 1690 | defer c_field_i += 1; | ||
| 1691 | const c_field = &c_fields[c_field_i]; | ||
| 1692 | |||
| 1693 | if (!self.eqlRecurse(field_ty, c_field.type, switch (self.kind) { | ||
| 1694 | .forward, .forward_parameter => .forward, | ||
| 1695 | .complete, .parameter => .complete, | ||
| 1696 | .global => .global, | ||
| 1697 | .payload => unreachable, | ||
| 1698 | }) or !mem.eql( | ||
| 1699 | u8, | ||
| 1700 | if (ty.isSimpleTuple()) | ||
| 1701 | std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable | ||
| 1702 | else switch (zig_ty_tag) { | ||
| 1703 | .Struct => ty.structFieldName(field_i), | ||
| 1704 | .Union => ty.unionFields().keys()[field_i], | ||
| 1705 | else => unreachable, | ||
| 1706 | }, | ||
| 1707 | mem.span(c_field.name), | ||
| 1708 | ) or AlignAs.fieldAlign(ty, field_i, target).@"align" != | ||
| 1709 | c_field.alignas.@"align") return false; | ||
| 1710 | } | ||
| 1711 | return true; | ||
| 1712 | }, | ||
| 1713 | |||
| 1714 | .unnamed_struct, | ||
| 1715 | .unnamed_union, | ||
| 1716 | .packed_unnamed_struct, | ||
| 1717 | .packed_unnamed_union, | ||
| 1718 | => switch (self.kind) { | ||
| 1719 | .forward, .forward_parameter, .complete, .parameter, .global => unreachable, | ||
| 1720 | .payload => if (ty.unionTagTypeSafety()) |_| { | ||
| 1721 | const data = cty.cast(Payload.Unnamed).?.data; | ||
| 1722 | return ty.getOwnerDecl() == data.owner_decl and data.id == 0; | ||
| 1723 | } else unreachable, | ||
| 1724 | }, | ||
| 1725 | |||
| 1726 | .anon_struct, | ||
| 1727 | .anon_union, | ||
| 1728 | .@"struct", | ||
| 1729 | .@"union", | ||
| 1730 | .packed_struct, | ||
| 1731 | .packed_union, | ||
| 1732 | => return self.eqlRecurse( | ||
| 1733 | ty, | ||
| 1734 | cty.cast(Payload.Aggregate).?.data.fwd_decl, | ||
| 1735 | .forward, | ||
| 1736 | ), | ||
| 1737 | |||
| 1738 | .function, | ||
| 1739 | .varargs_function, | ||
| 1740 | => { | ||
| 1741 | if (ty.zigTypeTag() != .Fn) return false; | ||
| 1742 | |||
| 1743 | const info = ty.fnInfo(); | ||
| 1744 | assert(!info.is_generic); | ||
| 1745 | const data = cty.cast(Payload.Function).?.data; | ||
| 1746 | const param_kind: Kind = switch (self.kind) { | ||
| 1747 | .forward, .forward_parameter => .forward_parameter, | ||
| 1748 | .complete, .parameter, .global => .parameter, | ||
| 1749 | .payload => unreachable, | ||
| 1750 | }; | ||
| 1751 | |||
| 1752 | if (!self.eqlRecurse(info.return_type, data.return_type, param_kind)) | ||
| 1753 | return false; | ||
| 1754 | |||
| 1755 | var c_param_i: usize = 0; | ||
| 1756 | for (info.param_types) |param_type| { | ||
| 1757 | if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1758 | |||
| 1759 | if (c_param_i >= data.param_types.len) return false; | ||
| 1760 | const param_cty = data.param_types[c_param_i]; | ||
| 1761 | c_param_i += 1; | ||
| 1762 | |||
| 1763 | if (!self.eqlRecurse(param_type, param_cty, param_kind)) | ||
| 1764 | return false; | ||
| 1765 | } | ||
| 1766 | return c_param_i == data.param_types.len; | ||
| 1767 | }, | ||
| 1768 | |||
| 1769 | else => unreachable, | ||
| 1770 | } | ||
| 1771 | }, | ||
| 1772 | } | ||
| 1773 | } | ||
| 1774 | |||
| 1775 | pub fn hash(self: @This(), ty: Type) u64 { | ||
| 1776 | var hasher = std.hash.Wyhash.init(0); | ||
| 1777 | self.updateHasher(&hasher, ty); | ||
| 1778 | return hasher.final(); | ||
| 1779 | } | ||
| 1780 | |||
| 1781 | fn updateHasherRecurse(self: @This(), hasher: anytype, ty: Type, kind: Kind) void { | ||
| 1782 | assert(!self.lookup.isMutable()); | ||
| 1783 | |||
| 1784 | var convert: Convert = undefined; | ||
| 1785 | convert.initType(ty, kind, self.lookup) catch unreachable; | ||
| 1786 | |||
| 1787 | const self_recurse = @This(){ .kind = kind, .lookup = self.lookup, .convert = &convert }; | ||
| 1788 | self_recurse.updateHasher(hasher, ty); | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | pub fn updateHasher(self: @This(), hasher: anytype, ty: Type) void { | ||
| 1792 | switch (self.convert.value) { | ||
| 1793 | .cty => |c| return c.updateHasher(hasher, self.lookup.getSet().?.*), | ||
| 1794 | .tag => |t| { | ||
| 1795 | autoHash(hasher, t); | ||
| 1796 | |||
| 1797 | const target = self.lookup.getTarget(); | ||
| 1798 | switch (t) { | ||
| 1799 | .fwd_anon_struct, | ||
| 1800 | .fwd_anon_union, | ||
| 1801 | => { | ||
| 1802 | var name_buf: [ | ||
| 1803 | std.fmt.count("f{}", .{std.math.maxInt(usize)}) | ||
| 1804 | ]u8 = undefined; | ||
| 1805 | |||
| 1806 | const zig_ty_tag = ty.zigTypeTag(); | ||
| 1807 | for (0..switch (ty.zigTypeTag()) { | ||
| 1808 | .Struct => ty.structFieldCount(), | ||
| 1809 | .Union => ty.unionFields().count(), | ||
| 1810 | else => unreachable, | ||
| 1811 | }) |field_i| { | ||
| 1812 | const field_ty = ty.structFieldType(field_i); | ||
| 1813 | if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or | ||
| 1814 | !field_ty.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1815 | |||
| 1816 | self.updateHasherRecurse(hasher, field_ty, switch (self.kind) { | ||
| 1817 | .forward, .forward_parameter => .forward, | ||
| 1818 | .complete, .parameter => .complete, | ||
| 1819 | .global => .global, | ||
| 1820 | .payload => unreachable, | ||
| 1821 | }); | ||
| 1822 | hasher.update(if (ty.isSimpleTuple()) | ||
| 1823 | std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable | ||
| 1824 | else switch (zig_ty_tag) { | ||
| 1825 | .Struct => ty.structFieldName(field_i), | ||
| 1826 | .Union => ty.unionFields().keys()[field_i], | ||
| 1827 | else => unreachable, | ||
| 1828 | }); | ||
| 1829 | autoHash(hasher, AlignAs.fieldAlign(ty, field_i, target).@"align"); | ||
| 1830 | } | ||
| 1831 | }, | ||
| 1832 | |||
| 1833 | .unnamed_struct, | ||
| 1834 | .unnamed_union, | ||
| 1835 | .packed_unnamed_struct, | ||
| 1836 | .packed_unnamed_union, | ||
| 1837 | => switch (self.kind) { | ||
| 1838 | .forward, .forward_parameter, .complete, .parameter, .global => unreachable, | ||
| 1839 | .payload => if (ty.unionTagTypeSafety()) |_| { | ||
| 1840 | autoHash(hasher, ty.getOwnerDecl()); | ||
| 1841 | autoHash(hasher, @as(u32, 0)); | ||
| 1842 | } else unreachable, | ||
| 1843 | }, | ||
| 1844 | |||
| 1845 | .anon_struct, | ||
| 1846 | .anon_union, | ||
| 1847 | .@"struct", | ||
| 1848 | .@"union", | ||
| 1849 | .packed_struct, | ||
| 1850 | .packed_union, | ||
| 1851 | => self.updateHasherRecurse(hasher, ty, .forward), | ||
| 1852 | |||
| 1853 | .function, | ||
| 1854 | .varargs_function, | ||
| 1855 | => { | ||
| 1856 | const info = ty.fnInfo(); | ||
| 1857 | assert(!info.is_generic); | ||
| 1858 | const param_kind: Kind = switch (self.kind) { | ||
| 1859 | .forward, .forward_parameter => .forward_parameter, | ||
| 1860 | .complete, .parameter, .global => .parameter, | ||
| 1861 | .payload => unreachable, | ||
| 1862 | }; | ||
| 1863 | |||
| 1864 | self.updateHasherRecurse(hasher, info.return_type, param_kind); | ||
| 1865 | for (info.param_types) |param_type| { | ||
| 1866 | if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; | ||
| 1867 | self.updateHasherRecurse(hasher, param_type, param_kind); | ||
| 1868 | } | ||
| 1869 | }, | ||
| 1870 | |||
| 1871 | else => unreachable, | ||
| 1872 | } | ||
| 1873 | }, | ||
| 1874 | } | ||
| 1875 | } | ||
| 1876 | }; | ||
| 1877 | |||
| 1878 | pub const TypeAdapter32 = struct { | ||
| 1879 | kind: Kind, | ||
| 1880 | lookup: Convert.Lookup, | ||
| 1881 | convert: *const Convert, | ||
| 1882 | |||
| 1883 | fn to64(self: @This()) TypeAdapter64 { | ||
| 1884 | return .{ .kind = self.kind, .lookup = self.lookup, .convert = self.convert }; | ||
| 1885 | } | ||
| 1886 | |||
| 1887 | pub fn eql(self: @This(), ty: Type, cty: CType, cty_index: usize) bool { | ||
| 1888 | _ = cty_index; | ||
| 1889 | return self.to64().eql(ty, cty); | ||
| 1890 | } | ||
| 1891 | |||
| 1892 | pub fn hash(self: @This(), ty: Type) u32 { | ||
| 1893 | return @truncate(u32, self.to64().hash(ty)); | ||
| 1894 | } | ||
| 1895 | }; | ||
| 1896 | }; | ||
src/codegen/llvm.zig+2-2| ... | @@ -6025,8 +6025,8 @@ pub const FuncGen = struct { | ... | @@ -6025,8 +6025,8 @@ pub const FuncGen = struct { |
| 6025 | const field_ptr = try self.resolveInst(extra.field_ptr); | 6025 | const field_ptr = try self.resolveInst(extra.field_ptr); |
| 6026 | 6026 | ||
| 6027 | const target = self.dg.module.getTarget(); | 6027 | const target = self.dg.module.getTarget(); |
| 6028 | const struct_ty = self.air.getRefType(ty_pl.ty).childType(); | 6028 | const parent_ty = self.air.getRefType(ty_pl.ty).childType(); |
| 6029 | const field_offset = struct_ty.structFieldOffset(extra.field_index, target); | 6029 | const field_offset = parent_ty.structFieldOffset(extra.field_index, target); |
| 6030 | 6030 | ||
| 6031 | const res_ty = try self.dg.lowerType(self.air.getRefType(ty_pl.ty)); | 6031 | const res_ty = try self.dg.lowerType(self.air.getRefType(ty_pl.ty)); |
| 6032 | if (field_offset == 0) { | 6032 | if (field_offset == 0) { |
src/link/C.zig+246-131| ... | @@ -22,27 +22,22 @@ base: link.File, | ... | @@ -22,27 +22,22 @@ base: link.File, |
| 22 | /// Instead, it tracks all declarations in this table, and iterates over it | 22 | /// Instead, it tracks all declarations in this table, and iterates over it |
| 23 | /// in the flush function, stitching pre-rendered pieces of C code together. | 23 | /// in the flush function, stitching pre-rendered pieces of C code together. |
| 24 | decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclBlock) = .{}, | 24 | decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclBlock) = .{}, |
| 25 | /// Stores Type/Value data for `typedefs` to reference. | ||
| 26 | /// Accumulates allocations and then there is a periodic garbage collection after flush(). | ||
| 27 | arena: std.heap.ArenaAllocator, | ||
| 28 | 25 | ||
| 29 | /// Per-declaration data. | 26 | /// Per-declaration data. |
| 30 | const DeclBlock = struct { | 27 | const DeclBlock = struct { |
| 31 | code: std.ArrayListUnmanaged(u8) = .{}, | 28 | code: std.ArrayListUnmanaged(u8) = .{}, |
| 32 | fwd_decl: std.ArrayListUnmanaged(u8) = .{}, | 29 | fwd_decl: std.ArrayListUnmanaged(u8) = .{}, |
| 33 | /// Each Decl stores a mapping of Zig Types to corresponding C types, for every | 30 | /// Each `Decl` stores a set of used `CType`s. In `flush()`, we iterate |
| 34 | /// Zig Type used by the Decl. In flush(), we iterate over each Decl | 31 | /// over each `Decl` and generate the definition for each used `CType` once. |
| 35 | /// and emit the typedef code for all types, making sure to not emit the same thing twice. | 32 | ctypes: codegen.CType.Store = .{}, |
| 36 | /// Any arena memory the Type points to lives in the `arena` field of `C`. | 33 | /// Key and Value storage use the ctype arena. |
| 37 | typedefs: codegen.TypedefMap.Unmanaged = .{}, | 34 | lazy_fns: codegen.LazyFnMap = .{}, |
| 38 | 35 | ||
| 39 | fn deinit(db: *DeclBlock, gpa: Allocator) void { | 36 | fn deinit(db: *DeclBlock, gpa: Allocator) void { |
| 40 | db.code.deinit(gpa); | 37 | db.lazy_fns.deinit(gpa); |
| 38 | db.ctypes.deinit(gpa); | ||
| 41 | db.fwd_decl.deinit(gpa); | 39 | db.fwd_decl.deinit(gpa); |
| 42 | for (db.typedefs.values()) |typedef| { | 40 | db.code.deinit(gpa); |
| 43 | gpa.free(typedef.rendered); | ||
| 44 | } | ||
| 45 | db.typedefs.deinit(gpa); | ||
| 46 | db.* = undefined; | 41 | db.* = undefined; |
| 47 | } | 42 | } |
| 48 | }; | 43 | }; |
| ... | @@ -64,7 +59,6 @@ pub fn openPath(gpa: Allocator, sub_path: []const u8, options: link.Options) !*C | ... | @@ -64,7 +59,6 @@ pub fn openPath(gpa: Allocator, sub_path: []const u8, options: link.Options) !*C |
| 64 | errdefer gpa.destroy(c_file); | 59 | errdefer gpa.destroy(c_file); |
| 65 | 60 | ||
| 66 | c_file.* = C{ | 61 | c_file.* = C{ |
| 67 | .arena = std.heap.ArenaAllocator.init(gpa), | ||
| 68 | .base = .{ | 62 | .base = .{ |
| 69 | .tag = .c, | 63 | .tag = .c, |
| 70 | .options = options, | 64 | .options = options, |
| ... | @@ -83,8 +77,6 @@ pub fn deinit(self: *C) void { | ... | @@ -83,8 +77,6 @@ pub fn deinit(self: *C) void { |
| 83 | db.deinit(gpa); | 77 | db.deinit(gpa); |
| 84 | } | 78 | } |
| 85 | self.decl_table.deinit(gpa); | 79 | self.decl_table.deinit(gpa); |
| 86 | |||
| 87 | self.arena.deinit(); | ||
| 88 | } | 80 | } |
| 89 | 81 | ||
| 90 | pub fn freeDecl(self: *C, decl_index: Module.Decl.Index) void { | 82 | pub fn freeDecl(self: *C, decl_index: Module.Decl.Index) void { |
| ... | @@ -99,124 +91,122 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes | ... | @@ -99,124 +91,122 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes |
| 99 | const tracy = trace(@src()); | 91 | const tracy = trace(@src()); |
| 100 | defer tracy.end(); | 92 | defer tracy.end(); |
| 101 | 93 | ||
| 94 | const gpa = self.base.allocator; | ||
| 95 | |||
| 102 | const decl_index = func.owner_decl; | 96 | const decl_index = func.owner_decl; |
| 103 | const gop = try self.decl_table.getOrPut(self.base.allocator, decl_index); | 97 | const gop = try self.decl_table.getOrPut(gpa, decl_index); |
| 104 | if (!gop.found_existing) { | 98 | if (!gop.found_existing) { |
| 105 | gop.value_ptr.* = .{}; | 99 | gop.value_ptr.* = .{}; |
| 106 | } | 100 | } |
| 101 | const ctypes = &gop.value_ptr.ctypes; | ||
| 102 | const lazy_fns = &gop.value_ptr.lazy_fns; | ||
| 107 | const fwd_decl = &gop.value_ptr.fwd_decl; | 103 | const fwd_decl = &gop.value_ptr.fwd_decl; |
| 108 | const typedefs = &gop.value_ptr.typedefs; | ||
| 109 | const code = &gop.value_ptr.code; | 104 | const code = &gop.value_ptr.code; |
| 105 | ctypes.clearRetainingCapacity(gpa); | ||
| 106 | lazy_fns.clearRetainingCapacity(); | ||
| 110 | fwd_decl.shrinkRetainingCapacity(0); | 107 | fwd_decl.shrinkRetainingCapacity(0); |
| 111 | for (typedefs.values()) |typedef| { | ||
| 112 | module.gpa.free(typedef.rendered); | ||
| 113 | } | ||
| 114 | typedefs.clearRetainingCapacity(); | ||
| 115 | code.shrinkRetainingCapacity(0); | 108 | code.shrinkRetainingCapacity(0); |
| 116 | 109 | ||
| 117 | var function: codegen.Function = .{ | 110 | var function: codegen.Function = .{ |
| 118 | .value_map = codegen.CValueMap.init(module.gpa), | 111 | .value_map = codegen.CValueMap.init(gpa), |
| 119 | .air = air, | 112 | .air = air, |
| 120 | .liveness = liveness, | 113 | .liveness = liveness, |
| 121 | .func = func, | 114 | .func = func, |
| 122 | .object = .{ | 115 | .object = .{ |
| 123 | .dg = .{ | 116 | .dg = .{ |
| 124 | .gpa = module.gpa, | 117 | .gpa = gpa, |
| 125 | .module = module, | 118 | .module = module, |
| 126 | .error_msg = null, | 119 | .error_msg = null, |
| 127 | .decl_index = decl_index, | 120 | .decl_index = decl_index.toOptional(), |
| 128 | .decl = module.declPtr(decl_index), | 121 | .decl = module.declPtr(decl_index), |
| 129 | .fwd_decl = fwd_decl.toManaged(module.gpa), | 122 | .fwd_decl = fwd_decl.toManaged(gpa), |
| 130 | .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), | 123 | .ctypes = ctypes.*, |
| 131 | .typedefs_arena = self.arena.allocator(), | ||
| 132 | }, | 124 | }, |
| 133 | .code = code.toManaged(module.gpa), | 125 | .code = code.toManaged(gpa), |
| 134 | .indent_writer = undefined, // set later so we can get a pointer to object.code | 126 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 135 | }, | 127 | }, |
| 136 | .arena = std.heap.ArenaAllocator.init(module.gpa), | 128 | .lazy_fns = lazy_fns.*, |
| 129 | .arena = std.heap.ArenaAllocator.init(gpa), | ||
| 137 | }; | 130 | }; |
| 138 | 131 | ||
| 139 | function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; | 132 | function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; |
| 140 | defer function.deinit(module.gpa); | 133 | defer function.deinit(); |
| 141 | 134 | ||
| 142 | codegen.genFunc(&function) catch |err| switch (err) { | 135 | codegen.genFunc(&function) catch |err| switch (err) { |
| 143 | error.AnalysisFail => { | 136 | error.AnalysisFail => { |
| 144 | try module.failed_decls.put(module.gpa, decl_index, function.object.dg.error_msg.?); | 137 | try module.failed_decls.put(gpa, decl_index, function.object.dg.error_msg.?); |
| 145 | return; | 138 | return; |
| 146 | }, | 139 | }, |
| 147 | else => |e| return e, | 140 | else => |e| return e, |
| 148 | }; | 141 | }; |
| 149 | 142 | ||
| 143 | ctypes.* = function.object.dg.ctypes.move(); | ||
| 144 | lazy_fns.* = function.lazy_fns.move(); | ||
| 150 | fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged(); | 145 | fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged(); |
| 151 | typedefs.* = function.object.dg.typedefs.unmanaged; | ||
| 152 | function.object.dg.typedefs.unmanaged = .{}; | ||
| 153 | code.* = function.object.code.moveToUnmanaged(); | 146 | code.* = function.object.code.moveToUnmanaged(); |
| 154 | 147 | ||
| 155 | // Free excess allocated memory for this Decl. | 148 | // Free excess allocated memory for this Decl. |
| 156 | fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len); | 149 | ctypes.shrinkAndFree(gpa, ctypes.count()); |
| 157 | code.shrinkAndFree(module.gpa, code.items.len); | 150 | lazy_fns.shrinkAndFree(gpa, lazy_fns.count()); |
| 151 | fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len); | ||
| 152 | code.shrinkAndFree(gpa, code.items.len); | ||
| 158 | } | 153 | } |
| 159 | 154 | ||
| 160 | pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { | 155 | pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { |
| 161 | const tracy = trace(@src()); | 156 | const tracy = trace(@src()); |
| 162 | defer tracy.end(); | 157 | defer tracy.end(); |
| 163 | 158 | ||
| 164 | const gop = try self.decl_table.getOrPut(self.base.allocator, decl_index); | 159 | const gpa = self.base.allocator; |
| 160 | |||
| 161 | const gop = try self.decl_table.getOrPut(gpa, decl_index); | ||
| 165 | if (!gop.found_existing) { | 162 | if (!gop.found_existing) { |
| 166 | gop.value_ptr.* = .{}; | 163 | gop.value_ptr.* = .{}; |
| 167 | } | 164 | } |
| 165 | const ctypes = &gop.value_ptr.ctypes; | ||
| 168 | const fwd_decl = &gop.value_ptr.fwd_decl; | 166 | const fwd_decl = &gop.value_ptr.fwd_decl; |
| 169 | const typedefs = &gop.value_ptr.typedefs; | ||
| 170 | const code = &gop.value_ptr.code; | 167 | const code = &gop.value_ptr.code; |
| 168 | ctypes.clearRetainingCapacity(gpa); | ||
| 171 | fwd_decl.shrinkRetainingCapacity(0); | 169 | fwd_decl.shrinkRetainingCapacity(0); |
| 172 | for (typedefs.values()) |value| { | ||
| 173 | module.gpa.free(value.rendered); | ||
| 174 | } | ||
| 175 | typedefs.clearRetainingCapacity(); | ||
| 176 | code.shrinkRetainingCapacity(0); | 170 | code.shrinkRetainingCapacity(0); |
| 177 | 171 | ||
| 178 | const decl = module.declPtr(decl_index); | 172 | const decl = module.declPtr(decl_index); |
| 179 | 173 | ||
| 180 | var object: codegen.Object = .{ | 174 | var object: codegen.Object = .{ |
| 181 | .dg = .{ | 175 | .dg = .{ |
| 182 | .gpa = module.gpa, | 176 | .gpa = gpa, |
| 183 | .module = module, | 177 | .module = module, |
| 184 | .error_msg = null, | 178 | .error_msg = null, |
| 185 | .decl_index = decl_index, | 179 | .decl_index = decl_index.toOptional(), |
| 186 | .decl = decl, | 180 | .decl = decl, |
| 187 | .fwd_decl = fwd_decl.toManaged(module.gpa), | 181 | .fwd_decl = fwd_decl.toManaged(gpa), |
| 188 | .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), | 182 | .ctypes = ctypes.*, |
| 189 | .typedefs_arena = self.arena.allocator(), | ||
| 190 | }, | 183 | }, |
| 191 | .code = code.toManaged(module.gpa), | 184 | .code = code.toManaged(gpa), |
| 192 | .indent_writer = undefined, // set later so we can get a pointer to object.code | 185 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 193 | }; | 186 | }; |
| 194 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; | 187 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; |
| 195 | defer { | 188 | defer { |
| 196 | object.code.deinit(); | 189 | object.code.deinit(); |
| 197 | for (object.dg.typedefs.values()) |typedef| { | 190 | object.dg.ctypes.deinit(object.dg.gpa); |
| 198 | module.gpa.free(typedef.rendered); | ||
| 199 | } | ||
| 200 | object.dg.typedefs.deinit(); | ||
| 201 | object.dg.fwd_decl.deinit(); | 191 | object.dg.fwd_decl.deinit(); |
| 202 | } | 192 | } |
| 203 | 193 | ||
| 204 | codegen.genDecl(&object) catch |err| switch (err) { | 194 | codegen.genDecl(&object) catch |err| switch (err) { |
| 205 | error.AnalysisFail => { | 195 | error.AnalysisFail => { |
| 206 | try module.failed_decls.put(module.gpa, decl_index, object.dg.error_msg.?); | 196 | try module.failed_decls.put(gpa, decl_index, object.dg.error_msg.?); |
| 207 | return; | 197 | return; |
| 208 | }, | 198 | }, |
| 209 | else => |e| return e, | 199 | else => |e| return e, |
| 210 | }; | 200 | }; |
| 211 | 201 | ||
| 202 | ctypes.* = object.dg.ctypes.move(); | ||
| 212 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); | 203 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); |
| 213 | typedefs.* = object.dg.typedefs.unmanaged; | ||
| 214 | object.dg.typedefs.unmanaged = .{}; | ||
| 215 | code.* = object.code.moveToUnmanaged(); | 204 | code.* = object.code.moveToUnmanaged(); |
| 216 | 205 | ||
| 217 | // Free excess allocated memory for this Decl. | 206 | // Free excess allocated memory for this Decl. |
| 218 | fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len); | 207 | ctypes.shrinkAndFree(gpa, ctypes.count()); |
| 219 | code.shrinkAndFree(module.gpa, code.items.len); | 208 | fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len); |
| 209 | code.shrinkAndFree(gpa, code.items.len); | ||
| 220 | } | 210 | } |
| 221 | 211 | ||
| 222 | pub fn updateDeclLineNumber(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { | 212 | pub fn updateDeclLineNumber(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { |
| ... | @@ -246,7 +236,7 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) | ... | @@ -246,7 +236,7 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) |
| 246 | sub_prog_node.activate(); | 236 | sub_prog_node.activate(); |
| 247 | defer sub_prog_node.end(); | 237 | defer sub_prog_node.end(); |
| 248 | 238 | ||
| 249 | const gpa = comp.gpa; | 239 | const gpa = self.base.allocator; |
| 250 | const module = self.base.options.module.?; | 240 | const module = self.base.options.module.?; |
| 251 | 241 | ||
| 252 | // This code path happens exclusively with -ofmt=c. The flush logic for | 242 | // This code path happens exclusively with -ofmt=c. The flush logic for |
| ... | @@ -257,30 +247,28 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) | ... | @@ -257,30 +247,28 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) |
| 257 | 247 | ||
| 258 | const abi_define = abiDefine(comp); | 248 | const abi_define = abiDefine(comp); |
| 259 | 249 | ||
| 260 | // Covers defines, zig.h, typedef, and asm. | 250 | // Covers defines, zig.h, ctypes, asm, lazy fwd. |
| 261 | var buf_count: usize = 2; | 251 | try f.all_buffers.ensureUnusedCapacity(gpa, 5); |
| 262 | if (abi_define != null) buf_count += 1; | ||
| 263 | try f.all_buffers.ensureUnusedCapacity(gpa, buf_count); | ||
| 264 | 252 | ||
| 265 | if (abi_define) |buf| f.appendBufAssumeCapacity(buf); | 253 | if (abi_define) |buf| f.appendBufAssumeCapacity(buf); |
| 266 | f.appendBufAssumeCapacity(zig_h); | 254 | f.appendBufAssumeCapacity(zig_h); |
| 267 | 255 | ||
| 268 | const typedef_index = f.all_buffers.items.len; | 256 | const ctypes_index = f.all_buffers.items.len; |
| 269 | f.all_buffers.items.len += 1; | 257 | f.all_buffers.items.len += 1; |
| 270 | 258 | ||
| 271 | { | 259 | { |
| 272 | var asm_buf = f.asm_buf.toManaged(module.gpa); | 260 | var asm_buf = f.asm_buf.toManaged(gpa); |
| 273 | defer asm_buf.deinit(); | 261 | defer f.asm_buf = asm_buf.moveToUnmanaged(); |
| 274 | 262 | try codegen.genGlobalAsm(module, asm_buf.writer()); | |
| 275 | try codegen.genGlobalAsm(module, &asm_buf); | 263 | f.appendBufAssumeCapacity(asm_buf.items); |
| 276 | |||
| 277 | f.asm_buf = asm_buf.moveToUnmanaged(); | ||
| 278 | f.appendBufAssumeCapacity(f.asm_buf.items); | ||
| 279 | } | 264 | } |
| 280 | 265 | ||
| 281 | try self.flushErrDecls(&f); | 266 | const lazy_index = f.all_buffers.items.len; |
| 267 | f.all_buffers.items.len += 1; | ||
| 268 | |||
| 269 | try self.flushErrDecls(&f.lazy_db); | ||
| 282 | 270 | ||
| 283 | // Typedefs, forward decls, and non-functions first. | 271 | // `CType`s, forward decls, and non-functions first. |
| 284 | // Unlike other backends, the .c code we are emitting is order-dependent. Therefore | 272 | // Unlike other backends, the .c code we are emitting is order-dependent. Therefore |
| 285 | // we must traverse the set of Decls that we are emitting according to their dependencies. | 273 | // we must traverse the set of Decls that we are emitting according to their dependencies. |
| 286 | // Our strategy is to populate a set of remaining decls, pop Decls one by one, | 274 | // Our strategy is to populate a set of remaining decls, pop Decls one by one, |
| ... | @@ -307,16 +295,33 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) | ... | @@ -307,16 +295,33 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) |
| 307 | } | 295 | } |
| 308 | } | 296 | } |
| 309 | 297 | ||
| 310 | f.all_buffers.items[typedef_index] = .{ | 298 | { |
| 311 | .iov_base = if (f.typedef_buf.items.len > 0) f.typedef_buf.items.ptr else "", | 299 | // We need to flush lazy ctypes after flushing all decls but before flushing any decl ctypes. |
| 312 | .iov_len = f.typedef_buf.items.len, | 300 | // This ensures that every lazy CType.Index exactly matches the global CType.Index. |
| 301 | assert(f.ctypes.count() == 0); | ||
| 302 | try self.flushCTypes(&f, .none, f.lazy_db.ctypes); | ||
| 303 | |||
| 304 | var it = self.decl_table.iterator(); | ||
| 305 | while (it.next()) |entry| | ||
| 306 | try self.flushCTypes(&f, entry.key_ptr.toOptional(), entry.value_ptr.ctypes); | ||
| 307 | } | ||
| 308 | |||
| 309 | f.all_buffers.items[ctypes_index] = .{ | ||
| 310 | .iov_base = if (f.ctypes_buf.items.len > 0) f.ctypes_buf.items.ptr else "", | ||
| 311 | .iov_len = f.ctypes_buf.items.len, | ||
| 313 | }; | 312 | }; |
| 314 | f.file_size += f.typedef_buf.items.len; | 313 | f.file_size += f.ctypes_buf.items.len; |
| 314 | |||
| 315 | f.all_buffers.items[lazy_index] = .{ | ||
| 316 | .iov_base = if (f.lazy_db.fwd_decl.items.len > 0) f.lazy_db.fwd_decl.items.ptr else "", | ||
| 317 | .iov_len = f.lazy_db.fwd_decl.items.len, | ||
| 318 | }; | ||
| 319 | f.file_size += f.lazy_db.fwd_decl.items.len; | ||
| 315 | 320 | ||
| 316 | // Now the code. | 321 | // Now the code. |
| 317 | try f.all_buffers.ensureUnusedCapacity(gpa, decl_values.len); | 322 | try f.all_buffers.ensureUnusedCapacity(gpa, 1 + decl_values.len); |
| 318 | for (decl_values) |decl| | 323 | f.appendBufAssumeCapacity(f.lazy_db.code.items); |
| 319 | f.appendBufAssumeCapacity(decl.code.items); | 324 | for (decl_values) |decl| f.appendBufAssumeCapacity(decl.code.items); |
| 320 | 325 | ||
| 321 | const file = self.base.file.?; | 326 | const file = self.base.file.?; |
| 322 | try file.setEndPos(f.file_size); | 327 | try file.setEndPos(f.file_size); |
| ... | @@ -324,22 +329,23 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) | ... | @@ -324,22 +329,23 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) |
| 324 | } | 329 | } |
| 325 | 330 | ||
| 326 | const Flush = struct { | 331 | const Flush = struct { |
| 327 | err_decls: DeclBlock = .{}, | ||
| 328 | remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{}, | 332 | remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{}, |
| 329 | typedefs: Typedefs = .{}, | 333 | |
| 330 | typedef_buf: std.ArrayListUnmanaged(u8) = .{}, | 334 | ctypes: codegen.CType.Store = .{}, |
| 335 | ctypes_map: std.ArrayListUnmanaged(codegen.CType.Index) = .{}, | ||
| 336 | ctypes_buf: std.ArrayListUnmanaged(u8) = .{}, | ||
| 337 | |||
| 338 | lazy_db: DeclBlock = .{}, | ||
| 339 | lazy_fns: LazyFns = .{}, | ||
| 340 | |||
| 331 | asm_buf: std.ArrayListUnmanaged(u8) = .{}, | 341 | asm_buf: std.ArrayListUnmanaged(u8) = .{}, |
| 342 | |||
| 332 | /// We collect a list of buffers to write, and write them all at once with pwritev 😎 | 343 | /// We collect a list of buffers to write, and write them all at once with pwritev 😎 |
| 333 | all_buffers: std.ArrayListUnmanaged(std.os.iovec_const) = .{}, | 344 | all_buffers: std.ArrayListUnmanaged(std.os.iovec_const) = .{}, |
| 334 | /// Keeps track of the total bytes of `all_buffers`. | 345 | /// Keeps track of the total bytes of `all_buffers`. |
| 335 | file_size: u64 = 0, | 346 | file_size: u64 = 0, |
| 336 | 347 | ||
| 337 | const Typedefs = std.HashMapUnmanaged( | 348 | const LazyFns = std.AutoHashMapUnmanaged(codegen.LazyFnKey, void); |
| 338 | Type, | ||
| 339 | void, | ||
| 340 | Type.HashContext64, | ||
| 341 | std.hash_map.default_max_load_percentage, | ||
| 342 | ); | ||
| 343 | 349 | ||
| 344 | fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void { | 350 | fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void { |
| 345 | if (buf.len == 0) return; | 351 | if (buf.len == 0) return; |
| ... | @@ -349,10 +355,13 @@ const Flush = struct { | ... | @@ -349,10 +355,13 @@ const Flush = struct { |
| 349 | 355 | ||
| 350 | fn deinit(f: *Flush, gpa: Allocator) void { | 356 | fn deinit(f: *Flush, gpa: Allocator) void { |
| 351 | f.all_buffers.deinit(gpa); | 357 | f.all_buffers.deinit(gpa); |
| 352 | f.typedef_buf.deinit(gpa); | 358 | f.asm_buf.deinit(gpa); |
| 353 | f.typedefs.deinit(gpa); | 359 | f.lazy_fns.deinit(gpa); |
| 360 | f.lazy_db.deinit(gpa); | ||
| 361 | f.ctypes_buf.deinit(gpa); | ||
| 362 | f.ctypes_map.deinit(gpa); | ||
| 363 | f.ctypes.deinit(gpa); | ||
| 354 | f.remaining_decls.deinit(gpa); | 364 | f.remaining_decls.deinit(gpa); |
| 355 | f.err_decls.deinit(gpa); | ||
| 356 | } | 365 | } |
| 357 | }; | 366 | }; |
| 358 | 367 | ||
| ... | @@ -360,53 +369,116 @@ const FlushDeclError = error{ | ... | @@ -360,53 +369,116 @@ const FlushDeclError = error{ |
| 360 | OutOfMemory, | 369 | OutOfMemory, |
| 361 | }; | 370 | }; |
| 362 | 371 | ||
| 363 | fn flushTypedefs(self: *C, f: *Flush, typedefs: codegen.TypedefMap.Unmanaged) FlushDeclError!void { | 372 | fn flushCTypes( |
| 364 | if (typedefs.count() == 0) return; | 373 | self: *C, |
| 374 | f: *Flush, | ||
| 375 | decl_index: Module.Decl.OptionalIndex, | ||
| 376 | decl_ctypes: codegen.CType.Store, | ||
| 377 | ) FlushDeclError!void { | ||
| 365 | const gpa = self.base.allocator; | 378 | const gpa = self.base.allocator; |
| 366 | const module = self.base.options.module.?; | 379 | const mod = self.base.options.module.?; |
| 367 | 380 | ||
| 368 | try f.typedefs.ensureUnusedCapacityContext(gpa, @intCast(u32, typedefs.count()), .{ | 381 | const decl_ctypes_len = decl_ctypes.count(); |
| 369 | .mod = module, | 382 | f.ctypes_map.clearRetainingCapacity(); |
| 370 | }); | 383 | try f.ctypes_map.ensureTotalCapacity(gpa, decl_ctypes_len); |
| 371 | var it = typedefs.iterator(); | 384 | |
| 372 | while (it.next()) |new| { | 385 | var global_ctypes = f.ctypes.promote(gpa); |
| 373 | const gop = f.typedefs.getOrPutAssumeCapacityContext(new.key_ptr.*, .{ | 386 | defer f.ctypes.demote(global_ctypes); |
| 374 | .mod = module, | 387 | |
| 388 | var ctypes_buf = f.ctypes_buf.toManaged(gpa); | ||
| 389 | defer f.ctypes_buf = ctypes_buf.moveToUnmanaged(); | ||
| 390 | const writer = ctypes_buf.writer(); | ||
| 391 | |||
| 392 | const slice = decl_ctypes.set.map.entries.slice(); | ||
| 393 | for (slice.items(.key), 0..) |decl_cty, decl_i| { | ||
| 394 | const Context = struct { | ||
| 395 | arena: Allocator, | ||
| 396 | ctypes_map: []codegen.CType.Index, | ||
| 397 | cached_hash: codegen.CType.Store.Set.Map.Hash, | ||
| 398 | idx: codegen.CType.Index, | ||
| 399 | |||
| 400 | pub fn hash(ctx: @This(), _: codegen.CType) codegen.CType.Store.Set.Map.Hash { | ||
| 401 | return ctx.cached_hash; | ||
| 402 | } | ||
| 403 | pub fn eql(ctx: @This(), lhs: codegen.CType, rhs: codegen.CType, _: usize) bool { | ||
| 404 | return lhs.eqlContext(rhs, ctx); | ||
| 405 | } | ||
| 406 | pub fn eqlIndex( | ||
| 407 | ctx: @This(), | ||
| 408 | lhs_idx: codegen.CType.Index, | ||
| 409 | rhs_idx: codegen.CType.Index, | ||
| 410 | ) bool { | ||
| 411 | if (lhs_idx < codegen.CType.Tag.no_payload_count or | ||
| 412 | rhs_idx < codegen.CType.Tag.no_payload_count) return lhs_idx == rhs_idx; | ||
| 413 | const lhs_i = lhs_idx - codegen.CType.Tag.no_payload_count; | ||
| 414 | if (lhs_i >= ctx.ctypes_map.len) return false; | ||
| 415 | return ctx.ctypes_map[lhs_i] == rhs_idx; | ||
| 416 | } | ||
| 417 | pub fn copyIndex(ctx: @This(), idx: codegen.CType.Index) codegen.CType.Index { | ||
| 418 | if (idx < codegen.CType.Tag.no_payload_count) return idx; | ||
| 419 | return ctx.ctypes_map[idx - codegen.CType.Tag.no_payload_count]; | ||
| 420 | } | ||
| 421 | }; | ||
| 422 | const decl_idx = @intCast(codegen.CType.Index, codegen.CType.Tag.no_payload_count + decl_i); | ||
| 423 | const ctx = Context{ | ||
| 424 | .arena = global_ctypes.arena.allocator(), | ||
| 425 | .ctypes_map = f.ctypes_map.items, | ||
| 426 | .cached_hash = decl_ctypes.indexToHash(decl_idx), | ||
| 427 | .idx = decl_idx, | ||
| 428 | }; | ||
| 429 | const gop = try global_ctypes.set.map.getOrPutContextAdapted(gpa, decl_cty, ctx, .{ | ||
| 430 | .store = &global_ctypes.set, | ||
| 375 | }); | 431 | }); |
| 432 | const global_idx = | ||
| 433 | @intCast(codegen.CType.Index, codegen.CType.Tag.no_payload_count + gop.index); | ||
| 434 | f.ctypes_map.appendAssumeCapacity(global_idx); | ||
| 376 | if (!gop.found_existing) { | 435 | if (!gop.found_existing) { |
| 377 | try f.typedef_buf.appendSlice(gpa, new.value_ptr.rendered); | 436 | errdefer _ = global_ctypes.set.map.pop(); |
| 437 | gop.key_ptr.* = try decl_cty.copyContext(ctx); | ||
| 438 | } | ||
| 439 | if (std.debug.runtime_safety) { | ||
| 440 | const global_cty = &global_ctypes.set.map.entries.items(.key)[gop.index]; | ||
| 441 | assert(global_cty == gop.key_ptr); | ||
| 442 | assert(decl_cty.eqlContext(global_cty.*, ctx)); | ||
| 443 | assert(decl_cty.hash(decl_ctypes.set) == global_cty.hash(global_ctypes.set)); | ||
| 378 | } | 444 | } |
| 445 | try codegen.genTypeDecl( | ||
| 446 | mod, | ||
| 447 | writer, | ||
| 448 | global_ctypes.set, | ||
| 449 | global_idx, | ||
| 450 | decl_index, | ||
| 451 | decl_ctypes.set, | ||
| 452 | decl_idx, | ||
| 453 | gop.found_existing, | ||
| 454 | ); | ||
| 379 | } | 455 | } |
| 380 | } | 456 | } |
| 381 | 457 | ||
| 382 | fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { | 458 | fn flushErrDecls(self: *C, db: *DeclBlock) FlushDeclError!void { |
| 383 | const module = self.base.options.module.?; | 459 | const gpa = self.base.allocator; |
| 384 | 460 | ||
| 385 | const fwd_decl = &f.err_decls.fwd_decl; | 461 | const fwd_decl = &db.fwd_decl; |
| 386 | const typedefs = &f.err_decls.typedefs; | 462 | const ctypes = &db.ctypes; |
| 387 | const code = &f.err_decls.code; | 463 | const code = &db.code; |
| 388 | 464 | ||
| 389 | var object = codegen.Object{ | 465 | var object = codegen.Object{ |
| 390 | .dg = .{ | 466 | .dg = .{ |
| 391 | .gpa = module.gpa, | 467 | .gpa = gpa, |
| 392 | .module = module, | 468 | .module = self.base.options.module.?, |
| 393 | .error_msg = null, | 469 | .error_msg = null, |
| 394 | .decl_index = undefined, | 470 | .decl_index = .none, |
| 395 | .decl = undefined, | 471 | .decl = null, |
| 396 | .fwd_decl = fwd_decl.toManaged(module.gpa), | 472 | .fwd_decl = fwd_decl.toManaged(gpa), |
| 397 | .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), | 473 | .ctypes = ctypes.*, |
| 398 | .typedefs_arena = self.arena.allocator(), | ||
| 399 | }, | 474 | }, |
| 400 | .code = code.toManaged(module.gpa), | 475 | .code = code.toManaged(gpa), |
| 401 | .indent_writer = undefined, // set later so we can get a pointer to object.code | 476 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 402 | }; | 477 | }; |
| 403 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; | 478 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; |
| 404 | defer { | 479 | defer { |
| 405 | object.code.deinit(); | 480 | object.code.deinit(); |
| 406 | for (object.dg.typedefs.values()) |typedef| { | 481 | object.dg.ctypes.deinit(gpa); |
| 407 | module.gpa.free(typedef.rendered); | ||
| 408 | } | ||
| 409 | object.dg.typedefs.deinit(); | ||
| 410 | object.dg.fwd_decl.deinit(); | 482 | object.dg.fwd_decl.deinit(); |
| 411 | } | 483 | } |
| 412 | 484 | ||
| ... | @@ -416,14 +488,58 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { | ... | @@ -416,14 +488,58 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { |
| 416 | }; | 488 | }; |
| 417 | 489 | ||
| 418 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); | 490 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); |
| 419 | typedefs.* = object.dg.typedefs.unmanaged; | 491 | ctypes.* = object.dg.ctypes.move(); |
| 420 | object.dg.typedefs.unmanaged = .{}; | 492 | code.* = object.code.moveToUnmanaged(); |
| 493 | } | ||
| 494 | |||
| 495 | fn flushLazyFn(self: *C, db: *DeclBlock, lazy_fn: codegen.LazyFnMap.Entry) FlushDeclError!void { | ||
| 496 | const gpa = self.base.allocator; | ||
| 497 | |||
| 498 | const fwd_decl = &db.fwd_decl; | ||
| 499 | const ctypes = &db.ctypes; | ||
| 500 | const code = &db.code; | ||
| 501 | |||
| 502 | var object = codegen.Object{ | ||
| 503 | .dg = .{ | ||
| 504 | .gpa = gpa, | ||
| 505 | .module = self.base.options.module.?, | ||
| 506 | .error_msg = null, | ||
| 507 | .decl_index = .none, | ||
| 508 | .decl = null, | ||
| 509 | .fwd_decl = fwd_decl.toManaged(gpa), | ||
| 510 | .ctypes = ctypes.*, | ||
| 511 | }, | ||
| 512 | .code = code.toManaged(gpa), | ||
| 513 | .indent_writer = undefined, // set later so we can get a pointer to object.code | ||
| 514 | }; | ||
| 515 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; | ||
| 516 | defer { | ||
| 517 | object.code.deinit(); | ||
| 518 | object.dg.ctypes.deinit(gpa); | ||
| 519 | object.dg.fwd_decl.deinit(); | ||
| 520 | } | ||
| 521 | |||
| 522 | codegen.genLazyFn(&object, lazy_fn) catch |err| switch (err) { | ||
| 523 | error.AnalysisFail => unreachable, | ||
| 524 | else => |e| return e, | ||
| 525 | }; | ||
| 526 | |||
| 527 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); | ||
| 528 | ctypes.* = object.dg.ctypes.move(); | ||
| 421 | code.* = object.code.moveToUnmanaged(); | 529 | code.* = object.code.moveToUnmanaged(); |
| 530 | } | ||
| 422 | 531 | ||
| 423 | try self.flushTypedefs(f, typedefs.*); | 532 | fn flushLazyFns(self: *C, f: *Flush, lazy_fns: codegen.LazyFnMap) FlushDeclError!void { |
| 424 | try f.all_buffers.ensureUnusedCapacity(self.base.allocator, 1); | 533 | const gpa = self.base.allocator; |
| 425 | f.appendBufAssumeCapacity(fwd_decl.items); | 534 | try f.lazy_fns.ensureUnusedCapacity(gpa, @intCast(Flush.LazyFns.Size, lazy_fns.count())); |
| 426 | f.appendBufAssumeCapacity(code.items); | 535 | |
| 536 | var it = lazy_fns.iterator(); | ||
| 537 | while (it.next()) |entry| { | ||
| 538 | const gop = f.lazy_fns.getOrPutAssumeCapacity(entry.key_ptr.*); | ||
| 539 | if (gop.found_existing) continue; | ||
| 540 | gop.value_ptr.* = {}; | ||
| 541 | try self.flushLazyFn(&f.lazy_db, entry); | ||
| 542 | } | ||
| 427 | } | 543 | } |
| 428 | 544 | ||
| 429 | /// Assumes `decl` was in the `remaining_decls` set, and has already been removed. | 545 | /// Assumes `decl` was in the `remaining_decls` set, and has already been removed. |
| ... | @@ -433,8 +549,8 @@ fn flushDecl( | ... | @@ -433,8 +549,8 @@ fn flushDecl( |
| 433 | decl_index: Module.Decl.Index, | 549 | decl_index: Module.Decl.Index, |
| 434 | export_names: std.StringHashMapUnmanaged(void), | 550 | export_names: std.StringHashMapUnmanaged(void), |
| 435 | ) FlushDeclError!void { | 551 | ) FlushDeclError!void { |
| 436 | const module = self.base.options.module.?; | 552 | const gpa = self.base.allocator; |
| 437 | const decl = module.declPtr(decl_index); | 553 | const decl = self.base.options.module.?.declPtr(decl_index); |
| 438 | // Before flushing any particular Decl we must ensure its | 554 | // Before flushing any particular Decl we must ensure its |
| 439 | // dependencies are already flushed, so that the order in the .c | 555 | // dependencies are already flushed, so that the order in the .c |
| 440 | // file comes out correctly. | 556 | // file comes out correctly. |
| ... | @@ -445,10 +561,9 @@ fn flushDecl( | ... | @@ -445,10 +561,9 @@ fn flushDecl( |
| 445 | } | 561 | } |
| 446 | 562 | ||
| 447 | const decl_block = self.decl_table.getPtr(decl_index).?; | 563 | const decl_block = self.decl_table.getPtr(decl_index).?; |
| 448 | const gpa = self.base.allocator; | ||
| 449 | 564 | ||
| 450 | try self.flushTypedefs(f, decl_block.typedefs); | 565 | try self.flushLazyFns(f, decl_block.lazy_fns); |
| 451 | try f.all_buffers.ensureUnusedCapacity(gpa, 2); | 566 | try f.all_buffers.ensureUnusedCapacity(gpa, 1); |
| 452 | if (!(decl.isExtern() and export_names.contains(mem.span(decl.name)))) | 567 | if (!(decl.isExtern() and export_names.contains(mem.span(decl.name)))) |
| 453 | f.appendBufAssumeCapacity(decl_block.fwd_decl.items); | 568 | f.appendBufAssumeCapacity(decl_block.fwd_decl.items); |
| 454 | } | 569 | } |
src/main.zig+140-81| ... | @@ -403,8 +403,11 @@ const usage_build_generic = | ... | @@ -403,8 +403,11 @@ const usage_build_generic = |
| 403 | \\ ReleaseFast Optimizations on, safety off | 403 | \\ ReleaseFast Optimizations on, safety off |
| 404 | \\ ReleaseSafe Optimizations on, safety on | 404 | \\ ReleaseSafe Optimizations on, safety on |
| 405 | \\ ReleaseSmall Optimize for small binary, safety off | 405 | \\ ReleaseSmall Optimize for small binary, safety off |
| 406 | \\ --pkg-begin [name] [path] Make pkg available to import and push current pkg | 406 | \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name |
| 407 | \\ --pkg-end Pop current pkg | 407 | \\ deps: [dep],[dep],... |
| 408 | \\ dep: [[import=]name] | ||
| 409 | \\ --deps [dep],[dep],... Set dependency names for the root package | ||
| 410 | \\ dep: [[import=]name] | ||
| 408 | \\ --main-pkg-path Set the directory of the root package | 411 | \\ --main-pkg-path Set the directory of the root package |
| 409 | \\ -fPIC Force-enable Position Independent Code | 412 | \\ -fPIC Force-enable Position Independent Code |
| 410 | \\ -fno-PIC Force-disable Position Independent Code | 413 | \\ -fno-PIC Force-disable Position Independent Code |
| ... | @@ -858,15 +861,21 @@ fn buildOutputType( | ... | @@ -858,15 +861,21 @@ fn buildOutputType( |
| 858 | var linker_export_symbol_names = std.ArrayList([]const u8).init(gpa); | 861 | var linker_export_symbol_names = std.ArrayList([]const u8).init(gpa); |
| 859 | defer linker_export_symbol_names.deinit(); | 862 | defer linker_export_symbol_names.deinit(); |
| 860 | 863 | ||
| 861 | // This package only exists to clean up the code parsing --pkg-begin and | 864 | // Contains every module specified via --mod. The dependencies are added |
| 862 | // --pkg-end flags. Use dummy values that are safe for the destroy call. | 865 | // after argument parsing is completed. We use a StringArrayHashMap to make |
| 863 | var pkg_tree_root: Package = .{ | 866 | // error output consistent. |
| 864 | .root_src_directory = .{ .path = null, .handle = fs.cwd() }, | 867 | var modules = std.StringArrayHashMap(struct { |
| 865 | .root_src_path = &[0]u8{}, | 868 | mod: *Package, |
| 866 | .name = &[0]u8{}, | 869 | deps_str: []const u8, // still in CLI arg format |
| 867 | }; | 870 | }).init(gpa); |
| 868 | defer freePkgTree(gpa, &pkg_tree_root, false); | 871 | defer { |
| 869 | var cur_pkg: *Package = &pkg_tree_root; | 872 | var it = modules.iterator(); |
| 873 | while (it.next()) |kv| kv.value_ptr.mod.destroy(gpa); | ||
| 874 | modules.deinit(); | ||
| 875 | } | ||
| 876 | |||
| 877 | // The dependency string for the root package | ||
| 878 | var root_deps_str: ?[]const u8 = null; | ||
| 870 | 879 | ||
| 871 | // before arg parsing, check for the NO_COLOR environment variable | 880 | // before arg parsing, check for the NO_COLOR environment variable |
| 872 | // if it exists, default the color setting to .off | 881 | // if it exists, default the color setting to .off |
| ... | @@ -943,34 +952,44 @@ fn buildOutputType( | ... | @@ -943,34 +952,44 @@ fn buildOutputType( |
| 943 | } else { | 952 | } else { |
| 944 | fatal("unexpected end-of-parameter mark: --", .{}); | 953 | fatal("unexpected end-of-parameter mark: --", .{}); |
| 945 | } | 954 | } |
| 946 | } else if (mem.eql(u8, arg, "--pkg-begin")) { | 955 | } else if (mem.eql(u8, arg, "--mod")) { |
| 947 | const opt_pkg_name = args_iter.next(); | 956 | const info = args_iter.nextOrFatal(); |
| 948 | const opt_pkg_path = args_iter.next(); | 957 | var info_it = mem.split(u8, info, ":"); |
| 949 | if (opt_pkg_name == null or opt_pkg_path == null) | 958 | const mod_name = info_it.next() orelse fatal("expected non-empty argument after {s}", .{arg}); |
| 950 | fatal("Expected 2 arguments after {s}", .{arg}); | 959 | const deps_str = info_it.next() orelse fatal("expected 'name:deps:path' after {s}", .{arg}); |
| 951 | 960 | const root_src_orig = info_it.rest(); | |
| 952 | const pkg_name = opt_pkg_name.?; | 961 | if (root_src_orig.len == 0) fatal("expected 'name:deps:path' after {s}", .{arg}); |
| 953 | const pkg_path = try introspect.resolvePath(arena, opt_pkg_path.?); | 962 | if (mod_name.len == 0) fatal("empty name for module at '{s}'", .{root_src_orig}); |
| 954 | 963 | ||
| 955 | const new_cur_pkg = Package.create( | 964 | const root_src = try introspect.resolvePath(arena, root_src_orig); |
| 956 | gpa, | 965 | |
| 957 | pkg_name, | 966 | for ([_][]const u8{ "std", "root", "builtin" }) |name| { |
| 958 | fs.path.dirname(pkg_path), | 967 | if (mem.eql(u8, mod_name, name)) { |
| 959 | fs.path.basename(pkg_path), | 968 | fatal("unable to add module '{s}' -> '{s}': conflicts with builtin module", .{ mod_name, root_src }); |
| 960 | ) catch |err| { | 969 | } |
| 961 | fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) }); | 970 | } |
| 962 | }; | ||
| 963 | 971 | ||
| 964 | if (mem.eql(u8, pkg_name, "std") or mem.eql(u8, pkg_name, "root") or mem.eql(u8, pkg_name, "builtin")) { | 972 | var mod_it = modules.iterator(); |
| 965 | fatal("unable to add package '{s}' -> '{s}': conflicts with builtin package", .{ pkg_name, pkg_path }); | 973 | while (mod_it.next()) |kv| { |
| 966 | } else if (cur_pkg.table.get(pkg_name)) |prev| { | 974 | if (std.mem.eql(u8, mod_name, kv.key_ptr.*)) { |
| 967 | fatal("unable to add package '{s}' -> '{s}': already exists as '{s}", .{ pkg_name, pkg_path, prev.root_src_path }); | 975 | fatal("unable to add module '{s}' -> '{s}': already exists as '{s}'", .{ mod_name, root_src, kv.value_ptr.mod.root_src_path }); |
| 976 | } | ||
| 977 | } | ||
| 978 | |||
| 979 | try modules.ensureUnusedCapacity(1); | ||
| 980 | modules.put(mod_name, .{ | ||
| 981 | .mod = try Package.create( | ||
| 982 | gpa, | ||
| 983 | fs.path.dirname(root_src), | ||
| 984 | fs.path.basename(root_src), | ||
| 985 | ), | ||
| 986 | .deps_str = deps_str, | ||
| 987 | }) catch unreachable; | ||
| 988 | } else if (mem.eql(u8, arg, "--deps")) { | ||
| 989 | if (root_deps_str != null) { | ||
| 990 | fatal("only one --deps argument is allowed", .{}); | ||
| 968 | } | 991 | } |
| 969 | try cur_pkg.addAndAdopt(gpa, new_cur_pkg); | 992 | root_deps_str = args_iter.nextOrFatal(); |
| 970 | cur_pkg = new_cur_pkg; | ||
| 971 | } else if (mem.eql(u8, arg, "--pkg-end")) { | ||
| 972 | cur_pkg = cur_pkg.parent orelse | ||
| 973 | fatal("encountered --pkg-end with no matching --pkg-begin", .{}); | ||
| 974 | } else if (mem.eql(u8, arg, "--main-pkg-path")) { | 993 | } else if (mem.eql(u8, arg, "--main-pkg-path")) { |
| 975 | main_pkg_path = args_iter.nextOrFatal(); | 994 | main_pkg_path = args_iter.nextOrFatal(); |
| 976 | } else if (mem.eql(u8, arg, "-cflags")) { | 995 | } else if (mem.eql(u8, arg, "-cflags")) { |
| ... | @@ -1955,12 +1974,15 @@ fn buildOutputType( | ... | @@ -1955,12 +1974,15 @@ fn buildOutputType( |
| 1955 | linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, arg1) orelse { | 1974 | linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, arg1) orelse { |
| 1956 | fatal("expected [none|zlib] after --compress-debug-sections, found '{s}'", .{arg1}); | 1975 | fatal("expected [none|zlib] after --compress-debug-sections, found '{s}'", .{arg1}); |
| 1957 | }; | 1976 | }; |
| 1958 | } else if (mem.eql(u8, arg, "-z")) { | 1977 | } else if (mem.startsWith(u8, arg, "-z")) { |
| 1959 | i += 1; | 1978 | var z_arg = arg[2..]; |
| 1960 | if (i >= linker_args.items.len) { | 1979 | if (z_arg.len == 0) { |
| 1961 | fatal("expected linker extension flag after '{s}'", .{arg}); | 1980 | i += 1; |
| 1981 | if (i >= linker_args.items.len) { | ||
| 1982 | fatal("expected linker extension flag after '{s}'", .{arg}); | ||
| 1983 | } | ||
| 1984 | z_arg = linker_args.items[i]; | ||
| 1962 | } | 1985 | } |
| 1963 | const z_arg = linker_args.items[i]; | ||
| 1964 | if (mem.eql(u8, z_arg, "nodelete")) { | 1986 | if (mem.eql(u8, z_arg, "nodelete")) { |
| 1965 | linker_z_nodelete = true; | 1987 | linker_z_nodelete = true; |
| 1966 | } else if (mem.eql(u8, z_arg, "notext")) { | 1988 | } else if (mem.eql(u8, z_arg, "notext")) { |
| ... | @@ -2304,6 +2326,31 @@ fn buildOutputType( | ... | @@ -2304,6 +2326,31 @@ fn buildOutputType( |
| 2304 | }, | 2326 | }, |
| 2305 | } | 2327 | } |
| 2306 | 2328 | ||
| 2329 | { | ||
| 2330 | // Resolve module dependencies | ||
| 2331 | var it = modules.iterator(); | ||
| 2332 | while (it.next()) |kv| { | ||
| 2333 | const deps_str = kv.value_ptr.deps_str; | ||
| 2334 | var deps_it = ModuleDepIterator.init(deps_str); | ||
| 2335 | while (deps_it.next()) |dep| { | ||
| 2336 | if (dep.expose.len == 0) { | ||
| 2337 | fatal("module '{s}' depends on '{s}' with a blank name", .{ kv.key_ptr.*, dep.name }); | ||
| 2338 | } | ||
| 2339 | |||
| 2340 | for ([_][]const u8{ "std", "root", "builtin" }) |name| { | ||
| 2341 | if (mem.eql(u8, dep.expose, name)) { | ||
| 2342 | fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ dep.name, dep.expose }); | ||
| 2343 | } | ||
| 2344 | } | ||
| 2345 | |||
| 2346 | const dep_mod = modules.get(dep.name) orelse | ||
| 2347 | fatal("module '{s}' depends on module '{s}' which does not exist", .{ kv.key_ptr.*, dep.name }); | ||
| 2348 | |||
| 2349 | try kv.value_ptr.mod.add(gpa, dep.expose, dep_mod.mod); | ||
| 2350 | } | ||
| 2351 | } | ||
| 2352 | } | ||
| 2353 | |||
| 2307 | if (arg_mode == .build and optimize_mode == .ReleaseSmall and strip == null) | 2354 | if (arg_mode == .build and optimize_mode == .ReleaseSmall and strip == null) |
| 2308 | strip = true; | 2355 | strip = true; |
| 2309 | 2356 | ||
| ... | @@ -2883,14 +2930,14 @@ fn buildOutputType( | ... | @@ -2883,14 +2930,14 @@ fn buildOutputType( |
| 2883 | if (main_pkg_path) |unresolved_main_pkg_path| { | 2930 | if (main_pkg_path) |unresolved_main_pkg_path| { |
| 2884 | const p = try introspect.resolvePath(arena, unresolved_main_pkg_path); | 2931 | const p = try introspect.resolvePath(arena, unresolved_main_pkg_path); |
| 2885 | if (p.len == 0) { | 2932 | if (p.len == 0) { |
| 2886 | break :blk try Package.create(gpa, "root", null, src_path); | 2933 | break :blk try Package.create(gpa, null, src_path); |
| 2887 | } else { | 2934 | } else { |
| 2888 | const rel_src_path = try fs.path.relative(arena, p, src_path); | 2935 | const rel_src_path = try fs.path.relative(arena, p, src_path); |
| 2889 | break :blk try Package.create(gpa, "root", p, rel_src_path); | 2936 | break :blk try Package.create(gpa, p, rel_src_path); |
| 2890 | } | 2937 | } |
| 2891 | } else { | 2938 | } else { |
| 2892 | const root_src_dir_path = fs.path.dirname(src_path); | 2939 | const root_src_dir_path = fs.path.dirname(src_path); |
| 2893 | break :blk Package.create(gpa, "root", root_src_dir_path, fs.path.basename(src_path)) catch |err| { | 2940 | break :blk Package.create(gpa, root_src_dir_path, fs.path.basename(src_path)) catch |err| { |
| 2894 | if (root_src_dir_path) |p| { | 2941 | if (root_src_dir_path) |p| { |
| 2895 | fatal("unable to open '{s}': {s}", .{ p, @errorName(err) }); | 2942 | fatal("unable to open '{s}': {s}", .{ p, @errorName(err) }); |
| 2896 | } else { | 2943 | } else { |
| ... | @@ -2901,23 +2948,24 @@ fn buildOutputType( | ... | @@ -2901,23 +2948,24 @@ fn buildOutputType( |
| 2901 | } else null; | 2948 | } else null; |
| 2902 | defer if (main_pkg) |p| p.destroy(gpa); | 2949 | defer if (main_pkg) |p| p.destroy(gpa); |
| 2903 | 2950 | ||
| 2904 | // Transfer packages added with --pkg-begin/--pkg-end to the root package | 2951 | // Transfer packages added with --deps to the root package |
| 2905 | if (main_pkg) |pkg| { | 2952 | if (main_pkg) |mod| { |
| 2906 | var it = pkg_tree_root.table.valueIterator(); | 2953 | var it = ModuleDepIterator.init(root_deps_str orelse ""); |
| 2907 | while (it.next()) |p| { | 2954 | while (it.next()) |dep| { |
| 2908 | if (p.*.parent == &pkg_tree_root) { | 2955 | if (dep.expose.len == 0) { |
| 2909 | p.*.parent = pkg; | 2956 | fatal("root module depends on '{s}' with a blank name", .{dep.name}); |
| 2910 | } | 2957 | } |
| 2911 | } | 2958 | |
| 2912 | pkg.table = pkg_tree_root.table; | 2959 | for ([_][]const u8{ "std", "root", "builtin" }) |name| { |
| 2913 | pkg_tree_root.table = .{}; | 2960 | if (mem.eql(u8, dep.expose, name)) { |
| 2914 | } else { | 2961 | fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ dep.name, dep.expose }); |
| 2915 | // Remove any dangling pointers just in case. | 2962 | } |
| 2916 | var it = pkg_tree_root.table.valueIterator(); | ||
| 2917 | while (it.next()) |p| { | ||
| 2918 | if (p.*.parent == &pkg_tree_root) { | ||
| 2919 | p.*.parent = null; | ||
| 2920 | } | 2963 | } |
| 2964 | |||
| 2965 | const dep_mod = modules.get(dep.name) orelse | ||
| 2966 | fatal("root module depends on module '{s}' which does not exist", .{dep.name}); | ||
| 2967 | |||
| 2968 | try mod.add(gpa, dep.expose, dep_mod.mod); | ||
| 2921 | } | 2969 | } |
| 2922 | } | 2970 | } |
| 2923 | 2971 | ||
| ... | @@ -3397,6 +3445,32 @@ fn buildOutputType( | ... | @@ -3397,6 +3445,32 @@ fn buildOutputType( |
| 3397 | return cleanExit(); | 3445 | return cleanExit(); |
| 3398 | } | 3446 | } |
| 3399 | 3447 | ||
| 3448 | const ModuleDepIterator = struct { | ||
| 3449 | split: mem.SplitIterator(u8), | ||
| 3450 | |||
| 3451 | fn init(deps_str: []const u8) ModuleDepIterator { | ||
| 3452 | return .{ .split = mem.split(u8, deps_str, ",") }; | ||
| 3453 | } | ||
| 3454 | |||
| 3455 | const Dependency = struct { | ||
| 3456 | expose: []const u8, | ||
| 3457 | name: []const u8, | ||
| 3458 | }; | ||
| 3459 | |||
| 3460 | fn next(it: *ModuleDepIterator) ?Dependency { | ||
| 3461 | if (it.split.buffer.len == 0) return null; // don't return "" for the first iteration on "" | ||
| 3462 | const str = it.split.next() orelse return null; | ||
| 3463 | if (mem.indexOfScalar(u8, str, '=')) |i| { | ||
| 3464 | return .{ | ||
| 3465 | .expose = str[0..i], | ||
| 3466 | .name = str[i + 1 ..], | ||
| 3467 | }; | ||
| 3468 | } else { | ||
| 3469 | return .{ .expose = str, .name = str }; | ||
| 3470 | } | ||
| 3471 | } | ||
| 3472 | }; | ||
| 3473 | |||
| 3400 | fn parseCrossTargetOrReportFatalError( | 3474 | fn parseCrossTargetOrReportFatalError( |
| 3401 | allocator: Allocator, | 3475 | allocator: Allocator, |
| 3402 | opts: std.zig.CrossTarget.ParseOptions, | 3476 | opts: std.zig.CrossTarget.ParseOptions, |
| ... | @@ -3623,18 +3697,6 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void | ... | @@ -3623,18 +3697,6 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void |
| 3623 | } | 3697 | } |
| 3624 | } | 3698 | } |
| 3625 | 3699 | ||
| 3626 | fn freePkgTree(gpa: Allocator, pkg: *Package, free_parent: bool) void { | ||
| 3627 | { | ||
| 3628 | var it = pkg.table.valueIterator(); | ||
| 3629 | while (it.next()) |value| { | ||
| 3630 | freePkgTree(gpa, value.*, true); | ||
| 3631 | } | ||
| 3632 | } | ||
| 3633 | if (free_parent) { | ||
| 3634 | pkg.destroy(gpa); | ||
| 3635 | } | ||
| 3636 | } | ||
| 3637 | |||
| 3638 | fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void { | 3700 | fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void { |
| 3639 | if (!build_options.have_llvm) | 3701 | if (!build_options.have_llvm) |
| 3640 | fatal("cannot translate-c: compiler built without LLVM extensions", .{}); | 3702 | fatal("cannot translate-c: compiler built without LLVM extensions", .{}); |
| ... | @@ -4138,7 +4200,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4138,7 +4200,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4138 | var main_pkg: Package = .{ | 4200 | var main_pkg: Package = .{ |
| 4139 | .root_src_directory = zig_lib_directory, | 4201 | .root_src_directory = zig_lib_directory, |
| 4140 | .root_src_path = "build_runner.zig", | 4202 | .root_src_path = "build_runner.zig", |
| 4141 | .name = "root", | ||
| 4142 | }; | 4203 | }; |
| 4143 | 4204 | ||
| 4144 | if (!build_options.omit_pkg_fetching_code) { | 4205 | if (!build_options.omit_pkg_fetching_code) { |
| ... | @@ -4181,22 +4242,20 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4181,22 +4242,20 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4181 | 4242 | ||
| 4182 | const deps_pkg = try Package.createFilePkg( | 4243 | const deps_pkg = try Package.createFilePkg( |
| 4183 | gpa, | 4244 | gpa, |
| 4184 | "@dependencies", | ||
| 4185 | local_cache_directory, | 4245 | local_cache_directory, |
| 4186 | "dependencies.zig", | 4246 | "dependencies.zig", |
| 4187 | dependencies_source.items, | 4247 | dependencies_source.items, |
| 4188 | ); | 4248 | ); |
| 4189 | 4249 | ||
| 4190 | mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table); | 4250 | mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table); |
| 4191 | try main_pkg.addAndAdopt(gpa, deps_pkg); | 4251 | try main_pkg.add(gpa, "@dependencies", deps_pkg); |
| 4192 | } | 4252 | } |
| 4193 | 4253 | ||
| 4194 | var build_pkg: Package = .{ | 4254 | var build_pkg: Package = .{ |
| 4195 | .root_src_directory = build_directory, | 4255 | .root_src_directory = build_directory, |
| 4196 | .root_src_path = build_zig_basename, | 4256 | .root_src_path = build_zig_basename, |
| 4197 | .name = "@build", | ||
| 4198 | }; | 4257 | }; |
| 4199 | try main_pkg.addAndAdopt(gpa, &build_pkg); | 4258 | try main_pkg.add(gpa, "@build", &build_pkg); |
| 4200 | 4259 | ||
| 4201 | const comp = Compilation.create(gpa, .{ | 4260 | const comp = Compilation.create(gpa, .{ |
| 4202 | .zig_lib_directory = zig_lib_directory, | 4261 | .zig_lib_directory = zig_lib_directory, |
| ... | @@ -4431,7 +4490,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void | ... | @@ -4431,7 +4490,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void |
| 4431 | .root_decl = .none, | 4490 | .root_decl = .none, |
| 4432 | }; | 4491 | }; |
| 4433 | 4492 | ||
| 4434 | file.pkg = try Package.create(gpa, "root", null, file.sub_file_path); | 4493 | file.pkg = try Package.create(gpa, null, file.sub_file_path); |
| 4435 | defer file.pkg.destroy(gpa); | 4494 | defer file.pkg.destroy(gpa); |
| 4436 | 4495 | ||
| 4437 | file.zir = try AstGen.generate(gpa, file.tree); | 4496 | file.zir = try AstGen.generate(gpa, file.tree); |
| ... | @@ -4642,7 +4701,7 @@ fn fmtPathFile( | ... | @@ -4642,7 +4701,7 @@ fn fmtPathFile( |
| 4642 | .root_decl = .none, | 4701 | .root_decl = .none, |
| 4643 | }; | 4702 | }; |
| 4644 | 4703 | ||
| 4645 | file.pkg = try Package.create(fmt.gpa, "root", null, file.sub_file_path); | 4704 | file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path); |
| 4646 | defer file.pkg.destroy(fmt.gpa); | 4705 | defer file.pkg.destroy(fmt.gpa); |
| 4647 | 4706 | ||
| 4648 | if (stat.size > max_src_size) | 4707 | if (stat.size > max_src_size) |
| ... | @@ -5354,7 +5413,7 @@ pub fn cmdAstCheck( | ... | @@ -5354,7 +5413,7 @@ pub fn cmdAstCheck( |
| 5354 | file.stat.size = source.len; | 5413 | file.stat.size = source.len; |
| 5355 | } | 5414 | } |
| 5356 | 5415 | ||
| 5357 | file.pkg = try Package.create(gpa, "root", null, file.sub_file_path); | 5416 | file.pkg = try Package.create(gpa, null, file.sub_file_path); |
| 5358 | defer file.pkg.destroy(gpa); | 5417 | defer file.pkg.destroy(gpa); |
| 5359 | 5418 | ||
| 5360 | file.tree = try Ast.parse(gpa, file.source, .zig); | 5419 | file.tree = try Ast.parse(gpa, file.source, .zig); |
| ... | @@ -5473,7 +5532,7 @@ pub fn cmdChangelist( | ... | @@ -5473,7 +5532,7 @@ pub fn cmdChangelist( |
| 5473 | .root_decl = .none, | 5532 | .root_decl = .none, |
| 5474 | }; | 5533 | }; |
| 5475 | 5534 | ||
| 5476 | file.pkg = try Package.create(gpa, "root", null, file.sub_file_path); | 5535 | file.pkg = try Package.create(gpa, null, file.sub_file_path); |
| 5477 | defer file.pkg.destroy(gpa); | 5536 | defer file.pkg.destroy(gpa); |
| 5478 | 5537 | ||
| 5479 | const source = try arena.allocSentinel(u8, @intCast(usize, stat.size), 0); | 5538 | const source = try arena.allocSentinel(u8, @intCast(usize, stat.size), 0); |
src/target.zig+1| ... | @@ -727,6 +727,7 @@ pub fn supportsFunctionAlignment(target: std.Target) bool { | ... | @@ -727,6 +727,7 @@ pub fn supportsFunctionAlignment(target: std.Target) bool { |
| 727 | pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend) bool { | 727 | pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend) bool { |
| 728 | switch (backend) { | 728 | switch (backend) { |
| 729 | .stage1, .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target), | 729 | .stage1, .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target), |
| 730 | .stage2_c => return true, | ||
| 730 | else => return false, | 731 | else => return false, |
| 731 | } | 732 | } |
| 732 | } | 733 | } |
src/test.zig+38-2| ... | @@ -583,6 +583,11 @@ pub const TestContext = struct { | ... | @@ -583,6 +583,11 @@ pub const TestContext = struct { |
| 583 | path: []const u8, | 583 | path: []const u8, |
| 584 | }; | 584 | }; |
| 585 | 585 | ||
| 586 | pub const DepModule = struct { | ||
| 587 | name: []const u8, | ||
| 588 | path: []const u8, | ||
| 589 | }; | ||
| 590 | |||
| 586 | pub const Backend = enum { | 591 | pub const Backend = enum { |
| 587 | stage1, | 592 | stage1, |
| 588 | stage2, | 593 | stage2, |
| ... | @@ -611,6 +616,7 @@ pub const TestContext = struct { | ... | @@ -611,6 +616,7 @@ pub const TestContext = struct { |
| 611 | link_libc: bool = false, | 616 | link_libc: bool = false, |
| 612 | 617 | ||
| 613 | files: std.ArrayList(File), | 618 | files: std.ArrayList(File), |
| 619 | deps: std.ArrayList(DepModule), | ||
| 614 | 620 | ||
| 615 | result: anyerror!void = {}, | 621 | result: anyerror!void = {}, |
| 616 | 622 | ||
| ... | @@ -618,6 +624,13 @@ pub const TestContext = struct { | ... | @@ -618,6 +624,13 @@ pub const TestContext = struct { |
| 618 | case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory"); | 624 | case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory"); |
| 619 | } | 625 | } |
| 620 | 626 | ||
| 627 | pub fn addDepModule(case: *Case, name: []const u8, path: []const u8) void { | ||
| 628 | case.deps.append(.{ | ||
| 629 | .name = name, | ||
| 630 | .path = path, | ||
| 631 | }) catch @panic("out of memory"); | ||
| 632 | } | ||
| 633 | |||
| 621 | /// Adds a subcase in which the module is updated with `src`, and a C | 634 | /// Adds a subcase in which the module is updated with `src`, and a C |
| 622 | /// header is generated. | 635 | /// header is generated. |
| 623 | pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void { | 636 | pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void { |
| ... | @@ -767,6 +780,7 @@ pub const TestContext = struct { | ... | @@ -767,6 +780,7 @@ pub const TestContext = struct { |
| 767 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | 780 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 768 | .output_mode = .Exe, | 781 | .output_mode = .Exe, |
| 769 | .files = std.ArrayList(File).init(ctx.arena), | 782 | .files = std.ArrayList(File).init(ctx.arena), |
| 783 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 770 | }) catch @panic("out of memory"); | 784 | }) catch @panic("out of memory"); |
| 771 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 785 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 772 | } | 786 | } |
| ... | @@ -787,6 +801,7 @@ pub const TestContext = struct { | ... | @@ -787,6 +801,7 @@ pub const TestContext = struct { |
| 787 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | 801 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 788 | .output_mode = .Exe, | 802 | .output_mode = .Exe, |
| 789 | .files = std.ArrayList(File).init(ctx.arena), | 803 | .files = std.ArrayList(File).init(ctx.arena), |
| 804 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 790 | .link_libc = true, | 805 | .link_libc = true, |
| 791 | }) catch @panic("out of memory"); | 806 | }) catch @panic("out of memory"); |
| 792 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 807 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| ... | @@ -801,6 +816,7 @@ pub const TestContext = struct { | ... | @@ -801,6 +816,7 @@ pub const TestContext = struct { |
| 801 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | 816 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 802 | .output_mode = .Exe, | 817 | .output_mode = .Exe, |
| 803 | .files = std.ArrayList(File).init(ctx.arena), | 818 | .files = std.ArrayList(File).init(ctx.arena), |
| 819 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 804 | .backend = .llvm, | 820 | .backend = .llvm, |
| 805 | .link_libc = true, | 821 | .link_libc = true, |
| 806 | }) catch @panic("out of memory"); | 822 | }) catch @panic("out of memory"); |
| ... | @@ -818,6 +834,7 @@ pub const TestContext = struct { | ... | @@ -818,6 +834,7 @@ pub const TestContext = struct { |
| 818 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | 834 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 819 | .output_mode = .Obj, | 835 | .output_mode = .Obj, |
| 820 | .files = std.ArrayList(File).init(ctx.arena), | 836 | .files = std.ArrayList(File).init(ctx.arena), |
| 837 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 821 | }) catch @panic("out of memory"); | 838 | }) catch @panic("out of memory"); |
| 822 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 839 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 823 | } | 840 | } |
| ... | @@ -834,6 +851,7 @@ pub const TestContext = struct { | ... | @@ -834,6 +851,7 @@ pub const TestContext = struct { |
| 834 | .output_mode = .Exe, | 851 | .output_mode = .Exe, |
| 835 | .is_test = true, | 852 | .is_test = true, |
| 836 | .files = std.ArrayList(File).init(ctx.arena), | 853 | .files = std.ArrayList(File).init(ctx.arena), |
| 854 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 837 | }) catch @panic("out of memory"); | 855 | }) catch @panic("out of memory"); |
| 838 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 856 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 839 | } | 857 | } |
| ... | @@ -858,6 +876,7 @@ pub const TestContext = struct { | ... | @@ -858,6 +876,7 @@ pub const TestContext = struct { |
| 858 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | 876 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 859 | .output_mode = .Obj, | 877 | .output_mode = .Obj, |
| 860 | .files = std.ArrayList(File).init(ctx.arena), | 878 | .files = std.ArrayList(File).init(ctx.arena), |
| 879 | .deps = std.ArrayList(DepModule).init(ctx.arena), | ||
| 861 | }) catch @panic("out of memory"); | 880 | }) catch @panic("out of memory"); |
| 862 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 881 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 863 | } | 882 | } |
| ... | @@ -1145,6 +1164,7 @@ pub const TestContext = struct { | ... | @@ -1145,6 +1164,7 @@ pub const TestContext = struct { |
| 1145 | .output_mode = output_mode, | 1164 | .output_mode = output_mode, |
| 1146 | .link_libc = backend == .llvm, | 1165 | .link_libc = backend == .llvm, |
| 1147 | .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), | 1166 | .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), |
| 1167 | .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), | ||
| 1148 | }); | 1168 | }); |
| 1149 | try cases.append(next); | 1169 | try cases.append(next); |
| 1150 | } | 1170 | } |
| ... | @@ -1497,9 +1517,25 @@ pub const TestContext = struct { | ... | @@ -1497,9 +1517,25 @@ pub const TestContext = struct { |
| 1497 | var main_pkg: Package = .{ | 1517 | var main_pkg: Package = .{ |
| 1498 | .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir }, | 1518 | .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir }, |
| 1499 | .root_src_path = tmp_src_path, | 1519 | .root_src_path = tmp_src_path, |
| 1500 | .name = "root", | ||
| 1501 | }; | 1520 | }; |
| 1502 | defer main_pkg.table.deinit(allocator); | 1521 | defer { |
| 1522 | var it = main_pkg.table.iterator(); | ||
| 1523 | while (it.next()) |kv| { | ||
| 1524 | allocator.free(kv.key_ptr.*); | ||
| 1525 | kv.value_ptr.*.destroy(allocator); | ||
| 1526 | } | ||
| 1527 | main_pkg.table.deinit(allocator); | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | for (case.deps.items) |dep| { | ||
| 1531 | var pkg = try Package.create( | ||
| 1532 | allocator, | ||
| 1533 | tmp_dir_path, | ||
| 1534 | dep.path, | ||
| 1535 | ); | ||
| 1536 | errdefer pkg.destroy(allocator); | ||
| 1537 | try main_pkg.add(allocator, dep.name, pkg); | ||
| 1538 | } | ||
| 1503 | 1539 | ||
| 1504 | const bin_name = try std.zig.binNameAlloc(arena, .{ | 1540 | const bin_name = try std.zig.binNameAlloc(arena, .{ |
| 1505 | .root_name = "test_case", | 1541 | .root_name = "test_case", |
src/value.zig+39-10| ... | @@ -1249,11 +1249,22 @@ pub const Value = extern union { | ... | @@ -1249,11 +1249,22 @@ pub const Value = extern union { |
| 1249 | }; | 1249 | }; |
| 1250 | } | 1250 | } |
| 1251 | 1251 | ||
| 1252 | fn isDeclRef(val: Value) bool { | ||
| 1253 | var check = val; | ||
| 1254 | while (true) switch (check.tag()) { | ||
| 1255 | .variable, .decl_ref, .decl_ref_mut, .comptime_field_ptr => return true, | ||
| 1256 | .field_ptr => check = check.castTag(.field_ptr).?.data.container_ptr, | ||
| 1257 | .elem_ptr => check = check.castTag(.elem_ptr).?.data.array_ptr, | ||
| 1258 | .eu_payload_ptr, .opt_payload_ptr => check = check.cast(Value.Payload.PayloadPtr).?.data.container_ptr, | ||
| 1259 | else => return false, | ||
| 1260 | }; | ||
| 1261 | } | ||
| 1262 | |||
| 1252 | /// Write a Value's contents to `buffer`. | 1263 | /// Write a Value's contents to `buffer`. |
| 1253 | /// | 1264 | /// |
| 1254 | /// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past | 1265 | /// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past |
| 1255 | /// the end of the value in memory. | 1266 | /// the end of the value in memory. |
| 1256 | pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) void { | 1267 | pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) error{ReinterpretDeclRef}!void { |
| 1257 | const target = mod.getTarget(); | 1268 | const target = mod.getTarget(); |
| 1258 | const endian = target.cpu.arch.endian(); | 1269 | const endian = target.cpu.arch.endian(); |
| 1259 | if (val.isUndef()) { | 1270 | if (val.isUndef()) { |
| ... | @@ -1309,7 +1320,7 @@ pub const Value = extern union { | ... | @@ -1309,7 +1320,7 @@ pub const Value = extern union { |
| 1309 | var buf_off: usize = 0; | 1320 | var buf_off: usize = 0; |
| 1310 | while (elem_i < len) : (elem_i += 1) { | 1321 | while (elem_i < len) : (elem_i += 1) { |
| 1311 | const elem_val = val.elemValueBuffer(mod, elem_i, &elem_value_buf); | 1322 | const elem_val = val.elemValueBuffer(mod, elem_i, &elem_value_buf); |
| 1312 | elem_val.writeToMemory(elem_ty, mod, buffer[buf_off..]); | 1323 | try elem_val.writeToMemory(elem_ty, mod, buffer[buf_off..]); |
| 1313 | buf_off += elem_size; | 1324 | buf_off += elem_size; |
| 1314 | } | 1325 | } |
| 1315 | }, | 1326 | }, |
| ... | @@ -1317,7 +1328,7 @@ pub const Value = extern union { | ... | @@ -1317,7 +1328,7 @@ pub const Value = extern union { |
| 1317 | // We use byte_count instead of abi_size here, so that any padding bytes | 1328 | // We use byte_count instead of abi_size here, so that any padding bytes |
| 1318 | // follow the data bytes, on both big- and little-endian systems. | 1329 | // follow the data bytes, on both big- and little-endian systems. |
| 1319 | const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8; | 1330 | const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8; |
| 1320 | writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); | 1331 | return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); |
| 1321 | }, | 1332 | }, |
| 1322 | .Struct => switch (ty.containerLayout()) { | 1333 | .Struct => switch (ty.containerLayout()) { |
| 1323 | .Auto => unreachable, // Sema is supposed to have emitted a compile error already | 1334 | .Auto => unreachable, // Sema is supposed to have emitted a compile error already |
| ... | @@ -1326,12 +1337,12 @@ pub const Value = extern union { | ... | @@ -1326,12 +1337,12 @@ pub const Value = extern union { |
| 1326 | const field_vals = val.castTag(.aggregate).?.data; | 1337 | const field_vals = val.castTag(.aggregate).?.data; |
| 1327 | for (fields, 0..) |field, i| { | 1338 | for (fields, 0..) |field, i| { |
| 1328 | const off = @intCast(usize, ty.structFieldOffset(i, target)); | 1339 | const off = @intCast(usize, ty.structFieldOffset(i, target)); |
| 1329 | writeToMemory(field_vals[i], field.ty, mod, buffer[off..]); | 1340 | try writeToMemory(field_vals[i], field.ty, mod, buffer[off..]); |
| 1330 | } | 1341 | } |
| 1331 | }, | 1342 | }, |
| 1332 | .Packed => { | 1343 | .Packed => { |
| 1333 | const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8; | 1344 | const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8; |
| 1334 | writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); | 1345 | return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); |
| 1335 | }, | 1346 | }, |
| 1336 | }, | 1347 | }, |
| 1337 | .ErrorSet => { | 1348 | .ErrorSet => { |
| ... | @@ -1345,9 +1356,14 @@ pub const Value = extern union { | ... | @@ -1345,9 +1356,14 @@ pub const Value = extern union { |
| 1345 | .Extern => @panic("TODO implement writeToMemory for extern unions"), | 1356 | .Extern => @panic("TODO implement writeToMemory for extern unions"), |
| 1346 | .Packed => { | 1357 | .Packed => { |
| 1347 | const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8; | 1358 | const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8; |
| 1348 | writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); | 1359 | return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); |
| 1349 | }, | 1360 | }, |
| 1350 | }, | 1361 | }, |
| 1362 | .Pointer => { | ||
| 1363 | assert(!ty.isSlice()); // No well defined layout. | ||
| 1364 | if (val.isDeclRef()) return error.ReinterpretDeclRef; | ||
| 1365 | return val.writeToMemory(Type.usize, mod, buffer); | ||
| 1366 | }, | ||
| 1351 | else => @panic("TODO implement writeToMemory for more types"), | 1367 | else => @panic("TODO implement writeToMemory for more types"), |
| 1352 | } | 1368 | } |
| 1353 | } | 1369 | } |
| ... | @@ -1356,7 +1372,7 @@ pub const Value = extern union { | ... | @@ -1356,7 +1372,7 @@ pub const Value = extern union { |
| 1356 | /// | 1372 | /// |
| 1357 | /// Both the start and the end of the provided buffer must be tight, since | 1373 | /// Both the start and the end of the provided buffer must be tight, since |
| 1358 | /// big-endian packed memory layouts start at the end of the buffer. | 1374 | /// big-endian packed memory layouts start at the end of the buffer. |
| 1359 | pub fn writeToPackedMemory(val: Value, ty: Type, mod: *Module, buffer: []u8, bit_offset: usize) void { | 1375 | pub fn writeToPackedMemory(val: Value, ty: Type, mod: *Module, buffer: []u8, bit_offset: usize) error{ReinterpretDeclRef}!void { |
| 1360 | const target = mod.getTarget(); | 1376 | const target = mod.getTarget(); |
| 1361 | const endian = target.cpu.arch.endian(); | 1377 | const endian = target.cpu.arch.endian(); |
| 1362 | if (val.isUndef()) { | 1378 | if (val.isUndef()) { |
| ... | @@ -1420,7 +1436,7 @@ pub const Value = extern union { | ... | @@ -1420,7 +1436,7 @@ pub const Value = extern union { |
| 1420 | // On big-endian systems, LLVM reverses the element order of vectors by default | 1436 | // On big-endian systems, LLVM reverses the element order of vectors by default |
| 1421 | const tgt_elem_i = if (endian == .Big) len - elem_i - 1 else elem_i; | 1437 | const tgt_elem_i = if (endian == .Big) len - elem_i - 1 else elem_i; |
| 1422 | const elem_val = val.elemValueBuffer(mod, tgt_elem_i, &elem_value_buf); | 1438 | const elem_val = val.elemValueBuffer(mod, tgt_elem_i, &elem_value_buf); |
| 1423 | elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits); | 1439 | try elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits); |
| 1424 | bits += elem_bit_size; | 1440 | bits += elem_bit_size; |
| 1425 | } | 1441 | } |
| 1426 | }, | 1442 | }, |
| ... | @@ -1433,7 +1449,7 @@ pub const Value = extern union { | ... | @@ -1433,7 +1449,7 @@ pub const Value = extern union { |
| 1433 | const field_vals = val.castTag(.aggregate).?.data; | 1449 | const field_vals = val.castTag(.aggregate).?.data; |
| 1434 | for (fields, 0..) |field, i| { | 1450 | for (fields, 0..) |field, i| { |
| 1435 | const field_bits = @intCast(u16, field.ty.bitSize(target)); | 1451 | const field_bits = @intCast(u16, field.ty.bitSize(target)); |
| 1436 | field_vals[i].writeToPackedMemory(field.ty, mod, buffer, bit_offset + bits); | 1452 | try field_vals[i].writeToPackedMemory(field.ty, mod, buffer, bit_offset + bits); |
| 1437 | bits += field_bits; | 1453 | bits += field_bits; |
| 1438 | } | 1454 | } |
| 1439 | }, | 1455 | }, |
| ... | @@ -1446,9 +1462,14 @@ pub const Value = extern union { | ... | @@ -1446,9 +1462,14 @@ pub const Value = extern union { |
| 1446 | const field_type = ty.unionFields().values()[field_index.?].ty; | 1462 | const field_type = ty.unionFields().values()[field_index.?].ty; |
| 1447 | const field_val = val.fieldValue(field_type, field_index.?); | 1463 | const field_val = val.fieldValue(field_type, field_index.?); |
| 1448 | 1464 | ||
| 1449 | field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); | 1465 | return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); |
| 1450 | }, | 1466 | }, |
| 1451 | }, | 1467 | }, |
| 1468 | .Pointer => { | ||
| 1469 | assert(!ty.isSlice()); // No well defined layout. | ||
| 1470 | if (val.isDeclRef()) return error.ReinterpretDeclRef; | ||
| 1471 | return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset); | ||
| 1472 | }, | ||
| 1452 | else => @panic("TODO implement writeToPackedMemory for more types"), | 1473 | else => @panic("TODO implement writeToPackedMemory for more types"), |
| 1453 | } | 1474 | } |
| 1454 | } | 1475 | } |
| ... | @@ -1553,6 +1574,10 @@ pub const Value = extern union { | ... | @@ -1553,6 +1574,10 @@ pub const Value = extern union { |
| 1553 | }; | 1574 | }; |
| 1554 | return Value.initPayload(&payload.base); | 1575 | return Value.initPayload(&payload.base); |
| 1555 | }, | 1576 | }, |
| 1577 | .Pointer => { | ||
| 1578 | assert(!ty.isSlice()); // No well defined layout. | ||
| 1579 | return readFromMemory(Type.usize, mod, buffer, arena); | ||
| 1580 | }, | ||
| 1556 | else => @panic("TODO implement readFromMemory for more types"), | 1581 | else => @panic("TODO implement readFromMemory for more types"), |
| 1557 | } | 1582 | } |
| 1558 | } | 1583 | } |
| ... | @@ -1640,6 +1665,10 @@ pub const Value = extern union { | ... | @@ -1640,6 +1665,10 @@ pub const Value = extern union { |
| 1640 | return Tag.aggregate.create(arena, field_vals); | 1665 | return Tag.aggregate.create(arena, field_vals); |
| 1641 | }, | 1666 | }, |
| 1642 | }, | 1667 | }, |
| 1668 | .Pointer => { | ||
| 1669 | assert(!ty.isSlice()); // No well defined layout. | ||
| 1670 | return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena); | ||
| 1671 | }, | ||
| 1643 | else => @panic("TODO implement readFromPackedMemory for more types"), | 1672 | else => @panic("TODO implement readFromPackedMemory for more types"), |
| 1644 | } | 1673 | } |
| 1645 | } | 1674 | } |
stage1/zig.h created+2486| ... | @@ -0,0 +1,2486 @@ | ||
| 1 | #undef linux | ||
| 2 | |||
| 3 | #define __STDC_WANT_IEC_60559_TYPES_EXT__ | ||
| 4 | #include <float.h> | ||
| 5 | #include <limits.h> | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <stdint.h> | ||
| 8 | |||
| 9 | #if _MSC_VER | ||
| 10 | #include <intrin.h> | ||
| 11 | #elif defined(__i386__) || defined(__x86_64__) | ||
| 12 | #include <cpuid.h> | ||
| 13 | #endif | ||
| 14 | |||
| 15 | #if !defined(__cplusplus) && __STDC_VERSION__ <= 201710L | ||
| 16 | #if __STDC_VERSION__ >= 199901L | ||
| 17 | #include <stdbool.h> | ||
| 18 | #else | ||
| 19 | typedef char bool; | ||
| 20 | #define false 0 | ||
| 21 | #define true 1 | ||
| 22 | #endif | ||
| 23 | #endif | ||
| 24 | |||
| 25 | #if defined(__has_builtin) | ||
| 26 | #define zig_has_builtin(builtin) __has_builtin(__builtin_##builtin) | ||
| 27 | #else | ||
| 28 | #define zig_has_builtin(builtin) 0 | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #if defined(__has_attribute) | ||
| 32 | #define zig_has_attribute(attribute) __has_attribute(attribute) | ||
| 33 | #else | ||
| 34 | #define zig_has_attribute(attribute) 0 | ||
| 35 | #endif | ||
| 36 | |||
| 37 | #if __STDC_VERSION__ >= 201112L | ||
| 38 | #define zig_threadlocal _Thread_local | ||
| 39 | #elif defined(__GNUC__) | ||
| 40 | #define zig_threadlocal __thread | ||
| 41 | #elif _MSC_VER | ||
| 42 | #define zig_threadlocal __declspec(thread) | ||
| 43 | #else | ||
| 44 | #define zig_threadlocal zig_threadlocal_unavailable | ||
| 45 | #endif | ||
| 46 | |||
| 47 | #if defined(__clang__) | ||
| 48 | #define zig_clang | ||
| 49 | #elif defined(__GNUC__) | ||
| 50 | #define zig_gnuc | ||
| 51 | #endif | ||
| 52 | |||
| 53 | #if _MSC_VER | ||
| 54 | #define zig_const_arr | ||
| 55 | #define zig_callconv(c) __##c | ||
| 56 | #else | ||
| 57 | #define zig_const_arr static const | ||
| 58 | #define zig_callconv(c) __attribute__((c)) | ||
| 59 | #endif | ||
| 60 | |||
| 61 | #if zig_has_attribute(naked) || defined(zig_gnuc) | ||
| 62 | #define zig_naked_decl __attribute__((naked)) | ||
| 63 | #define zig_naked __attribute__((naked)) | ||
| 64 | #elif defined(_MSC_VER) | ||
| 65 | #define zig_naked_decl | ||
| 66 | #define zig_naked __declspec(naked) | ||
| 67 | #else | ||
| 68 | #define zig_naked_decl zig_naked_unavailable | ||
| 69 | #define zig_naked zig_naked_unavailable | ||
| 70 | #endif | ||
| 71 | |||
| 72 | #if zig_has_attribute(cold) | ||
| 73 | #define zig_cold __attribute__((cold)) | ||
| 74 | #else | ||
| 75 | #define zig_cold | ||
| 76 | #endif | ||
| 77 | |||
| 78 | #if __STDC_VERSION__ >= 199901L | ||
| 79 | #define zig_restrict restrict | ||
| 80 | #elif defined(__GNUC__) | ||
| 81 | #define zig_restrict __restrict | ||
| 82 | #else | ||
| 83 | #define zig_restrict | ||
| 84 | #endif | ||
| 85 | |||
| 86 | #if __STDC_VERSION__ >= 201112L | ||
| 87 | #define zig_align(alignment) _Alignas(alignment) | ||
| 88 | #elif zig_has_attribute(aligned) | ||
| 89 | #define zig_align(alignment) __attribute__((aligned(alignment))) | ||
| 90 | #elif _MSC_VER | ||
| 91 | #define zig_align(alignment) __declspec(align(alignment)) | ||
| 92 | #else | ||
| 93 | #define zig_align zig_align_unavailable | ||
| 94 | #endif | ||
| 95 | |||
| 96 | #if zig_has_attribute(aligned) | ||
| 97 | #define zig_under_align(alignment) __attribute__((aligned(alignment))) | ||
| 98 | #elif _MSC_VER | ||
| 99 | #define zig_under_align(alignment) zig_align(alignment) | ||
| 100 | #else | ||
| 101 | #define zig_align zig_align_unavailable | ||
| 102 | #endif | ||
| 103 | |||
| 104 | #if zig_has_attribute(aligned) | ||
| 105 | #define zig_align_fn(alignment) __attribute__((aligned(alignment))) | ||
| 106 | #elif _MSC_VER | ||
| 107 | #define zig_align_fn(alignment) | ||
| 108 | #else | ||
| 109 | #define zig_align_fn zig_align_fn_unavailable | ||
| 110 | #endif | ||
| 111 | |||
| 112 | #if zig_has_attribute(packed) | ||
| 113 | #define zig_packed(definition) __attribute__((packed)) definition | ||
| 114 | #elif _MSC_VER | ||
| 115 | #define zig_packed(definition) __pragma(pack(1)) definition __pragma(pack()) | ||
| 116 | #else | ||
| 117 | #define zig_packed(definition) zig_packed_unavailable | ||
| 118 | #endif | ||
| 119 | |||
| 120 | #if zig_has_attribute(section) | ||
| 121 | #define zig_linksection(name, def, ...) def __attribute__((section(name))) | ||
| 122 | #elif _MSC_VER | ||
| 123 | #define zig_linksection(name, def, ...) __pragma(section(name, __VA_ARGS__)) __declspec(allocate(name)) def | ||
| 124 | #else | ||
| 125 | #define zig_linksection(name, def, ...) zig_linksection_unavailable | ||
| 126 | #endif | ||
| 127 | |||
| 128 | #if zig_has_builtin(unreachable) || defined(zig_gnuc) | ||
| 129 | #define zig_unreachable() __builtin_unreachable() | ||
| 130 | #else | ||
| 131 | #define zig_unreachable() | ||
| 132 | #endif | ||
| 133 | |||
| 134 | #if defined(__cplusplus) | ||
| 135 | #define zig_extern extern "C" | ||
| 136 | #else | ||
| 137 | #define zig_extern extern | ||
| 138 | #endif | ||
| 139 | |||
| 140 | #if zig_has_attribute(alias) | ||
| 141 | #define zig_export(sig, symbol, name) zig_extern sig __attribute__((alias(symbol))) | ||
| 142 | #elif _MSC_VER | ||
| 143 | #if _M_X64 | ||
| 144 | #define zig_export(sig, symbol, name) sig;\ | ||
| 145 | __pragma(comment(linker, "/alternatename:" name "=" symbol )) | ||
| 146 | #else /*_M_X64 */ | ||
| 147 | #define zig_export(sig, symbol, name) sig;\ | ||
| 148 | __pragma(comment(linker, "/alternatename:_" name "=_" symbol )) | ||
| 149 | #endif /*_M_X64 */ | ||
| 150 | #else | ||
| 151 | #define zig_export(sig, symbol, name) __asm(name " = " symbol) | ||
| 152 | #endif | ||
| 153 | |||
| 154 | #if zig_has_builtin(debugtrap) | ||
| 155 | #define zig_breakpoint() __builtin_debugtrap() | ||
| 156 | #elif zig_has_builtin(trap) || defined(zig_gnuc) | ||
| 157 | #define zig_breakpoint() __builtin_trap() | ||
| 158 | #elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) | ||
| 159 | #define zig_breakpoint() __debugbreak() | ||
| 160 | #elif defined(__i386__) || defined(__x86_64__) | ||
| 161 | #define zig_breakpoint() __asm__ volatile("int $0x03"); | ||
| 162 | #else | ||
| 163 | #define zig_breakpoint() raise(SIGTRAP) | ||
| 164 | #endif | ||
| 165 | |||
| 166 | #if zig_has_builtin(return_address) || defined(zig_gnuc) | ||
| 167 | #define zig_return_address() __builtin_extract_return_addr(__builtin_return_address(0)) | ||
| 168 | #elif defined(_MSC_VER) | ||
| 169 | #define zig_return_address() _ReturnAddress() | ||
| 170 | #else | ||
| 171 | #define zig_return_address() 0 | ||
| 172 | #endif | ||
| 173 | |||
| 174 | #if zig_has_builtin(frame_address) || defined(zig_gnuc) | ||
| 175 | #define zig_frame_address() __builtin_frame_address(0) | ||
| 176 | #else | ||
| 177 | #define zig_frame_address() 0 | ||
| 178 | #endif | ||
| 179 | |||
| 180 | #if zig_has_builtin(prefetch) || defined(zig_gnuc) | ||
| 181 | #define zig_prefetch(addr, rw, locality) __builtin_prefetch(addr, rw, locality) | ||
| 182 | #else | ||
| 183 | #define zig_prefetch(addr, rw, locality) | ||
| 184 | #endif | ||
| 185 | |||
| 186 | #if zig_has_builtin(memory_size) && zig_has_builtin(memory_grow) | ||
| 187 | #define zig_wasm_memory_size(index) __builtin_wasm_memory_size(index) | ||
| 188 | #define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta) | ||
| 189 | #else | ||
| 190 | #define zig_wasm_memory_size(index) zig_unimplemented() | ||
| 191 | #define zig_wasm_memory_grow(index, delta) zig_unimplemented() | ||
| 192 | #endif | ||
| 193 | |||
| 194 | #define zig_concat(lhs, rhs) lhs##rhs | ||
| 195 | #define zig_expand_concat(lhs, rhs) zig_concat(lhs, rhs) | ||
| 196 | |||
| 197 | #if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) | ||
| 198 | #include <stdatomic.h> | ||
| 199 | #define zig_atomic(type) _Atomic(type) | ||
| 200 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail) | ||
| 201 | #define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) atomic_compare_exchange_weak_explicit (obj, &(expected), desired, succ, fail) | ||
| 202 | #define zig_atomicrmw_xchg(obj, arg, order, type) atomic_exchange_explicit (obj, arg, order) | ||
| 203 | #define zig_atomicrmw_add(obj, arg, order, type) atomic_fetch_add_explicit (obj, arg, order) | ||
| 204 | #define zig_atomicrmw_sub(obj, arg, order, type) atomic_fetch_sub_explicit (obj, arg, order) | ||
| 205 | #define zig_atomicrmw_or(obj, arg, order, type) atomic_fetch_or_explicit (obj, arg, order) | ||
| 206 | #define zig_atomicrmw_xor(obj, arg, order, type) atomic_fetch_xor_explicit (obj, arg, order) | ||
| 207 | #define zig_atomicrmw_and(obj, arg, order, type) atomic_fetch_and_explicit (obj, arg, order) | ||
| 208 | #define zig_atomicrmw_nand(obj, arg, order, type) __atomic_fetch_nand (obj, arg, order) | ||
| 209 | #define zig_atomicrmw_min(obj, arg, order, type) __atomic_fetch_min (obj, arg, order) | ||
| 210 | #define zig_atomicrmw_max(obj, arg, order, type) __atomic_fetch_max (obj, arg, order) | ||
| 211 | #define zig_atomic_store(obj, arg, order, type) atomic_store_explicit (obj, arg, order) | ||
| 212 | #define zig_atomic_load(obj, order, type) atomic_load_explicit (obj, order) | ||
| 213 | #define zig_fence(order) atomic_thread_fence(order) | ||
| 214 | #elif defined(__GNUC__) | ||
| 215 | #define memory_order_relaxed __ATOMIC_RELAXED | ||
| 216 | #define memory_order_consume __ATOMIC_CONSUME | ||
| 217 | #define memory_order_acquire __ATOMIC_ACQUIRE | ||
| 218 | #define memory_order_release __ATOMIC_RELEASE | ||
| 219 | #define memory_order_acq_rel __ATOMIC_ACQ_REL | ||
| 220 | #define memory_order_seq_cst __ATOMIC_SEQ_CST | ||
| 221 | #define zig_atomic(type) type | ||
| 222 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) __atomic_compare_exchange_n(obj, &(expected), desired, false, succ, fail) | ||
| 223 | #define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) __atomic_compare_exchange_n(obj, &(expected), desired, true , succ, fail) | ||
| 224 | #define zig_atomicrmw_xchg(obj, arg, order, type) __atomic_exchange_n(obj, arg, order) | ||
| 225 | #define zig_atomicrmw_add(obj, arg, order, type) __atomic_fetch_add (obj, arg, order) | ||
| 226 | #define zig_atomicrmw_sub(obj, arg, order, type) __atomic_fetch_sub (obj, arg, order) | ||
| 227 | #define zig_atomicrmw_or(obj, arg, order, type) __atomic_fetch_or (obj, arg, order) | ||
| 228 | #define zig_atomicrmw_xor(obj, arg, order, type) __atomic_fetch_xor (obj, arg, order) | ||
| 229 | #define zig_atomicrmw_and(obj, arg, order, type) __atomic_fetch_and (obj, arg, order) | ||
| 230 | #define zig_atomicrmw_nand(obj, arg, order, type) __atomic_fetch_nand(obj, arg, order) | ||
| 231 | #define zig_atomicrmw_min(obj, arg, order, type) __atomic_fetch_min (obj, arg, order) | ||
| 232 | #define zig_atomicrmw_max(obj, arg, order, type) __atomic_fetch_max (obj, arg, order) | ||
| 233 | #define zig_atomic_store(obj, arg, order, type) __atomic_store_n (obj, arg, order) | ||
| 234 | #define zig_atomic_load(obj, order, type) __atomic_load_n (obj, order) | ||
| 235 | #define zig_fence(order) __atomic_thread_fence(order) | ||
| 236 | #elif _MSC_VER && (_M_IX86 || _M_X64) | ||
| 237 | #define memory_order_relaxed 0 | ||
| 238 | #define memory_order_consume 1 | ||
| 239 | #define memory_order_acquire 2 | ||
| 240 | #define memory_order_release 3 | ||
| 241 | #define memory_order_acq_rel 4 | ||
| 242 | #define memory_order_seq_cst 5 | ||
| 243 | #define zig_atomic(type) type | ||
| 244 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) zig_expand_concat(zig_msvc_cmpxchg_, type)(obj, &(expected), desired) | ||
| 245 | #define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) | ||
| 246 | #define zig_atomicrmw_xchg(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_xchg_, type)(obj, arg) | ||
| 247 | #define zig_atomicrmw_add(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_add_, type)(obj, arg) | ||
| 248 | #define zig_atomicrmw_sub(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_sub_, type)(obj, arg) | ||
| 249 | #define zig_atomicrmw_or(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_or_, type)(obj, arg) | ||
| 250 | #define zig_atomicrmw_xor(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_xor_, type)(obj, arg) | ||
| 251 | #define zig_atomicrmw_and(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_and_, type)(obj, arg) | ||
| 252 | #define zig_atomicrmw_nand(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_nand_, type)(obj, arg) | ||
| 253 | #define zig_atomicrmw_min(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_min_, type)(obj, arg) | ||
| 254 | #define zig_atomicrmw_max(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_max_, type)(obj, arg) | ||
| 255 | #define zig_atomic_store(obj, arg, order, type) zig_expand_concat(zig_msvc_atomic_store_, type)(obj, arg) | ||
| 256 | #define zig_atomic_load(obj, order, type) zig_expand_concat(zig_msvc_atomic_load_, type)(obj) | ||
| 257 | #if _M_X64 | ||
| 258 | #define zig_fence(order) __faststorefence() | ||
| 259 | #else | ||
| 260 | #define zig_fence(order) zig_msvc_atomic_barrier() | ||
| 261 | #endif | ||
| 262 | |||
| 263 | // TODO: _MSC_VER && (_M_ARM || _M_ARM64) | ||
| 264 | #else | ||
| 265 | #define memory_order_relaxed 0 | ||
| 266 | #define memory_order_consume 1 | ||
| 267 | #define memory_order_acquire 2 | ||
| 268 | #define memory_order_release 3 | ||
| 269 | #define memory_order_acq_rel 4 | ||
| 270 | #define memory_order_seq_cst 5 | ||
| 271 | #define zig_atomic(type) type | ||
| 272 | #define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) zig_unimplemented() | ||
| 273 | #define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) zig_unimplemented() | ||
| 274 | #define zig_atomicrmw_xchg(obj, arg, order, type) zig_unimplemented() | ||
| 275 | #define zig_atomicrmw_add(obj, arg, order, type) zig_unimplemented() | ||
| 276 | #define zig_atomicrmw_sub(obj, arg, order, type) zig_unimplemented() | ||
| 277 | #define zig_atomicrmw_or(obj, arg, order, type) zig_unimplemented() | ||
| 278 | #define zig_atomicrmw_xor(obj, arg, order, type) zig_unimplemented() | ||
| 279 | #define zig_atomicrmw_and(obj, arg, order, type) zig_unimplemented() | ||
| 280 | #define zig_atomicrmw_nand(obj, arg, order, type) zig_unimplemented() | ||
| 281 | #define zig_atomicrmw_min(obj, arg, order, type) zig_unimplemented() | ||
| 282 | #define zig_atomicrmw_max(obj, arg, order, type) zig_unimplemented() | ||
| 283 | #define zig_atomic_store(obj, arg, order, type) zig_unimplemented() | ||
| 284 | #define zig_atomic_load(obj, order, type) zig_unimplemented() | ||
| 285 | #define zig_fence(order) zig_unimplemented() | ||
| 286 | #endif | ||
| 287 | |||
| 288 | #if __STDC_VERSION__ >= 201112L | ||
| 289 | #define zig_noreturn _Noreturn void | ||
| 290 | #elif zig_has_attribute(noreturn) || defined(zig_gnuc) | ||
| 291 | #define zig_noreturn __attribute__((noreturn)) void | ||
| 292 | #elif _MSC_VER | ||
| 293 | #define zig_noreturn __declspec(noreturn) void | ||
| 294 | #else | ||
| 295 | #define zig_noreturn void | ||
| 296 | #endif | ||
| 297 | |||
| 298 | #define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) | ||
| 299 | |||
| 300 | typedef uintptr_t zig_usize; | ||
| 301 | typedef intptr_t zig_isize; | ||
| 302 | typedef signed short int zig_c_short; | ||
| 303 | typedef unsigned short int zig_c_ushort; | ||
| 304 | typedef signed int zig_c_int; | ||
| 305 | typedef unsigned int zig_c_uint; | ||
| 306 | typedef signed long int zig_c_long; | ||
| 307 | typedef unsigned long int zig_c_ulong; | ||
| 308 | typedef signed long long int zig_c_longlong; | ||
| 309 | typedef unsigned long long int zig_c_ulonglong; | ||
| 310 | |||
| 311 | typedef uint8_t zig_u8; | ||
| 312 | typedef int8_t zig_i8; | ||
| 313 | typedef uint16_t zig_u16; | ||
| 314 | typedef int16_t zig_i16; | ||
| 315 | typedef uint32_t zig_u32; | ||
| 316 | typedef int32_t zig_i32; | ||
| 317 | typedef uint64_t zig_u64; | ||
| 318 | typedef int64_t zig_i64; | ||
| 319 | |||
| 320 | #define zig_as_u8(val) UINT8_C(val) | ||
| 321 | #define zig_as_i8(val) INT8_C(val) | ||
| 322 | #define zig_as_u16(val) UINT16_C(val) | ||
| 323 | #define zig_as_i16(val) INT16_C(val) | ||
| 324 | #define zig_as_u32(val) UINT32_C(val) | ||
| 325 | #define zig_as_i32(val) INT32_C(val) | ||
| 326 | #define zig_as_u64(val) UINT64_C(val) | ||
| 327 | #define zig_as_i64(val) INT64_C(val) | ||
| 328 | |||
| 329 | #define zig_minInt_u8 zig_as_u8(0) | ||
| 330 | #define zig_maxInt_u8 UINT8_MAX | ||
| 331 | #define zig_minInt_i8 INT8_MIN | ||
| 332 | #define zig_maxInt_i8 INT8_MAX | ||
| 333 | #define zig_minInt_u16 zig_as_u16(0) | ||
| 334 | #define zig_maxInt_u16 UINT16_MAX | ||
| 335 | #define zig_minInt_i16 INT16_MIN | ||
| 336 | #define zig_maxInt_i16 INT16_MAX | ||
| 337 | #define zig_minInt_u32 zig_as_u32(0) | ||
| 338 | #define zig_maxInt_u32 UINT32_MAX | ||
| 339 | #define zig_minInt_i32 INT32_MIN | ||
| 340 | #define zig_maxInt_i32 INT32_MAX | ||
| 341 | #define zig_minInt_u64 zig_as_u64(0) | ||
| 342 | #define zig_maxInt_u64 UINT64_MAX | ||
| 343 | #define zig_minInt_i64 INT64_MIN | ||
| 344 | #define zig_maxInt_i64 INT64_MAX | ||
| 345 | |||
| 346 | #define zig_compiler_rt_abbrev_u32 si | ||
| 347 | #define zig_compiler_rt_abbrev_i32 si | ||
| 348 | #define zig_compiler_rt_abbrev_u64 di | ||
| 349 | #define zig_compiler_rt_abbrev_i64 di | ||
| 350 | #define zig_compiler_rt_abbrev_u128 ti | ||
| 351 | #define zig_compiler_rt_abbrev_i128 ti | ||
| 352 | #define zig_compiler_rt_abbrev_f16 hf | ||
| 353 | #define zig_compiler_rt_abbrev_f32 sf | ||
| 354 | #define zig_compiler_rt_abbrev_f64 df | ||
| 355 | #define zig_compiler_rt_abbrev_f80 xf | ||
| 356 | #define zig_compiler_rt_abbrev_f128 tf | ||
| 357 | |||
| 358 | zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, zig_usize); | ||
| 359 | zig_extern void *memset (void *, int, zig_usize); | ||
| 360 | |||
| 361 | /* ==================== 8/16/32/64-bit Integer Routines ===================== */ | ||
| 362 | |||
| 363 | #define zig_maxInt(Type, bits) zig_shr_##Type(zig_maxInt_##Type, (zig_bitSizeOf(zig_##Type) - bits)) | ||
| 364 | #define zig_expand_maxInt(Type, bits) zig_maxInt(Type, bits) | ||
| 365 | #define zig_minInt(Type, bits) zig_not_##Type(zig_maxInt(Type, bits), bits) | ||
| 366 | #define zig_expand_minInt(Type, bits) zig_minInt(Type, bits) | ||
| 367 | |||
| 368 | #define zig_int_operator(Type, RhsType, operation, operator) \ | ||
| 369 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##RhsType rhs) { \ | ||
| 370 | return lhs operator rhs; \ | ||
| 371 | } | ||
| 372 | #define zig_int_basic_operator(Type, operation, operator) \ | ||
| 373 | zig_int_operator(Type, Type, operation, operator) | ||
| 374 | #define zig_int_shift_operator(Type, operation, operator) \ | ||
| 375 | zig_int_operator(Type, u8, operation, operator) | ||
| 376 | #define zig_int_helpers(w) \ | ||
| 377 | zig_int_basic_operator(u##w, and, &) \ | ||
| 378 | zig_int_basic_operator(i##w, and, &) \ | ||
| 379 | zig_int_basic_operator(u##w, or, |) \ | ||
| 380 | zig_int_basic_operator(i##w, or, |) \ | ||
| 381 | zig_int_basic_operator(u##w, xor, ^) \ | ||
| 382 | zig_int_basic_operator(i##w, xor, ^) \ | ||
| 383 | zig_int_shift_operator(u##w, shl, <<) \ | ||
| 384 | zig_int_shift_operator(i##w, shl, <<) \ | ||
| 385 | zig_int_shift_operator(u##w, shr, >>) \ | ||
| 386 | \ | ||
| 387 | static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \ | ||
| 388 | zig_i##w sign_mask = lhs < zig_as_i##w(0) ? -zig_as_i##w(1) : zig_as_i##w(0); \ | ||
| 389 | return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ | ||
| 390 | } \ | ||
| 391 | \ | ||
| 392 | static inline zig_u##w zig_not_u##w(zig_u##w val, zig_u8 bits) { \ | ||
| 393 | return val ^ zig_maxInt(u##w, bits); \ | ||
| 394 | } \ | ||
| 395 | \ | ||
| 396 | static inline zig_i##w zig_not_i##w(zig_i##w val, zig_u8 bits) { \ | ||
| 397 | (void)bits; \ | ||
| 398 | return ~val; \ | ||
| 399 | } \ | ||
| 400 | \ | ||
| 401 | static inline zig_u##w zig_wrap_u##w(zig_u##w val, zig_u8 bits) { \ | ||
| 402 | return val & zig_maxInt(u##w, bits); \ | ||
| 403 | } \ | ||
| 404 | \ | ||
| 405 | static inline zig_i##w zig_wrap_i##w(zig_i##w val, zig_u8 bits) { \ | ||
| 406 | return (val & zig_as_u##w(1) << (bits - zig_as_u8(1))) != 0 \ | ||
| 407 | ? val | zig_minInt(i##w, bits) : val & zig_maxInt(i##w, bits); \ | ||
| 408 | } \ | ||
| 409 | \ | ||
| 410 | zig_int_basic_operator(u##w, div_floor, /) \ | ||
| 411 | \ | ||
| 412 | static inline zig_i##w zig_div_floor_i##w(zig_i##w lhs, zig_i##w rhs) { \ | ||
| 413 | return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < zig_as_i##w(0)); \ | ||
| 414 | } \ | ||
| 415 | \ | ||
| 416 | zig_int_basic_operator(u##w, mod, %) \ | ||
| 417 | \ | ||
| 418 | static inline zig_i##w zig_mod_i##w(zig_i##w lhs, zig_i##w rhs) { \ | ||
| 419 | zig_i##w rem = lhs % rhs; \ | ||
| 420 | return rem + (((lhs ^ rhs) & rem) < zig_as_i##w(0) ? rhs : zig_as_i##w(0)); \ | ||
| 421 | } \ | ||
| 422 | \ | ||
| 423 | static inline zig_u##w zig_shlw_u##w(zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ | ||
| 424 | return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \ | ||
| 425 | } \ | ||
| 426 | \ | ||
| 427 | static inline zig_i##w zig_shlw_i##w(zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ | ||
| 428 | return zig_wrap_i##w((zig_i##w)zig_shl_u##w((zig_u##w)lhs, (zig_u##w)rhs), bits); \ | ||
| 429 | } \ | ||
| 430 | \ | ||
| 431 | static inline zig_u##w zig_addw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | ||
| 432 | return zig_wrap_u##w(lhs + rhs, bits); \ | ||
| 433 | } \ | ||
| 434 | \ | ||
| 435 | static inline zig_i##w zig_addw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | ||
| 436 | return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs + (zig_u##w)rhs), bits); \ | ||
| 437 | } \ | ||
| 438 | \ | ||
| 439 | static inline zig_u##w zig_subw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | ||
| 440 | return zig_wrap_u##w(lhs - rhs, bits); \ | ||
| 441 | } \ | ||
| 442 | \ | ||
| 443 | static inline zig_i##w zig_subw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | ||
| 444 | return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs - (zig_u##w)rhs), bits); \ | ||
| 445 | } \ | ||
| 446 | \ | ||
| 447 | static inline zig_u##w zig_mulw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | ||
| 448 | return zig_wrap_u##w(lhs * rhs, bits); \ | ||
| 449 | } \ | ||
| 450 | \ | ||
| 451 | static inline zig_i##w zig_mulw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | ||
| 452 | return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs * (zig_u##w)rhs), bits); \ | ||
| 453 | } | ||
| 454 | zig_int_helpers(8) | ||
| 455 | zig_int_helpers(16) | ||
| 456 | zig_int_helpers(32) | ||
| 457 | zig_int_helpers(64) | ||
| 458 | |||
| 459 | static inline bool zig_addo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { | ||
| 460 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | ||
| 461 | zig_u32 full_res; | ||
| 462 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | ||
| 463 | *res = zig_wrap_u32(full_res, bits); | ||
| 464 | return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); | ||
| 465 | #else | ||
| 466 | *res = zig_addw_u32(lhs, rhs, bits); | ||
| 467 | return *res < lhs; | ||
| 468 | #endif | ||
| 469 | } | ||
| 470 | |||
| 471 | static inline void zig_vaddo_u32(zig_u8 *ov, zig_u32 *res, int n, | ||
| 472 | const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) | ||
| 473 | { | ||
| 474 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits); | ||
| 475 | } | ||
| 476 | |||
| 477 | zig_extern zig_i32 __addosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); | ||
| 478 | static inline bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { | ||
| 479 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | ||
| 480 | zig_i32 full_res; | ||
| 481 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | ||
| 482 | #else | ||
| 483 | zig_c_int overflow_int; | ||
| 484 | zig_i32 full_res = __addosi4(lhs, rhs, &overflow_int); | ||
| 485 | bool overflow = overflow_int != 0; | ||
| 486 | #endif | ||
| 487 | *res = zig_wrap_i32(full_res, bits); | ||
| 488 | return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); | ||
| 489 | } | ||
| 490 | |||
| 491 | static inline void zig_vaddo_i32(zig_u8 *ov, zig_i32 *res, int n, | ||
| 492 | const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) | ||
| 493 | { | ||
| 494 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits); | ||
| 495 | } | ||
| 496 | |||
| 497 | static inline bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { | ||
| 498 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | ||
| 499 | zig_u64 full_res; | ||
| 500 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | ||
| 501 | *res = zig_wrap_u64(full_res, bits); | ||
| 502 | return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); | ||
| 503 | #else | ||
| 504 | *res = zig_addw_u64(lhs, rhs, bits); | ||
| 505 | return *res < lhs; | ||
| 506 | #endif | ||
| 507 | } | ||
| 508 | |||
| 509 | static inline void zig_vaddo_u64(zig_u8 *ov, zig_u64 *res, int n, | ||
| 510 | const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) | ||
| 511 | { | ||
| 512 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits); | ||
| 513 | } | ||
| 514 | |||
| 515 | zig_extern zig_i64 __addodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); | ||
| 516 | static inline bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { | ||
| 517 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | ||
| 518 | zig_i64 full_res; | ||
| 519 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | ||
| 520 | #else | ||
| 521 | zig_c_int overflow_int; | ||
| 522 | zig_i64 full_res = __addodi4(lhs, rhs, &overflow_int); | ||
| 523 | bool overflow = overflow_int != 0; | ||
| 524 | #endif | ||
| 525 | *res = zig_wrap_i64(full_res, bits); | ||
| 526 | return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); | ||
| 527 | } | ||
| 528 | |||
| 529 | static inline void zig_vaddo_i64(zig_u8 *ov, zig_i64 *res, int n, | ||
| 530 | const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) | ||
| 531 | { | ||
| 532 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits); | ||
| 533 | } | ||
| 534 | |||
| 535 | static inline bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { | ||
| 536 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | ||
| 537 | zig_u8 full_res; | ||
| 538 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | ||
| 539 | *res = zig_wrap_u8(full_res, bits); | ||
| 540 | return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); | ||
| 541 | #else | ||
| 542 | zig_u32 full_res; | ||
| 543 | bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); | ||
| 544 | *res = (zig_u8)full_res; | ||
| 545 | return overflow; | ||
| 546 | #endif | ||
| 547 | } | ||
| 548 | |||
| 549 | static inline void zig_vaddo_u8(zig_u8 *ov, zig_u8 *res, int n, | ||
| 550 | const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) | ||
| 551 | { | ||
| 552 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits); | ||
| 553 | } | ||
| 554 | |||
| 555 | static inline bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { | ||
| 556 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | ||
| 557 | zig_i8 full_res; | ||
| 558 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | ||
| 559 | *res = zig_wrap_i8(full_res, bits); | ||
| 560 | return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); | ||
| 561 | #else | ||
| 562 | zig_i32 full_res; | ||
| 563 | bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); | ||
| 564 | *res = (zig_i8)full_res; | ||
| 565 | return overflow; | ||
| 566 | #endif | ||
| 567 | } | ||
| 568 | |||
| 569 | static inline void zig_vaddo_i8(zig_u8 *ov, zig_i8 *res, int n, | ||
| 570 | const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) | ||
| 571 | { | ||
| 572 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits); | ||
| 573 | } | ||
| 574 | |||
| 575 | static inline bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { | ||
| 576 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | ||
| 577 | zig_u16 full_res; | ||
| 578 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | ||
| 579 | *res = zig_wrap_u16(full_res, bits); | ||
| 580 | return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); | ||
| 581 | #else | ||
| 582 | zig_u32 full_res; | ||
| 583 | bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); | ||
| 584 | *res = (zig_u16)full_res; | ||
| 585 | return overflow; | ||
| 586 | #endif | ||
| 587 | } | ||
| 588 | |||
| 589 | static inline void zig_vaddo_u16(zig_u8 *ov, zig_u16 *res, int n, | ||
| 590 | const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) | ||
| 591 | { | ||
| 592 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits); | ||
| 593 | } | ||
| 594 | |||
| 595 | static inline bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { | ||
| 596 | #if zig_has_builtin(add_overflow) || defined(zig_gnuc) | ||
| 597 | zig_i16 full_res; | ||
| 598 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | ||
| 599 | *res = zig_wrap_i16(full_res, bits); | ||
| 600 | return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); | ||
| 601 | #else | ||
| 602 | zig_i32 full_res; | ||
| 603 | bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); | ||
| 604 | *res = (zig_i16)full_res; | ||
| 605 | return overflow; | ||
| 606 | #endif | ||
| 607 | } | ||
| 608 | |||
| 609 | static inline void zig_vaddo_i16(zig_u8 *ov, zig_i16 *res, int n, | ||
| 610 | const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) | ||
| 611 | { | ||
| 612 | for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits); | ||
| 613 | } | ||
| 614 | |||
| 615 | static inline bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { | ||
| 616 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | ||
| 617 | zig_u32 full_res; | ||
| 618 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | ||
| 619 | *res = zig_wrap_u32(full_res, bits); | ||
| 620 | return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); | ||
| 621 | #else | ||
| 622 | *res = zig_subw_u32(lhs, rhs, bits); | ||
| 623 | return *res > lhs; | ||
| 624 | #endif | ||
| 625 | } | ||
| 626 | |||
| 627 | static inline void zig_vsubo_u32(zig_u8 *ov, zig_u32 *res, int n, | ||
| 628 | const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) | ||
| 629 | { | ||
| 630 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits); | ||
| 631 | } | ||
| 632 | |||
| 633 | zig_extern zig_i32 __subosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); | ||
| 634 | static inline bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { | ||
| 635 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | ||
| 636 | zig_i32 full_res; | ||
| 637 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | ||
| 638 | #else | ||
| 639 | zig_c_int overflow_int; | ||
| 640 | zig_i32 full_res = __subosi4(lhs, rhs, &overflow_int); | ||
| 641 | bool overflow = overflow_int != 0; | ||
| 642 | #endif | ||
| 643 | *res = zig_wrap_i32(full_res, bits); | ||
| 644 | return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); | ||
| 645 | } | ||
| 646 | |||
| 647 | static inline void zig_vsubo_i32(zig_u8 *ov, zig_i32 *res, int n, | ||
| 648 | const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) | ||
| 649 | { | ||
| 650 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits); | ||
| 651 | } | ||
| 652 | |||
| 653 | static inline bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { | ||
| 654 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | ||
| 655 | zig_u64 full_res; | ||
| 656 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | ||
| 657 | *res = zig_wrap_u64(full_res, bits); | ||
| 658 | return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); | ||
| 659 | #else | ||
| 660 | *res = zig_subw_u64(lhs, rhs, bits); | ||
| 661 | return *res > lhs; | ||
| 662 | #endif | ||
| 663 | } | ||
| 664 | |||
| 665 | static inline void zig_vsubo_u64(zig_u8 *ov, zig_u64 *res, int n, | ||
| 666 | const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) | ||
| 667 | { | ||
| 668 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits); | ||
| 669 | } | ||
| 670 | |||
| 671 | zig_extern zig_i64 __subodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); | ||
| 672 | static inline bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { | ||
| 673 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | ||
| 674 | zig_i64 full_res; | ||
| 675 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | ||
| 676 | #else | ||
| 677 | zig_c_int overflow_int; | ||
| 678 | zig_i64 full_res = __subodi4(lhs, rhs, &overflow_int); | ||
| 679 | bool overflow = overflow_int != 0; | ||
| 680 | #endif | ||
| 681 | *res = zig_wrap_i64(full_res, bits); | ||
| 682 | return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); | ||
| 683 | } | ||
| 684 | |||
| 685 | static inline void zig_vsubo_i64(zig_u8 *ov, zig_i64 *res, int n, | ||
| 686 | const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) | ||
| 687 | { | ||
| 688 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits); | ||
| 689 | } | ||
| 690 | |||
| 691 | static inline bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { | ||
| 692 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | ||
| 693 | zig_u8 full_res; | ||
| 694 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | ||
| 695 | *res = zig_wrap_u8(full_res, bits); | ||
| 696 | return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); | ||
| 697 | #else | ||
| 698 | zig_u32 full_res; | ||
| 699 | bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); | ||
| 700 | *res = (zig_u8)full_res; | ||
| 701 | return overflow; | ||
| 702 | #endif | ||
| 703 | } | ||
| 704 | |||
| 705 | static inline void zig_vsubo_u8(zig_u8 *ov, zig_u8 *res, int n, | ||
| 706 | const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) | ||
| 707 | { | ||
| 708 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits); | ||
| 709 | } | ||
| 710 | |||
| 711 | static inline bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { | ||
| 712 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | ||
| 713 | zig_i8 full_res; | ||
| 714 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | ||
| 715 | *res = zig_wrap_i8(full_res, bits); | ||
| 716 | return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); | ||
| 717 | #else | ||
| 718 | zig_i32 full_res; | ||
| 719 | bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); | ||
| 720 | *res = (zig_i8)full_res; | ||
| 721 | return overflow; | ||
| 722 | #endif | ||
| 723 | } | ||
| 724 | |||
| 725 | static inline void zig_vsubo_i8(zig_u8 *ov, zig_i8 *res, int n, | ||
| 726 | const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) | ||
| 727 | { | ||
| 728 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits); | ||
| 729 | } | ||
| 730 | |||
| 731 | |||
| 732 | static inline bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { | ||
| 733 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | ||
| 734 | zig_u16 full_res; | ||
| 735 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | ||
| 736 | *res = zig_wrap_u16(full_res, bits); | ||
| 737 | return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); | ||
| 738 | #else | ||
| 739 | zig_u32 full_res; | ||
| 740 | bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); | ||
| 741 | *res = (zig_u16)full_res; | ||
| 742 | return overflow; | ||
| 743 | #endif | ||
| 744 | } | ||
| 745 | |||
| 746 | static inline void zig_vsubo_u16(zig_u8 *ov, zig_u16 *res, int n, | ||
| 747 | const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) | ||
| 748 | { | ||
| 749 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits); | ||
| 750 | } | ||
| 751 | |||
| 752 | |||
| 753 | static inline bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { | ||
| 754 | #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) | ||
| 755 | zig_i16 full_res; | ||
| 756 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | ||
| 757 | *res = zig_wrap_i16(full_res, bits); | ||
| 758 | return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); | ||
| 759 | #else | ||
| 760 | zig_i32 full_res; | ||
| 761 | bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); | ||
| 762 | *res = (zig_i16)full_res; | ||
| 763 | return overflow; | ||
| 764 | #endif | ||
| 765 | } | ||
| 766 | |||
| 767 | static inline void zig_vsubo_i16(zig_u8 *ov, zig_i16 *res, int n, | ||
| 768 | const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) | ||
| 769 | { | ||
| 770 | for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits); | ||
| 771 | } | ||
| 772 | |||
| 773 | static inline bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { | ||
| 774 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | ||
| 775 | zig_u32 full_res; | ||
| 776 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | ||
| 777 | *res = zig_wrap_u32(full_res, bits); | ||
| 778 | return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); | ||
| 779 | #else | ||
| 780 | *res = zig_mulw_u32(lhs, rhs, bits); | ||
| 781 | return rhs != zig_as_u32(0) && lhs > zig_maxInt(u32, bits) / rhs; | ||
| 782 | #endif | ||
| 783 | } | ||
| 784 | |||
| 785 | static inline void zig_vmulo_u32(zig_u8 *ov, zig_u32 *res, int n, | ||
| 786 | const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) | ||
| 787 | { | ||
| 788 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits); | ||
| 789 | } | ||
| 790 | |||
| 791 | zig_extern zig_i32 __mulosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); | ||
| 792 | static inline bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { | ||
| 793 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | ||
| 794 | zig_i32 full_res; | ||
| 795 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | ||
| 796 | #else | ||
| 797 | zig_c_int overflow_int; | ||
| 798 | zig_i32 full_res = __mulosi4(lhs, rhs, &overflow_int); | ||
| 799 | bool overflow = overflow_int != 0; | ||
| 800 | #endif | ||
| 801 | *res = zig_wrap_i32(full_res, bits); | ||
| 802 | return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); | ||
| 803 | } | ||
| 804 | |||
| 805 | static inline void zig_vmulo_i32(zig_u8 *ov, zig_i32 *res, int n, | ||
| 806 | const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) | ||
| 807 | { | ||
| 808 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits); | ||
| 809 | } | ||
| 810 | |||
| 811 | static inline bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { | ||
| 812 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | ||
| 813 | zig_u64 full_res; | ||
| 814 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | ||
| 815 | *res = zig_wrap_u64(full_res, bits); | ||
| 816 | return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); | ||
| 817 | #else | ||
| 818 | *res = zig_mulw_u64(lhs, rhs, bits); | ||
| 819 | return rhs != zig_as_u64(0) && lhs > zig_maxInt(u64, bits) / rhs; | ||
| 820 | #endif | ||
| 821 | } | ||
| 822 | |||
| 823 | static inline void zig_vmulo_u64(zig_u8 *ov, zig_u64 *res, int n, | ||
| 824 | const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) | ||
| 825 | { | ||
| 826 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits); | ||
| 827 | } | ||
| 828 | |||
| 829 | zig_extern zig_i64 __mulodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); | ||
| 830 | static inline bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { | ||
| 831 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | ||
| 832 | zig_i64 full_res; | ||
| 833 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | ||
| 834 | #else | ||
| 835 | zig_c_int overflow_int; | ||
| 836 | zig_i64 full_res = __mulodi4(lhs, rhs, &overflow_int); | ||
| 837 | bool overflow = overflow_int != 0; | ||
| 838 | #endif | ||
| 839 | *res = zig_wrap_i64(full_res, bits); | ||
| 840 | return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); | ||
| 841 | } | ||
| 842 | |||
| 843 | static inline void zig_vmulo_i64(zig_u8 *ov, zig_i64 *res, int n, | ||
| 844 | const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) | ||
| 845 | { | ||
| 846 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits); | ||
| 847 | } | ||
| 848 | |||
| 849 | static inline bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { | ||
| 850 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | ||
| 851 | zig_u8 full_res; | ||
| 852 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | ||
| 853 | *res = zig_wrap_u8(full_res, bits); | ||
| 854 | return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); | ||
| 855 | #else | ||
| 856 | zig_u32 full_res; | ||
| 857 | bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); | ||
| 858 | *res = (zig_u8)full_res; | ||
| 859 | return overflow; | ||
| 860 | #endif | ||
| 861 | } | ||
| 862 | |||
| 863 | static inline void zig_vmulo_u8(zig_u8 *ov, zig_u8 *res, int n, | ||
| 864 | const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) | ||
| 865 | { | ||
| 866 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits); | ||
| 867 | } | ||
| 868 | |||
| 869 | static inline bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { | ||
| 870 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | ||
| 871 | zig_i8 full_res; | ||
| 872 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | ||
| 873 | *res = zig_wrap_i8(full_res, bits); | ||
| 874 | return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); | ||
| 875 | #else | ||
| 876 | zig_i32 full_res; | ||
| 877 | bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); | ||
| 878 | *res = (zig_i8)full_res; | ||
| 879 | return overflow; | ||
| 880 | #endif | ||
| 881 | } | ||
| 882 | |||
| 883 | static inline void zig_vmulo_i8(zig_u8 *ov, zig_i8 *res, int n, | ||
| 884 | const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) | ||
| 885 | { | ||
| 886 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits); | ||
| 887 | } | ||
| 888 | |||
| 889 | static inline bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { | ||
| 890 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | ||
| 891 | zig_u16 full_res; | ||
| 892 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | ||
| 893 | *res = zig_wrap_u16(full_res, bits); | ||
| 894 | return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); | ||
| 895 | #else | ||
| 896 | zig_u32 full_res; | ||
| 897 | bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); | ||
| 898 | *res = (zig_u16)full_res; | ||
| 899 | return overflow; | ||
| 900 | #endif | ||
| 901 | } | ||
| 902 | |||
| 903 | static inline void zig_vmulo_u16(zig_u8 *ov, zig_u16 *res, int n, | ||
| 904 | const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) | ||
| 905 | { | ||
| 906 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits); | ||
| 907 | } | ||
| 908 | |||
| 909 | static inline bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { | ||
| 910 | #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) | ||
| 911 | zig_i16 full_res; | ||
| 912 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | ||
| 913 | *res = zig_wrap_i16(full_res, bits); | ||
| 914 | return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); | ||
| 915 | #else | ||
| 916 | zig_i32 full_res; | ||
| 917 | bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); | ||
| 918 | *res = (zig_i16)full_res; | ||
| 919 | return overflow; | ||
| 920 | #endif | ||
| 921 | } | ||
| 922 | |||
| 923 | static inline void zig_vmulo_i16(zig_u8 *ov, zig_i16 *res, int n, | ||
| 924 | const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) | ||
| 925 | { | ||
| 926 | for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits); | ||
| 927 | } | ||
| 928 | |||
| 929 | #define zig_int_builtins(w) \ | ||
| 930 | static inline bool zig_shlo_u##w(zig_u##w *res, zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ | ||
| 931 | *res = zig_shlw_u##w(lhs, rhs, bits); \ | ||
| 932 | return lhs > zig_maxInt(u##w, bits) >> rhs; \ | ||
| 933 | } \ | ||
| 934 | \ | ||
| 935 | static inline bool zig_shlo_i##w(zig_i##w *res, zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ | ||
| 936 | *res = zig_shlw_i##w(lhs, rhs, bits); \ | ||
| 937 | zig_i##w mask = (zig_i##w)(zig_maxInt_u##w << (bits - rhs - 1)); \ | ||
| 938 | return (lhs & mask) != zig_as_i##w(0) && (lhs & mask) != mask; \ | ||
| 939 | } \ | ||
| 940 | \ | ||
| 941 | static inline zig_u##w zig_shls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | ||
| 942 | zig_u##w res; \ | ||
| 943 | if (rhs >= bits) return lhs != zig_as_u##w(0) ? zig_maxInt(u##w, bits) : lhs; \ | ||
| 944 | return zig_shlo_u##w(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u##w, bits) : res; \ | ||
| 945 | } \ | ||
| 946 | \ | ||
| 947 | static inline zig_i##w zig_shls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | ||
| 948 | zig_i##w res; \ | ||
| 949 | if ((zig_u##w)rhs < (zig_u##w)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \ | ||
| 950 | return lhs < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | ||
| 951 | } \ | ||
| 952 | \ | ||
| 953 | static inline zig_u##w zig_adds_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | ||
| 954 | zig_u##w res; \ | ||
| 955 | return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ | ||
| 956 | } \ | ||
| 957 | \ | ||
| 958 | static inline zig_i##w zig_adds_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | ||
| 959 | zig_i##w res; \ | ||
| 960 | if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \ | ||
| 961 | return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | ||
| 962 | } \ | ||
| 963 | \ | ||
| 964 | static inline zig_u##w zig_subs_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | ||
| 965 | zig_u##w res; \ | ||
| 966 | return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt(u##w, bits) : res; \ | ||
| 967 | } \ | ||
| 968 | \ | ||
| 969 | static inline zig_i##w zig_subs_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | ||
| 970 | zig_i##w res; \ | ||
| 971 | if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \ | ||
| 972 | return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | ||
| 973 | } \ | ||
| 974 | \ | ||
| 975 | static inline zig_u##w zig_muls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ | ||
| 976 | zig_u##w res; \ | ||
| 977 | return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ | ||
| 978 | } \ | ||
| 979 | \ | ||
| 980 | static inline zig_i##w zig_muls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ | ||
| 981 | zig_i##w res; \ | ||
| 982 | if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \ | ||
| 983 | return (lhs ^ rhs) < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ | ||
| 984 | } | ||
| 985 | zig_int_builtins(8) | ||
| 986 | zig_int_builtins(16) | ||
| 987 | zig_int_builtins(32) | ||
| 988 | zig_int_builtins(64) | ||
| 989 | |||
| 990 | #define zig_builtin8(name, val) __builtin_##name(val) | ||
| 991 | typedef zig_c_uint zig_Builtin8; | ||
| 992 | |||
| 993 | #define zig_builtin16(name, val) __builtin_##name(val) | ||
| 994 | typedef zig_c_uint zig_Builtin16; | ||
| 995 | |||
| 996 | #if INT_MIN <= INT32_MIN | ||
| 997 | #define zig_builtin32(name, val) __builtin_##name(val) | ||
| 998 | typedef zig_c_uint zig_Builtin32; | ||
| 999 | #elif LONG_MIN <= INT32_MIN | ||
| 1000 | #define zig_builtin32(name, val) __builtin_##name##l(val) | ||
| 1001 | typedef zig_c_ulong zig_Builtin32; | ||
| 1002 | #endif | ||
| 1003 | |||
| 1004 | #if INT_MIN <= INT64_MIN | ||
| 1005 | #define zig_builtin64(name, val) __builtin_##name(val) | ||
| 1006 | typedef zig_c_uint zig_Builtin64; | ||
| 1007 | #elif LONG_MIN <= INT64_MIN | ||
| 1008 | #define zig_builtin64(name, val) __builtin_##name##l(val) | ||
| 1009 | typedef zig_c_ulong zig_Builtin64; | ||
| 1010 | #elif LLONG_MIN <= INT64_MIN | ||
| 1011 | #define zig_builtin64(name, val) __builtin_##name##ll(val) | ||
| 1012 | typedef zig_c_ulonglong zig_Builtin64; | ||
| 1013 | #endif | ||
| 1014 | |||
| 1015 | static inline zig_u8 zig_byte_swap_u8(zig_u8 val, zig_u8 bits) { | ||
| 1016 | return zig_wrap_u8(val >> (8 - bits), bits); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | static inline zig_i8 zig_byte_swap_i8(zig_i8 val, zig_u8 bits) { | ||
| 1020 | return zig_wrap_i8((zig_i8)zig_byte_swap_u8((zig_u8)val, bits), bits); | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | static inline zig_u16 zig_byte_swap_u16(zig_u16 val, zig_u8 bits) { | ||
| 1024 | zig_u16 full_res; | ||
| 1025 | #if zig_has_builtin(bswap16) || defined(zig_gnuc) | ||
| 1026 | full_res = __builtin_bswap16(val); | ||
| 1027 | #else | ||
| 1028 | full_res = (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 0), 8) << 8 | | ||
| 1029 | (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 8), 8) >> 0; | ||
| 1030 | #endif | ||
| 1031 | return zig_wrap_u16(full_res >> (16 - bits), bits); | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | static inline zig_i16 zig_byte_swap_i16(zig_i16 val, zig_u8 bits) { | ||
| 1035 | return zig_wrap_i16((zig_i16)zig_byte_swap_u16((zig_u16)val, bits), bits); | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | static inline zig_u32 zig_byte_swap_u32(zig_u32 val, zig_u8 bits) { | ||
| 1039 | zig_u32 full_res; | ||
| 1040 | #if zig_has_builtin(bswap32) || defined(zig_gnuc) | ||
| 1041 | full_res = __builtin_bswap32(val); | ||
| 1042 | #else | ||
| 1043 | full_res = (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 0), 16) << 16 | | ||
| 1044 | (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 16), 16) >> 0; | ||
| 1045 | #endif | ||
| 1046 | return zig_wrap_u32(full_res >> (32 - bits), bits); | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | static inline zig_i32 zig_byte_swap_i32(zig_i32 val, zig_u8 bits) { | ||
| 1050 | return zig_wrap_i32((zig_i32)zig_byte_swap_u32((zig_u32)val, bits), bits); | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | static inline zig_u64 zig_byte_swap_u64(zig_u64 val, zig_u8 bits) { | ||
| 1054 | zig_u64 full_res; | ||
| 1055 | #if zig_has_builtin(bswap64) || defined(zig_gnuc) | ||
| 1056 | full_res = __builtin_bswap64(val); | ||
| 1057 | #else | ||
| 1058 | full_res = (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 0), 32) << 32 | | ||
| 1059 | (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 32), 32) >> 0; | ||
| 1060 | #endif | ||
| 1061 | return zig_wrap_u64(full_res >> (64 - bits), bits); | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | static inline zig_i64 zig_byte_swap_i64(zig_i64 val, zig_u8 bits) { | ||
| 1065 | return zig_wrap_i64((zig_i64)zig_byte_swap_u64((zig_u64)val, bits), bits); | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { | ||
| 1069 | zig_u8 full_res; | ||
| 1070 | #if zig_has_builtin(bitreverse8) | ||
| 1071 | full_res = __builtin_bitreverse8(val); | ||
| 1072 | #else | ||
| 1073 | static zig_u8 const lut[0x10] = { | ||
| 1074 | 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, | ||
| 1075 | 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf | ||
| 1076 | }; | ||
| 1077 | full_res = lut[val >> 0 & 0xF] << 4 | lut[val >> 4 & 0xF] << 0; | ||
| 1078 | #endif | ||
| 1079 | return zig_wrap_u8(full_res >> (8 - bits), bits); | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | static inline zig_i8 zig_bit_reverse_i8(zig_i8 val, zig_u8 bits) { | ||
| 1083 | return zig_wrap_i8((zig_i8)zig_bit_reverse_u8((zig_u8)val, bits), bits); | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | static inline zig_u16 zig_bit_reverse_u16(zig_u16 val, zig_u8 bits) { | ||
| 1087 | zig_u16 full_res; | ||
| 1088 | #if zig_has_builtin(bitreverse16) | ||
| 1089 | full_res = __builtin_bitreverse16(val); | ||
| 1090 | #else | ||
| 1091 | full_res = (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 0), 8) << 8 | | ||
| 1092 | (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 8), 8) >> 0; | ||
| 1093 | #endif | ||
| 1094 | return zig_wrap_u16(full_res >> (16 - bits), bits); | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | static inline zig_i16 zig_bit_reverse_i16(zig_i16 val, zig_u8 bits) { | ||
| 1098 | return zig_wrap_i16((zig_i16)zig_bit_reverse_u16((zig_u16)val, bits), bits); | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | static inline zig_u32 zig_bit_reverse_u32(zig_u32 val, zig_u8 bits) { | ||
| 1102 | zig_u32 full_res; | ||
| 1103 | #if zig_has_builtin(bitreverse32) | ||
| 1104 | full_res = __builtin_bitreverse32(val); | ||
| 1105 | #else | ||
| 1106 | full_res = (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 0), 16) << 16 | | ||
| 1107 | (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 16), 16) >> 0; | ||
| 1108 | #endif | ||
| 1109 | return zig_wrap_u32(full_res >> (32 - bits), bits); | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | static inline zig_i32 zig_bit_reverse_i32(zig_i32 val, zig_u8 bits) { | ||
| 1113 | return zig_wrap_i32((zig_i32)zig_bit_reverse_u32((zig_u32)val, bits), bits); | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | static inline zig_u64 zig_bit_reverse_u64(zig_u64 val, zig_u8 bits) { | ||
| 1117 | zig_u64 full_res; | ||
| 1118 | #if zig_has_builtin(bitreverse64) | ||
| 1119 | full_res = __builtin_bitreverse64(val); | ||
| 1120 | #else | ||
| 1121 | full_res = (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 0), 32) << 32 | | ||
| 1122 | (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 32), 32) >> 0; | ||
| 1123 | #endif | ||
| 1124 | return zig_wrap_u64(full_res >> (64 - bits), bits); | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { | ||
| 1128 | return zig_wrap_i64((zig_i64)zig_bit_reverse_u64((zig_u64)val, bits), bits); | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | #define zig_builtin_popcount_common(w) \ | ||
| 1132 | static inline zig_u8 zig_popcount_i##w(zig_i##w val, zig_u8 bits) { \ | ||
| 1133 | return zig_popcount_u##w((zig_u##w)val, bits); \ | ||
| 1134 | } | ||
| 1135 | #if zig_has_builtin(popcount) || defined(zig_gnuc) | ||
| 1136 | #define zig_builtin_popcount(w) \ | ||
| 1137 | static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ | ||
| 1138 | (void)bits; \ | ||
| 1139 | return zig_builtin##w(popcount, val); \ | ||
| 1140 | } \ | ||
| 1141 | \ | ||
| 1142 | zig_builtin_popcount_common(w) | ||
| 1143 | #else | ||
| 1144 | #define zig_builtin_popcount(w) \ | ||
| 1145 | static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ | ||
| 1146 | (void)bits; \ | ||
| 1147 | zig_u##w temp = val - ((val >> 1) & (zig_maxInt_u##w / 3)); \ | ||
| 1148 | temp = (temp & (zig_maxInt_u##w / 5)) + ((temp >> 2) & (zig_maxInt_u##w / 5)); \ | ||
| 1149 | temp = (temp + (temp >> 4)) & (zig_maxInt_u##w / 17); \ | ||
| 1150 | return temp * (zig_maxInt_u##w / 255) >> (w - 8); \ | ||
| 1151 | } \ | ||
| 1152 | \ | ||
| 1153 | zig_builtin_popcount_common(w) | ||
| 1154 | #endif | ||
| 1155 | zig_builtin_popcount(8) | ||
| 1156 | zig_builtin_popcount(16) | ||
| 1157 | zig_builtin_popcount(32) | ||
| 1158 | zig_builtin_popcount(64) | ||
| 1159 | |||
| 1160 | #define zig_builtin_ctz_common(w) \ | ||
| 1161 | static inline zig_u8 zig_ctz_i##w(zig_i##w val, zig_u8 bits) { \ | ||
| 1162 | return zig_ctz_u##w((zig_u##w)val, bits); \ | ||
| 1163 | } | ||
| 1164 | #if zig_has_builtin(ctz) || defined(zig_gnuc) | ||
| 1165 | #define zig_builtin_ctz(w) \ | ||
| 1166 | static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ | ||
| 1167 | if (val == 0) return bits; \ | ||
| 1168 | return zig_builtin##w(ctz, val); \ | ||
| 1169 | } \ | ||
| 1170 | \ | ||
| 1171 | zig_builtin_ctz_common(w) | ||
| 1172 | #else | ||
| 1173 | #define zig_builtin_ctz(w) \ | ||
| 1174 | static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ | ||
| 1175 | return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \ | ||
| 1176 | } \ | ||
| 1177 | \ | ||
| 1178 | zig_builtin_ctz_common(w) | ||
| 1179 | #endif | ||
| 1180 | zig_builtin_ctz(8) | ||
| 1181 | zig_builtin_ctz(16) | ||
| 1182 | zig_builtin_ctz(32) | ||
| 1183 | zig_builtin_ctz(64) | ||
| 1184 | |||
| 1185 | #define zig_builtin_clz_common(w) \ | ||
| 1186 | static inline zig_u8 zig_clz_i##w(zig_i##w val, zig_u8 bits) { \ | ||
| 1187 | return zig_clz_u##w((zig_u##w)val, bits); \ | ||
| 1188 | } | ||
| 1189 | #if zig_has_builtin(clz) || defined(zig_gnuc) | ||
| 1190 | #define zig_builtin_clz(w) \ | ||
| 1191 | static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ | ||
| 1192 | if (val == 0) return bits; \ | ||
| 1193 | return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \ | ||
| 1194 | } \ | ||
| 1195 | \ | ||
| 1196 | zig_builtin_clz_common(w) | ||
| 1197 | #else | ||
| 1198 | #define zig_builtin_clz(w) \ | ||
| 1199 | static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ | ||
| 1200 | return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \ | ||
| 1201 | } \ | ||
| 1202 | \ | ||
| 1203 | zig_builtin_clz_common(w) | ||
| 1204 | #endif | ||
| 1205 | zig_builtin_clz(8) | ||
| 1206 | zig_builtin_clz(16) | ||
| 1207 | zig_builtin_clz(32) | ||
| 1208 | zig_builtin_clz(64) | ||
| 1209 | |||
| 1210 | /* ======================== 128-bit Integer Routines ======================== */ | ||
| 1211 | |||
| 1212 | #if !defined(zig_has_int128) | ||
| 1213 | # if defined(__SIZEOF_INT128__) | ||
| 1214 | # define zig_has_int128 1 | ||
| 1215 | # else | ||
| 1216 | # define zig_has_int128 0 | ||
| 1217 | # endif | ||
| 1218 | #endif | ||
| 1219 | |||
| 1220 | #if zig_has_int128 | ||
| 1221 | |||
| 1222 | typedef unsigned __int128 zig_u128; | ||
| 1223 | typedef signed __int128 zig_i128; | ||
| 1224 | |||
| 1225 | #define zig_as_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) | ||
| 1226 | #define zig_as_i128(hi, lo) ((zig_i128)zig_as_u128(hi, lo)) | ||
| 1227 | #define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) | ||
| 1228 | #define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) | ||
| 1229 | #define zig_hi_u128(val) ((zig_u64)((val) >> 64)) | ||
| 1230 | #define zig_lo_u128(val) ((zig_u64)((val) >> 0)) | ||
| 1231 | #define zig_hi_i128(val) ((zig_i64)((val) >> 64)) | ||
| 1232 | #define zig_lo_i128(val) ((zig_u64)((val) >> 0)) | ||
| 1233 | #define zig_bitcast_u128(val) ((zig_u128)(val)) | ||
| 1234 | #define zig_bitcast_i128(val) ((zig_i128)(val)) | ||
| 1235 | #define zig_cmp_int128(Type) \ | ||
| 1236 | static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 1237 | return (lhs > rhs) - (lhs < rhs); \ | ||
| 1238 | } | ||
| 1239 | #define zig_bit_int128(Type, operation, operator) \ | ||
| 1240 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 1241 | return lhs operator rhs; \ | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | #else /* zig_has_int128 */ | ||
| 1245 | |||
| 1246 | #if __LITTLE_ENDIAN__ || _MSC_VER | ||
| 1247 | typedef struct { zig_align(16) zig_u64 lo; zig_u64 hi; } zig_u128; | ||
| 1248 | typedef struct { zig_align(16) zig_u64 lo; zig_i64 hi; } zig_i128; | ||
| 1249 | #else | ||
| 1250 | typedef struct { zig_align(16) zig_u64 hi; zig_u64 lo; } zig_u128; | ||
| 1251 | typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; | ||
| 1252 | #endif | ||
| 1253 | |||
| 1254 | #define zig_as_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) | ||
| 1255 | #define zig_as_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) | ||
| 1256 | |||
| 1257 | #if _MSC_VER | ||
| 1258 | #define zig_as_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } | ||
| 1259 | #define zig_as_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } | ||
| 1260 | #else | ||
| 1261 | #define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) | ||
| 1262 | #define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) | ||
| 1263 | #endif | ||
| 1264 | #define zig_hi_u128(val) ((val).hi) | ||
| 1265 | #define zig_lo_u128(val) ((val).lo) | ||
| 1266 | #define zig_hi_i128(val) ((val).hi) | ||
| 1267 | #define zig_lo_i128(val) ((val).lo) | ||
| 1268 | #define zig_bitcast_u128(val) zig_as_u128((zig_u64)(val).hi, (val).lo) | ||
| 1269 | #define zig_bitcast_i128(val) zig_as_i128((zig_i64)(val).hi, (val).lo) | ||
| 1270 | #define zig_cmp_int128(Type) \ | ||
| 1271 | static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 1272 | return (lhs.hi == rhs.hi) \ | ||
| 1273 | ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \ | ||
| 1274 | : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \ | ||
| 1275 | } | ||
| 1276 | #define zig_bit_int128(Type, operation, operator) \ | ||
| 1277 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 1278 | return (zig_##Type){ .hi = lhs.hi operator rhs.hi, .lo = lhs.lo operator rhs.lo }; \ | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | #endif /* zig_has_int128 */ | ||
| 1282 | |||
| 1283 | #define zig_minInt_u128 zig_as_u128(zig_minInt_u64, zig_minInt_u64) | ||
| 1284 | #define zig_maxInt_u128 zig_as_u128(zig_maxInt_u64, zig_maxInt_u64) | ||
| 1285 | #define zig_minInt_i128 zig_as_i128(zig_minInt_i64, zig_minInt_u64) | ||
| 1286 | #define zig_maxInt_i128 zig_as_i128(zig_maxInt_i64, zig_maxInt_u64) | ||
| 1287 | |||
| 1288 | zig_cmp_int128(u128) | ||
| 1289 | zig_cmp_int128(i128) | ||
| 1290 | |||
| 1291 | zig_bit_int128(u128, and, &) | ||
| 1292 | zig_bit_int128(i128, and, &) | ||
| 1293 | |||
| 1294 | zig_bit_int128(u128, or, |) | ||
| 1295 | zig_bit_int128(i128, or, |) | ||
| 1296 | |||
| 1297 | zig_bit_int128(u128, xor, ^) | ||
| 1298 | zig_bit_int128(i128, xor, ^) | ||
| 1299 | |||
| 1300 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs); | ||
| 1301 | |||
| 1302 | #if zig_has_int128 | ||
| 1303 | |||
| 1304 | static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { | ||
| 1305 | return val ^ zig_maxInt(u128, bits); | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { | ||
| 1309 | (void)bits; | ||
| 1310 | return ~val; | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { | ||
| 1314 | return lhs >> rhs; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { | ||
| 1318 | return lhs << rhs; | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { | ||
| 1322 | return lhs << rhs; | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1326 | return lhs + rhs; | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | static inline zig_i128 zig_add_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1330 | return lhs + rhs; | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | static inline zig_u128 zig_sub_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1334 | return lhs - rhs; | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1338 | return lhs - rhs; | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | static inline zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1342 | return lhs * rhs; | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | static inline zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1346 | return lhs * rhs; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | static inline zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1350 | return lhs / rhs; | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | static inline zig_i128 zig_div_trunc_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1354 | return lhs / rhs; | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | static inline zig_u128 zig_rem_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1358 | return lhs % rhs; | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1362 | return lhs % rhs; | ||
| 1363 | } | ||
| 1364 | |||
| 1365 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1366 | return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_as_i128(0, 0)); | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1370 | zig_i128 rem = zig_rem_i128(lhs, rhs); | ||
| 1371 | return rem + (((lhs ^ rhs) & rem) < zig_as_i128(0, 0) ? rhs : zig_as_i128(0, 0)); | ||
| 1372 | } | ||
| 1373 | |||
| 1374 | #else /* zig_has_int128 */ | ||
| 1375 | |||
| 1376 | static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { | ||
| 1377 | return (zig_u128){ .hi = zig_not_u64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { | ||
| 1381 | return (zig_i128){ .hi = zig_not_i64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { | ||
| 1385 | if (rhs == zig_as_u8(0)) return lhs; | ||
| 1386 | if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - zig_as_u8(64)) }; | ||
| 1387 | return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (zig_as_u8(64) - rhs) | lhs.lo >> rhs }; | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { | ||
| 1391 | if (rhs == zig_as_u8(0)) return lhs; | ||
| 1392 | if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; | ||
| 1393 | return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { | ||
| 1397 | if (rhs == zig_as_u8(0)) return lhs; | ||
| 1398 | if (rhs >= zig_as_u8(64)) return (zig_i128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; | ||
| 1399 | return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1403 | zig_u128 res; | ||
| 1404 | res.hi = lhs.hi + rhs.hi + zig_addo_u64(&res.lo, lhs.lo, rhs.lo, 64); | ||
| 1405 | return res; | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | static inline zig_i128 zig_add_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1409 | zig_i128 res; | ||
| 1410 | res.hi = lhs.hi + rhs.hi + zig_addo_u64(&res.lo, lhs.lo, rhs.lo, 64); | ||
| 1411 | return res; | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | static inline zig_u128 zig_sub_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1415 | zig_u128 res; | ||
| 1416 | res.hi = lhs.hi - rhs.hi - zig_subo_u64(&res.lo, lhs.lo, rhs.lo, 64); | ||
| 1417 | return res; | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1421 | zig_i128 res; | ||
| 1422 | res.hi = lhs.hi - rhs.hi - zig_subo_u64(&res.lo, lhs.lo, rhs.lo, 64); | ||
| 1423 | return res; | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs); | ||
| 1427 | static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1428 | return zig_bitcast_u128(__multi3(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs))); | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1432 | return __multi3(lhs, rhs); | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); | ||
| 1436 | static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1437 | return __udivti3(lhs, rhs); | ||
| 1438 | }; | ||
| 1439 | |||
| 1440 | zig_extern zig_i128 __divti3(zig_i128 lhs, zig_i128 rhs); | ||
| 1441 | static zig_i128 zig_div_trunc_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1442 | return __divti3(lhs, rhs); | ||
| 1443 | }; | ||
| 1444 | |||
| 1445 | zig_extern zig_u128 __umodti3(zig_u128 lhs, zig_u128 rhs); | ||
| 1446 | static zig_u128 zig_rem_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1447 | return __umodti3(lhs, rhs); | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | zig_extern zig_i128 __modti3(zig_i128 lhs, zig_i128 rhs); | ||
| 1451 | static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1452 | return __modti3(lhs, rhs); | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1456 | zig_i128 rem = zig_rem_i128(lhs, rhs); | ||
| 1457 | return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < zig_as_i64(0) ? rhs : zig_as_i128(0, 0))); | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1461 | return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_as_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_as_i128(0, 0)) < zig_as_i32(0))); | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | #endif /* zig_has_int128 */ | ||
| 1465 | |||
| 1466 | #define zig_div_floor_u128 zig_div_trunc_u128 | ||
| 1467 | #define zig_mod_u128 zig_rem_u128 | ||
| 1468 | |||
| 1469 | static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1470 | return zig_not_u128(zig_and_u128(lhs, rhs), 128); | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1474 | return zig_cmp_u128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | static inline zig_i128 zig_min_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1478 | return zig_cmp_i128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; | ||
| 1479 | } | ||
| 1480 | |||
| 1481 | static inline zig_u128 zig_max_u128(zig_u128 lhs, zig_u128 rhs) { | ||
| 1482 | return zig_cmp_u128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) { | ||
| 1486 | return zig_cmp_i128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) { | ||
| 1490 | zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_sub_i128(zig_as_i128(0, 0), zig_as_i128(0, 1)) : zig_as_i128(0, 0); | ||
| 1491 | return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask); | ||
| 1492 | } | ||
| 1493 | |||
| 1494 | static inline zig_u128 zig_wrap_u128(zig_u128 val, zig_u8 bits) { | ||
| 1495 | return zig_and_u128(val, zig_maxInt(u128, bits)); | ||
| 1496 | } | ||
| 1497 | |||
| 1498 | static inline zig_i128 zig_wrap_i128(zig_i128 val, zig_u8 bits) { | ||
| 1499 | return zig_as_i128(zig_wrap_i64(zig_hi_i128(val), bits - zig_as_u8(64)), zig_lo_i128(val)); | ||
| 1500 | } | ||
| 1501 | |||
| 1502 | static inline zig_u128 zig_shlw_u128(zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { | ||
| 1503 | return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits); | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | static inline zig_i128 zig_shlw_i128(zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { | ||
| 1507 | return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits); | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1511 | return zig_wrap_u128(zig_add_u128(lhs, rhs), bits); | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1515 | return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); | ||
| 1516 | } | ||
| 1517 | |||
| 1518 | static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1519 | return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits); | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1523 | return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1527 | return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1531 | return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | #if zig_has_int128 | ||
| 1535 | |||
| 1536 | static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1537 | #if zig_has_builtin(add_overflow) | ||
| 1538 | zig_u128 full_res; | ||
| 1539 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | ||
| 1540 | *res = zig_wrap_u128(full_res, bits); | ||
| 1541 | return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); | ||
| 1542 | #else | ||
| 1543 | *res = zig_addw_u128(lhs, rhs, bits); | ||
| 1544 | return *res < lhs; | ||
| 1545 | #endif | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | ||
| 1549 | static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1550 | #if zig_has_builtin(add_overflow) | ||
| 1551 | zig_i128 full_res; | ||
| 1552 | bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); | ||
| 1553 | #else | ||
| 1554 | zig_c_int overflow_int; | ||
| 1555 | zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); | ||
| 1556 | bool overflow = overflow_int != 0; | ||
| 1557 | #endif | ||
| 1558 | *res = zig_wrap_i128(full_res, bits); | ||
| 1559 | return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1563 | #if zig_has_builtin(sub_overflow) | ||
| 1564 | zig_u128 full_res; | ||
| 1565 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | ||
| 1566 | *res = zig_wrap_u128(full_res, bits); | ||
| 1567 | return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); | ||
| 1568 | #else | ||
| 1569 | *res = zig_subw_u128(lhs, rhs, bits); | ||
| 1570 | return *res > lhs; | ||
| 1571 | #endif | ||
| 1572 | } | ||
| 1573 | |||
| 1574 | zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | ||
| 1575 | static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1576 | #if zig_has_builtin(sub_overflow) | ||
| 1577 | zig_i128 full_res; | ||
| 1578 | bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); | ||
| 1579 | #else | ||
| 1580 | zig_c_int overflow_int; | ||
| 1581 | zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); | ||
| 1582 | bool overflow = overflow_int != 0; | ||
| 1583 | #endif | ||
| 1584 | *res = zig_wrap_i128(full_res, bits); | ||
| 1585 | return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); | ||
| 1586 | } | ||
| 1587 | |||
| 1588 | static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1589 | #if zig_has_builtin(mul_overflow) | ||
| 1590 | zig_u128 full_res; | ||
| 1591 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | ||
| 1592 | *res = zig_wrap_u128(full_res, bits); | ||
| 1593 | return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); | ||
| 1594 | #else | ||
| 1595 | *res = zig_mulw_u128(lhs, rhs, bits); | ||
| 1596 | return rhs != zig_as_u128(0, 0) && lhs > zig_maxInt(u128, bits) / rhs; | ||
| 1597 | #endif | ||
| 1598 | } | ||
| 1599 | |||
| 1600 | zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | ||
| 1601 | static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1602 | #if zig_has_builtin(mul_overflow) | ||
| 1603 | zig_i128 full_res; | ||
| 1604 | bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); | ||
| 1605 | #else | ||
| 1606 | zig_c_int overflow_int; | ||
| 1607 | zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); | ||
| 1608 | bool overflow = overflow_int != 0; | ||
| 1609 | #endif | ||
| 1610 | *res = zig_wrap_i128(full_res, bits); | ||
| 1611 | return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); | ||
| 1612 | } | ||
| 1613 | |||
| 1614 | #else /* zig_has_int128 */ | ||
| 1615 | |||
| 1616 | static inline bool zig_overflow_u128(bool overflow, zig_u128 full_res, zig_u8 bits) { | ||
| 1617 | return overflow || | ||
| 1618 | zig_cmp_u128(full_res, zig_minInt(u128, bits)) < zig_as_i32(0) || | ||
| 1619 | zig_cmp_u128(full_res, zig_maxInt(u128, bits)) > zig_as_i32(0); | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | static inline bool zig_overflow_i128(bool overflow, zig_i128 full_res, zig_u8 bits) { | ||
| 1623 | return overflow || | ||
| 1624 | zig_cmp_i128(full_res, zig_minInt(i128, bits)) < zig_as_i32(0) || | ||
| 1625 | zig_cmp_i128(full_res, zig_maxInt(i128, bits)) > zig_as_i32(0); | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1629 | zig_u128 full_res; | ||
| 1630 | bool overflow = | ||
| 1631 | zig_addo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | | ||
| 1632 | zig_addo_u64(&full_res.hi, full_res.hi, zig_addo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); | ||
| 1633 | *res = zig_wrap_u128(full_res, bits); | ||
| 1634 | return zig_overflow_u128(overflow, full_res, bits); | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | ||
| 1638 | static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1639 | zig_c_int overflow_int; | ||
| 1640 | zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); | ||
| 1641 | *res = zig_wrap_i128(full_res, bits); | ||
| 1642 | return zig_overflow_i128(overflow_int, full_res, bits); | ||
| 1643 | } | ||
| 1644 | |||
| 1645 | static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1646 | zig_u128 full_res; | ||
| 1647 | bool overflow = | ||
| 1648 | zig_subo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | | ||
| 1649 | zig_subo_u64(&full_res.hi, full_res.hi, zig_subo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); | ||
| 1650 | *res = zig_wrap_u128(full_res, bits); | ||
| 1651 | return zig_overflow_u128(overflow, full_res, bits); | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | ||
| 1655 | static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1656 | zig_c_int overflow_int; | ||
| 1657 | zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); | ||
| 1658 | *res = zig_wrap_i128(full_res, bits); | ||
| 1659 | return zig_overflow_i128(overflow_int, full_res, bits); | ||
| 1660 | } | ||
| 1661 | |||
| 1662 | static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1663 | *res = zig_mulw_u128(lhs, rhs, bits); | ||
| 1664 | return zig_cmp_u128(*res, zig_as_u128(0, 0)) != zig_as_i32(0) && | ||
| 1665 | zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); | ||
| 1669 | static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1670 | zig_c_int overflow_int; | ||
| 1671 | zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); | ||
| 1672 | *res = zig_wrap_i128(full_res, bits); | ||
| 1673 | return zig_overflow_i128(overflow_int, full_res, bits); | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | #endif /* zig_has_int128 */ | ||
| 1677 | |||
| 1678 | static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { | ||
| 1679 | *res = zig_shlw_u128(lhs, rhs, bits); | ||
| 1680 | return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); | ||
| 1681 | } | ||
| 1682 | |||
| 1683 | static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { | ||
| 1684 | *res = zig_shlw_i128(lhs, rhs, bits); | ||
| 1685 | zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - zig_as_u8(1))); | ||
| 1686 | return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != zig_as_i32(0) && | ||
| 1687 | zig_cmp_i128(zig_and_i128(lhs, mask), mask) != zig_as_i32(0); | ||
| 1688 | } | ||
| 1689 | |||
| 1690 | static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1691 | zig_u128 res; | ||
| 1692 | if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= zig_as_i32(0)) | ||
| 1693 | return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != zig_as_i32(0) ? zig_maxInt(u128, bits) : lhs; | ||
| 1694 | |||
| 1695 | #if zig_has_int128 | ||
| 1696 | return zig_shlo_u128(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u128, bits) : res; | ||
| 1697 | #else | ||
| 1698 | return zig_shlo_u128(&res, lhs, (zig_u8)rhs.lo, bits) ? zig_maxInt(u128, bits) : res; | ||
| 1699 | #endif | ||
| 1700 | } | ||
| 1701 | |||
| 1702 | static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1703 | zig_i128 res; | ||
| 1704 | if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_as_u128(0, bits)) < zig_as_i32(0) && !zig_shlo_i128(&res, lhs, zig_lo_i128(rhs), bits)) return res; | ||
| 1705 | return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1709 | zig_u128 res; | ||
| 1710 | return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; | ||
| 1711 | } | ||
| 1712 | |||
| 1713 | static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1714 | zig_i128 res; | ||
| 1715 | if (!zig_addo_i128(&res, lhs, rhs, bits)) return res; | ||
| 1716 | return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1720 | zig_u128 res; | ||
| 1721 | return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt(u128, bits) : res; | ||
| 1722 | } | ||
| 1723 | |||
| 1724 | static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1725 | zig_i128 res; | ||
| 1726 | if (!zig_subo_i128(&res, lhs, rhs, bits)) return res; | ||
| 1727 | return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | ||
| 1728 | } | ||
| 1729 | |||
| 1730 | static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { | ||
| 1731 | zig_u128 res; | ||
| 1732 | return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { | ||
| 1736 | zig_i128 res; | ||
| 1737 | if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res; | ||
| 1738 | return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); | ||
| 1739 | } | ||
| 1740 | |||
| 1741 | static inline zig_u8 zig_clz_u128(zig_u128 val, zig_u8 bits) { | ||
| 1742 | if (bits <= zig_as_u8(64)) return zig_clz_u64(zig_lo_u128(val), bits); | ||
| 1743 | if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - zig_as_u8(64)); | ||
| 1744 | return zig_clz_u64(zig_lo_u128(val), zig_as_u8(64)) + (bits - zig_as_u8(64)); | ||
| 1745 | } | ||
| 1746 | |||
| 1747 | static inline zig_u8 zig_clz_i128(zig_i128 val, zig_u8 bits) { | ||
| 1748 | return zig_clz_u128(zig_bitcast_u128(val), bits); | ||
| 1749 | } | ||
| 1750 | |||
| 1751 | static inline zig_u8 zig_ctz_u128(zig_u128 val, zig_u8 bits) { | ||
| 1752 | if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), zig_as_u8(64)); | ||
| 1753 | return zig_ctz_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + zig_as_u8(64); | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | static inline zig_u8 zig_ctz_i128(zig_i128 val, zig_u8 bits) { | ||
| 1757 | return zig_ctz_u128(zig_bitcast_u128(val), bits); | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | static inline zig_u8 zig_popcount_u128(zig_u128 val, zig_u8 bits) { | ||
| 1761 | return zig_popcount_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + | ||
| 1762 | zig_popcount_u64(zig_lo_u128(val), zig_as_u8(64)); | ||
| 1763 | } | ||
| 1764 | |||
| 1765 | static inline zig_u8 zig_popcount_i128(zig_i128 val, zig_u8 bits) { | ||
| 1766 | return zig_popcount_u128(zig_bitcast_u128(val), bits); | ||
| 1767 | } | ||
| 1768 | |||
| 1769 | static inline zig_u128 zig_byte_swap_u128(zig_u128 val, zig_u8 bits) { | ||
| 1770 | zig_u128 full_res; | ||
| 1771 | #if zig_has_builtin(bswap128) | ||
| 1772 | full_res = __builtin_bswap128(val); | ||
| 1773 | #else | ||
| 1774 | full_res = zig_as_u128(zig_byte_swap_u64(zig_lo_u128(val), zig_as_u8(64)), | ||
| 1775 | zig_byte_swap_u64(zig_hi_u128(val), zig_as_u8(64))); | ||
| 1776 | #endif | ||
| 1777 | return zig_shr_u128(full_res, zig_as_u8(128) - bits); | ||
| 1778 | } | ||
| 1779 | |||
| 1780 | static inline zig_i128 zig_byte_swap_i128(zig_i128 val, zig_u8 bits) { | ||
| 1781 | return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits)); | ||
| 1782 | } | ||
| 1783 | |||
| 1784 | static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, zig_u8 bits) { | ||
| 1785 | return zig_shr_u128(zig_as_u128(zig_bit_reverse_u64(zig_lo_u128(val), zig_as_u8(64)), | ||
| 1786 | zig_bit_reverse_u64(zig_hi_u128(val), zig_as_u8(64))), | ||
| 1787 | zig_as_u8(128) - bits); | ||
| 1788 | } | ||
| 1789 | |||
| 1790 | static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { | ||
| 1791 | return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | /* ========================= Floating Point Support ========================= */ | ||
| 1795 | |||
| 1796 | #if _MSC_VER | ||
| 1797 | #define zig_msvc_flt_inf ((double)(1e+300 * 1e+300)) | ||
| 1798 | #define zig_msvc_flt_inff ((float)(1e+300 * 1e+300)) | ||
| 1799 | #define zig_msvc_flt_infl ((long double)(1e+300 * 1e+300)) | ||
| 1800 | #define zig_msvc_flt_nan ((double)(zig_msvc_flt_inf * 0.f)) | ||
| 1801 | #define zig_msvc_flt_nanf ((float)(zig_msvc_flt_inf * 0.f)) | ||
| 1802 | #define zig_msvc_flt_nanl ((long double)(zig_msvc_flt_inf * 0.f)) | ||
| 1803 | #define __builtin_nan(str) nan(str) | ||
| 1804 | #define __builtin_nanf(str) nanf(str) | ||
| 1805 | #define __builtin_nanl(str) nanl(str) | ||
| 1806 | #define __builtin_inf() zig_msvc_flt_inf | ||
| 1807 | #define __builtin_inff() zig_msvc_flt_inff | ||
| 1808 | #define __builtin_infl() zig_msvc_flt_infl | ||
| 1809 | #endif | ||
| 1810 | |||
| 1811 | #if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc) | ||
| 1812 | #define zig_has_float_builtins 1 | ||
| 1813 | #define zig_as_special_f16(sign, name, arg, repr) sign zig_as_f16(__builtin_##name, )(arg) | ||
| 1814 | #define zig_as_special_f32(sign, name, arg, repr) sign zig_as_f32(__builtin_##name, )(arg) | ||
| 1815 | #define zig_as_special_f64(sign, name, arg, repr) sign zig_as_f64(__builtin_##name, )(arg) | ||
| 1816 | #define zig_as_special_f80(sign, name, arg, repr) sign zig_as_f80(__builtin_##name, )(arg) | ||
| 1817 | #define zig_as_special_f128(sign, name, arg, repr) sign zig_as_f128(__builtin_##name, )(arg) | ||
| 1818 | #define zig_as_special_c_longdouble(sign, name, arg, repr) sign zig_as_c_longdouble(__builtin_##name, )(arg) | ||
| 1819 | #else | ||
| 1820 | #define zig_has_float_builtins 0 | ||
| 1821 | #define zig_as_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) | ||
| 1822 | #define zig_as_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) | ||
| 1823 | #define zig_as_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) | ||
| 1824 | #define zig_as_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) | ||
| 1825 | #define zig_as_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) | ||
| 1826 | #define zig_as_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr) | ||
| 1827 | #endif | ||
| 1828 | |||
| 1829 | #define zig_has_f16 1 | ||
| 1830 | #define zig_bitSizeOf_f16 16 | ||
| 1831 | #define zig_libc_name_f16(name) __##name##h | ||
| 1832 | #define zig_as_special_constant_f16(sign, name, arg, repr) zig_as_special_f16(sign, name, arg, repr) | ||
| 1833 | #if FLT_MANT_DIG == 11 | ||
| 1834 | typedef float zig_f16; | ||
| 1835 | #define zig_as_f16(fp, repr) fp##f | ||
| 1836 | #elif DBL_MANT_DIG == 11 | ||
| 1837 | typedef double zig_f16; | ||
| 1838 | #define zig_as_f16(fp, repr) fp | ||
| 1839 | #elif LDBL_MANT_DIG == 11 | ||
| 1840 | #define zig_bitSizeOf_c_longdouble 16 | ||
| 1841 | typedef long double zig_f16; | ||
| 1842 | #define zig_as_f16(fp, repr) fp##l | ||
| 1843 | #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) | ||
| 1844 | typedef _Float16 zig_f16; | ||
| 1845 | #define zig_as_f16(fp, repr) fp##f16 | ||
| 1846 | #elif defined(__SIZEOF_FP16__) | ||
| 1847 | typedef __fp16 zig_f16; | ||
| 1848 | #define zig_as_f16(fp, repr) fp##f16 | ||
| 1849 | #else | ||
| 1850 | #undef zig_has_f16 | ||
| 1851 | #define zig_has_f16 0 | ||
| 1852 | #define zig_repr_f16 i16 | ||
| 1853 | typedef zig_i16 zig_f16; | ||
| 1854 | #define zig_as_f16(fp, repr) repr | ||
| 1855 | #undef zig_as_special_f16 | ||
| 1856 | #define zig_as_special_f16(sign, name, arg, repr) repr | ||
| 1857 | #undef zig_as_special_constant_f16 | ||
| 1858 | #define zig_as_special_constant_f16(sign, name, arg, repr) repr | ||
| 1859 | #endif | ||
| 1860 | |||
| 1861 | #define zig_has_f32 1 | ||
| 1862 | #define zig_bitSizeOf_f32 32 | ||
| 1863 | #define zig_libc_name_f32(name) name##f | ||
| 1864 | #if _MSC_VER | ||
| 1865 | #define zig_as_special_constant_f32(sign, name, arg, repr) sign zig_as_f32(zig_msvc_flt_##name, ) | ||
| 1866 | #else | ||
| 1867 | #define zig_as_special_constant_f32(sign, name, arg, repr) zig_as_special_f32(sign, name, arg, repr) | ||
| 1868 | #endif | ||
| 1869 | #if FLT_MANT_DIG == 24 | ||
| 1870 | typedef float zig_f32; | ||
| 1871 | #define zig_as_f32(fp, repr) fp##f | ||
| 1872 | #elif DBL_MANT_DIG == 24 | ||
| 1873 | typedef double zig_f32; | ||
| 1874 | #define zig_as_f32(fp, repr) fp | ||
| 1875 | #elif LDBL_MANT_DIG == 24 | ||
| 1876 | #define zig_bitSizeOf_c_longdouble 32 | ||
| 1877 | typedef long double zig_f32; | ||
| 1878 | #define zig_as_f32(fp, repr) fp##l | ||
| 1879 | #elif FLT32_MANT_DIG == 24 | ||
| 1880 | typedef _Float32 zig_f32; | ||
| 1881 | #define zig_as_f32(fp, repr) fp##f32 | ||
| 1882 | #else | ||
| 1883 | #undef zig_has_f32 | ||
| 1884 | #define zig_has_f32 0 | ||
| 1885 | #define zig_repr_f32 i32 | ||
| 1886 | typedef zig_i32 zig_f32; | ||
| 1887 | #define zig_as_f32(fp, repr) repr | ||
| 1888 | #undef zig_as_special_f32 | ||
| 1889 | #define zig_as_special_f32(sign, name, arg, repr) repr | ||
| 1890 | #undef zig_as_special_constant_f32 | ||
| 1891 | #define zig_as_special_constant_f32(sign, name, arg, repr) repr | ||
| 1892 | #endif | ||
| 1893 | |||
| 1894 | #define zig_has_f64 1 | ||
| 1895 | #define zig_bitSizeOf_f64 64 | ||
| 1896 | #define zig_libc_name_f64(name) name | ||
| 1897 | #if _MSC_VER | ||
| 1898 | #ifdef ZIG_TARGET_ABI_MSVC | ||
| 1899 | #define zig_bitSizeOf_c_longdouble 64 | ||
| 1900 | #endif | ||
| 1901 | #define zig_as_special_constant_f64(sign, name, arg, repr) sign zig_as_f64(zig_msvc_flt_##name, ) | ||
| 1902 | #else /* _MSC_VER */ | ||
| 1903 | #define zig_as_special_constant_f64(sign, name, arg, repr) zig_as_special_f64(sign, name, arg, repr) | ||
| 1904 | #endif /* _MSC_VER */ | ||
| 1905 | #if FLT_MANT_DIG == 53 | ||
| 1906 | typedef float zig_f64; | ||
| 1907 | #define zig_as_f64(fp, repr) fp##f | ||
| 1908 | #elif DBL_MANT_DIG == 53 | ||
| 1909 | typedef double zig_f64; | ||
| 1910 | #define zig_as_f64(fp, repr) fp | ||
| 1911 | #elif LDBL_MANT_DIG == 53 | ||
| 1912 | #define zig_bitSizeOf_c_longdouble 64 | ||
| 1913 | typedef long double zig_f64; | ||
| 1914 | #define zig_as_f64(fp, repr) fp##l | ||
| 1915 | #elif FLT64_MANT_DIG == 53 | ||
| 1916 | typedef _Float64 zig_f64; | ||
| 1917 | #define zig_as_f64(fp, repr) fp##f64 | ||
| 1918 | #elif FLT32X_MANT_DIG == 53 | ||
| 1919 | typedef _Float32x zig_f64; | ||
| 1920 | #define zig_as_f64(fp, repr) fp##f32x | ||
| 1921 | #else | ||
| 1922 | #undef zig_has_f64 | ||
| 1923 | #define zig_has_f64 0 | ||
| 1924 | #define zig_repr_f64 i64 | ||
| 1925 | typedef zig_i64 zig_f64; | ||
| 1926 | #define zig_as_f64(fp, repr) repr | ||
| 1927 | #undef zig_as_special_f64 | ||
| 1928 | #define zig_as_special_f64(sign, name, arg, repr) repr | ||
| 1929 | #undef zig_as_special_constant_f64 | ||
| 1930 | #define zig_as_special_constant_f64(sign, name, arg, repr) repr | ||
| 1931 | #endif | ||
| 1932 | |||
| 1933 | #define zig_has_f80 1 | ||
| 1934 | #define zig_bitSizeOf_f80 80 | ||
| 1935 | #define zig_libc_name_f80(name) __##name##x | ||
| 1936 | #define zig_as_special_constant_f80(sign, name, arg, repr) zig_as_special_f80(sign, name, arg, repr) | ||
| 1937 | #if FLT_MANT_DIG == 64 | ||
| 1938 | typedef float zig_f80; | ||
| 1939 | #define zig_as_f80(fp, repr) fp##f | ||
| 1940 | #elif DBL_MANT_DIG == 64 | ||
| 1941 | typedef double zig_f80; | ||
| 1942 | #define zig_as_f80(fp, repr) fp | ||
| 1943 | #elif LDBL_MANT_DIG == 64 | ||
| 1944 | #define zig_bitSizeOf_c_longdouble 80 | ||
| 1945 | typedef long double zig_f80; | ||
| 1946 | #define zig_as_f80(fp, repr) fp##l | ||
| 1947 | #elif FLT80_MANT_DIG == 64 | ||
| 1948 | typedef _Float80 zig_f80; | ||
| 1949 | #define zig_as_f80(fp, repr) fp##f80 | ||
| 1950 | #elif FLT64X_MANT_DIG == 64 | ||
| 1951 | typedef _Float64x zig_f80; | ||
| 1952 | #define zig_as_f80(fp, repr) fp##f64x | ||
| 1953 | #elif defined(__SIZEOF_FLOAT80__) | ||
| 1954 | typedef __float80 zig_f80; | ||
| 1955 | #define zig_as_f80(fp, repr) fp##l | ||
| 1956 | #else | ||
| 1957 | #undef zig_has_f80 | ||
| 1958 | #define zig_has_f80 0 | ||
| 1959 | #define zig_repr_f80 i128 | ||
| 1960 | typedef zig_i128 zig_f80; | ||
| 1961 | #define zig_as_f80(fp, repr) repr | ||
| 1962 | #undef zig_as_special_f80 | ||
| 1963 | #define zig_as_special_f80(sign, name, arg, repr) repr | ||
| 1964 | #undef zig_as_special_constant_f80 | ||
| 1965 | #define zig_as_special_constant_f80(sign, name, arg, repr) repr | ||
| 1966 | #endif | ||
| 1967 | |||
| 1968 | #define zig_has_f128 1 | ||
| 1969 | #define zig_bitSizeOf_f128 128 | ||
| 1970 | #define zig_libc_name_f128(name) name##q | ||
| 1971 | #define zig_as_special_constant_f128(sign, name, arg, repr) zig_as_special_f128(sign, name, arg, repr) | ||
| 1972 | #if FLT_MANT_DIG == 113 | ||
| 1973 | typedef float zig_f128; | ||
| 1974 | #define zig_as_f128(fp, repr) fp##f | ||
| 1975 | #elif DBL_MANT_DIG == 113 | ||
| 1976 | typedef double zig_f128; | ||
| 1977 | #define zig_as_f128(fp, repr) fp | ||
| 1978 | #elif LDBL_MANT_DIG == 113 | ||
| 1979 | #define zig_bitSizeOf_c_longdouble 128 | ||
| 1980 | typedef long double zig_f128; | ||
| 1981 | #define zig_as_f128(fp, repr) fp##l | ||
| 1982 | #elif FLT128_MANT_DIG == 113 | ||
| 1983 | typedef _Float128 zig_f128; | ||
| 1984 | #define zig_as_f128(fp, repr) fp##f128 | ||
| 1985 | #elif FLT64X_MANT_DIG == 113 | ||
| 1986 | typedef _Float64x zig_f128; | ||
| 1987 | #define zig_as_f128(fp, repr) fp##f64x | ||
| 1988 | #elif defined(__SIZEOF_FLOAT128__) | ||
| 1989 | typedef __float128 zig_f128; | ||
| 1990 | #define zig_as_f128(fp, repr) fp##q | ||
| 1991 | #undef zig_as_special_f128 | ||
| 1992 | #define zig_as_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg) | ||
| 1993 | #else | ||
| 1994 | #undef zig_has_f128 | ||
| 1995 | #define zig_has_f128 0 | ||
| 1996 | #define zig_repr_f128 i128 | ||
| 1997 | typedef zig_i128 zig_f128; | ||
| 1998 | #define zig_as_f128(fp, repr) repr | ||
| 1999 | #undef zig_as_special_f128 | ||
| 2000 | #define zig_as_special_f128(sign, name, arg, repr) repr | ||
| 2001 | #undef zig_as_special_constant_f128 | ||
| 2002 | #define zig_as_special_constant_f128(sign, name, arg, repr) repr | ||
| 2003 | #endif | ||
| 2004 | |||
| 2005 | #define zig_has_c_longdouble 1 | ||
| 2006 | |||
| 2007 | #ifdef ZIG_TARGET_ABI_MSVC | ||
| 2008 | #define zig_libc_name_c_longdouble(name) name | ||
| 2009 | #else | ||
| 2010 | #define zig_libc_name_c_longdouble(name) name##l | ||
| 2011 | #endif | ||
| 2012 | |||
| 2013 | #define zig_as_special_constant_c_longdouble(sign, name, arg, repr) zig_as_special_c_longdouble(sign, name, arg, repr) | ||
| 2014 | #ifdef zig_bitSizeOf_c_longdouble | ||
| 2015 | |||
| 2016 | #ifdef ZIG_TARGET_ABI_MSVC | ||
| 2017 | typedef double zig_c_longdouble; | ||
| 2018 | #undef zig_bitSizeOf_c_longdouble | ||
| 2019 | #define zig_bitSizeOf_c_longdouble 64 | ||
| 2020 | #define zig_as_c_longdouble(fp, repr) fp | ||
| 2021 | #else | ||
| 2022 | typedef long double zig_c_longdouble; | ||
| 2023 | #define zig_as_c_longdouble(fp, repr) fp##l | ||
| 2024 | #endif | ||
| 2025 | |||
| 2026 | #else /* zig_bitSizeOf_c_longdouble */ | ||
| 2027 | |||
| 2028 | #undef zig_has_c_longdouble | ||
| 2029 | #define zig_has_c_longdouble 0 | ||
| 2030 | #define zig_bitSizeOf_c_longdouble 80 | ||
| 2031 | #define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80 | ||
| 2032 | #define zig_repr_c_longdouble i128 | ||
| 2033 | typedef zig_i128 zig_c_longdouble; | ||
| 2034 | #define zig_as_c_longdouble(fp, repr) repr | ||
| 2035 | #undef zig_as_special_c_longdouble | ||
| 2036 | #define zig_as_special_c_longdouble(sign, name, arg, repr) repr | ||
| 2037 | #undef zig_as_special_constant_c_longdouble | ||
| 2038 | #define zig_as_special_constant_c_longdouble(sign, name, arg, repr) repr | ||
| 2039 | |||
| 2040 | #endif /* zig_bitSizeOf_c_longdouble */ | ||
| 2041 | |||
| 2042 | #if !zig_has_float_builtins | ||
| 2043 | #define zig_float_from_repr(Type, ReprType) \ | ||
| 2044 | static inline zig_##Type zig_float_from_repr_##Type(zig_##ReprType repr) { \ | ||
| 2045 | return *((zig_##Type*)&repr); \ | ||
| 2046 | } | ||
| 2047 | |||
| 2048 | zig_float_from_repr(f16, u16) | ||
| 2049 | zig_float_from_repr(f32, u32) | ||
| 2050 | zig_float_from_repr(f64, u64) | ||
| 2051 | zig_float_from_repr(f80, u128) | ||
| 2052 | zig_float_from_repr(f128, u128) | ||
| 2053 | #if zig_bitSizeOf_c_longdouble == 80 | ||
| 2054 | zig_float_from_repr(c_longdouble, u128) | ||
| 2055 | #else | ||
| 2056 | #define zig_expand_float_from_repr(Type, ReprType) zig_float_from_repr(Type, ReprType) | ||
| 2057 | zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_longdouble)) | ||
| 2058 | #endif | ||
| 2059 | #endif | ||
| 2060 | |||
| 2061 | #define zig_cast_f16 (zig_f16) | ||
| 2062 | #define zig_cast_f32 (zig_f32) | ||
| 2063 | #define zig_cast_f64 (zig_f64) | ||
| 2064 | |||
| 2065 | #if _MSC_VER && !zig_has_f128 | ||
| 2066 | #define zig_cast_f80 | ||
| 2067 | #define zig_cast_c_longdouble | ||
| 2068 | #define zig_cast_f128 | ||
| 2069 | #else | ||
| 2070 | #define zig_cast_f80 (zig_f80) | ||
| 2071 | #define zig_cast_c_longdouble (zig_c_longdouble) | ||
| 2072 | #define zig_cast_f128 (zig_f128) | ||
| 2073 | #endif | ||
| 2074 | |||
| 2075 | #define zig_convert_builtin(ResType, operation, ArgType, version) \ | ||
| 2076 | zig_extern zig_##ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ | ||
| 2077 | zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(zig_##ArgType); | ||
| 2078 | zig_convert_builtin(f16, trunc, f32, 2) | ||
| 2079 | zig_convert_builtin(f16, trunc, f64, 2) | ||
| 2080 | zig_convert_builtin(f16, trunc, f80, 2) | ||
| 2081 | zig_convert_builtin(f16, trunc, f128, 2) | ||
| 2082 | zig_convert_builtin(f32, extend, f16, 2) | ||
| 2083 | zig_convert_builtin(f32, trunc, f64, 2) | ||
| 2084 | zig_convert_builtin(f32, trunc, f80, 2) | ||
| 2085 | zig_convert_builtin(f32, trunc, f128, 2) | ||
| 2086 | zig_convert_builtin(f64, extend, f16, 2) | ||
| 2087 | zig_convert_builtin(f64, extend, f32, 2) | ||
| 2088 | zig_convert_builtin(f64, trunc, f80, 2) | ||
| 2089 | zig_convert_builtin(f64, trunc, f128, 2) | ||
| 2090 | zig_convert_builtin(f80, extend, f16, 2) | ||
| 2091 | zig_convert_builtin(f80, extend, f32, 2) | ||
| 2092 | zig_convert_builtin(f80, extend, f64, 2) | ||
| 2093 | zig_convert_builtin(f80, trunc, f128, 2) | ||
| 2094 | zig_convert_builtin(f128, extend, f16, 2) | ||
| 2095 | zig_convert_builtin(f128, extend, f32, 2) | ||
| 2096 | zig_convert_builtin(f128, extend, f64, 2) | ||
| 2097 | zig_convert_builtin(f128, extend, f80, 2) | ||
| 2098 | |||
| 2099 | #define zig_float_negate_builtin_0(Type) \ | ||
| 2100 | static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ | ||
| 2101 | return zig_expand_concat(zig_xor_, zig_repr_##Type)(arg, zig_expand_minInt(zig_repr_##Type, zig_bitSizeOf_##Type)); \ | ||
| 2102 | } | ||
| 2103 | #define zig_float_negate_builtin_1(Type) \ | ||
| 2104 | static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ | ||
| 2105 | return -arg; \ | ||
| 2106 | } | ||
| 2107 | |||
| 2108 | #define zig_float_less_builtin_0(Type, operation) \ | ||
| 2109 | zig_extern zig_i32 zig_expand_concat(zig_expand_concat(__##operation, \ | ||
| 2110 | zig_compiler_rt_abbrev_##Type), 2)(zig_##Type, zig_##Type); \ | ||
| 2111 | static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 2112 | return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 2)(lhs, rhs); \ | ||
| 2113 | } | ||
| 2114 | #define zig_float_less_builtin_1(Type, operation) \ | ||
| 2115 | static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 2116 | return (!(lhs <= rhs) - (lhs < rhs)); \ | ||
| 2117 | } | ||
| 2118 | |||
| 2119 | #define zig_float_greater_builtin_0(Type, operation) \ | ||
| 2120 | zig_float_less_builtin_0(Type, operation) | ||
| 2121 | #define zig_float_greater_builtin_1(Type, operation) \ | ||
| 2122 | static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 2123 | return ((lhs > rhs) - !(lhs >= rhs)); \ | ||
| 2124 | } | ||
| 2125 | |||
| 2126 | #define zig_float_binary_builtin_0(Type, operation, operator) \ | ||
| 2127 | zig_extern zig_##Type zig_expand_concat(zig_expand_concat(__##operation, \ | ||
| 2128 | zig_compiler_rt_abbrev_##Type), 3)(zig_##Type, zig_##Type); \ | ||
| 2129 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 2130 | return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 3)(lhs, rhs); \ | ||
| 2131 | } | ||
| 2132 | #define zig_float_binary_builtin_1(Type, operation, operator) \ | ||
| 2133 | static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 2134 | return lhs operator rhs; \ | ||
| 2135 | } | ||
| 2136 | |||
| 2137 | #define zig_float_builtins(Type) \ | ||
| 2138 | zig_convert_builtin(i32, fix, Type, ) \ | ||
| 2139 | zig_convert_builtin(u32, fixuns, Type, ) \ | ||
| 2140 | zig_convert_builtin(i64, fix, Type, ) \ | ||
| 2141 | zig_convert_builtin(u64, fixuns, Type, ) \ | ||
| 2142 | zig_convert_builtin(i128, fix, Type, ) \ | ||
| 2143 | zig_convert_builtin(u128, fixuns, Type, ) \ | ||
| 2144 | zig_convert_builtin(Type, float, i32, ) \ | ||
| 2145 | zig_convert_builtin(Type, floatun, u32, ) \ | ||
| 2146 | zig_convert_builtin(Type, float, i64, ) \ | ||
| 2147 | zig_convert_builtin(Type, floatun, u64, ) \ | ||
| 2148 | zig_convert_builtin(Type, float, i128, ) \ | ||
| 2149 | zig_convert_builtin(Type, floatun, u128, ) \ | ||
| 2150 | zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \ | ||
| 2151 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \ | ||
| 2152 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \ | ||
| 2153 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, eq) \ | ||
| 2154 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, lt) \ | ||
| 2155 | zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, le) \ | ||
| 2156 | zig_expand_concat(zig_float_greater_builtin_, zig_has_##Type)(Type, gt) \ | ||
| 2157 | zig_expand_concat(zig_float_greater_builtin_, zig_has_##Type)(Type, ge) \ | ||
| 2158 | zig_expand_concat(zig_float_binary_builtin_, zig_has_##Type)(Type, add, +) \ | ||
| 2159 | zig_expand_concat(zig_float_binary_builtin_, zig_has_##Type)(Type, sub, -) \ | ||
| 2160 | zig_expand_concat(zig_float_binary_builtin_, zig_has_##Type)(Type, mul, *) \ | ||
| 2161 | zig_expand_concat(zig_float_binary_builtin_, zig_has_##Type)(Type, div, /) \ | ||
| 2162 | zig_extern zig_##Type zig_libc_name_##Type(sqrt)(zig_##Type); \ | ||
| 2163 | zig_extern zig_##Type zig_libc_name_##Type(sin)(zig_##Type); \ | ||
| 2164 | zig_extern zig_##Type zig_libc_name_##Type(cos)(zig_##Type); \ | ||
| 2165 | zig_extern zig_##Type zig_libc_name_##Type(tan)(zig_##Type); \ | ||
| 2166 | zig_extern zig_##Type zig_libc_name_##Type(exp)(zig_##Type); \ | ||
| 2167 | zig_extern zig_##Type zig_libc_name_##Type(exp2)(zig_##Type); \ | ||
| 2168 | zig_extern zig_##Type zig_libc_name_##Type(log)(zig_##Type); \ | ||
| 2169 | zig_extern zig_##Type zig_libc_name_##Type(log2)(zig_##Type); \ | ||
| 2170 | zig_extern zig_##Type zig_libc_name_##Type(log10)(zig_##Type); \ | ||
| 2171 | zig_extern zig_##Type zig_libc_name_##Type(fabs)(zig_##Type); \ | ||
| 2172 | zig_extern zig_##Type zig_libc_name_##Type(floor)(zig_##Type); \ | ||
| 2173 | zig_extern zig_##Type zig_libc_name_##Type(ceil)(zig_##Type); \ | ||
| 2174 | zig_extern zig_##Type zig_libc_name_##Type(round)(zig_##Type); \ | ||
| 2175 | zig_extern zig_##Type zig_libc_name_##Type(trunc)(zig_##Type); \ | ||
| 2176 | zig_extern zig_##Type zig_libc_name_##Type(fmod)(zig_##Type, zig_##Type); \ | ||
| 2177 | zig_extern zig_##Type zig_libc_name_##Type(fmin)(zig_##Type, zig_##Type); \ | ||
| 2178 | zig_extern zig_##Type zig_libc_name_##Type(fmax)(zig_##Type, zig_##Type); \ | ||
| 2179 | zig_extern zig_##Type zig_libc_name_##Type(fma)(zig_##Type, zig_##Type, zig_##Type); \ | ||
| 2180 | \ | ||
| 2181 | static inline zig_##Type zig_div_trunc_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 2182 | return zig_libc_name_##Type(trunc)(zig_div_##Type(lhs, rhs)); \ | ||
| 2183 | } \ | ||
| 2184 | \ | ||
| 2185 | static inline zig_##Type zig_div_floor_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 2186 | return zig_libc_name_##Type(floor)(zig_div_##Type(lhs, rhs)); \ | ||
| 2187 | } \ | ||
| 2188 | \ | ||
| 2189 | static inline zig_##Type zig_mod_##Type(zig_##Type lhs, zig_##Type rhs) { \ | ||
| 2190 | return zig_sub_##Type(lhs, zig_mul_##Type(zig_div_floor_##Type(lhs, rhs), rhs)); \ | ||
| 2191 | } | ||
| 2192 | zig_float_builtins(f16) | ||
| 2193 | zig_float_builtins(f32) | ||
| 2194 | zig_float_builtins(f64) | ||
| 2195 | zig_float_builtins(f80) | ||
| 2196 | zig_float_builtins(f128) | ||
| 2197 | zig_float_builtins(c_longdouble) | ||
| 2198 | |||
| 2199 | #if _MSC_VER && (_M_IX86 || _M_X64) | ||
| 2200 | |||
| 2201 | // TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 | ||
| 2202 | |||
| 2203 | #define zig_msvc_atomics(Type, suffix) \ | ||
| 2204 | static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ | ||
| 2205 | zig_##Type comparand = *expected; \ | ||
| 2206 | zig_##Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \ | ||
| 2207 | bool exchanged = initial == comparand; \ | ||
| 2208 | if (!exchanged) { \ | ||
| 2209 | *expected = initial; \ | ||
| 2210 | } \ | ||
| 2211 | return exchanged; \ | ||
| 2212 | } \ | ||
| 2213 | static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2214 | return _InterlockedExchange##suffix(obj, value); \ | ||
| 2215 | } \ | ||
| 2216 | static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2217 | return _InterlockedExchangeAdd##suffix(obj, value); \ | ||
| 2218 | } \ | ||
| 2219 | static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2220 | bool success = false; \ | ||
| 2221 | zig_##Type new; \ | ||
| 2222 | zig_##Type prev; \ | ||
| 2223 | while (!success) { \ | ||
| 2224 | prev = *obj; \ | ||
| 2225 | new = prev - value; \ | ||
| 2226 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | ||
| 2227 | } \ | ||
| 2228 | return prev; \ | ||
| 2229 | } \ | ||
| 2230 | static inline zig_##Type zig_msvc_atomicrmw_or_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2231 | return _InterlockedOr##suffix(obj, value); \ | ||
| 2232 | } \ | ||
| 2233 | static inline zig_##Type zig_msvc_atomicrmw_xor_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2234 | return _InterlockedXor##suffix(obj, value); \ | ||
| 2235 | } \ | ||
| 2236 | static inline zig_##Type zig_msvc_atomicrmw_and_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2237 | return _InterlockedAnd##suffix(obj, value); \ | ||
| 2238 | } \ | ||
| 2239 | static inline zig_##Type zig_msvc_atomicrmw_nand_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2240 | bool success = false; \ | ||
| 2241 | zig_##Type new; \ | ||
| 2242 | zig_##Type prev; \ | ||
| 2243 | while (!success) { \ | ||
| 2244 | prev = *obj; \ | ||
| 2245 | new = ~(prev & value); \ | ||
| 2246 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | ||
| 2247 | } \ | ||
| 2248 | return prev; \ | ||
| 2249 | } \ | ||
| 2250 | static inline zig_##Type zig_msvc_atomicrmw_min_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2251 | bool success = false; \ | ||
| 2252 | zig_##Type new; \ | ||
| 2253 | zig_##Type prev; \ | ||
| 2254 | while (!success) { \ | ||
| 2255 | prev = *obj; \ | ||
| 2256 | new = value < prev ? value : prev; \ | ||
| 2257 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | ||
| 2258 | } \ | ||
| 2259 | return prev; \ | ||
| 2260 | } \ | ||
| 2261 | static inline zig_##Type zig_msvc_atomicrmw_max_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2262 | bool success = false; \ | ||
| 2263 | zig_##Type new; \ | ||
| 2264 | zig_##Type prev; \ | ||
| 2265 | while (!success) { \ | ||
| 2266 | prev = *obj; \ | ||
| 2267 | new = value > prev ? value : prev; \ | ||
| 2268 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | ||
| 2269 | } \ | ||
| 2270 | return prev; \ | ||
| 2271 | } \ | ||
| 2272 | static inline void zig_msvc_atomic_store_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2273 | _InterlockedExchange##suffix(obj, value); \ | ||
| 2274 | } \ | ||
| 2275 | static inline zig_##Type zig_msvc_atomic_load_##Type(zig_##Type volatile* obj) { \ | ||
| 2276 | return _InterlockedOr##suffix(obj, 0); \ | ||
| 2277 | } | ||
| 2278 | |||
| 2279 | zig_msvc_atomics(u8, 8) | ||
| 2280 | zig_msvc_atomics(i8, 8) | ||
| 2281 | zig_msvc_atomics(u16, 16) | ||
| 2282 | zig_msvc_atomics(i16, 16) | ||
| 2283 | zig_msvc_atomics(u32, ) | ||
| 2284 | zig_msvc_atomics(i32, ) | ||
| 2285 | |||
| 2286 | #if _M_X64 | ||
| 2287 | zig_msvc_atomics(u64, 64) | ||
| 2288 | zig_msvc_atomics(i64, 64) | ||
| 2289 | #endif | ||
| 2290 | |||
| 2291 | #define zig_msvc_flt_atomics(Type, ReprType, suffix) \ | ||
| 2292 | static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ | ||
| 2293 | zig_##ReprType comparand = *((zig_##ReprType*)expected); \ | ||
| 2294 | zig_##ReprType initial = _InterlockedCompareExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&desired), comparand); \ | ||
| 2295 | bool exchanged = initial == comparand; \ | ||
| 2296 | if (!exchanged) { \ | ||
| 2297 | *expected = *((zig_##Type*)&initial); \ | ||
| 2298 | } \ | ||
| 2299 | return exchanged; \ | ||
| 2300 | } \ | ||
| 2301 | static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2302 | zig_##ReprType initial = _InterlockedExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&value)); \ | ||
| 2303 | return *((zig_##Type*)&initial); \ | ||
| 2304 | } \ | ||
| 2305 | static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2306 | bool success = false; \ | ||
| 2307 | zig_##ReprType new; \ | ||
| 2308 | zig_##Type prev; \ | ||
| 2309 | while (!success) { \ | ||
| 2310 | prev = *obj; \ | ||
| 2311 | new = prev + value; \ | ||
| 2312 | success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ | ||
| 2313 | } \ | ||
| 2314 | return prev; \ | ||
| 2315 | } \ | ||
| 2316 | static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2317 | bool success = false; \ | ||
| 2318 | zig_##ReprType new; \ | ||
| 2319 | zig_##Type prev; \ | ||
| 2320 | while (!success) { \ | ||
| 2321 | prev = *obj; \ | ||
| 2322 | new = prev - value; \ | ||
| 2323 | success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ | ||
| 2324 | } \ | ||
| 2325 | return prev; \ | ||
| 2326 | } | ||
| 2327 | |||
| 2328 | zig_msvc_flt_atomics(f32, u32, ) | ||
| 2329 | #if _M_X64 | ||
| 2330 | zig_msvc_flt_atomics(f64, u64, 64) | ||
| 2331 | #endif | ||
| 2332 | |||
| 2333 | #if _M_IX86 | ||
| 2334 | static inline void zig_msvc_atomic_barrier() { | ||
| 2335 | zig_i32 barrier; | ||
| 2336 | __asm { | ||
| 2337 | xchg barrier, eax | ||
| 2338 | } | ||
| 2339 | } | ||
| 2340 | |||
| 2341 | static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, zig_u32* arg) { | ||
| 2342 | return _InterlockedExchangePointer(obj, arg); | ||
| 2343 | } | ||
| 2344 | |||
| 2345 | static inline void zig_msvc_atomic_store_p32(void** obj, zig_u32* arg) { | ||
| 2346 | _InterlockedExchangePointer(obj, arg); | ||
| 2347 | } | ||
| 2348 | |||
| 2349 | static inline void* zig_msvc_atomic_load_p32(void** obj) { | ||
| 2350 | return (void*)_InterlockedOr((void*)obj, 0); | ||
| 2351 | } | ||
| 2352 | |||
| 2353 | static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desired) { | ||
| 2354 | void* comparand = *expected; | ||
| 2355 | void* initial = _InterlockedCompareExchangePointer(obj, desired, comparand); | ||
| 2356 | bool exchanged = initial == comparand; | ||
| 2357 | if (!exchanged) { | ||
| 2358 | *expected = initial; | ||
| 2359 | } | ||
| 2360 | return exchanged; | ||
| 2361 | } | ||
| 2362 | #else /* _M_IX86 */ | ||
| 2363 | static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, zig_u64* arg) { | ||
| 2364 | return _InterlockedExchangePointer(obj, arg); | ||
| 2365 | } | ||
| 2366 | |||
| 2367 | static inline void zig_msvc_atomic_store_p64(void** obj, zig_u64* arg) { | ||
| 2368 | _InterlockedExchangePointer(obj, arg); | ||
| 2369 | } | ||
| 2370 | |||
| 2371 | static inline void* zig_msvc_atomic_load_p64(void** obj) { | ||
| 2372 | return (void*)_InterlockedOr64((void*)obj, 0); | ||
| 2373 | } | ||
| 2374 | |||
| 2375 | static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desired) { | ||
| 2376 | void* comparand = *expected; | ||
| 2377 | void* initial = _InterlockedCompareExchangePointer(obj, desired, comparand); | ||
| 2378 | bool exchanged = initial == comparand; | ||
| 2379 | if (!exchanged) { | ||
| 2380 | *expected = initial; | ||
| 2381 | } | ||
| 2382 | return exchanged; | ||
| 2383 | } | ||
| 2384 | |||
| 2385 | static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) { | ||
| 2386 | return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_i64*)expected); | ||
| 2387 | } | ||
| 2388 | |||
| 2389 | static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) { | ||
| 2390 | return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_u64*)expected); | ||
| 2391 | } | ||
| 2392 | |||
| 2393 | #define zig_msvc_atomics_128xchg(Type) \ | ||
| 2394 | static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2395 | bool success = false; \ | ||
| 2396 | zig_##Type prev; \ | ||
| 2397 | while (!success) { \ | ||
| 2398 | prev = *obj; \ | ||
| 2399 | success = zig_msvc_cmpxchg_##Type(obj, &prev, value); \ | ||
| 2400 | } \ | ||
| 2401 | return prev; \ | ||
| 2402 | } | ||
| 2403 | |||
| 2404 | zig_msvc_atomics_128xchg(u128) | ||
| 2405 | zig_msvc_atomics_128xchg(i128) | ||
| 2406 | |||
| 2407 | #define zig_msvc_atomics_128op(Type, operation) \ | ||
| 2408 | static inline zig_##Type zig_msvc_atomicrmw_##operation##_##Type(zig_##Type volatile* obj, zig_##Type value) { \ | ||
| 2409 | bool success = false; \ | ||
| 2410 | zig_##Type new; \ | ||
| 2411 | zig_##Type prev; \ | ||
| 2412 | while (!success) { \ | ||
| 2413 | prev = *obj; \ | ||
| 2414 | new = zig_##operation##_##Type(prev, value); \ | ||
| 2415 | success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ | ||
| 2416 | } \ | ||
| 2417 | return prev; \ | ||
| 2418 | } | ||
| 2419 | |||
| 2420 | zig_msvc_atomics_128op(u128, add) | ||
| 2421 | zig_msvc_atomics_128op(u128, sub) | ||
| 2422 | zig_msvc_atomics_128op(u128, or) | ||
| 2423 | zig_msvc_atomics_128op(u128, xor) | ||
| 2424 | zig_msvc_atomics_128op(u128, and) | ||
| 2425 | zig_msvc_atomics_128op(u128, nand) | ||
| 2426 | zig_msvc_atomics_128op(u128, min) | ||
| 2427 | zig_msvc_atomics_128op(u128, max) | ||
| 2428 | #endif /* _M_IX86 */ | ||
| 2429 | |||
| 2430 | #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ | ||
| 2431 | |||
| 2432 | /* ========================= Special Case Intrinsics ========================= */ | ||
| 2433 | |||
| 2434 | #if (_MSC_VER && _M_X64) || defined(__x86_64__) | ||
| 2435 | |||
| 2436 | static inline void* zig_x86_64_windows_teb(void) { | ||
| 2437 | #if _MSC_VER | ||
| 2438 | return (void*)__readgsqword(0x30); | ||
| 2439 | #else | ||
| 2440 | void* teb; | ||
| 2441 | __asm volatile(" movq %%gs:0x30, %[ptr]": [ptr]"=r"(teb)::); | ||
| 2442 | return teb; | ||
| 2443 | #endif | ||
| 2444 | } | ||
| 2445 | |||
| 2446 | #elif (_MSC_VER && _M_IX86) || defined(__i386__) || defined(__X86__) | ||
| 2447 | |||
| 2448 | static inline void* zig_x86_windows_teb(void) { | ||
| 2449 | #if _MSC_VER | ||
| 2450 | return (void*)__readfsdword(0x18); | ||
| 2451 | #else | ||
| 2452 | void* teb; | ||
| 2453 | __asm volatile(" movl %%fs:0x18, %[ptr]": [ptr]"=r"(teb)::); | ||
| 2454 | return teb; | ||
| 2455 | #endif | ||
| 2456 | } | ||
| 2457 | |||
| 2458 | #endif | ||
| 2459 | |||
| 2460 | #if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__) | ||
| 2461 | |||
| 2462 | static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, zig_u32* ebx, zig_u32* ecx, zig_u32* edx) { | ||
| 2463 | zig_u32 cpu_info[4]; | ||
| 2464 | #if _MSC_VER | ||
| 2465 | __cpuidex(cpu_info, leaf_id, subid); | ||
| 2466 | #else | ||
| 2467 | __cpuid_count(leaf_id, subid, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]); | ||
| 2468 | #endif | ||
| 2469 | *eax = cpu_info[0]; | ||
| 2470 | *ebx = cpu_info[1]; | ||
| 2471 | *ecx = cpu_info[2]; | ||
| 2472 | *edx = cpu_info[3]; | ||
| 2473 | } | ||
| 2474 | |||
| 2475 | static inline zig_u32 zig_x86_get_xcr0(void) { | ||
| 2476 | #if _MSC_VER | ||
| 2477 | return (zig_u32)_xgetbv(0); | ||
| 2478 | #else | ||
| 2479 | zig_u32 eax; | ||
| 2480 | zig_u32 edx; | ||
| 2481 | __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); | ||
| 2482 | return eax; | ||
| 2483 | #endif | ||
| 2484 | } | ||
| 2485 | |||
| 2486 | #endif | ||
stage1/zig1.wasm| Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ | |||
test/behavior/align.zig+5-1| ... | @@ -551,7 +551,11 @@ test "align(N) on functions" { | ... | @@ -551,7 +551,11 @@ test "align(N) on functions" { |
| 551 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 551 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 552 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 552 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 553 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 553 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 554 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO this is not supported on MSVC | 554 | |
| 555 | // This is not supported on MSVC | ||
| 556 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) { | ||
| 557 | return error.SkipZigTest; | ||
| 558 | } | ||
| 555 | 559 | ||
| 556 | // function alignment is a compile error on wasm32/wasm64 | 560 | // function alignment is a compile error on wasm32/wasm64 |
| 557 | if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; | 561 | if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; |
test/behavior/asm.zig+13-5| ... | @@ -7,6 +7,7 @@ const is_x86_64_linux = builtin.cpu.arch == .x86_64 and builtin.os.tag == .linux | ... | @@ -7,6 +7,7 @@ const is_x86_64_linux = builtin.cpu.arch == .x86_64 and builtin.os.tag == .linux |
| 7 | comptime { | 7 | comptime { |
| 8 | if (builtin.zig_backend != .stage2_arm and | 8 | if (builtin.zig_backend != .stage2_arm and |
| 9 | builtin.zig_backend != .stage2_aarch64 and | 9 | builtin.zig_backend != .stage2_aarch64 and |
| 10 | !(builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) and // MSVC doesn't support inline assembly | ||
| 10 | is_x86_64_linux) | 11 | is_x86_64_linux) |
| 11 | { | 12 | { |
| 12 | asm ( | 13 | asm ( |
| ... | @@ -23,7 +24,8 @@ test "module level assembly" { | ... | @@ -23,7 +24,8 @@ test "module level assembly" { |
| 23 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 24 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 24 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 25 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 25 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 26 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 26 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 27 | |
| 28 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly | ||
| 27 | 29 | ||
| 28 | if (is_x86_64_linux) { | 30 | if (is_x86_64_linux) { |
| 29 | try expect(this_is_my_alias() == 1234); | 31 | try expect(this_is_my_alias() == 1234); |
| ... | @@ -36,7 +38,8 @@ test "output constraint modifiers" { | ... | @@ -36,7 +38,8 @@ test "output constraint modifiers" { |
| 36 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 38 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 37 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 39 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 38 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 40 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 39 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 41 | |
| 42 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly | ||
| 40 | 43 | ||
| 41 | // This is only testing compilation. | 44 | // This is only testing compilation. |
| 42 | var a: u32 = 3; | 45 | var a: u32 = 3; |
| ... | @@ -58,7 +61,8 @@ test "alternative constraints" { | ... | @@ -58,7 +61,8 @@ test "alternative constraints" { |
| 58 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 61 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 59 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 62 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 60 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 63 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 61 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 64 | |
| 65 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly | ||
| 62 | 66 | ||
| 63 | // Make sure we allow commas as a separator for alternative constraints. | 67 | // Make sure we allow commas as a separator for alternative constraints. |
| 64 | var a: u32 = 3; | 68 | var a: u32 = 3; |
| ... | @@ -75,7 +79,8 @@ test "sized integer/float in asm input" { | ... | @@ -75,7 +79,8 @@ test "sized integer/float in asm input" { |
| 75 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 79 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 76 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 80 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 77 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 81 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 78 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 82 | |
| 83 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly | ||
| 79 | 84 | ||
| 80 | asm volatile ("" | 85 | asm volatile ("" |
| 81 | : | 86 | : |
| ... | @@ -125,7 +130,8 @@ test "struct/array/union types as input values" { | ... | @@ -125,7 +130,8 @@ test "struct/array/union types as input values" { |
| 125 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 130 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 126 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 131 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 127 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 132 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 128 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 133 | |
| 134 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly | ||
| 129 | 135 | ||
| 130 | asm volatile ("" | 136 | asm volatile ("" |
| 131 | : | 137 | : |
| ... | @@ -151,6 +157,8 @@ test "asm modifiers (AArch64)" { | ... | @@ -151,6 +157,8 @@ test "asm modifiers (AArch64)" { |
| 151 | if (builtin.target.cpu.arch != .aarch64) return error.SkipZigTest; | 157 | if (builtin.target.cpu.arch != .aarch64) return error.SkipZigTest; |
| 152 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 158 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 153 | 159 | ||
| 160 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly | ||
| 161 | |||
| 154 | var x: u32 = 15; | 162 | var x: u32 = 15; |
| 155 | const double = asm ("add %[ret:w], %[in:w], %[in:w]" | 163 | const double = asm ("add %[ret:w], %[in:w], %[in:w]" |
| 156 | : [ret] "=r" (-> u32), | 164 | : [ret] "=r" (-> u32), |
test/behavior/basic.zig+15| ... | @@ -387,6 +387,7 @@ fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { | ... | @@ -387,6 +387,7 @@ fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { |
| 387 | } | 387 | } |
| 388 | 388 | ||
| 389 | test "take address of parameter" { | 389 | test "take address of parameter" { |
| 390 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 390 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 391 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 391 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 392 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 392 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 393 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| ... | @@ -1143,3 +1144,17 @@ test "orelse coercion as function argument" { | ... | @@ -1143,3 +1144,17 @@ test "orelse coercion as function argument" { |
| 1143 | var foo = Container.init(optional orelse .{}); | 1144 | var foo = Container.init(optional orelse .{}); |
| 1144 | try expect(foo.a.?.start == -1); | 1145 | try expect(foo.a.?.start == -1); |
| 1145 | } | 1146 | } |
| 1147 | |||
| 1148 | test "runtime-known globals initialized with undefined" { | ||
| 1149 | const S = struct { | ||
| 1150 | var array: [10]u32 = [_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | ||
| 1151 | var vp: [*]u32 = undefined; | ||
| 1152 | var s: []u32 = undefined; | ||
| 1153 | }; | ||
| 1154 | |||
| 1155 | S.vp = &S.array; | ||
| 1156 | S.s = S.vp[0..5]; | ||
| 1157 | |||
| 1158 | try expect(S.s[0] == 1); | ||
| 1159 | try expect(S.s[4] == 5); | ||
| 1160 | } |
test/behavior/cast.zig+9| ... | @@ -1568,3 +1568,12 @@ test "@volatileCast without a result location" { | ... | @@ -1568,3 +1568,12 @@ test "@volatileCast without a result location" { |
| 1568 | try expect(@TypeOf(z) == *i32); | 1568 | try expect(@TypeOf(z) == *i32); |
| 1569 | try expect(z.* == 1234); | 1569 | try expect(z.* == 1234); |
| 1570 | } | 1570 | } |
| 1571 | |||
| 1572 | test "coercion from single-item pointer to @as to slice" { | ||
| 1573 | var x: u32 = 1; | ||
| 1574 | |||
| 1575 | // Why the following line gets a compile error? | ||
| 1576 | const t: []u32 = @as(*[1]u32, &x); | ||
| 1577 | |||
| 1578 | try expect(t[0] == 1); | ||
| 1579 | } |
test/behavior/const_slice_child.zig+1| ... | @@ -9,6 +9,7 @@ var argv: [*]const [*]const u8 = undefined; | ... | @@ -9,6 +9,7 @@ var argv: [*]const [*]const u8 = undefined; |
| 9 | test "const slice child" { | 9 | test "const slice child" { |
| 10 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 10 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 11 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 11 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 12 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 12 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 13 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 13 | 14 | ||
| 14 | const strs = [_][*]const u8{ "one", "two", "three" }; | 15 | const strs = [_][*]const u8{ "one", "two", "three" }; |
test/behavior/eval.zig+1| ... | @@ -1338,6 +1338,7 @@ test "lazy sizeof is resolved in division" { | ... | @@ -1338,6 +1338,7 @@ test "lazy sizeof is resolved in division" { |
| 1338 | 1338 | ||
| 1339 | test "lazy value is resolved as slice operand" { | 1339 | test "lazy value is resolved as slice operand" { |
| 1340 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1340 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1341 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 1341 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1342 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1342 | 1343 | ||
| 1343 | const A = struct { a: u32 }; | 1344 | const A = struct { a: u32 }; |
test/behavior/field_parent_ptr.zig+78| ... | @@ -44,3 +44,81 @@ fn testParentFieldPtrFirst(a: *const bool) !void { | ... | @@ -44,3 +44,81 @@ fn testParentFieldPtrFirst(a: *const bool) !void { |
| 44 | try expect(base == &foo); | 44 | try expect(base == &foo); |
| 45 | try expect(&base.a == a); | 45 | try expect(&base.a == a); |
| 46 | } | 46 | } |
| 47 | |||
| 48 | test "@fieldParentPtr untagged union" { | ||
| 49 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 50 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 51 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 52 | |||
| 53 | try testFieldParentPtrUnion(&bar.c); | ||
| 54 | comptime try testFieldParentPtrUnion(&bar.c); | ||
| 55 | } | ||
| 56 | |||
| 57 | const Bar = union(enum) { | ||
| 58 | a: bool, | ||
| 59 | b: f32, | ||
| 60 | c: i32, | ||
| 61 | d: i32, | ||
| 62 | }; | ||
| 63 | |||
| 64 | const bar = Bar{ .c = 42 }; | ||
| 65 | |||
| 66 | fn testFieldParentPtrUnion(c: *const i32) !void { | ||
| 67 | try expect(c == &bar.c); | ||
| 68 | |||
| 69 | const base = @fieldParentPtr(Bar, "c", c); | ||
| 70 | try expect(base == &bar); | ||
| 71 | try expect(&base.c == c); | ||
| 72 | } | ||
| 73 | |||
| 74 | test "@fieldParentPtr tagged union" { | ||
| 75 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 76 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 77 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 78 | |||
| 79 | try testFieldParentPtrTaggedUnion(&bar_tagged.c); | ||
| 80 | comptime try testFieldParentPtrTaggedUnion(&bar_tagged.c); | ||
| 81 | } | ||
| 82 | |||
| 83 | const BarTagged = union(enum) { | ||
| 84 | a: bool, | ||
| 85 | b: f32, | ||
| 86 | c: i32, | ||
| 87 | d: i32, | ||
| 88 | }; | ||
| 89 | |||
| 90 | const bar_tagged = BarTagged{ .c = 42 }; | ||
| 91 | |||
| 92 | fn testFieldParentPtrTaggedUnion(c: *const i32) !void { | ||
| 93 | try expect(c == &bar_tagged.c); | ||
| 94 | |||
| 95 | const base = @fieldParentPtr(BarTagged, "c", c); | ||
| 96 | try expect(base == &bar_tagged); | ||
| 97 | try expect(&base.c == c); | ||
| 98 | } | ||
| 99 | |||
| 100 | test "@fieldParentPtr extern union" { | ||
| 101 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 102 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 103 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 104 | |||
| 105 | try testFieldParentPtrExternUnion(&bar_extern.c); | ||
| 106 | comptime try testFieldParentPtrExternUnion(&bar_extern.c); | ||
| 107 | } | ||
| 108 | |||
| 109 | const BarExtern = extern union { | ||
| 110 | a: bool, | ||
| 111 | b: f32, | ||
| 112 | c: i32, | ||
| 113 | d: i32, | ||
| 114 | }; | ||
| 115 | |||
| 116 | const bar_extern = BarExtern{ .c = 42 }; | ||
| 117 | |||
| 118 | fn testFieldParentPtrExternUnion(c: *const i32) !void { | ||
| 119 | try expect(c == &bar_extern.c); | ||
| 120 | |||
| 121 | const base = @fieldParentPtr(BarExtern, "c", c); | ||
| 122 | try expect(base == &bar_extern); | ||
| 123 | try expect(&base.c == c); | ||
| 124 | } |
test/behavior/int_comparison_elision.zig-1| ... | @@ -13,7 +13,6 @@ test "int comparison elision" { | ... | @@ -13,7 +13,6 @@ test "int comparison elision" { |
| 13 | 13 | ||
| 14 | // TODO: support int types > 128 bits wide in other backends | 14 | // TODO: support int types > 128 bits wide in other backends |
| 15 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 15 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 16 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 17 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 16 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 18 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 17 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 19 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 18 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
test/behavior/lower_strlit_to_vector.zig-1| ... | @@ -7,7 +7,6 @@ test "strlit to vector" { | ... | @@ -7,7 +7,6 @@ test "strlit to vector" { |
| 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 8 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 9 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 10 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 11 | 10 | ||
| 12 | const strlit = "0123456789abcdef0123456789ABCDEF"; | 11 | const strlit = "0123456789abcdef0123456789ABCDEF"; |
| 13 | const vec_from_strlit: @Vector(32, u8) = strlit.*; | 12 | const vec_from_strlit: @Vector(32, u8) = strlit.*; |
test/behavior/math.zig-1| ... | @@ -1463,7 +1463,6 @@ test "vector integer addition" { | ... | @@ -1463,7 +1463,6 @@ test "vector integer addition" { |
| 1463 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1463 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1464 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1464 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1465 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1465 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1466 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1467 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1466 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1468 | 1467 | ||
| 1469 | const S = struct { | 1468 | const S = struct { |
test/behavior/packed-struct.zig-1| ... | @@ -603,7 +603,6 @@ test "packed struct initialized in bitcast" { | ... | @@ -603,7 +603,6 @@ test "packed struct initialized in bitcast" { |
| 603 | test "pointer to container level packed struct field" { | 603 | test "pointer to container level packed struct field" { |
| 604 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 604 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 605 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 605 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 606 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 607 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 606 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 608 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 607 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 609 | 608 |
test/behavior/pointers.zig-1| ... | @@ -507,7 +507,6 @@ test "ptrCast comptime known slice to C pointer" { | ... | @@ -507,7 +507,6 @@ test "ptrCast comptime known slice to C pointer" { |
| 507 | } | 507 | } |
| 508 | 508 | ||
| 509 | test "ptrToInt on a generic function" { | 509 | test "ptrToInt on a generic function" { |
| 510 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 511 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 510 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 512 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 511 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 513 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 512 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
test/behavior/struct.zig+35-1| ... | @@ -1330,7 +1330,6 @@ test "struct field init value is size of the struct" { | ... | @@ -1330,7 +1330,6 @@ test "struct field init value is size of the struct" { |
| 1330 | } | 1330 | } |
| 1331 | 1331 | ||
| 1332 | test "under-aligned struct field" { | 1332 | test "under-aligned struct field" { |
| 1333 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1334 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1333 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1335 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1334 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1336 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1335 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -1578,3 +1577,38 @@ test "directly initiating tuple like struct" { | ... | @@ -1578,3 +1577,38 @@ test "directly initiating tuple like struct" { |
| 1578 | const a = struct { u8 }{8}; | 1577 | const a = struct { u8 }{8}; |
| 1579 | try expect(a[0] == 8); | 1578 | try expect(a[0] == 8); |
| 1580 | } | 1579 | } |
| 1580 | |||
| 1581 | test "instantiate struct with comptime field" { | ||
| 1582 | { | ||
| 1583 | var things = struct { | ||
| 1584 | comptime foo: i8 = 1, | ||
| 1585 | }{}; | ||
| 1586 | |||
| 1587 | comptime std.debug.assert(things.foo == 1); | ||
| 1588 | } | ||
| 1589 | |||
| 1590 | { | ||
| 1591 | const T = struct { | ||
| 1592 | comptime foo: i8 = 1, | ||
| 1593 | }; | ||
| 1594 | var things = T{}; | ||
| 1595 | |||
| 1596 | comptime std.debug.assert(things.foo == 1); | ||
| 1597 | } | ||
| 1598 | |||
| 1599 | { | ||
| 1600 | var things: struct { | ||
| 1601 | comptime foo: i8 = 1, | ||
| 1602 | } = .{}; | ||
| 1603 | |||
| 1604 | comptime std.debug.assert(things.foo == 1); | ||
| 1605 | } | ||
| 1606 | |||
| 1607 | { | ||
| 1608 | var things: struct { | ||
| 1609 | comptime foo: i8 = 1, | ||
| 1610 | } = undefined; // Segmentation fault at address 0x0 | ||
| 1611 | |||
| 1612 | comptime std.debug.assert(things.foo == 1); | ||
| 1613 | } | ||
| 1614 | } |
test/behavior/type_info.zig+6| ... | @@ -603,3 +603,9 @@ test "@typeInfo decls ignore dependency loops" { | ... | @@ -603,3 +603,9 @@ test "@typeInfo decls ignore dependency loops" { |
| 603 | }; | 603 | }; |
| 604 | _ = S.foo; | 604 | _ = S.foo; |
| 605 | } | 605 | } |
| 606 | |||
| 607 | test "type info of tuple of string literal default value" { | ||
| 608 | const struct_field = @typeInfo(@TypeOf(.{"hi"})).Struct.fields[0]; | ||
| 609 | const value = @ptrCast(*align(1) const *const [2:0]u8, struct_field.default_value.?).*; | ||
| 610 | comptime std.debug.assert(value[0] == 'h'); | ||
| 611 | } |
test/behavior/var_args.zig+16-8| ... | @@ -96,10 +96,9 @@ fn doNothingWithFirstArg(args: anytype) void { | ... | @@ -96,10 +96,9 @@ fn doNothingWithFirstArg(args: anytype) void { |
| 96 | test "simple variadic function" { | 96 | test "simple variadic function" { |
| 97 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 97 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 98 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 98 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 99 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 100 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 99 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 101 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 100 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 102 | if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { | 101 | if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { |
| 103 | // https://github.com/ziglang/zig/issues/14096 | 102 | // https://github.com/ziglang/zig/issues/14096 |
| 104 | return error.SkipZigTest; | 103 | return error.SkipZigTest; |
| 105 | } | 104 | } |
| ... | @@ -112,6 +111,12 @@ test "simple variadic function" { | ... | @@ -112,6 +111,12 @@ test "simple variadic function" { |
| 112 | return @cVaArg(&ap, c_int); | 111 | return @cVaArg(&ap, c_int); |
| 113 | } | 112 | } |
| 114 | 113 | ||
| 114 | fn compatible(_: c_int, ...) callconv(.C) c_int { | ||
| 115 | var ap = @cVaStart(); | ||
| 116 | defer @cVaEnd(&ap); | ||
| 117 | return @cVaArg(&ap, c_int); | ||
| 118 | } | ||
| 119 | |||
| 115 | fn add(count: c_int, ...) callconv(.C) c_int { | 120 | fn add(count: c_int, ...) callconv(.C) c_int { |
| 116 | var ap = @cVaStart(); | 121 | var ap = @cVaStart(); |
| 117 | defer @cVaEnd(&ap); | 122 | defer @cVaEnd(&ap); |
| ... | @@ -124,8 +129,13 @@ test "simple variadic function" { | ... | @@ -124,8 +129,13 @@ test "simple variadic function" { |
| 124 | } | 129 | } |
| 125 | }; | 130 | }; |
| 126 | 131 | ||
| 127 | try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); | 132 | if (builtin.zig_backend != .stage2_c) { |
| 128 | try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); | 133 | // pre C23 doesn't support varargs without a preceding runtime arg. |
| 134 | try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); | ||
| 135 | try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); | ||
| 136 | } | ||
| 137 | try std.testing.expectEqual(@as(c_int, 0), S.compatible(undefined, @as(c_int, 0))); | ||
| 138 | try std.testing.expectEqual(@as(c_int, 1024), S.compatible(undefined, @as(c_int, 1024))); | ||
| 129 | try std.testing.expectEqual(@as(c_int, 0), S.add(0)); | 139 | try std.testing.expectEqual(@as(c_int, 0), S.add(0)); |
| 130 | try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1))); | 140 | try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1))); |
| 131 | try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2))); | 141 | try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2))); |
| ... | @@ -134,10 +144,9 @@ test "simple variadic function" { | ... | @@ -134,10 +144,9 @@ test "simple variadic function" { |
| 134 | test "variadic functions" { | 144 | test "variadic functions" { |
| 135 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 145 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 136 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 146 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 137 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 138 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 147 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 139 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 148 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 140 | if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { | 149 | if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { |
| 141 | // https://github.com/ziglang/zig/issues/14096 | 150 | // https://github.com/ziglang/zig/issues/14096 |
| 142 | return error.SkipZigTest; | 151 | return error.SkipZigTest; |
| 143 | } | 152 | } |
| ... | @@ -178,10 +187,9 @@ test "variadic functions" { | ... | @@ -178,10 +187,9 @@ test "variadic functions" { |
| 178 | test "copy VaList" { | 187 | test "copy VaList" { |
| 179 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 188 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 180 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 189 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 181 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 182 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 190 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 183 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 191 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 184 | if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { | 192 | if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { |
| 185 | // https://github.com/ziglang/zig/issues/14096 | 193 | // https://github.com/ziglang/zig/issues/14096 |
| 186 | return error.SkipZigTest; | 194 | return error.SkipZigTest; |
| 187 | } | 195 | } |
test/behavior/vector.zig+2-5| ... | @@ -75,7 +75,6 @@ test "vector int operators" { | ... | @@ -75,7 +75,6 @@ test "vector int operators" { |
| 75 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 75 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 76 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 76 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 77 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 77 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 78 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 79 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 78 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 80 | 79 | ||
| 81 | const S = struct { | 80 | const S = struct { |
| ... | @@ -178,7 +177,6 @@ test "tuple to vector" { | ... | @@ -178,7 +177,6 @@ test "tuple to vector" { |
| 178 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 177 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 179 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 178 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 180 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 179 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 181 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 182 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 180 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 183 | 181 | ||
| 184 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { | 182 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { |
| ... | @@ -943,7 +941,6 @@ test "multiplication-assignment operator with an array operand" { | ... | @@ -943,7 +941,6 @@ test "multiplication-assignment operator with an array operand" { |
| 943 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 941 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 944 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 942 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 945 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 943 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 946 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 947 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 944 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 948 | 945 | ||
| 949 | const S = struct { | 946 | const S = struct { |
| ... | @@ -1247,10 +1244,10 @@ test "array operands to shuffle are coerced to vectors" { | ... | @@ -1247,10 +1244,10 @@ test "array operands to shuffle are coerced to vectors" { |
| 1247 | test "load packed vector element" { | 1244 | test "load packed vector element" { |
| 1248 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1245 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1249 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1246 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1250 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1251 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1247 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1252 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1248 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1253 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1249 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1250 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1254 | 1251 | ||
| 1255 | var x: @Vector(2, u15) = .{ 1, 4 }; | 1252 | var x: @Vector(2, u15) = .{ 1, 4 }; |
| 1256 | try expect((&x[0]).* == 1); | 1253 | try expect((&x[0]).* == 1); |
| ... | @@ -1260,10 +1257,10 @@ test "load packed vector element" { | ... | @@ -1260,10 +1257,10 @@ test "load packed vector element" { |
| 1260 | test "store packed vector element" { | 1257 | test "store packed vector element" { |
| 1261 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1258 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1262 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1259 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1263 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1264 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1260 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1265 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 1261 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 1266 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1262 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1263 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1267 | 1264 | ||
| 1268 | var v = @Vector(4, u1){ 1, 1, 1, 1 }; | 1265 | var v = @Vector(4, u1){ 1, 1, 1, 1 }; |
| 1269 | try expectEqual(@Vector(4, u1){ 1, 1, 1, 1 }, v); | 1266 | try expectEqual(@Vector(4, u1){ 1, 1, 1, 1 }, v); |
test/cases/compile_errors/bitCast_same_size_but_bit_count_mismatch.zig+1-1| ... | @@ -7,4 +7,4 @@ export fn entry(byte: u8) void { | ... | @@ -7,4 +7,4 @@ export fn entry(byte: u8) void { |
| 7 | // backend=stage2 | 7 | // backend=stage2 |
| 8 | // target=native | 8 | // target=native |
| 9 | // | 9 | // |
| 10 | // :2:29: error: @bitCast size mismatch: destination type 'u7' has 7 bits but source type 'u8' has 8 bits | 10 | // :2:16: error: @bitCast size mismatch: destination type 'u7' has 7 bits but source type 'u8' has 8 bits |
test/cases/compile_errors/bitCast_with_different_sizes_inside_an_expression.zig+1-1| ... | @@ -7,4 +7,4 @@ export fn entry() void { | ... | @@ -7,4 +7,4 @@ export fn entry() void { |
| 7 | // backend=stage2 | 7 | // backend=stage2 |
| 8 | // target=native | 8 | // target=native |
| 9 | // | 9 | // |
| 10 | // :2:29: error: @bitCast size mismatch: destination type 'u8' has 8 bits but source type 'f32' has 32 bits | 10 | // :2:16: error: @bitCast size mismatch: destination type 'u8' has 8 bits but source type 'f32' has 32 bits |
test/cases/compile_errors/error_set_membership.zig created+31| ... | @@ -0,0 +1,31 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | const Error = error{InvalidCharacter}; | ||
| 4 | |||
| 5 | const Direction = enum { upside_down }; | ||
| 6 | |||
| 7 | const Barrrr = union(enum) { | ||
| 8 | float: f64, | ||
| 9 | direction: Direction, | ||
| 10 | }; | ||
| 11 | |||
| 12 | fn fooey(bar: std.meta.Tag(Barrrr), args: []const []const u8) !Barrrr { | ||
| 13 | return switch (bar) { | ||
| 14 | .float => .{ .float = try std.fmt.parseFloat(f64, args[0]) }, | ||
| 15 | .direction => if (std.mem.eql(u8, args[0], "upside_down")) | ||
| 16 | Barrrr{ .direction = .upside_down } | ||
| 17 | else | ||
| 18 | error.InvalidDirection, | ||
| 19 | }; | ||
| 20 | } | ||
| 21 | |||
| 22 | pub fn main() Error!void { | ||
| 23 | std.debug.print("{}", .{try fooey(.direction, &[_][]const u8{ "one", "two", "three" })}); | ||
| 24 | } | ||
| 25 | |||
| 26 | // error | ||
| 27 | // backend=llvm | ||
| 28 | // target=native | ||
| 29 | // | ||
| 30 | // :23:29: error: expected type 'error{InvalidCharacter}', found '@typeInfo(@typeInfo(@TypeOf(tmp.fooey)).Fn.return_type.?).ErrorUnion.error_set' | ||
| 31 | // :23:29: note: 'error.InvalidDirection' not a member of destination error set | ||
test/cases/compile_errors/fieldParentPtr-non_struct.zig+1-1| ... | @@ -7,4 +7,4 @@ export fn foo(a: *i32) *Foo { | ... | @@ -7,4 +7,4 @@ export fn foo(a: *i32) *Foo { |
| 7 | // backend=llvm | 7 | // backend=llvm |
| 8 | // target=native | 8 | // target=native |
| 9 | // | 9 | // |
| 10 | // :3:28: error: expected struct type, found 'i32' | 10 | // :3:28: error: expected struct or union type, found 'i32' |
test/compile_errors.zig+22| ... | @@ -288,4 +288,26 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -288,4 +288,26 @@ pub fn addCases(ctx: *TestContext) !void { |
| 288 | //, &[_][]const u8{ | 288 | //, &[_][]const u8{ |
| 289 | // "tmp.zig:4:1: error: unable to inline function", | 289 | // "tmp.zig:4:1: error: unable to inline function", |
| 290 | //}); | 290 | //}); |
| 291 | |||
| 292 | { | ||
| 293 | const case = ctx.obj("file in multiple modules", .{}); | ||
| 294 | case.backend = .stage2; | ||
| 295 | |||
| 296 | case.addSourceFile("foo.zig", | ||
| 297 | \\const dummy = 0; | ||
| 298 | ); | ||
| 299 | |||
| 300 | case.addDepModule("foo", "foo.zig"); | ||
| 301 | |||
| 302 | case.addError( | ||
| 303 | \\comptime { | ||
| 304 | \\ _ = @import("foo"); | ||
| 305 | \\ _ = @import("foo.zig"); | ||
| 306 | \\} | ||
| 307 | , &[_][]const u8{ | ||
| 308 | ":1:1: error: file exists in multiple modules", | ||
| 309 | ":1:1: note: root of module root.foo", | ||
| 310 | ":3:17: note: imported from module root", | ||
| 311 | }); | ||
| 312 | } | ||
| 291 | } | 313 | } |
test/link/macho/dead_strip_dylibs/build.zig+1-1| ... | @@ -29,7 +29,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -29,7 +29,7 @@ pub fn build(b: *std.Build) void { |
| 29 | exe.dead_strip_dylibs = true; | 29 | exe.dead_strip_dylibs = true; |
| 30 | 30 | ||
| 31 | const run_cmd = exe.run(); | 31 | const run_cmd = exe.run(); |
| 32 | run_cmd.expected_exit_code = @bitCast(u8, @as(i8, -2)); // should fail | 32 | run_cmd.expected_term = .{ .Exited = @bitCast(u8, @as(i8, -2)) }; // should fail |
| 33 | test_step.dependOn(&run_cmd.step); | 33 | test_step.dependOn(&run_cmd.step); |
| 34 | } | 34 | } |
| 35 | } | 35 | } |
test/src/compare_output.zig+1-1| ... | @@ -168,7 +168,7 @@ pub const CompareOutputContext = struct { | ... | @@ -168,7 +168,7 @@ pub const CompareOutputContext = struct { |
| 168 | run.addArgs(case.cli_args); | 168 | run.addArgs(case.cli_args); |
| 169 | run.stderr_action = .ignore; | 169 | run.stderr_action = .ignore; |
| 170 | run.stdout_action = .ignore; | 170 | run.stdout_action = .ignore; |
| 171 | run.expected_exit_code = 126; | 171 | run.expected_term = .{ .Exited = 126 }; |
| 172 | 172 | ||
| 173 | self.step.dependOn(&run.step); | 173 | self.step.dependOn(&run.step); |
| 174 | }, | 174 | }, |
test/stage2/cbe.zig+6-6| ... | @@ -959,7 +959,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -959,7 +959,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 959 | \\ _ = a; | 959 | \\ _ = a; |
| 960 | \\} | 960 | \\} |
| 961 | , | 961 | , |
| 962 | \\zig_extern void start(zig_u8 const a0); | 962 | \\zig_extern void start(uint8_t const a0); |
| 963 | \\ | 963 | \\ |
| 964 | ); | 964 | ); |
| 965 | ctx.h("header with multiple param function", linux_x64, | 965 | ctx.h("header with multiple param function", linux_x64, |
| ... | @@ -967,19 +967,19 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -967,19 +967,19 @@ pub fn addCases(ctx: *TestContext) !void { |
| 967 | \\ _ = a; _ = b; _ = c; | 967 | \\ _ = a; _ = b; _ = c; |
| 968 | \\} | 968 | \\} |
| 969 | , | 969 | , |
| 970 | \\zig_extern void start(zig_u8 const a0, zig_u8 const a1, zig_u8 const a2); | 970 | \\zig_extern void start(uint8_t const a0, uint8_t const a1, uint8_t const a2); |
| 971 | \\ | 971 | \\ |
| 972 | ); | 972 | ); |
| 973 | ctx.h("header with u32 param function", linux_x64, | 973 | ctx.h("header with u32 param function", linux_x64, |
| 974 | \\export fn start(a: u32) void{ _ = a; } | 974 | \\export fn start(a: u32) void{ _ = a; } |
| 975 | , | 975 | , |
| 976 | \\zig_extern void start(zig_u32 const a0); | 976 | \\zig_extern void start(uint32_t const a0); |
| 977 | \\ | 977 | \\ |
| 978 | ); | 978 | ); |
| 979 | ctx.h("header with usize param function", linux_x64, | 979 | ctx.h("header with usize param function", linux_x64, |
| 980 | \\export fn start(a: usize) void{ _ = a; } | 980 | \\export fn start(a: usize) void{ _ = a; } |
| 981 | , | 981 | , |
| 982 | \\zig_extern void start(zig_usize const a0); | 982 | \\zig_extern void start(uintptr_t const a0); |
| 983 | \\ | 983 | \\ |
| 984 | ); | 984 | ); |
| 985 | ctx.h("header with bool param function", linux_x64, | 985 | ctx.h("header with bool param function", linux_x64, |
| ... | @@ -993,7 +993,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -993,7 +993,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 993 | \\ unreachable; | 993 | \\ unreachable; |
| 994 | \\} | 994 | \\} |
| 995 | , | 995 | , |
| 996 | \\zig_extern zig_noreturn start(void); | 996 | \\zig_extern zig_noreturn void start(void); |
| 997 | \\ | 997 | \\ |
| 998 | ); | 998 | ); |
| 999 | ctx.h("header with multiple functions", linux_x64, | 999 | ctx.h("header with multiple functions", linux_x64, |
| ... | @@ -1009,7 +1009,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -1009,7 +1009,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1009 | ctx.h("header with multiple includes", linux_x64, | 1009 | ctx.h("header with multiple includes", linux_x64, |
| 1010 | \\export fn start(a: u32, b: usize) void{ _ = a; _ = b; } | 1010 | \\export fn start(a: u32, b: usize) void{ _ = a; _ = b; } |
| 1011 | , | 1011 | , |
| 1012 | \\zig_extern void start(zig_u32 const a0, zig_usize const a1); | 1012 | \\zig_extern void start(uint32_t const a0, uintptr_t const a1); |
| 1013 | \\ | 1013 | \\ |
| 1014 | ); | 1014 | ); |
| 1015 | } | 1015 | } |
test/stage2/nvptx.zig+1| ... | @@ -97,6 +97,7 @@ pub fn addPtx( | ... | @@ -97,6 +97,7 @@ pub fn addPtx( |
| 97 | .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), | 97 | .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), |
| 98 | .output_mode = .Obj, | 98 | .output_mode = .Obj, |
| 99 | .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), | 99 | .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), |
| 100 | .deps = std.ArrayList(TestContext.DepModule).init(ctx.cases.allocator), | ||
| 100 | .link_libc = false, | 101 | .link_libc = false, |
| 101 | .backend = .llvm, | 102 | .backend = .llvm, |
| 102 | // Bug in Debug mode | 103 | // Bug in Debug mode |
test/standalone.zig+9| ... | @@ -84,6 +84,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void { | ... | @@ -84,6 +84,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 84 | cases.addBuildFile("test/standalone/pie/build.zig", .{}); | 84 | cases.addBuildFile("test/standalone/pie/build.zig", .{}); |
| 85 | } | 85 | } |
| 86 | cases.addBuildFile("test/standalone/issue_12706/build.zig", .{}); | 86 | cases.addBuildFile("test/standalone/issue_12706/build.zig", .{}); |
| 87 | if (std.os.have_sigpipe_support) { | ||
| 88 | cases.addBuildFile("test/standalone/sigpipe/build.zig", .{}); | ||
| 89 | } | ||
| 87 | 90 | ||
| 88 | // Ensure the development tools are buildable. Alphabetically sorted. | 91 | // Ensure the development tools are buildable. Alphabetically sorted. |
| 89 | // No need to build `tools/spirv/grammar.zig`. | 92 | // No need to build `tools/spirv/grammar.zig`. |
| ... | @@ -104,4 +107,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { | ... | @@ -104,4 +107,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 104 | cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); | 107 | cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); |
| 105 | cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); | 108 | cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); |
| 106 | cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{}); | 109 | cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{}); |
| 110 | |||
| 111 | cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{}); | ||
| 112 | cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{}); | ||
| 113 | cases.addBuildFile("test/standalone/dep_recursive/build.zig", .{}); | ||
| 114 | cases.addBuildFile("test/standalone/dep_mutually_recursive/build.zig", .{}); | ||
| 115 | cases.addBuildFile("test/standalone/dep_shared_builtin/build.zig", .{}); | ||
| 107 | } | 116 | } |
test/standalone/dep_diamond/bar.zig created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | pub const shared = @import("shared"); | ||
test/standalone/dep_diamond/build.zig created+28| ... | @@ -0,0 +1,28 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const optimize = b.standardOptimizeOption(.{}); | ||
| 5 | |||
| 6 | const shared = b.createModule(.{ | ||
| 7 | .source_file = .{ .path = "shared.zig" }, | ||
| 8 | }); | ||
| 9 | |||
| 10 | const exe = b.addExecutable(.{ | ||
| 11 | .name = "test", | ||
| 12 | .root_source_file = .{ .path = "test.zig" }, | ||
| 13 | .optimize = optimize, | ||
| 14 | }); | ||
| 15 | exe.addAnonymousModule("foo", .{ | ||
| 16 | .source_file = .{ .path = "foo.zig" }, | ||
| 17 | .dependencies = &.{.{ .name = "shared", .module = shared }}, | ||
| 18 | }); | ||
| 19 | exe.addAnonymousModule("bar", .{ | ||
| 20 | .source_file = .{ .path = "bar.zig" }, | ||
| 21 | .dependencies = &.{.{ .name = "shared", .module = shared }}, | ||
| 22 | }); | ||
| 23 | |||
| 24 | const run = exe.run(); | ||
| 25 | |||
| 26 | const test_step = b.step("test", "Test it"); | ||
| 27 | test_step.dependOn(&run.step); | ||
| 28 | } | ||
test/standalone/dep_diamond/foo.zig created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | pub const shared = @import("shared"); | ||
test/standalone/dep_diamond/shared.zig created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | // (empty) | ||
test/standalone/dep_diamond/test.zig created+7| ... | @@ -0,0 +1,7 @@ | ||
| 1 | const foo = @import("foo"); | ||
| 2 | const bar = @import("bar"); | ||
| 3 | const assert = @import("std").debug.assert; | ||
| 4 | |||
| 5 | pub fn main() void { | ||
| 6 | assert(foo.shared == bar.shared); | ||
| 7 | } | ||
test/standalone/dep_mutually_recursive/bar.zig created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | const assert = @import("std").debug.assert; | ||
| 2 | pub const foo = @import("foo"); | ||
| 3 | |||
| 4 | comptime { | ||
| 5 | assert(foo.bar == @This()); | ||
| 6 | } | ||
test/standalone/dep_mutually_recursive/build.zig created+26| ... | @@ -0,0 +1,26 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const optimize = b.standardOptimizeOption(.{}); | ||
| 5 | |||
| 6 | const foo = b.createModule(.{ | ||
| 7 | .source_file = .{ .path = "foo.zig" }, | ||
| 8 | }); | ||
| 9 | const bar = b.createModule(.{ | ||
| 10 | .source_file = .{ .path = "bar.zig" }, | ||
| 11 | }); | ||
| 12 | foo.dependencies.put("bar", bar) catch @panic("OOM"); | ||
| 13 | bar.dependencies.put("foo", foo) catch @panic("OOM"); | ||
| 14 | |||
| 15 | const exe = b.addExecutable(.{ | ||
| 16 | .name = "test", | ||
| 17 | .root_source_file = .{ .path = "test.zig" }, | ||
| 18 | .optimize = optimize, | ||
| 19 | }); | ||
| 20 | exe.addModule("foo", foo); | ||
| 21 | |||
| 22 | const run = exe.run(); | ||
| 23 | |||
| 24 | const test_step = b.step("test", "Test it"); | ||
| 25 | test_step.dependOn(&run.step); | ||
| 26 | } | ||
test/standalone/dep_mutually_recursive/foo.zig created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | const assert = @import("std").debug.assert; | ||
| 2 | pub const bar = @import("bar"); | ||
| 3 | |||
| 4 | comptime { | ||
| 5 | assert(bar.foo == @This()); | ||
| 6 | } | ||
test/standalone/dep_mutually_recursive/test.zig created+7| ... | @@ -0,0 +1,7 @@ | ||
| 1 | const foo = @import("foo"); | ||
| 2 | const assert = @import("std").debug.assert; | ||
| 3 | |||
| 4 | pub fn main() void { | ||
| 5 | assert(foo == foo.bar.foo); | ||
| 6 | assert(foo == foo.bar.foo.bar.foo); | ||
| 7 | } | ||
test/standalone/dep_recursive/build.zig created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const optimize = b.standardOptimizeOption(.{}); | ||
| 5 | |||
| 6 | const foo = b.createModule(.{ | ||
| 7 | .source_file = .{ .path = "foo.zig" }, | ||
| 8 | }); | ||
| 9 | foo.dependencies.put("foo", foo) catch @panic("OOM"); | ||
| 10 | |||
| 11 | const exe = b.addExecutable(.{ | ||
| 12 | .name = "test", | ||
| 13 | .root_source_file = .{ .path = "test.zig" }, | ||
| 14 | .optimize = optimize, | ||
| 15 | }); | ||
| 16 | exe.addModule("foo", foo); | ||
| 17 | |||
| 18 | const run = exe.run(); | ||
| 19 | |||
| 20 | const test_step = b.step("test", "Test it"); | ||
| 21 | test_step.dependOn(&run.step); | ||
| 22 | } | ||
test/standalone/dep_recursive/foo.zig created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | const assert = @import("std").debug.assert; | ||
| 2 | pub const foo = @import("foo"); | ||
| 3 | |||
| 4 | comptime { | ||
| 5 | assert(foo == @This()); | ||
| 6 | } | ||
test/standalone/dep_recursive/test.zig created+8| ... | @@ -0,0 +1,8 @@ | ||
| 1 | const foo = @import("foo"); | ||
| 2 | const shared = @import("shared"); | ||
| 3 | const assert = @import("std").debug.assert; | ||
| 4 | |||
| 5 | pub fn main() void { | ||
| 6 | assert(foo == foo.foo); | ||
| 7 | assert(foo == foo.foo.foo); | ||
| 8 | } | ||
test/standalone/dep_shared_builtin/build.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const optimize = b.standardOptimizeOption(.{}); | ||
| 5 | |||
| 6 | const exe = b.addExecutable(.{ | ||
| 7 | .name = "test", | ||
| 8 | .root_source_file = .{ .path = "test.zig" }, | ||
| 9 | .optimize = optimize, | ||
| 10 | }); | ||
| 11 | exe.addAnonymousModule("foo", .{ | ||
| 12 | .source_file = .{ .path = "foo.zig" }, | ||
| 13 | }); | ||
| 14 | |||
| 15 | const run = exe.run(); | ||
| 16 | |||
| 17 | const test_step = b.step("test", "Test it"); | ||
| 18 | test_step.dependOn(&run.step); | ||
| 19 | } | ||
test/standalone/dep_shared_builtin/foo.zig created+3| ... | @@ -0,0 +1,3 @@ | ||
| 1 | pub const std = @import("std"); | ||
| 2 | pub const builtin = @import("builtin"); | ||
| 3 | pub const root = @import("root"); | ||
test/standalone/dep_shared_builtin/test.zig created+11| ... | @@ -0,0 +1,11 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const root = @import("root"); | ||
| 4 | const foo = @import("foo"); | ||
| 5 | |||
| 6 | pub fn main() void { | ||
| 7 | std.debug.assert(root == @This()); | ||
| 8 | std.debug.assert(std == foo.std); | ||
| 9 | std.debug.assert(builtin == foo.builtin); | ||
| 10 | std.debug.assert(root == foo.root); | ||
| 11 | } | ||
test/standalone/dep_triangle/build.zig created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const optimize = b.standardOptimizeOption(.{}); | ||
| 5 | |||
| 6 | const shared = b.createModule(.{ | ||
| 7 | .source_file = .{ .path = "shared.zig" }, | ||
| 8 | }); | ||
| 9 | |||
| 10 | const exe = b.addExecutable(.{ | ||
| 11 | .name = "test", | ||
| 12 | .root_source_file = .{ .path = "test.zig" }, | ||
| 13 | .optimize = optimize, | ||
| 14 | }); | ||
| 15 | exe.addAnonymousModule("foo", .{ | ||
| 16 | .source_file = .{ .path = "foo.zig" }, | ||
| 17 | .dependencies = &.{.{ .name = "shared", .module = shared }}, | ||
| 18 | }); | ||
| 19 | exe.addModule("shared", shared); | ||
| 20 | |||
| 21 | const run = exe.run(); | ||
| 22 | |||
| 23 | const test_step = b.step("test", "Test it"); | ||
| 24 | test_step.dependOn(&run.step); | ||
| 25 | } | ||
test/standalone/dep_triangle/foo.zig created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | pub const shared = @import("shared"); | ||
test/standalone/dep_triangle/shared.zig created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | // (empty) | ||
test/standalone/dep_triangle/test.zig created+7| ... | @@ -0,0 +1,7 @@ | ||
| 1 | const foo = @import("foo"); | ||
| 2 | const shared = @import("shared"); | ||
| 3 | const assert = @import("std").debug.assert; | ||
| 4 | |||
| 5 | pub fn main() void { | ||
| 6 | assert(foo.shared == shared); | ||
| 7 | } | ||
test/standalone/install_raw_hex/build.zig+10-5| ... | @@ -3,6 +3,9 @@ const std = @import("std"); | ... | @@ -3,6 +3,9 @@ const std = @import("std"); |
| 3 | const CheckFileStep = std.Build.CheckFileStep; | 3 | const CheckFileStep = std.Build.CheckFileStep; |
| 4 | 4 | ||
| 5 | pub fn build(b: *std.Build) void { | 5 | pub fn build(b: *std.Build) void { |
| 6 | const test_step = b.step("test", "Test the program"); | ||
| 7 | b.default_step.dependOn(test_step); | ||
| 8 | |||
| 6 | const target = .{ | 9 | const target = .{ |
| 7 | .cpu_arch = .thumb, | 10 | .cpu_arch = .thumb, |
| 8 | .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 }, | 11 | .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 }, |
| ... | @@ -19,12 +22,14 @@ pub fn build(b: *std.Build) void { | ... | @@ -19,12 +22,14 @@ pub fn build(b: *std.Build) void { |
| 19 | .optimize = optimize, | 22 | .optimize = optimize, |
| 20 | }); | 23 | }); |
| 21 | 24 | ||
| 22 | const test_step = b.step("test", "Test the program"); | 25 | const hex_step = elf.addObjCopy(.{ |
| 23 | b.default_step.dependOn(test_step); | 26 | .basename = "hello.hex", |
| 24 | 27 | }); | |
| 25 | const hex_step = b.addInstallRaw(elf, "hello.hex", .{}); | ||
| 26 | test_step.dependOn(&hex_step.step); | 28 | test_step.dependOn(&hex_step.step); |
| 27 | 29 | ||
| 28 | const explicit_format_hex_step = b.addInstallRaw(elf, "hello.foo", .{ .format = .hex }); | 30 | const explicit_format_hex_step = elf.addObjCopy(.{ |
| 31 | .basename = "hello.foo", | ||
| 32 | .format = .hex, | ||
| 33 | }); | ||
| 29 | test_step.dependOn(&explicit_format_hex_step.step); | 34 | test_step.dependOn(&explicit_format_hex_step.step); |
| 30 | } | 35 | } |
test/standalone/sigpipe/breakpipe.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const build_options = @import("build_options"); | ||
| 3 | |||
| 4 | pub const std_options = if (build_options.keep_sigpipe) struct { | ||
| 5 | pub const keep_sigpipe = true; | ||
| 6 | } else struct { | ||
| 7 | // intentionally not setting keep_sigpipe to ensure the default behavior is equivalent to false | ||
| 8 | }; | ||
| 9 | |||
| 10 | pub fn main() !void { | ||
| 11 | const pipe = try std.os.pipe(); | ||
| 12 | std.os.close(pipe[0]); | ||
| 13 | _ = std.os.write(pipe[1], "a") catch |err| switch (err) { | ||
| 14 | error.BrokenPipe => { | ||
| 15 | try std.io.getStdOut().writer().writeAll("BrokenPipe\n"); | ||
| 16 | std.os.exit(123); | ||
| 17 | }, | ||
| 18 | else => |e| return e, | ||
| 19 | }; | ||
| 20 | unreachable; | ||
| 21 | } | ||
test/standalone/sigpipe/build.zig created+35| ... | @@ -0,0 +1,35 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const os = std.os; | ||
| 3 | |||
| 4 | pub fn build(b: *std.build.Builder) !void { | ||
| 5 | const test_step = b.step("test", "Run the tests"); | ||
| 6 | |||
| 7 | // This test runs "breakpipe" as a child process and that process | ||
| 8 | // depends on inheriting a SIGPIPE disposition of "default". | ||
| 9 | { | ||
| 10 | const act = os.Sigaction{ | ||
| 11 | .handler = .{ .handler = os.SIG.DFL }, | ||
| 12 | .mask = os.empty_sigset, | ||
| 13 | .flags = 0, | ||
| 14 | }; | ||
| 15 | try os.sigaction(os.SIG.PIPE, &act, null); | ||
| 16 | } | ||
| 17 | |||
| 18 | for ([_]bool{ false, true }) |keep_sigpipe| { | ||
| 19 | const options = b.addOptions(); | ||
| 20 | options.addOption(bool, "keep_sigpipe", keep_sigpipe); | ||
| 21 | const exe = b.addExecutable(.{ | ||
| 22 | .name = "breakpipe", | ||
| 23 | .root_source_file = .{ .path = "breakpipe.zig" }, | ||
| 24 | }); | ||
| 25 | exe.addOptions("build_options", options); | ||
| 26 | const run = exe.run(); | ||
| 27 | if (keep_sigpipe) { | ||
| 28 | run.expected_term = .{ .Signal = std.os.SIG.PIPE }; | ||
| 29 | } else { | ||
| 30 | run.stdout_action = .{ .expect_exact = "BrokenPipe\n" }; | ||
| 31 | run.expected_term = .{ .Exited = 123 }; | ||
| 32 | } | ||
| 33 | test_step.dependOn(&run.step); | ||
| 34 | } | ||
| 35 | } | ||
test/tests.zig+8-8| ... | @@ -58,14 +58,14 @@ const test_targets = blk: { | ... | @@ -58,14 +58,14 @@ const test_targets = blk: { |
| 58 | .link_libc = true, | 58 | .link_libc = true, |
| 59 | .backend = .stage2_c, | 59 | .backend = .stage2_c, |
| 60 | }, | 60 | }, |
| 61 | //.{ | 61 | .{ |
| 62 | // .target = .{ | 62 | .target = .{ |
| 63 | // .cpu_arch = .x86_64, | 63 | .cpu_arch = .x86_64, |
| 64 | // .os_tag = .linux, | 64 | .os_tag = .linux, |
| 65 | // .abi = .none, | 65 | .abi = .none, |
| 66 | // }, | 66 | }, |
| 67 | // .backend = .stage2_x86_64, | 67 | .backend = .stage2_x86_64, |
| 68 | //}, | 68 | }, |
| 69 | .{ | 69 | .{ |
| 70 | .target = .{ | 70 | .target = .{ |
| 71 | .cpu_arch = .aarch64, | 71 | .cpu_arch = .aarch64, |